Game Scene Split up
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
using NUnit.Framework;
|
||||
using ProjectM.Server;
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Core;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Plain-Entities EditMode tests for the server-only <see cref="ThreatDirectorSystem"/> — the composite
|
||||
/// base-attack scheduler. A bare world is seeded with a NetworkTime singleton and a CycleDirector entity
|
||||
/// carrying CycleState + ThreatState + ThreatConfig. These pin the post-expedition source (a return arms a
|
||||
/// siege of the configured size, with simultaneous returns de-duped to one), that the event-siege size is the
|
||||
/// config floor — never the WaveSystem escalation curve — that the telegraph ArmTick is now + delay, and that
|
||||
/// an unattended siege auto-collapses after the timeout (no soft-lock). All timing is wrap-safe NetworkTick.
|
||||
/// </summary>
|
||||
public class ThreatDirectorSystemTests
|
||||
{
|
||||
static (World world, SimulationSystemGroup group) MakeWorld(string name, uint serverTick)
|
||||
{
|
||||
var world = new World(name);
|
||||
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
||||
group.AddSystemToUpdateList(world.GetOrCreateSystem<ThreatDirectorSystem>());
|
||||
group.SortSystems();
|
||||
world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f));
|
||||
var em = world.EntityManager;
|
||||
var nt = em.CreateEntity(typeof(NetworkTime));
|
||||
em.SetComponentData(nt, new NetworkTime { ServerTick = new NetworkTick(serverTick) });
|
||||
return (world, group);
|
||||
}
|
||||
|
||||
static ThreatConfig DefaultConfig() => new ThreatConfig
|
||||
{
|
||||
PostExpeditionEnabled = 1,
|
||||
PostExpeditionDelayTicks = 300,
|
||||
SizeBase = 5,
|
||||
SizePerExpeditionResource = 0,
|
||||
StartCondition = ThreatStartCondition.Immediate,
|
||||
SiegeTimeoutTicks = 3600,
|
||||
};
|
||||
|
||||
static Entity MakeDirector(EntityManager em, byte phase, ThreatState threat, ThreatConfig config)
|
||||
{
|
||||
var e = em.CreateEntity(typeof(CycleState), typeof(ThreatState), typeof(ThreatConfig));
|
||||
em.SetComponentData(e, new CycleState { Phase = phase, CycleNumber = 1 });
|
||||
em.SetComponentData(e, threat);
|
||||
em.SetComponentData(e, config);
|
||||
return e;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PostExpedition_Return_Edge_Sets_PendingSiegeSize()
|
||||
{
|
||||
var (world, group) = MakeWorld("ThreatReturn", serverTick: 200);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var dir = MakeDirector(em, CyclePhase.Calm, new ThreatState { PendingReturns = 1 }, DefaultConfig());
|
||||
|
||||
group.Update();
|
||||
|
||||
var ts = em.GetComponentData<ThreatState>(dir);
|
||||
Assert.AreEqual(5, ts.PendingSiegeSize, "A return arms a siege of SizeBase Husks.");
|
||||
Assert.AreNotEqual(0u, ts.ArmTick, "The siege is armed with a telegraph tick.");
|
||||
Assert.AreEqual(0, ts.PendingReturns, "The return is consumed.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Multi_Player_Simultaneous_Return_Charges_Pending_Once()
|
||||
{
|
||||
var (world, group) = MakeWorld("ThreatMultiReturn", serverTick: 200);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var dir = MakeDirector(em, CyclePhase.Calm, new ThreatState { PendingReturns = 3 }, DefaultConfig());
|
||||
|
||||
group.Update();
|
||||
|
||||
var ts = em.GetComponentData<ThreatState>(dir);
|
||||
Assert.AreEqual(5, ts.PendingSiegeSize, "Three simultaneous returns still arm exactly one siege (de-dup).");
|
||||
Assert.AreEqual(0, ts.PendingReturns, "All returns are consumed.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Siege_Size_Equals_Config_Not_Escalation_Curve()
|
||||
{
|
||||
var (world, group) = MakeWorld("ThreatSizeConfig", serverTick: 200);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var dir = MakeDirector(em, CyclePhase.Calm, new ThreatState { PendingReturns = 1 }, DefaultConfig());
|
||||
// A high wave number must NOT influence the event-siege size.
|
||||
var w = em.CreateEntity(typeof(WaveState));
|
||||
em.SetComponentData(w, new WaveState { WaveNumber = 30 });
|
||||
|
||||
group.Update();
|
||||
|
||||
Assert.AreEqual(5, em.GetComponentData<ThreatState>(dir).PendingSiegeSize,
|
||||
"Event-siege size is the config SizeBase, never the WaveSystem escalation curve.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StartCondition_Immediate_Arms_Via_ArmTick()
|
||||
{
|
||||
var (world, group) = MakeWorld("ThreatArm", serverTick: 1000);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var config = DefaultConfig();
|
||||
config.PostExpeditionDelayTicks = 120;
|
||||
var dir = MakeDirector(em, CyclePhase.Calm, new ThreatState { PendingReturns = 1 }, config);
|
||||
|
||||
group.Update();
|
||||
|
||||
Assert.AreEqual(1120u, em.GetComponentData<ThreatState>(dir).ArmTick,
|
||||
"Immediate start arms the siege at now + the telegraph delay (1000 + 120).");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Empty_Base_Siege_Auto_Resolves_Bounded()
|
||||
{
|
||||
var (world, group) = MakeWorld("ThreatTimeout", serverTick: 200);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var config = DefaultConfig();
|
||||
config.SiegeTimeoutTicks = 60;
|
||||
// SiegeStartTick 100, now 200 => 100 ticks elapsed > 60 timeout.
|
||||
var dir = MakeDirector(em, CyclePhase.Siege, new ThreatState { SiegeStartTick = 100 }, config);
|
||||
|
||||
var w = em.CreateEntity(typeof(WaveState));
|
||||
em.SetComponentData(w, new WaveState { RemainingToSpawn = 2, Phase = WavePhase.Spawning });
|
||||
|
||||
// Three Husks still on the field with no one to clear them.
|
||||
for (int i = 0; i < 3; i++)
|
||||
em.CreateEntity(typeof(EnemyTag));
|
||||
|
||||
group.Update();
|
||||
|
||||
using var huskQuery = em.CreateEntityQuery(typeof(EnemyTag));
|
||||
Assert.AreEqual(0, huskQuery.CalculateEntityCount(),
|
||||
"A timed-out (unattended) siege culls the remaining Husks so it can never soft-lock.");
|
||||
Assert.AreEqual(0, em.GetComponentData<WaveState>(w).RemainingToSpawn,
|
||||
"The wave stops spawning when the siege collapses.");
|
||||
Assert.AreEqual(0u, em.GetComponentData<ThreatState>(dir).SiegeStartTick,
|
||||
"The siege clock resets after collapse.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user