173 lines
7.2 KiB
C#
173 lines
7.2 KiB
C#
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Entities;
|
|
using Unity.Mathematics;
|
|
using Unity.Transforms;
|
|
using Unity.NetCode;
|
|
using EE2Clone.Components;
|
|
using EE2Clone.Core;
|
|
|
|
namespace EE2Clone.Systems
|
|
{
|
|
/// <summary>
|
|
/// Server-side: processes GatherCommandRpc — assigns a gather target to citizens.
|
|
/// </summary>
|
|
[BurstCompile]
|
|
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
|
public partial struct GatherCommandSystem : ISystem
|
|
{
|
|
[BurstCompile]
|
|
public void OnUpdate(ref SystemState state)
|
|
{
|
|
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
|
|
|
foreach (var (cmd, source, entity) in
|
|
SystemAPI.Query<RefRO<GatherCommandRpc>, RefRO<ReceiveRpcCommandRequest>>()
|
|
.WithEntityAccess())
|
|
{
|
|
var citizen = cmd.ValueRO.CitizenEntity;
|
|
var node = cmd.ValueRO.ResourceNodeEntity;
|
|
|
|
if (state.EntityManager.Exists(citizen) && state.EntityManager.Exists(node) &&
|
|
state.EntityManager.HasComponent<CitizenStateComponent>(citizen) &&
|
|
state.EntityManager.HasComponent<ResourceNode>(node))
|
|
{
|
|
state.EntityManager.SetComponentData(citizen, new GatherTarget { Target = node });
|
|
state.EntityManager.SetComponentData(citizen, new CitizenStateComponent { Value = CitizenState.MovingToGather });
|
|
|
|
// Set move target to the resource node's position
|
|
var nodePos = state.EntityManager.GetComponentData<LocalTransform>(node).Position;
|
|
state.EntityManager.SetComponentData(citizen, new MoveTarget
|
|
{
|
|
Position = nodePos,
|
|
IsActive = true
|
|
});
|
|
}
|
|
|
|
ecb.DestroyEntity(entity);
|
|
}
|
|
|
|
ecb.Playback(state.EntityManager);
|
|
ecb.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Server-side: citizen gathering state machine.
|
|
/// Handles: MovingToGather → Gathering → MovingToDropoff → Depositing → repeat.
|
|
/// </summary>
|
|
[BurstCompile]
|
|
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
|
[UpdateAfter(typeof(GatherCommandSystem))]
|
|
public partial struct GatheringSystem : ISystem
|
|
{
|
|
private float _gatherTimer;
|
|
|
|
public void OnCreate(ref SystemState state)
|
|
{
|
|
_gatherTimer = 0f;
|
|
}
|
|
|
|
[BurstCompile]
|
|
public void OnUpdate(ref SystemState state)
|
|
{
|
|
float dt = SystemAPI.Time.DeltaTime;
|
|
_gatherTimer += dt;
|
|
bool gatherTick = _gatherTimer >= GameConstants.GatherTickInterval;
|
|
if (gatherTick) _gatherTimer -= GameConstants.GatherTickInterval;
|
|
|
|
foreach (var (citizenState, gatherTarget, carried, transform, moveTarget, owner) in
|
|
SystemAPI.Query<RefRW<CitizenStateComponent>, RefRW<GatherTarget>,
|
|
RefRW<CarriedResource>, RefRO<LocalTransform>, RefRW<MoveTarget>, RefRO<OwnerPlayer>>()
|
|
.WithAll<CitizenTag>())
|
|
{
|
|
switch (citizenState.ValueRO.Value)
|
|
{
|
|
case CitizenState.MovingToGather:
|
|
// Check if arrived (MoveTarget will be deactivated by movement system)
|
|
if (!moveTarget.ValueRO.IsActive)
|
|
{
|
|
citizenState.ValueRW.Value = CitizenState.Gathering;
|
|
}
|
|
break;
|
|
|
|
case CitizenState.Gathering:
|
|
if (!gatherTick) break;
|
|
|
|
var targetEntity = gatherTarget.ValueRO.Target;
|
|
if (targetEntity == Entity.Null || !state.EntityManager.Exists(targetEntity))
|
|
{
|
|
citizenState.ValueRW.Value = CitizenState.Idle;
|
|
break;
|
|
}
|
|
|
|
var node = state.EntityManager.GetComponentData<ResourceNode>(targetEntity);
|
|
if (node.RemainingAmount <= 0)
|
|
{
|
|
citizenState.ValueRW.Value = CitizenState.Idle;
|
|
gatherTarget.ValueRW.Target = Entity.Null;
|
|
break;
|
|
}
|
|
|
|
// Gather one unit
|
|
int gatherAmount = math.min(1, node.RemainingAmount);
|
|
gatherAmount = math.min(gatherAmount, carried.ValueRO.MaxCarryCapacity - carried.ValueRO.Amount);
|
|
|
|
if (gatherAmount > 0)
|
|
{
|
|
node.RemainingAmount -= gatherAmount;
|
|
state.EntityManager.SetComponentData(targetEntity, node);
|
|
|
|
carried.ValueRW.Amount += gatherAmount;
|
|
carried.ValueRW.Type = node.Type;
|
|
}
|
|
|
|
// If carrying capacity full, go to dropoff
|
|
if (carried.ValueRO.Amount >= carried.ValueRO.MaxCarryCapacity)
|
|
{
|
|
citizenState.ValueRW.Value = CitizenState.MovingToDropoff;
|
|
// TODO: Find nearest dropoff building and set as move target
|
|
// For now, move back toward origin as placeholder
|
|
moveTarget.ValueRW = new MoveTarget
|
|
{
|
|
Position = float3.zero,
|
|
IsActive = true
|
|
};
|
|
}
|
|
break;
|
|
|
|
case CitizenState.MovingToDropoff:
|
|
if (!moveTarget.ValueRO.IsActive)
|
|
{
|
|
citizenState.ValueRW.Value = CitizenState.Depositing;
|
|
}
|
|
break;
|
|
|
|
case CitizenState.Depositing:
|
|
// Deposit resources into player economy
|
|
// TODO: Find PlayerResourcesComponent for this owner and add resources
|
|
carried.ValueRW.Amount = 0;
|
|
|
|
// Go back to gather if the node still exists
|
|
if (gatherTarget.ValueRO.Target != Entity.Null &&
|
|
state.EntityManager.Exists(gatherTarget.ValueRO.Target))
|
|
{
|
|
var nodePos = state.EntityManager.GetComponentData<LocalTransform>(gatherTarget.ValueRO.Target).Position;
|
|
moveTarget.ValueRW = new MoveTarget
|
|
{
|
|
Position = nodePos,
|
|
IsActive = true
|
|
};
|
|
citizenState.ValueRW.Value = CitizenState.MovingToGather;
|
|
}
|
|
else
|
|
{
|
|
citizenState.ValueRW.Value = CitizenState.Idle;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|