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.");
}
}
}
}