Files
Babushka/scripts/CSharp/Common/Inventory/InventoryInstance.cs
T

235 lines
7.0 KiB
C#
Raw Normal View History

2025-03-25 19:39:00 +01:00
#nullable enable
using System;
using Godot;
using System.Collections.Generic;
using System.Linq;
2025-11-18 18:30:17 +01:00
using Babushka.scripts.CSharp.Common.Savegame;
2025-03-25 19:39:00 +01:00
namespace Babushka.scripts.CSharp.Common.Inventory;
public partial class InventoryInstance : Node, ISaveable
2025-03-25 19:39:00 +01:00
{
private List<InventorySlot> _slots = new();
public IReadOnlyList<InventorySlot> Slots => _slots;
[Signal]
public delegate void SlotAmountChangedEventHandler();
2025-04-17 16:10:12 +02:00
2025-03-25 19:39:00 +01:00
[Signal]
public delegate void InventoryContentsChangedEventHandler();
public static string ID = "inventoryInstance";
2025-11-18 18:30:17 +01:00
2025-09-17 15:07:17 +02:00
/// <summary>
/// The total amount of Inventoryslots in the inventory (empty and occupied).
/// </summary>
2025-03-25 19:39:00 +01:00
[Export]
public int SlotAmount
{
get => _slots.Count;
set
{
if (value < _slots.Count)
{
_slots.RemoveRange(value, _slots.Count - value);
}
else if (value > _slots.Count)
{
for (var i = _slots.Count; i < value; i++)
{
_slots.Add(new InventorySlot());
}
}
2025-04-17 16:10:12 +02:00
2025-03-25 19:39:00 +01:00
EmitSignal(SignalName.SlotAmountChanged);
}
}
2025-04-17 16:10:12 +02:00
2025-11-19 15:18:33 +01:00
public override void _EnterTree()
{
LoadFromSaveData();
InventoryContentsChanged += UpdateSaveData;
SlotAmountChanged += UpdateSaveData;
2025-12-02 12:08:19 +01:00
SavegameService.OnSaveGameReset += SaveGameReset;
2025-11-19 15:18:33 +01:00
}
2025-12-02 12:08:19 +01:00
2025-11-19 15:18:33 +01:00
public override void _ExitTree()
{
InventoryContentsChanged -= UpdateSaveData;
SlotAmountChanged -= UpdateSaveData;
2025-12-02 12:08:19 +01:00
SavegameService.OnSaveGameReset -= SaveGameReset;
2025-11-19 15:18:33 +01:00
}
2025-04-17 16:10:12 +02:00
public InventoryActionResult AddItem(ItemInstance newItem)
2025-03-25 19:39:00 +01:00
{
2025-04-17 16:10:12 +02:00
var result = AddItemAndStackRecursive(newItem, 0);
EmitSignal(SignalName.InventoryContentsChanged);
return result;
}
private InventoryActionResult AddItemAndStackRecursive(ItemInstance newItem, int slotSearch)
{
if (newItem.blueprint == null || newItem.amount == 0)
2025-09-17 15:07:17 +02:00
return InventoryActionResult.SourceDoesNotExist;
2025-04-17 16:10:12 +02:00
var slotIndex = -1;
// find stackable slot
for (var i = slotSearch; i < _slots.Count; i++)
2025-03-25 19:39:00 +01:00
{
2025-04-17 16:10:12 +02:00
if (_slots[i].itemInstance?.blueprint == newItem.blueprint)
{
slotIndex = i;
break;
}
2025-03-25 19:39:00 +01:00
}
2025-04-17 16:10:12 +02:00
if (slotIndex < 0)
2025-03-25 19:39:00 +01:00
{
2025-04-17 16:10:12 +02:00
// find empty slot
for (var i = slotSearch; i < _slots.Count; i++)
{
if (_slots[i].IsEmpty())
{
slotIndex = i;
break;
}
}
2025-03-25 19:39:00 +01:00
}
2025-04-17 16:10:12 +02:00
if (slotIndex < 0)
2025-03-25 19:39:00 +01:00
{
2025-04-17 16:10:12 +02:00
return InventoryActionResult.DestinationFull;
2025-03-25 19:39:00 +01:00
}
2025-04-17 16:10:12 +02:00
var itemInstance = _slots[slotIndex].itemInstance ?? new ItemInstance { blueprint = newItem.blueprint, amount = 0 };
var maxStack = itemInstance!.blueprint.maxStack;
var freeOnStack = maxStack - itemInstance.amount;
var moveAmount = Math.Min(freeOnStack, newItem.amount);
itemInstance.amount += moveAmount;
newItem.amount -= moveAmount;
_slots[slotIndex].itemInstance = itemInstance;
return newItem.amount <= 0
? InventoryActionResult.Success
: AddItemAndStackRecursive(newItem, slotIndex + 1);
2025-03-25 19:39:00 +01:00
}
2025-04-17 16:10:12 +02:00
public InventoryActionResult RemoveItem(int inventorySlot, out ItemInstance? itemInstance)
2025-03-25 19:39:00 +01:00
{
if (inventorySlot < 0 || inventorySlot >= _slots.Count)
{
itemInstance = null;
2025-09-17 15:07:17 +02:00
return InventoryActionResult.SourceDoesNotExist;
2025-03-25 19:39:00 +01:00
}
2025-04-17 16:10:12 +02:00
2025-03-25 19:39:00 +01:00
if (_slots[inventorySlot].IsEmpty())
{
itemInstance = null;
return InventoryActionResult.SourceIsEmpty;
}
2025-04-17 16:10:12 +02:00
2025-03-25 19:39:00 +01:00
itemInstance = _slots[inventorySlot].itemInstance;
2025-09-17 15:07:17 +02:00
if (itemInstance == null)
return InventoryActionResult.SourceDoesNotExist;
itemInstance.amount -= 1;
2025-09-26 00:31:11 +02:00
if(itemInstance.amount == 0)
_slots[inventorySlot].itemInstance = null;
2025-03-25 19:39:00 +01:00
EmitSignal(SignalName.InventoryContentsChanged);
return InventoryActionResult.Success;
}
2025-04-17 16:10:12 +02:00
2025-03-25 19:39:00 +01:00
public InventoryActionResult RemoveItem(int inventorySlot)
{
return RemoveItem(inventorySlot, out _);
}
2025-04-17 16:10:12 +02:00
public InventoryActionResult AddItemToSlot(ItemInstance itemInstance, int destinationSlot)
{
if (destinationSlot < 0 || destinationSlot >= _slots.Count)
2025-09-17 15:07:17 +02:00
return InventoryActionResult.DestinationDoesNotExist;
2025-04-17 16:10:12 +02:00
if (!_slots[destinationSlot].IsEmpty())
return InventoryActionResult.DestinationFull;
2025-04-17 16:10:12 +02:00
_slots[destinationSlot].itemInstance = itemInstance;
EmitSignal(SignalName.InventoryContentsChanged);
return InventoryActionResult.Success;
}
public int TotalItemsOfBlueprint(ItemResource blueprint)
{
return _slots
.Where(i => !i.IsEmpty() && i.itemInstance!.blueprint == blueprint)
.Sum(i => i.itemInstance!.amount);
}
public bool HasItems(ItemInstance item)
{
return TotalItemsOfBlueprint(item.blueprint) >= item.amount;
}
public bool HasItems(IEnumerable<ItemInstance> items)
{
return items.All(HasItems);
}
2025-11-18 18:30:17 +01:00
#region SAVE AND LOAD
public void UpdateSaveData()
{
var payloadData = new Godot.Collections.Dictionary<string, Variant>();
for (int i = 0; i < _slots.Count; i++)
{
if (!_slots[i].IsEmpty())
{
string key = i.ToString();
string[] value = new string[2];
value[0] = _slots[i].itemInstance.blueprint.ResourcePath;
value[1] = _slots[i].itemInstance.amount.ToString();
payloadData.Add(key,value);
}
}
SavegameService.AppendDataToSave(ID, payloadData);
2025-11-18 18:30:17 +01:00
}
public void LoadFromSaveData()
{
var id = ID;
Godot.Collections.Dictionary<string, Variant> save = SavegameService.GetSaveData(id);
2025-11-18 18:30:17 +01:00
if (save.Count > 0)
{
for (int i = 0; i < _slots.Count; i++)
{
2025-11-19 15:18:33 +01:00
if (save.TryGetValue(i.ToString(), out Variant inventoryItemData))
2025-11-18 18:30:17 +01:00
{
2025-11-19 15:18:33 +01:00
string[] savePayload = inventoryItemData.AsStringArray();
ItemResource resource = ResourceLoader.Load<ItemResource>(savePayload[0]);
int _amount = int.Parse(savePayload[1]);
ItemInstance instance = new ItemInstance { blueprint = resource, amount = _amount };
AddItem(instance);
2025-11-18 18:30:17 +01:00
}
}
}
}
2025-12-02 12:08:19 +01:00
/// <summary>
/// Called when a new save is created.
/// Needs to do a runtime check because the InventoryInstance is already in existence at the beginning of the first scene.
/// </summary>
private void SaveGameReset()
{
foreach (var slot in _slots)
{
slot.itemInstance = null;
}
}
2025-11-18 18:30:17 +01:00
#endregion
}