using NUnit.Framework; using ProjectM.Server; using ProjectM.Simulation; using Unity.Core; using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; namespace ProjectM.Tests { /// /// Plain-Entities EditMode tests for the server-only (walk-in region /// transit). A bare world is seeded with an ExpeditionGate (+ LocalTransform) and a player /// (RegionTag + LocalTransform + PlayerTag). A player whose region matches the gate's FromRegion and who is /// within the gate radius is transited (RegionTag flipped + LocalTransform teleported to ArrivalPos). /// Returning to base signals the ThreatDirector (the post-expedition retaliation source) exactly once. Pins /// the proximity gate, the region/radius guards, and the return signal. /// public class ExpeditionGateSystemTests { static (World world, SimulationSystemGroup group) MakeWorld(string name) { var world = new World(name); var group = world.GetOrCreateSystemManaged(); group.AddSystemToUpdateList(world.GetOrCreateSystem()); group.SortSystems(); world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f)); return (world, group); } static void MakeGate(EntityManager em, float3 pos, byte from, byte to, float radius, float3 arrival) { var e = em.CreateEntity(); em.AddComponentData(e, LocalTransform.FromPosition(pos)); em.AddComponentData(e, new ExpeditionGate { FromRegion = from, ToRegion = to, Radius = radius, ArrivalPos = arrival }); } static Entity MakePlayer(EntityManager em, float3 pos, byte region) { var e = em.CreateEntity(); em.AddComponentData(e, LocalTransform.FromPosition(pos)); em.AddComponentData(e, new RegionTag { Region = region }); em.AddComponent(e); return e; } [Test] public void Player_In_Gate_Radius_Is_Transited_And_Teleported() { var (world, group) = MakeWorld("GateTransitWorld"); using (world) { var em = world.EntityManager; var arrival = new float3(1000, 1, 0); MakeGate(em, new float3(0, 1, 0), RegionId.Base, RegionId.Expedition, radius: 15f, arrival: arrival); var player = MakePlayer(em, new float3(5, 1, 0), RegionId.Base); group.Update(); Assert.AreEqual(RegionId.Expedition, em.GetComponentData(player).Region, "Region flips to the gate's ToRegion."); var p = em.GetComponentData(player).Position; Assert.AreEqual(1000f, p.x, 1e-3f, "Player is teleported to the gate's ArrivalPos (x)."); Assert.AreEqual(0f, p.z, 1e-3f, "Player is teleported to the gate's ArrivalPos (z)."); } } [Test] public void Player_Outside_Radius_Is_Not_Transited() { var (world, group) = MakeWorld("GateNoTransitWorld"); using (world) { var em = world.EntityManager; MakeGate(em, new float3(0, 1, 0), RegionId.Base, RegionId.Expedition, radius: 15f, arrival: new float3(1000, 1, 0)); var player = MakePlayer(em, new float3(50, 1, 0), RegionId.Base); group.Update(); Assert.AreEqual(RegionId.Base, em.GetComponentData(player).Region, "A player beyond the gate radius stays in its region."); } } [Test] public void Player_Wrong_Region_Is_Not_Transited() { var (world, group) = MakeWorld("GateWrongRegionWorld"); using (world) { var em = world.EntityManager; // Gate only acts on players currently in the Base region. MakeGate(em, new float3(0, 1, 0), RegionId.Base, RegionId.Expedition, radius: 15f, arrival: new float3(1000, 1, 0)); var player = MakePlayer(em, new float3(1, 1, 0), RegionId.Expedition); group.Update(); Assert.AreEqual(RegionId.Expedition, em.GetComponentData(player).Region, "A player whose region does not match FromRegion is ignored even inside the radius."); } } [Test] public void Return_To_Base_Signals_ThreatDirector_Once() { var (world, group) = MakeWorld("GateReturnSignalWorld"); using (world) { var em = world.EntityManager; MakeGate(em, new float3(0, 1, 0), RegionId.Expedition, RegionId.Base, radius: 15f, arrival: new float3(0, 1, 0)); MakePlayer(em, new float3(3, 1, 0), RegionId.Expedition); var threat = em.CreateEntity(typeof(ThreatState)); em.SetComponentData(threat, new ThreatState()); group.Update(); var ts = em.GetComponentData(threat); Assert.AreEqual(1, ts.PendingReturns, "Returning to base signals the ThreatDirector exactly once (the gate teleports the returner out of its radius)."); Assert.AreEqual(1, ts.ExpeditionsCompleted, "A completed expedition is counted."); } } } }