Sorry for long message, this is precisely what I'm attempting to achieve.
When an item is removed by X amount it doesn't properly update the inventory.
I figured that the problem is a lack of an event when an item is removed partially, because I can insert but not remove.
explanation
this is my current implementation
Code: Select all
class InventoryThinker : Thinker final
{
const STAT_NUM = Thinker.STAT_USER;
const INVENTORY_COLUMNS = 6;
const INVENTORY_ROWS = 2;
const MAX_INVENTORY_SLOTS = INVENTORY_COLUMNS * INVENTORY_ROWS;
const NO_SLOT_FOUND = -1;
private PlayerPawn _owner;
private class<Inventory> _slots[MAX_INVENTORY_SLOTS];
/// returns the inventory attached to the player or null if it doesn't exist.
clearscope static InventoryThinker Find(PlayerPawn from)
{
let iter = ThinkerIterator.Create("InventoryThinker", InventoryThinker.STAT_NUM);
let ret = InventoryThinker(iter.Next());
for (; ret; ret = InventoryThinker(iter.Next()))
if (ret._owner == from)
break;
iter.Destroy();
return ret;
}
/// returns or creates a new inventory to attach at player.
static InventoryThinker FindOrCreate(PlayerPawn from)
{
let ret = InventoryThinker.Find(from);
if (!ret)
{
ret = New("InventoryThinker");
ret.ChangeStatNum(InventoryThinker.STAT_NUM);
ret._owner = from;
}
return ret;
}
static clearscope bool IsInventoryInsertable(Inventory item)
{
return item.bINVBAR || item is "Weapon";
}
// ------------------------------------------------------------------------------------------------
/// inserts the item into slot then retuns true, or if the slot is invalid or already occupied then
/// returns false instead.
bool Insert(class<Inventory> item, int slot)
{
if (slot < 0 || slot > InventoryThinker.MAX_INVENTORY_SLOTS || _slots[slot])
return false;
_slots[slot] = item;
return true;
}
/// inserts the item into the first empty slot avaiable then returns the insertion index, or if
/// if fails then returns NO_SLOT_FOUND instead.
int InsertEmpty(class<Inventory> item)
{
for (let i = 0; i != InventoryThinker.MAX_INVENTORY_SLOTS; ++i)
{
if (!_slots[i])
{
_slots[i] = item;
return i;
}
}
return InventoryThinker.NO_SLOT_FOUND;
}
/// returns true only if there's at least one empty slot.
clearscope bool HasEmpty(void)
{
foreach (item : _slots)
if (!item)
return true;
return false;
}
/// returns the next empty slot index starting from slot, or NO_SLOT_FOUND if not valid empty slot exists.
clearscope int FindEmpty(int slot = 0)
{
if (slot >= 0)
for (; slot < InventoryThinker.NO_SLOT_FOUND; ++slot)
if (!_slots[slot])
return slot;
return InventoryThinker.NO_SLOT_FOUND;
}
clearscope Inventory FindInventory(int slot)
{
class<Inventory> itemClass;
if (slot < 0 || slot > InventoryThinker.MAX_INVENTORY_SLOTS || !(itemClass = _slots[slot]))
return null;
return _owner.FindInventory(itemClass, subclass: false);
}
}
class CharacterActor : MyStandaloneGamePlayer final
{
default
{
Radius 14;
Height 28;
}
// ------------------------------------------------------------------------------------------------
override void PostBeginPlay(void)
{
super.PostBeginPlay();
let inv = InventoryThinker.FindOrCreate(self);
}
override bool CanReceive(Inventory item)
{
bool ret = true;
if (InventoryThinker.IsInventoryInsertable(item))
{
let inv = InventoryThinker.Find(self);
ret = inv && inv.HasEmpty();
}
return ret && super.CanReceive(item);
}
override void HasReceived(Inventory item)
{
Console.Printf("HasReceived %s", item.GetTag());
if (InventoryThinker.IsInventoryInsertable(item))
{
let inv = InventoryThinker.Find(self);
if (inv)
inv.InsertEmpty(item.GetClassName());
}
super.HasReceived(item);
}
}
I want to do it like this for compatibility with existing inventory manipulation methods.
As bare bone possible, an array is used to keep track of the empty slots and those occupied.
The slots store the item class name, because I use FindInventory to get the item reference.
The same item can be stored in multiple slots as long the Amount matches, even Weapons I can just set the MaxAmount to make it work.
the problem
If I need to insert an item, then the PlayerPawn's CanReceive, HasReceived methods are fine.
But what about removal of an item?
There are RemoveInventory, UseInventory, ClearInventory methods. But there's not partial remove method.
I've seen that when an item is used the Amount is directly set, this is no good.
What approaches I could attempt to fix this problem? I'm really losing my mind over this really important thing.
the expectations
I want that a pickup, ex. Banana, with amount 6. When picked up it will insert the item into the first 6 slots avaiable.
Then if I TakeInventory or use the take command to remove 4 of them , then the first 4 slots disappear and I keep the last 2.
Do I have to change how my inventory system works?
Do I have to change my expectations for this system?