using ProjectM.Simulation;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
namespace ProjectM.Server
{
///
/// Server-only walk-in gate transit: a player who walks within a gate's radius (and whose region matches the
/// gate's ) is transited to the gate's ToRegion at its ArrivalPos
/// (RegionTag flipped + LocalTransform teleported — GhostRelevancy re-scopes their ghosts, as in
/// RegionTransitSystem). Returning to BASE signals the ThreatDirector (a completed expedition can draw a
/// retaliation siege) by incrementing . Plain server
/// SimulationSystemGroup, ordered BEFORE CyclePhaseSystem (Gate -> ThreatDirector -> RunState) so the return is
/// consumed the same tick. Arrival points are offset from the destination gate so a transited player does not
/// immediately re-trigger.
///
[BurstCompile]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
[UpdateInGroup(typeof(SimulationSystemGroup))]
[UpdateBefore(typeof(CyclePhaseSystem))]
public partial struct ExpeditionGateSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// Snapshot gates once.
var gateFrom = new NativeList(Allocator.Temp);
var gateTo = new NativeList(Allocator.Temp);
var gateRadiusSq = new NativeList(Allocator.Temp);
var gatePos = new NativeList(Allocator.Temp);
var gateArrival = new NativeList(Allocator.Temp);
foreach (var (gate, xform) in SystemAPI.Query, RefRO>())
{
gateFrom.Add(gate.ValueRO.FromRegion);
gateTo.Add(gate.ValueRO.ToRegion);
gateRadiusSq.Add(gate.ValueRO.Radius * gate.ValueRO.Radius);
gatePos.Add(xform.ValueRO.Position.xz);
gateArrival.Add(gate.ValueRO.ArrivalPos);
}
bool returnedToBase = false;
foreach (var (region, xform) in
SystemAPI.Query, RefRW>().WithAll())
{
byte r = region.ValueRO.Region;
float2 pp = xform.ValueRO.Position.xz;
for (int i = 0; i < gateFrom.Length; i++)
{
if (gateFrom[i] != r) continue;
if (math.distancesq(pp, gatePos[i]) > gateRadiusSq[i]) continue;
region.ValueRW.Region = gateTo[i];
xform.ValueRW.Position = gateArrival[i];
if (gateTo[i] == RegionId.Base)
returnedToBase = true;
break;
}
}
gateFrom.Dispose();
gateTo.Dispose();
gateRadiusSq.Dispose();
gatePos.Dispose();
gateArrival.Dispose();
// A player returned to base from an expedition -> signal the ThreatDirector (it sizes/arms any
// retaliation siege). The gate teleports the returner out of its radius, so this fires once per return.
if (returnedToBase && SystemAPI.TryGetSingletonEntity(out var threatEntity))
{
var threat = SystemAPI.GetComponent(threatEntity);
threat.PendingReturns += 1;
threat.ExpeditionsCompleted += 1;
SystemAPI.SetComponent(threatEntity, threat);
}
}
}
}