Add weapon acquisition and swapping systems with data handling

This commit is contained in:
2025-10-29 01:26:35 +01:00
parent 09fa293c81
commit 6d00b8d6ab
12 changed files with 185 additions and 1 deletions

View File

@@ -0,0 +1,6 @@
namespace GameCore.Combat.Interfaces;
public interface IWeaponDataService
{
bool TryGetWeaponData(string weaponId, out WeaponData weaponData);
}

View File

@@ -0,0 +1,39 @@
using GameCore.ECS;
using GameCore.ECS.Interfaces;
using GameCore.Events;
using GameCore.Player;
namespace GameCore.Combat;
public class WeaponAcquisitionSystem : ISystem
{
private const string WeaponItemPrefix = "weapon_";
private readonly World _world;
public WeaponAcquisitionSystem(World world)
{
_world = world;
_world.Subscribe<AddItemToInventoryEvent>(OnItemAdded);
}
public void Update(World world, float deltaTime)
{
}
private void OnItemAdded(AddItemToInventoryEvent e)
{
if (!e.Item.ItemId.StartsWith(WeaponItemPrefix)) return;
var equipment = _world.GetComponent<EquipmentComponent>(e.Target);
if (equipment == null) return;
if (!equipment.EquippableWeaponItemIds.Contains(e.Item.ItemId))
equipment.EquippableWeaponItemIds.Add(e.Item.ItemId);
if (equipment.CurrentWeaponIndex == -1)
{
equipment.CurrentWeaponIndex = equipment.EquippableWeaponItemIds.Count - 1;
_world.PublishEvent(new EquipWeaponEvent(e.Target, e.Item.ItemId));
}
}
}

View File

@@ -0,0 +1,15 @@
using GameCore.Combat.Interfaces;
namespace GameCore.Combat;
public class WeaponData(
float fireRate,
List<ICostEffect> fireCosts,
List<IEffect> onFireEffects,
List<IEffect> onHitEffects)
{
public float FireRate { get; set; } = fireRate;
public List<ICostEffect> FireCosts { get; set; } = fireCosts;
public List<IEffect> OnFireEffects { get; set; } = onFireEffects;
public List<IEffect> OnHitEffects { get; set; } = onHitEffects;
}

View File

@@ -0,0 +1,50 @@
using GameCore.ECS;
using GameCore.ECS.Interfaces;
using GameCore.Events;
using GameCore.Input;
using GameCore.Player;
namespace GameCore.Combat;
public class WeaponSwapSystem : ISystem
{
public void Update(World world, float deltaTime)
{
var entities = world.GetEntitiesWith<InputStateComponent>();
foreach (var entity in entities)
{
var input = world.GetComponent<InputStateComponent>(entity);
var equipment = world.GetComponent<EquipmentComponent>(entity);
if (input == null || equipment == null || equipment.EquippableWeaponItemIds.Count <= 1)
continue;
var direction = SwapDirection.None;
if (input.IsSwapWeaponNext) direction = SwapDirection.Next;
if (input.IsSwapWeaponPrevious) direction = SwapDirection.Previous;
if (direction != SwapDirection.None)
{
var newIndex = equipment.CurrentWeaponIndex + (int)direction;
var weaponCount = equipment.EquippableWeaponItemIds.Count;
if (newIndex >= weaponCount) newIndex = 0;
if (newIndex < 0) newIndex = weaponCount - 1;
if (newIndex != equipment.CurrentWeaponIndex)
{
equipment.CurrentWeaponIndex = newIndex;
var newWeaponId = equipment.EquippableWeaponItemIds[newIndex];
world.PublishEvent(new EquipWeaponEvent(entity, newWeaponId));
}
}
}
}
private enum SwapDirection
{
None = 0,
Next = 1,
Previous = -1
}
}

View File

@@ -34,7 +34,7 @@ public class WeaponSystem : ISystem
if (!cost.CanAfford(context))
{
canFire = false;
//TODO: Publish an event or notify the player they can't fire
world.PublishEvent(new WeaponFireFailedEvent());
break;
}

View File

@@ -0,0 +1,10 @@
using GameCore.ECS;
using GameCore.Events.Interfaces;
namespace GameCore.Events;
public readonly struct EquipWeaponEvent(Entity owner, string newWeaponItemId) : IEvent
{
public readonly Entity Owner = owner;
public readonly string NewWeaponItemId = newWeaponItemId;
}

View File

@@ -0,0 +1,7 @@
using GameCore.Events.Interfaces;
namespace GameCore.Events;
public readonly struct WeaponFireFailedEvent : IEvent
{
}

View File

@@ -13,4 +13,6 @@ public class InputStateComponent : IComponent, IInputService
public bool IsJumping { get; set; }
public Vector3 MoveDirection { get; set; }
public Vector3 LookDirection { get; set; }
public bool IsSwapWeaponNext { get; set; }
public bool IsSwapWeaponPrevious { get; set; }
}

View File

@@ -9,4 +9,6 @@ public interface IInputService
public bool IsJumping { get; }
public Vector3 MoveDirection { get; }
public Vector3 LookDirection { get; }
public bool IsSwapWeaponNext { get; }
public bool IsSwapWeaponPrevious { get; }
}

View File

@@ -20,5 +20,7 @@ public class PlayerInputSystem : ISystem
inputState.IsJumping = world.InputService.IsJumping;
inputState.IsInteracting = world.InputService.IsInteracting;
inputState.IsFiring = world.InputService.IsFiring;
inputState.IsSwapWeaponNext = world.InputService.IsSwapWeaponNext;
inputState.IsSwapWeaponPrevious = world.InputService.IsSwapWeaponPrevious;
}
}

View File

@@ -0,0 +1,9 @@
using GameCore.ECS.Interfaces;
namespace GameCore.Player;
public class EquipmentComponent : IComponent
{
public List<string> EquippableWeaponItemIds { get; set; } = [];
public int CurrentWeaponIndex { get; set; } = -1; // -1 indicates no weapon equipped
}

View File

@@ -0,0 +1,42 @@
using GameCore.Combat;
using GameCore.Combat.Interfaces;
using GameCore.ECS;
using GameCore.ECS.Interfaces;
using GameCore.Events;
namespace GameCore.Player;
public class EquipmentSystem : ISystem
{
private readonly IWeaponDataService _weaponDataService;
private readonly World _world;
public EquipmentSystem(World world, IWeaponDataService weaponDataService)
{
_world = world;
_weaponDataService = weaponDataService;
_world.Subscribe<EquipWeaponEvent>(OnEquipWeapon);
}
public void Update(World world, float deltaTime)
{
}
private void OnEquipWeapon(EquipWeaponEvent e)
{
if (!_weaponDataService.TryGetWeaponData(e.NewWeaponItemId, out var newData)) return;
var weaponComponent = _world.GetComponent<WeaponComponent>(e.Owner);
if (weaponComponent == null)
{
weaponComponent = new WeaponComponent();
_world.AddComponent(e.Owner, weaponComponent);
}
weaponComponent.FireRate = newData.FireRate;
weaponComponent.FireCosts = newData.FireCosts;
weaponComponent.OnFireEffects = newData.OnFireEffects;
weaponComponent.OnHitEffects = newData.OnHitEffects;
weaponComponent.CooldownTimer = 0f;
}
}