using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Transforms;
namespace ProjectM.Client
{
///
/// Client-only sender for build + upgrade RPCs. Keyboard: B builds a Turret at the local player's current
/// grid cell; U upgrades ability damage. Editor-only statics (PlaceStructure / PlaceTurret / UpgradeAbility)
/// drive the same path from execute_code for headless validation (a one-shot key can't be injected on an
/// unfocused editor — the StorageOpSendSystem idiom). Managed SystemBase (reads the Input System);
/// UnityEngine.InputSystem is fully qualified to avoid the ProjectM.Simulation.PlayerInput name collision.
/// The server re-validates legality + cost authoritatively; this only sends a hint.
///
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial class BuildSendSystem : SystemBase
{
#if UNITY_EDITOR
struct PendingBuild { public byte Type; public int CellX; public int CellZ; }
static readonly System.Collections.Generic.Queue s_PendingBuild =
new System.Collections.Generic.Queue();
static int s_PendingUpgrades = 0;
/// EDITOR / execute_code hook: queue a structure placement at a specific cell.
public static void PlaceStructure(byte type, int cellX, int cellZ) =>
s_PendingBuild.Enqueue(new PendingBuild { Type = type, CellX = cellX, CellZ = cellZ });
/// EDITOR / execute_code hook: queue a turret placement at a specific cell.
public static void PlaceTurret(int cellX, int cellZ) => PlaceStructure(StructureType.Turret, cellX, cellZ);
/// EDITOR / execute_code hook: queue an ability-damage upgrade.
public static void UpgradeAbility() => s_PendingUpgrades++;
#endif
protected override void OnCreate()
{
RequireForUpdate();
}
protected override void OnUpdate()
{
if (!SystemAPI.TryGetSingletonEntity(out var connection))
return;
var keyboard = UnityEngine.InputSystem.Keyboard.current;
if (keyboard != null)
{
if (keyboard.bKey.wasPressedThisFrame && TryGetLocalPlayerCell(out int2 cell))
SendBuild(connection, StructureType.Turret, cell.x, cell.y);
if (keyboard.uKey.wasPressedThisFrame)
SendUpgrade(connection);
}
#if UNITY_EDITOR
while (s_PendingBuild.Count > 0)
{
var b = s_PendingBuild.Dequeue();
SendBuild(connection, b.Type, b.CellX, b.CellZ);
}
while (s_PendingUpgrades > 0)
{
s_PendingUpgrades--;
SendUpgrade(connection);
}
#endif
}
bool TryGetLocalPlayerCell(out int2 cell)
{
cell = default;
if (!SystemAPI.TryGetSingleton(out var anchor))
return false;
foreach (var xform in SystemAPI.Query>().WithAll())
{
cell = BaseGridMath.WorldToCell(anchor, xform.ValueRO.Position);
return true;
}
return false;
}
void SendBuild(Entity connection, byte type, int cellX, int cellZ)
{
var e = EntityManager.CreateEntity();
EntityManager.AddComponentData(e, new BuildPlaceRequest { StructureType = type, CellX = cellX, CellZ = cellZ });
EntityManager.AddComponentData(e, new SendRpcCommandRequest { TargetConnection = connection });
}
void SendUpgrade(Entity connection)
{
var e = EntityManager.CreateEntity();
EntityManager.AddComponentData(e, new AbilityUpgradeRequest());
EntityManager.AddComponentData(e, new SendRpcCommandRequest { TargetConnection = connection });
}
}
}