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 { /// /// Server-side system that processes MoveCommandRpc and sets MoveTarget on units. /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] public partial struct MoveCommandSystem : ISystem { [BurstCompile] public void OnUpdate(ref SystemState state) { var ecb = new EntityCommandBuffer(Allocator.Temp); foreach (var (cmd, source, entity) in SystemAPI.Query, RefRO>() .WithEntityAccess()) { var unitEntity = cmd.ValueRO.UnitEntity; if (state.EntityManager.Exists(unitEntity) && state.EntityManager.HasComponent(unitEntity)) { state.EntityManager.SetComponentData(unitEntity, new MoveTarget { Position = cmd.ValueRO.TargetPosition, IsActive = true }); // Clear combat target when moving if (state.EntityManager.HasComponent(unitEntity)) { state.EntityManager.SetComponentData(unitEntity, new CombatTarget { Target = Entity.Null }); } // Set state to Moving if (state.EntityManager.HasComponent(unitEntity)) { state.EntityManager.SetComponentData(unitEntity, new UnitStateComponent { Value = UnitState.Moving }); } } ecb.DestroyEntity(entity); } ecb.Playback(state.EntityManager); ecb.Dispose(); } } /// /// Server-side system that moves units toward their MoveTarget. /// Placeholder for flow-field pathfinding — currently uses direct steering. /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] [UpdateAfter(typeof(MoveCommandSystem))] public partial struct UnitMovementSystem : ISystem { [BurstCompile] public void OnUpdate(ref SystemState state) { float dt = SystemAPI.Time.DeltaTime; foreach (var (transform, moveTarget, speed, unitState) in SystemAPI.Query, RefRW, RefRO, RefRW>() .WithAll()) { if (!moveTarget.ValueRO.IsActive) continue; float3 pos = transform.ValueRO.Position; float3 target = moveTarget.ValueRO.Position; float3 direction = target - pos; direction.y = 0; // Keep on ground plane float distance = math.length(direction); if (distance < 0.5f) { // Arrived moveTarget.ValueRW.IsActive = false; if (unitState.ValueRO.Value == UnitState.Moving) unitState.ValueRW.Value = UnitState.Idle; } else { float3 moveDir = math.normalize(direction); float moveAmount = speed.ValueRO.Value * dt; moveAmount = math.min(moveAmount, distance); var newPos = pos + moveDir * moveAmount; transform.ValueRW.Position = newPos; // Face movement direction transform.ValueRW.Rotation = quaternion.LookRotationSafe(moveDir, math.up()); } } } } /// /// Server-side: processes StopCommandRpc — halts unit movement and clears targets. /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] public partial struct StopCommandSystem : ISystem { [BurstCompile] public void OnUpdate(ref SystemState state) { var ecb = new EntityCommandBuffer(Allocator.Temp); foreach (var (cmd, source, entity) in SystemAPI.Query, RefRO>() .WithEntityAccess()) { var unitEntity = cmd.ValueRO.UnitEntity; if (state.EntityManager.Exists(unitEntity)) { if (state.EntityManager.HasComponent(unitEntity)) state.EntityManager.SetComponentData(unitEntity, new MoveTarget { IsActive = false }); if (state.EntityManager.HasComponent(unitEntity)) state.EntityManager.SetComponentData(unitEntity, new CombatTarget { Target = Entity.Null }); if (state.EntityManager.HasComponent(unitEntity)) state.EntityManager.SetComponentData(unitEntity, new UnitStateComponent { Value = UnitState.Idle }); } ecb.DestroyEntity(entity); } ecb.Playback(state.EntityManager); ecb.Dispose(); } } }