From 09fa293c8124ecfdc72bee539ae09949fad257c6 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Wed, 29 Oct 2025 00:13:19 +0100 Subject: [PATCH] Add ammo consumption and item pickup systems --- GameCore/Combat/Effects/ConsumeAmmoCost.cs | 19 +++++++++ GameCore/Combat/Interfaces/ICostEffect.cs | 8 ++++ GameCore/Combat/WeaponComponent.cs | 1 + GameCore/Combat/WeaponSystem.cs | 15 +++++-- GameCore/Events/ItemPickupAttemptEvent.cs | 17 ++++++++ GameCore/Inventory/ItemAcquisitionSystem.cs | 47 +++++++++++++++++++++ GameCore/Inventory/PickupComponent.cs | 2 + GameCore/Inventory/PickupSystem.cs | 13 +++--- 8 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 GameCore/Combat/Effects/ConsumeAmmoCost.cs create mode 100644 GameCore/Combat/Interfaces/ICostEffect.cs create mode 100644 GameCore/Events/ItemPickupAttemptEvent.cs create mode 100644 GameCore/Inventory/ItemAcquisitionSystem.cs diff --git a/GameCore/Combat/Effects/ConsumeAmmoCost.cs b/GameCore/Combat/Effects/ConsumeAmmoCost.cs new file mode 100644 index 0000000..4138c06 --- /dev/null +++ b/GameCore/Combat/Effects/ConsumeAmmoCost.cs @@ -0,0 +1,19 @@ +using GameCore.Combat.Interfaces; +using GameCore.Inventory; + +namespace GameCore.Combat.Effects; + +public class ConsumeAmmoCost(string ammoId, int amount) : ICostEffect +{ + public void Execute(EffectContext context) + { + var inventory = context.World.GetComponent(context.Owner); + inventory?.RemoveItem(ammoId, amount); + } + + public bool CanAfford(EffectContext context) + { + var inventory = context.World.GetComponent(context.Owner); + return inventory != null && inventory.HasItem(ammoId, amount); + } +} \ No newline at end of file diff --git a/GameCore/Combat/Interfaces/ICostEffect.cs b/GameCore/Combat/Interfaces/ICostEffect.cs new file mode 100644 index 0000000..2deb271 --- /dev/null +++ b/GameCore/Combat/Interfaces/ICostEffect.cs @@ -0,0 +1,8 @@ +using GameCore.Combat.Effects; + +namespace GameCore.Combat.Interfaces; + +public interface ICostEffect : IEffect +{ + bool CanAfford(EffectContext context); +} \ No newline at end of file diff --git a/GameCore/Combat/WeaponComponent.cs b/GameCore/Combat/WeaponComponent.cs index 1e07dfe..f0cbab4 100644 --- a/GameCore/Combat/WeaponComponent.cs +++ b/GameCore/Combat/WeaponComponent.cs @@ -7,6 +7,7 @@ public class WeaponComponent : IComponent { public float FireRate { get; set; } = 1f; + public List FireCosts { get; set; } = []; public List OnFireEffects { get; set; } = []; public List OnHitEffects { get; set; } = []; diff --git a/GameCore/Combat/WeaponSystem.cs b/GameCore/Combat/WeaponSystem.cs index 6a7a712..44d3eec 100644 --- a/GameCore/Combat/WeaponSystem.cs +++ b/GameCore/Combat/WeaponSystem.cs @@ -27,12 +27,21 @@ 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); + var canFire = true; + foreach (var cost in weapon.FireCosts) + if (!cost.CanAfford(context)) + { + canFire = false; + //TODO: Publish an event or notify the player they can't fire + break; + } + if (!canFire) continue; + + foreach (var cost in weapon.FireCosts) cost.Execute(context); + foreach (var effect in weapon.OnFireEffects) effect.Execute(context); world.PublishEvent(new WeaponFiredEvent(entity, input.MuzzlePosition)); weapon.CooldownTimer = 1f / weapon.FireRate; } diff --git a/GameCore/Events/ItemPickupAttemptEvent.cs b/GameCore/Events/ItemPickupAttemptEvent.cs new file mode 100644 index 0000000..e9205b8 --- /dev/null +++ b/GameCore/Events/ItemPickupAttemptEvent.cs @@ -0,0 +1,17 @@ +using GameCore.ECS; +using GameCore.Events.Interfaces; + +namespace GameCore.Events; + +public readonly struct ItemPickupAttemptEvent( + Entity picker, + Entity itemEntity, + string itemId, + int quantity +) : IEvent +{ + public readonly Entity Picker = picker; + public readonly Entity ItemEntity = itemEntity; + public readonly string ItemId = itemId; + public readonly int Quantity = quantity; +} \ No newline at end of file diff --git a/GameCore/Inventory/ItemAcquisitionSystem.cs b/GameCore/Inventory/ItemAcquisitionSystem.cs new file mode 100644 index 0000000..0c3d08e --- /dev/null +++ b/GameCore/Inventory/ItemAcquisitionSystem.cs @@ -0,0 +1,47 @@ +using GameCore.Combat; +using GameCore.Combat.Effects; +using GameCore.ECS; +using GameCore.ECS.Interfaces; +using GameCore.Events; + +namespace GameCore.Inventory; + +public class ItemAcquisitionSystem : ISystem +{ + private readonly World _world; + + public ItemAcquisitionSystem(World world) + { + _world = world; + _world.Subscribe(OnItemPickupAttempt); + } + + public void Update(World world, float deltaTime) + { + } + + private void OnItemPickupAttempt(ItemPickupAttemptEvent e) + { + var pickupComponent = _world.GetComponent(e.ItemEntity); + if (pickupComponent == null) return; + + if (pickupComponent.IsInstantUse) + { + var context = new EffectContext + { + World = _world, + Owner = e.Picker, + Target = e.Picker + }; + + foreach (var effect in pickupComponent.OnAcquireEffects) effect.Execute(context); + } + else + { + var item = new Item(e.ItemId, e.Quantity); + _world.PublishEvent(new AddItemToInventoryEvent(e.Picker, item)); + } + + _world.AddComponent(e.ItemEntity, new DeathComponent()); + } +} \ No newline at end of file diff --git a/GameCore/Inventory/PickupComponent.cs b/GameCore/Inventory/PickupComponent.cs index 1c67649..cbeab63 100644 --- a/GameCore/Inventory/PickupComponent.cs +++ b/GameCore/Inventory/PickupComponent.cs @@ -1,3 +1,4 @@ +using GameCore.Combat.Interfaces; using GameCore.ECS.Interfaces; namespace GameCore.Inventory; @@ -7,4 +8,5 @@ public class PickupComponent : IComponent public string ItemId { get; set; } public int Quantity { get; set; } = 1; public bool IsInstantUse { get; set; } = false; + public List OnAcquireEffects { get; set; } = []; } \ No newline at end of file diff --git a/GameCore/Inventory/PickupSystem.cs b/GameCore/Inventory/PickupSystem.cs index aed5555..e0e54ad 100644 --- a/GameCore/Inventory/PickupSystem.cs +++ b/GameCore/Inventory/PickupSystem.cs @@ -1,4 +1,3 @@ -using GameCore.Combat; using GameCore.ECS; using GameCore.ECS.Interfaces; using GameCore.Events; @@ -20,15 +19,17 @@ public class PickupSystem : ISystem var collision = world.GetComponent(entity); if (collision == null) continue; - var pickup = world.GetComponent(entity); + var pickup = world.GetComponent(collision.OtherEntity); 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()); + world.PublishEvent(new ItemPickupAttemptEvent( + entity, + collision.OtherEntity, + pickup.ItemId, + pickup.Quantity + )); } foreach (var entity in withCollisions) world.RemoveComponent(entity);