Code looks fine compared to the GitHub commit. Not sure why this is happening. Everything is linked up in the unity inspector too. Looks like there’s some sort of reference that is returning null?
While the topic is !HasInventorySpace, the actual null reference appears to be in the HasSufficientFunds() method…
What’s odd here is tthat the line (203) has only one thing that can be null, the currentShopper… It seems unlikely that the currentShopper would be null at this point…
Paste in the entire text of the Shop.cs script (the text, NOT a screenshot)
That’s what I’m thinking. Here’s my Shop.cs script:
using GameDevTV.Inventories;
using RPG.Control;
using RPG.Inventories;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RPG.Shops
{
public class Shop : MonoBehaviour, IRaycastable
{
public event Action OnChange;
[SerializeField] string shopName;
[Range(0, 100)]
[SerializeField] float sellingPercentage = 20f;
[SerializeField] StockItemConfig[] stockConfig;
Dictionary<InventoryItem, int> transaction = new Dictionary<InventoryItem, int>();
Dictionary<InventoryItem, int> stock = new Dictionary<InventoryItem, int>();
Shopper currentShopper;
bool isBuyingMode = true;
[Serializable]
class StockItemConfig
{
public InventoryItem item;
public int initialStock;
[Range(0, 100)]
public float buyingDiscountPercent;
}
void Awake()
{
foreach (StockItemConfig config in stockConfig)
{
stock[config.item] = config.initialStock;
}
}
void Update()
{
print("IsTransactionEmpty: " + IsTransactionEmpty());
print("!HasSufficientFunds: " + !HasSufficientFunds());
print("!HasInventorySpace: " + !HasInventorySpace());
}
public void SetShopper(Shopper shopper)
{
currentShopper = shopper;
}
public IEnumerable<ShopItem> GetFilteredItems()
{
return GetAllItems();
}
public IEnumerable<ShopItem> GetAllItems()
{
foreach (StockItemConfig config in stockConfig)
{
float price = GetPrice(config);
int quantityInTransation = 0;
transaction.TryGetValue(config.item, out quantityInTransation);
int availability = GetAvailability(config.item);
yield return new ShopItem(config.item, availability, price, quantityInTransation); //The ShopItem works as a middle man / interface between UI and the backend
}
float GetPrice(StockItemConfig config)
{
if (isBuyingMode)
{
return config.item.GetPrice() * (1 - config.buyingDiscountPercent / 100); //the 1 there is the multiplication of the price itself times the discount percent.
}
else
{
return config.item.GetPrice() * (sellingPercentage / 100);
}
}
}
public ItemCategory GetFilter()
{
return ItemCategory.None;
}
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 SelectFilter(ItemCategory category)
{
}
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;
}
int availability = GetAvailability(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);
stock[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);
stock[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;
}
int GetAvailability(InventoryItem item)
{
if (isBuyingMode)
{
return stock[item];
}
else
{
return CountItemsInInventory(item);
}
}
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;
}
}
}
I take it back, currentShopper can be null in this case… the Update will run every frame, regardless of whether there is a currentShopper or not… So the Debugs will fire and IsTransactionEmpty will try to get access a currentShopper who does not exist.
void Update()
{
if(currentShopper==null) return;
print("IsTransactionEmpty: " + IsTransactionEmpty());
print("!HasSufficientFunds: " + !HasSufficientFunds());
print("!HasInventorySpace: " + !HasInventorySpace());
}
Sorry for the super late response, I’ve been out of town.
Found my issue. Was a very small, insignificant typo, but it was within the Inventory.HasSpaceFor() method. There was a >= checker that should’ve been a <=. This was more work than what it was worth, and I probably should’ve checked there first, but it’s a learning experience.
This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.