220 lines
7.5 KiB
C#
220 lines
7.5 KiB
C#
using Unity.Entities;
|
|
using Unity.Mathematics;
|
|
using Unity.Transforms;
|
|
using Unity.Collections;
|
|
using Unity.Physics;
|
|
using UnityEngine;
|
|
using EE2Clone.Components;
|
|
using EE2Clone.NetCode;
|
|
|
|
namespace EE2Clone.Hybrid
|
|
{
|
|
/// <summary>
|
|
/// Processes right-click context commands: move, attack, gather, build/repair.
|
|
/// Sends the appropriate RPC to the server based on what was right-clicked.
|
|
/// Client-side MonoBehaviour.
|
|
/// </summary>
|
|
public class CommandDispatcher : MonoBehaviour
|
|
{
|
|
private Camera _mainCamera;
|
|
|
|
private void Start()
|
|
{
|
|
_mainCamera = Camera.main;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
var input = RTSInputActions.Instance;
|
|
if (input == null || _mainCamera == null) return;
|
|
|
|
if (input.RightClickPressed)
|
|
HandleRightClick(input.MousePosition);
|
|
|
|
if (input.StopPressed)
|
|
HandleStop();
|
|
}
|
|
|
|
private void HandleRightClick(Vector2 screenPos)
|
|
{
|
|
var selection = SelectionManager.Instance;
|
|
if (selection == null || selection.SelectedEntities.Count == 0) return;
|
|
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null || !world.IsCreated) return;
|
|
|
|
var em = world.EntityManager;
|
|
var ray = _mainCamera.ScreenPointToRay(new Vector3(screenPos.x, screenPos.y, 0));
|
|
|
|
// Raycast to find what was clicked
|
|
var physicsWorldQuery = em.CreateEntityQuery(typeof(PhysicsWorldSingleton));
|
|
if (physicsWorldQuery.IsEmpty) return;
|
|
|
|
var physicsWorld = physicsWorldQuery.GetSingleton<PhysicsWorldSingleton>();
|
|
var rayInput = new RaycastInput
|
|
{
|
|
Start = ray.origin,
|
|
End = ray.origin + ray.direction * 500f,
|
|
Filter = new CollisionFilter
|
|
{
|
|
BelongsTo = ~0u,
|
|
CollidesWith = ~0u,
|
|
GroupIndex = 0
|
|
}
|
|
};
|
|
|
|
Entity hitEntity = Entity.Null;
|
|
float3 hitPosition = float3.zero;
|
|
|
|
if (physicsWorld.CastRay(rayInput, out var hit))
|
|
{
|
|
hitEntity = physicsWorld.Bodies[hit.RigidBodyIndex].Entity;
|
|
hitPosition = hit.Position;
|
|
}
|
|
else
|
|
{
|
|
// No hit — use ground plane intersection
|
|
if (ray.direction.y != 0)
|
|
{
|
|
float t = -ray.origin.y / ray.direction.y;
|
|
if (t > 0)
|
|
hitPosition = new float3(ray.origin.x + ray.direction.x * t, 0, ray.origin.z + ray.direction.z * t);
|
|
}
|
|
return; // Only move command if no entity hit
|
|
}
|
|
|
|
// Determine command type based on what was clicked
|
|
if (hitEntity != Entity.Null && em.Exists(hitEntity))
|
|
{
|
|
if (em.HasComponent<ResourceNodeTag>(hitEntity))
|
|
{
|
|
// Gather command for citizens
|
|
SendGatherCommands(hitEntity);
|
|
return;
|
|
}
|
|
|
|
if (em.HasComponent<UnitTag>(hitEntity) || em.HasComponent<BuildingTag>(hitEntity))
|
|
{
|
|
if (em.HasComponent<OwnerPlayer>(hitEntity))
|
|
{
|
|
var owner = em.GetComponentData<OwnerPlayer>(hitEntity);
|
|
// TODO: compare with local player id
|
|
// For now, treat as enemy → attack
|
|
SendAttackCommands(hitEntity);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (em.HasComponent<BuildingTag>(hitEntity) && em.HasComponent<UnderConstructionTag>(hitEntity))
|
|
{
|
|
// Build/repair command for citizens
|
|
SendBuildRepairCommands(hitEntity);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Default: move command
|
|
SendMoveCommands(hitPosition);
|
|
}
|
|
|
|
private void SendMoveCommands(float3 position)
|
|
{
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null) return;
|
|
var em = world.EntityManager;
|
|
|
|
foreach (var unit in SelectionManager.Instance.SelectedEntities)
|
|
{
|
|
if (!em.Exists(unit) || !em.HasComponent<UnitTag>(unit)) continue;
|
|
|
|
var rpcEntity = em.CreateEntity();
|
|
em.AddComponentData(rpcEntity, new MoveCommandRpc
|
|
{
|
|
UnitEntity = unit,
|
|
TargetPosition = position
|
|
});
|
|
em.AddComponent<SendRpcCommandRequest>(rpcEntity);
|
|
}
|
|
}
|
|
|
|
private void SendAttackCommands(Entity target)
|
|
{
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null) return;
|
|
var em = world.EntityManager;
|
|
|
|
foreach (var unit in SelectionManager.Instance.SelectedEntities)
|
|
{
|
|
if (!em.Exists(unit) || !em.HasComponent<UnitTag>(unit)) continue;
|
|
|
|
var rpcEntity = em.CreateEntity();
|
|
em.AddComponentData(rpcEntity, new AttackCommandRpc
|
|
{
|
|
AttackerEntity = unit,
|
|
TargetEntity = target
|
|
});
|
|
em.AddComponent<SendRpcCommandRequest>(rpcEntity);
|
|
}
|
|
}
|
|
|
|
private void SendGatherCommands(Entity resourceNode)
|
|
{
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null) return;
|
|
var em = world.EntityManager;
|
|
|
|
foreach (var unit in SelectionManager.Instance.SelectedEntities)
|
|
{
|
|
if (!em.Exists(unit) || !em.HasComponent<CitizenTag>(unit)) continue;
|
|
|
|
var rpcEntity = em.CreateEntity();
|
|
em.AddComponentData(rpcEntity, new GatherCommandRpc
|
|
{
|
|
CitizenEntity = unit,
|
|
ResourceNodeEntity = resourceNode
|
|
});
|
|
em.AddComponent<SendRpcCommandRequest>(rpcEntity);
|
|
}
|
|
}
|
|
|
|
private void SendBuildRepairCommands(Entity building)
|
|
{
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null) return;
|
|
var em = world.EntityManager;
|
|
|
|
foreach (var unit in SelectionManager.Instance.SelectedEntities)
|
|
{
|
|
if (!em.Exists(unit) || !em.HasComponent<CitizenTag>(unit)) continue;
|
|
|
|
var rpcEntity = em.CreateEntity();
|
|
em.AddComponentData(rpcEntity, new BuildRepairCommandRpc
|
|
{
|
|
CitizenEntity = unit,
|
|
BuildingEntity = building
|
|
});
|
|
em.AddComponent<SendRpcCommandRequest>(rpcEntity);
|
|
}
|
|
}
|
|
|
|
private void HandleStop()
|
|
{
|
|
var world = World.DefaultGameObjectInjectionWorld;
|
|
if (world == null) return;
|
|
var em = world.EntityManager;
|
|
|
|
foreach (var unit in SelectionManager.Instance.SelectedEntities)
|
|
{
|
|
if (!em.Exists(unit) || !em.HasComponent<UnitTag>(unit)) continue;
|
|
|
|
var rpcEntity = em.CreateEntity();
|
|
em.AddComponentData(rpcEntity, new StopCommandRpc
|
|
{
|
|
UnitEntity = unit
|
|
});
|
|
em.AddComponent<SendRpcCommandRequest>(rpcEntity);
|
|
}
|
|
}
|
|
}
|
|
}
|