From 56ffa8e126994daa0c10cde121004d189174a4fb Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Mon, 13 Oct 2025 18:29:50 +0200 Subject: [PATCH] Implement inventory system with item management and pickup functionality --- GameCore/Combat/WeaponSystem.cs | 2 + GameCore/Events/AddItemToInventoryEvent.cs | 11 +++++ GameCore/Events/PlayerJumpEvent.cs | 7 ++++ GameCore/Inventory/InventoryComponent.cs | 46 +++++++++++++++++++++ GameCore/Inventory/InventorySystem.cs | 26 ++++++++++++ GameCore/Inventory/Item.cs | 7 ++++ GameCore/Inventory/PickupComponent.cs | 10 +++++ GameCore/Inventory/PickupSystem.cs | 36 ++++++++++++++++ GameCore/Movement/JumpSystem.cs | 2 + GameCore/Physics/CollisionEventComponent.cs | 9 ++++ 10 files changed, 156 insertions(+) create mode 100644 GameCore/Events/AddItemToInventoryEvent.cs create mode 100644 GameCore/Events/PlayerJumpEvent.cs create mode 100644 GameCore/Inventory/InventoryComponent.cs create mode 100644 GameCore/Inventory/InventorySystem.cs create mode 100644 GameCore/Inventory/Item.cs create mode 100644 GameCore/Inventory/PickupComponent.cs create mode 100644 GameCore/Inventory/PickupSystem.cs create mode 100644 GameCore/Physics/CollisionEventComponent.cs diff --git a/GameCore/Combat/WeaponSystem.cs b/GameCore/Combat/WeaponSystem.cs index 2da37f9..6a7a712 100644 --- a/GameCore/Combat/WeaponSystem.cs +++ b/GameCore/Combat/WeaponSystem.cs @@ -27,6 +27,8 @@ public class WeaponSystem : ISystem if (input.IsFiring && weapon.CooldownTimer <= 0f) { + // Check for ammo if applicable + var context = new EffectContext { World = world, Owner = entity }; foreach (var effect in weapon.OnFireEffects) effect.Execute(context); diff --git a/GameCore/Events/AddItemToInventoryEvent.cs b/GameCore/Events/AddItemToInventoryEvent.cs new file mode 100644 index 0000000..282920c --- /dev/null +++ b/GameCore/Events/AddItemToInventoryEvent.cs @@ -0,0 +1,11 @@ +using GameCore.ECS; +using GameCore.Events.Interfaces; +using GameCore.Inventory; + +namespace GameCore.Events; + +public readonly struct AddItemToInventoryEvent(Entity target, Item item) : IEvent +{ + public readonly Entity Target = target; + public readonly Item Item = item; +} \ No newline at end of file diff --git a/GameCore/Events/PlayerJumpEvent.cs b/GameCore/Events/PlayerJumpEvent.cs new file mode 100644 index 0000000..3a977f1 --- /dev/null +++ b/GameCore/Events/PlayerJumpEvent.cs @@ -0,0 +1,7 @@ +using GameCore.Events.Interfaces; + +namespace GameCore.Events; + +public readonly struct PlayerJumpEvent : IEvent +{ +} \ No newline at end of file diff --git a/GameCore/Inventory/InventoryComponent.cs b/GameCore/Inventory/InventoryComponent.cs new file mode 100644 index 0000000..fc088cc --- /dev/null +++ b/GameCore/Inventory/InventoryComponent.cs @@ -0,0 +1,46 @@ +using GameCore.ECS.Interfaces; + +namespace GameCore.Inventory; + +public class InventoryComponent : IComponent +{ + private readonly Dictionary _items = new(); + + public bool AddItem(Item itemToAdd) + { + if (_items.TryGetValue(itemToAdd.ItemId, out var exisitingItem)) + { + exisitingItem.Quantity += itemToAdd.Quantity; + _items[itemToAdd.ItemId] = exisitingItem; + } + else + { + _items.Add(itemToAdd.ItemId, itemToAdd); + } + + return true; + } + + public bool RemoveItem(string itemId, int quantity) + { + if (!_items.TryGetValue(itemId, out var existingItem) || existingItem.Quantity < quantity) return false; + + existingItem.Quantity -= quantity; + if (existingItem.Quantity <= 0) + _items.Remove(itemId); + else + _items[itemId] = existingItem; + + return true; + } + + public int GetItemCount(string itemId) + { + return _items.TryGetValue(itemId, out var existingItem) ? existingItem.Quantity : 0; + } + + public bool HasItem(string itemId, int quantity = 1) + { + return GetItemCount(itemId) >= quantity; + } +} \ No newline at end of file diff --git a/GameCore/Inventory/InventorySystem.cs b/GameCore/Inventory/InventorySystem.cs new file mode 100644 index 0000000..20acf6e --- /dev/null +++ b/GameCore/Inventory/InventorySystem.cs @@ -0,0 +1,26 @@ +using GameCore.ECS; +using GameCore.ECS.Interfaces; +using GameCore.Events; + +namespace GameCore.Inventory; + +public class InventorySystem : ISystem +{ + private readonly World _world; + + public InventorySystem(World world) + { + _world = world; + _world.Subscribe(OnAddItem); + } + + public void Update(World world, float deltaTime) + { + } + + private void OnAddItem(AddItemToInventoryEvent e) + { + var inventory = _world.GetComponent(e.Target); + inventory?.AddItem(e.Item); + } +} \ No newline at end of file diff --git a/GameCore/Inventory/Item.cs b/GameCore/Inventory/Item.cs new file mode 100644 index 0000000..3e555b9 --- /dev/null +++ b/GameCore/Inventory/Item.cs @@ -0,0 +1,7 @@ +namespace GameCore.Inventory; + +public struct Item(string itemId, int quantity) +{ + public string ItemId { get; set; } = itemId; + public int Quantity { get; set; } = quantity; +} \ No newline at end of file diff --git a/GameCore/Inventory/PickupComponent.cs b/GameCore/Inventory/PickupComponent.cs new file mode 100644 index 0000000..1c67649 --- /dev/null +++ b/GameCore/Inventory/PickupComponent.cs @@ -0,0 +1,10 @@ +using GameCore.ECS.Interfaces; + +namespace GameCore.Inventory; + +public class PickupComponent : IComponent +{ + public string ItemId { get; set; } + public int Quantity { get; set; } = 1; + public bool IsInstantUse { get; set; } = false; +} \ No newline at end of file diff --git a/GameCore/Inventory/PickupSystem.cs b/GameCore/Inventory/PickupSystem.cs new file mode 100644 index 0000000..aed5555 --- /dev/null +++ b/GameCore/Inventory/PickupSystem.cs @@ -0,0 +1,36 @@ +using GameCore.Combat; +using GameCore.ECS; +using GameCore.ECS.Interfaces; +using GameCore.Events; +using GameCore.Physics; +using GameCore.Player; + +namespace GameCore.Inventory; + +public class PickupSystem : ISystem +{ + public void Update(World world, float deltaTime) + { + var entitiesWithCollisions = world.GetEntitiesWith(); + var withCollisions = entitiesWithCollisions.ToList(); + foreach (var entity in withCollisions) + { + if (world.GetComponent(entity) == null) return; + + var collision = world.GetComponent(entity); + if (collision == null) continue; + + var pickup = world.GetComponent(entity); + var inventory = world.GetComponent(entity); + + if (pickup == null || inventory == null) continue; + + var item = new Item(pickup.ItemId, pickup.Quantity); + // In the future, handle IsInstantUse items here + world.PublishEvent(new AddItemToInventoryEvent(entity, item)); + world.AddComponent(collision.OtherEntity, new DeathComponent()); + } + + foreach (var entity in withCollisions) world.RemoveComponent(entity); + } +} \ No newline at end of file diff --git a/GameCore/Movement/JumpSystem.cs b/GameCore/Movement/JumpSystem.cs index 83bb0e6..2aa83d8 100644 --- a/GameCore/Movement/JumpSystem.cs +++ b/GameCore/Movement/JumpSystem.cs @@ -1,6 +1,7 @@ using GameCore.Attributes; using GameCore.ECS; using GameCore.ECS.Interfaces; +using GameCore.Events; using GameCore.Input; using GameCore.Physics; using Attribute = GameCore.Attributes.Attribute; @@ -26,6 +27,7 @@ public class JumpSystem : ISystem { var jumpHeight = attributes.GetValue(Attribute.JumpHeight); velocity.DesiredVelocity.Y = (float)System.Math.Sqrt(2f * world.Config.GravityStrength * jumpHeight); + world.PublishEvent(new PlayerJumpEvent()); } } } diff --git a/GameCore/Physics/CollisionEventComponent.cs b/GameCore/Physics/CollisionEventComponent.cs new file mode 100644 index 0000000..cd21784 --- /dev/null +++ b/GameCore/Physics/CollisionEventComponent.cs @@ -0,0 +1,9 @@ +using GameCore.ECS; +using GameCore.ECS.Interfaces; + +namespace GameCore.Physics; + +public class CollisionEventComponent(Entity otherEntity) : IComponent +{ + public readonly Entity OtherEntity = otherEntity; +} \ No newline at end of file