So, a few lessons ago (prior to the introduction of buying/selling level restrictions), I had things set up nicely to buy/sell items that the trader didn’t have listings for, and have these items transfer to the seller’s buy inventory after the sale. I implemented the changes outlined here to achieve that: Sell Items that aren’t in shop stock - Unity Courses / Show - GameDev.tv
Now that we’ve further modified the system, a lot of what was written there isn’t working anymore. I’ve made some (likely bodgy) modifications that have the system ‘mostly’ working. The two remaining issues are that I can only buy/sell one item of a single quantity per transaction, and that sold items aren’t appearing in the merchant’s buy window.
Edit: Futher testing has revealed that stock that has been bought from the merchant depletes his stock correctly, and then when sold back to him will raise his stock level correctly. Additional items of the same type found and sold to him will NOT increase his set initial stock level. (So, he starts with 5 foot cream and I buy two, he’ll have three left. I sell him two and his stock is back to five. I find another foot cream and sell that to him. His available stock remains at five.)
I’ll post the Shop.cs below. A lot of it probably needs fixing, as I’ve duct-taped old and new functions together to achieve a semi-working system that allows you to sell items to a merchant that doesn’t sell those items.
using System;
using System.Collections.Generic;
using UnityEngine;
using GameDevTV.Inventories;
using RPG.Control;
using RPG.Inventories;
using RPG.Movement;
using RPG.Saving;
using RPG.Stats;
namespace RPG.Shops
{
public class Shop : MonoBehaviour, IRaycastable, ISaveable
{
[SerializeField] string shopName;
[Range(0, 100)]
[SerializeField] float sellingPercentage = 50f;
[SerializeField] List<StockItemConfig> stockConfig = new List<StockItemConfig>();
List<InventoryItem> sellingList = new List<InventoryItem>();
[System.Serializable]
class StockItemConfig
{
public StockItemConfig(InventoryItem item, int initialStock)
{
this.item = item;
this.initialStock = initialStock;
buyingDiscountPercentage = 0;
}
public InventoryItem item;
public int initialStock;
[Range(0,100)]
public float buyingDiscountPercentage;
public int levelToUnlock = 0;
}
Dictionary<InventoryItem, int> transaction = new Dictionary<InventoryItem, int>();
Dictionary<InventoryItem, int> stockSold = new Dictionary<InventoryItem, int>();
Shopper currentShopper = null;
bool isBuyingMode = true;
ItemCategory filter = ItemCategory.None;
public event Action onChange;
public void SetShopper(Shopper shopper)
{
currentShopper = shopper;
}
public IEnumerable<ShopItem> GetAllItems()
{
sellingList.Clear();
Dictionary<InventoryItem, float> prices = GetPrices();
Dictionary<InventoryItem, int> availabilities = GetAvailabilities();
if (isBuyingMode)
{
foreach (InventoryItem item in availabilities.Keys)
{
if (availabilities[item] <= 0) { continue; }
float price = prices[item];
int quantityInTransaction = 0;
transaction.TryGetValue(item, out quantityInTransaction);
int availibility = availabilities[item];
yield return new ShopItem(item, availibility, price, quantityInTransaction);
}
}
else
{
Inventory shopperInventory = currentShopper.GetComponent<Inventory>();
for (int i = 0; i < shopperInventory.GetSize(); i++)
{
InventoryItem item = shopperInventory.GetItemInSlot(i);
if (item != null)
{
int quantityInTransaction = 0;
transaction.TryGetValue(item, out quantityInTransaction);
float price = 0;
if (stockSold.ContainsKey(item))
{
foreach (StockItemConfig config in stockConfig)
{
if (config.item == item)
{
price = item.GetPrice();
}
}
}
else
{
price = item.GetPrice() * 0.3f;
}
if (!sellingList.Contains(item))
{
sellingList.Add(item);
var getAvailability = CountItemsInInventory(item);
yield return new ShopItem(item, getAvailability, price, quantityInTransaction);
}
}
}
}
}
public IEnumerable<ShopItem> GetFilteredItems()
{
foreach (ShopItem shopItem in GetAllItems())
{
InventoryItem item = shopItem.GetInventoryItem();
if (filter == ItemCategory.None || item.GetCategory() == filter)
{
yield return shopItem;
}
}
}
public void SelectFilter(ItemCategory category)
{
filter = category;
if (onChange != null)
{
onChange();
}
}
public ItemCategory GetFilter()
{
return filter;
}
public void SelectMode(bool isBuying)
{
transaction.Clear();
isBuyingMode = isBuying;
if (onChange != null)
{
onChange();
}
}
public bool IsBuyingMode()
{
return isBuyingMode;
}
public void AddToTransaction(InventoryItem item, int quantity)
{
if (!transaction.ContainsKey(item))
{
transaction[item] = 0;
}
var availabilities = GetAvailabilities();
if (!availabilities.ContainsKey(item))
{
availabilities.Add(item, quantity);
}
int availability = availabilities[item];
{
transaction[item] = 0;
}
if (transaction[item] + quantity > availability)
{
transaction[item] = availability;
}
else
{
transaction[item] += quantity;
}
if (transaction[item] <= 0)
{
transaction.Remove(item);
}
if (onChange != null)
{
onChange();
}
}
public string GetShopName()
{
return shopName;
}
public bool CanTransact()
{
if (IsTransactionEmpty()) { return false; }
if (!HasSufficientFunds()) { return false; }
if (!HasInventorySpace()) { return false; }
return true;
}
public bool IsTransactionEmpty()
{
return transaction.Count == 0;
}
public bool HasSufficientFunds()
{
if (!isBuyingMode) { return true; }
Purse purse = currentShopper.GetComponent<Purse>();
if (purse == null) { return false; }
return purse.GetBalance() > TransactionTotal();
}
public bool HasInventorySpace()
{
if (!isBuyingMode) { return true; }
Inventory shopperInventory = currentShopper.GetComponent<Inventory>();
if (shopperInventory == null) { return false; }
List<InventoryItem> flatItems = new List<InventoryItem>();
foreach (ShopItem shopItem in GetAllItems())
{
InventoryItem item = shopItem.GetInventoryItem();
int quantity = shopItem.GetQuantityInTransaction();
for (int i = 0; i < quantity; i++)
{
flatItems.Add(item);
}
}
return shopperInventory.HasSpaceFor(flatItems);
}
public void ConfirmTransaction()
{
Inventory shopperInventory = currentShopper.GetComponent<Inventory>();
Purse shopperPurse = currentShopper.GetComponent<Purse>();
if (shopperInventory == null || shopperPurse == null) { return; }
foreach (ShopItem shopItem in GetAllItems())
{
InventoryItem item = shopItem.GetInventoryItem();
int quantity = shopItem.GetQuantityInTransaction();
float price = shopItem.GetPrice();
for (int i = 0; i < quantity; i++)
{
if (isBuyingMode)
{
BuyItem(shopperInventory, shopperPurse, item, price);
}
else
{
SellItem(shopperInventory, shopperPurse, item, price);
}
}
}
if (onChange != null)
{
onChange();
}
}
public float TransactionTotal()
{
float total = 0;
foreach (ShopItem item in GetAllItems())
{
total += item.GetPrice() * item.GetQuantityInTransaction();
}
return total;
}
public CursorType GetCursorType()
{
return CursorType.Shop;
}
public bool HandleRaycast(PlayerController callingController)
{
if (Input.GetMouseButtonDown(0))
{
if (Vector3.Distance(callingController.transform.position, this.transform.position) > 3)
{
callingController.GetComponent<Mover>().StartMoveAction(this.transform.position, 1);
}
else
{
callingController.GetComponent<Shopper>().SetActiveShop(this);
}
}
return true;
}
private int CountItemsInInventory(InventoryItem item)
{
Inventory inventory = currentShopper.GetComponent<Inventory>();
if (inventory == null) { return 0; }
int total = 0;
for (int i = 0; i < inventory.GetSize(); i++)
{
if (inventory.GetItemInSlot(i) == item)
{
total += inventory.GetNumberInSlot(i);
}
}
return total;
}
private 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 sold = 0;
stockSold.TryGetValue(config.item, out sold);
availabilities[config.item] = -sold;
}
availabilities[config.item] += config.initialStock;
}
else
{
availabilities[config.item] = CountItemsInInventory(config.item);
}
}
return availabilities;
}
private 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.buyingDiscountPercentage / 100);
}
else
{
prices[config.item] = config.item.GetPrice() * (sellingPercentage / 100);
}
}
return prices;
}
private IEnumerable<StockItemConfig> GetAvailableConfigs()
{
int shopperLevel = GetShopperLevel();
foreach (var config in stockConfig)
{
if (config.levelToUnlock > shopperLevel) { continue; }
yield return config;
}
}
private void BuyItem(Inventory shopperInventory, Purse shopperPurse, InventoryItem item, float price)
{
if (shopperPurse.GetBalance() < price) { return; }
bool success = shopperInventory.AddToFirstEmptySlot(item, 1);
if (success)
{
AddToTransaction(item, -1);
if (!stockSold.ContainsKey(item))
{
stockSold[item] = 0;
}
stockSold[item]++;
shopperPurse.UpdateBalance(-price);
}
}
private void SellItem(Inventory shopperInventory, Purse shopperPurse, 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]--;
}
else
{
stockConfig.Add(new StockItemConfig(item, 1));
stockSold[item] = 1;
}
shopperPurse.UpdateBalance(price);
}
private int FindFirstItemSlot(Inventory shopperInventory, InventoryItem item)
{
for (int i = 0; i < shopperInventory.GetSize(); i++)
{
if (shopperInventory.GetItemInSlot(i) == item)
{
return i;
}
}
return -1;
}
private 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)
{
Dictionary<string, int> saveObject = (Dictionary<string, int>) state;
stockSold.Clear();
foreach (var pair in saveObject)
{
stockSold[InventoryItem.GetFromID(pair.Key)] = pair.Value;
}
}
}
}
Originally, I was getting Dictionary issues because those items weren’t in the seller’s dictionary. I got around that with this:
if (!availabilities.ContainsKey(item))
{
availabilities.Add(item, quantity);
}
but I’m not sure if that’s part of the problem.
I appreciate any and all assistance you can provide on this one!
Thanks again,
Mark.