I’ve triple checked everything I could possibly think of at this point, and the referenced code matches up(to my knowledge), so let’s see if we can fix this.
When I open up any shop, all of the items and their correctly discounted prices show up. When I purchase an item with a discount, my wallet actually ends up reflecting a new balance as if I bought the item at full price. Same deal with selling; if I go to the selling menu, the properly discounted price shows up, but if I sell the item, my balance is back up to where it started as if I didn’t buy anything.
Basically, the discounts are only being reflected in the UI, and not in the actual transaction.
I am willing to share more code if needed.
Here are the scripts that I believe are in-play here:
Shop.cs:
using GameDevTV.Inventories;
using GameDevTV.Saving;
using RPG.Control;
using RPG.Inventories;
using RPG.Stats;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RPG.Shops
{
public class Shop : MonoBehaviour, IRaycastable, ISaveable
{
public event Action OnChange;
[SerializeField] string shopName;
[Range(0, 100)]
[SerializeField] float sellingPercentage = 80f;
[SerializeField] StockItemConfig[] stockConfig;
Dictionary<InventoryItem, int> transaction = new Dictionary<InventoryItem, int>();
Dictionary<InventoryItem, int> stockSold = new Dictionary<InventoryItem, int>();
Shopper currentShopper;
ItemCategory filter;
bool isBuyingMode = true;
[Serializable]
class StockItemConfig
{
public InventoryItem item;
public int initialStock;
[Range(0, 100)]
public float buyingDiscountPercent;
public int levelRequiredToUnlock;
}
public void SetShopper(Shopper shopper)
{
currentShopper = shopper;
}
public IEnumerable<ShopItem> GetFilteredItems()
{
foreach (ShopItem shopItem in GetAllItems())
{
InventoryItem item = shopItem.GetInventoryItem();
if (filter == ItemCategory.None || item.GetCategory() == filter)
{
yield return shopItem;
}
}
}
public IEnumerable<ShopItem> GetAllItems()
{
Dictionary<InventoryItem, float> prices = GetPrices();
Dictionary<InventoryItem, int> availabilities = GetAvailabilities();
foreach (InventoryItem item in availabilities.Keys)
{
if (availabilities[item] <= 0) continue;
float price = prices[item];
int quantityInTransation = 0;
transaction.TryGetValue(item, out quantityInTransation);
int availability = availabilities[item];
yield return new ShopItem(item, availability, price, quantityInTransation); //The ShopItem works as a middle man / interface between UI and the backend
}
}
public void SelectFilter(ItemCategory category)
{
filter = category;
OnChange?.Invoke();
}
public ItemCategory GetFilter()
{
return filter;
}
public void ConfirmTransaction()
{
Inventory shopperInventory = currentShopper.GetComponent<Inventory>();
Wallet wallet = currentShopper.GetComponent<Wallet>();
if (shopperInventory == null || wallet == null) return;
foreach (ShopItem shopItem in GetAllItems())
{
InventoryItem item = shopItem.GetInventoryItem();
int quantityInTransation = shopItem.GetQuantity();
float price = item.GetPrice();
for (int i = 0; i < quantityInTransation; i++) //This for loop goes through each item within the transaction and adds it one by one so non-stackables wont be stacked.
{
if (isBuyingMode)
{
BuyItem(shopperInventory, wallet, item, price);
}
else
{
SellItem(shopperInventory, wallet, item, price);
}
}
}
OnChange?.Invoke();
}
public void SelectMode(bool isBuying)
{
isBuyingMode = isBuying;
OnChange?.Invoke();
}
public bool IsBuyingMode()
{
return isBuyingMode;
}
public string GetShopName()
{
return shopName;
}
public bool CanTransact()
{
if (IsTransactionEmpty()) return false;
if (!HasSufficientFunds()) return false;
if (!HasInventorySpace()) return false;
return true;
}
public float TransactionTotal()
{
float transactionTotal = 0;
foreach (var item in GetAllItems())
{
transactionTotal += item.GetPrice() * item.GetQuantity();
}
return transactionTotal;
}
public void AddToTransaction(InventoryItem item, int quantity)
{
if (!transaction.ContainsKey(item))
{
transaction[item] = 0;
}
var availabilities = GetAvailabilities();
int availability = availabilities[item];
if (transaction[item] + quantity > availability)
{
transaction[item] = availability;
}
else
{
transaction[item] += quantity;
}
if (transaction[item] <= 0)
{
transaction.Remove(item);
}
OnChange?.Invoke();
}
public CursorType GetCursorType()
{
return CursorType.Shop;
}
public bool HandleRaycast(PlayerController callingController)
{
if (Input.GetMouseButtonDown(0))
{
callingController.GetComponent<Shopper>().SetActiveShop(this);
}
return true;
}
public bool HasSufficientFunds()
{
if (!isBuyingMode) return true;
Wallet playerWallet = currentShopper.GetComponent<Wallet>();
if (playerWallet == null)
{
print("playerWallet is null for whatever reason");
return false;
}
return playerWallet.GetBalance() >= TransactionTotal();
}
public bool HasInventorySpace()
{
if (!isBuyingMode) return true;
Inventory playerInventory = currentShopper.GetComponent<Inventory>();
if (playerInventory == null)
{
print("Playerinventory is null");
return false;
}
List<InventoryItem> flatItems = new List<InventoryItem>();
foreach (var shopItem in GetAllItems())
{
InventoryItem item = shopItem.GetInventoryItem();
int quantityInTransation = shopItem.GetQuantity();
for (int i = 0; i < quantityInTransation; i++)
{
flatItems.Add(item);
}
}
return playerInventory.HasSpaceFor(flatItems);
}
void BuyItem(Inventory shopperInventory, Wallet wallet, InventoryItem item, float price)
{
if (wallet.GetBalance() < price) return;
bool success = shopperInventory.AddToFirstEmptySlot(item, 1);
if (success)
{
AddToTransaction(item, -1);
if (!stockSold.ContainsKey(item))
{
stockSold[item] = 0;
}
stockSold[item]++;
wallet.UpdateBalance(-price);
}
}
void SellItem(Inventory shopperInventory, Wallet wallet, InventoryItem item, float price)
{
int slot = FindFirstItemSlot(shopperInventory, item);
if (slot == -1) return;
AddToTransaction(item, -1);
shopperInventory.RemoveFromSlot(slot, 1);
if (!stockSold.ContainsKey(item))
{
stockSold[item] = 0;
}
stockSold[item]--;
wallet.UpdateBalance(price);
}
int FindFirstItemSlot(Inventory shopperInventory, InventoryItem item)
{
for (int i = 0; i < shopperInventory.GetSize(); i++)
{
if (shopperInventory.GetItemInSlot(i) == item)
{
return i;
}
}
return -1;
}
Dictionary<InventoryItem, float> GetPrices()
{
Dictionary<InventoryItem, float> prices = new Dictionary<InventoryItem, float>();
foreach (var config in GetAvailableConfigs())
{
if (isBuyingMode)
{
if (!prices.ContainsKey(config.item))
{
prices[config.item] = config.item.GetPrice();
}
prices[config.item] *= (1 - config.buyingDiscountPercent / 100);
}
else
{
prices[config.item] = config.item.GetPrice() * (sellingPercentage / 100);
}
}
return prices;
}
Dictionary<InventoryItem, int> GetAvailabilities()
{
Dictionary<InventoryItem, int> availabilities = new Dictionary<InventoryItem, int>();
foreach (var config in GetAvailableConfigs())
{
if (isBuyingMode)
{
if (!availabilities.ContainsKey(config.item))
{
int soldAmount = 0;
stockSold.TryGetValue(config.item, out soldAmount);
availabilities[config.item] = -soldAmount;
}
availabilities[config.item] += config.initialStock; //accumulates all availabilities for one item so that way multiple instances of an item can still be considered the same inventory item.
}
else
{
availabilities[config.item] = CountItemsInInventory(config.item);
}
}
return availabilities;
}
IEnumerable<StockItemConfig> GetAvailableConfigs()
{
int shopperLevel = GetShopperLevel();
foreach (var config in stockConfig)
{
if (config.levelRequiredToUnlock > shopperLevel) continue;
yield return config;
}
}
int CountItemsInInventory(InventoryItem item)
{
var shopperInventory = currentShopper.GetComponent<Inventory>();
int total = 0;
if (shopperInventory == null) return 0;
for (int i = 0; i < shopperInventory.GetSize(); i++)
{
if (shopperInventory.GetItemInSlot(i) == item)
{
total += shopperInventory.GetNumberInSlot(i);
}
}
return total;
}
bool IsTransactionEmpty()
{
return transaction.Count == 0;
}
int GetShopperLevel()
{
BaseStats stats = currentShopper.GetComponent<BaseStats>();
if (stats == null) return 0;
return stats.GetLevel();
}
public object CaptureState()
{
Dictionary<string, int> saveObject = new Dictionary<string, int>();
foreach (var pair in stockSold)
{
saveObject[pair.Key.GetItemID()] = pair.Value;
}
return saveObject;
}
public void RestoreState(object state)
{
stockSold.Clear();
Dictionary<string, int> saveObject = (Dictionary<string, int>) state;
foreach (var pair in saveObject)
{
stockSold[InventoryItem.GetFromID(pair.Key)] = pair.Value;
}
}
}
}
Wallet.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using GameDevTV.Saving;
namespace RPG.Inventories
{
public class Wallet : MonoBehaviour, ISaveable
{
[SerializeField] float startingBalance = 100f;
float currentBalance;
public event Action OnChange;
void Awake()
{
currentBalance = startingBalance;
}
public float GetBalance()
{
return currentBalance;
}
public void UpdateBalance(float amount)
{
currentBalance += amount;
OnChange?.Invoke();
}
public object CaptureState()
{
return currentBalance;
}
public void RestoreState(object state)
{
currentBalance = (float) state;
}
}
}