143 lines
6.3 KiB
C#
143 lines
6.3 KiB
C#
using NUnit.Framework;
|
|
using ProjectM.Server;
|
|
using ProjectM.Simulation;
|
|
using Unity.Core;
|
|
using Unity.Entities;
|
|
using Unity.NetCode;
|
|
using Unity.Transforms;
|
|
|
|
namespace ProjectM.Tests
|
|
{
|
|
/// <summary>
|
|
/// Plain-Entities EditMode tests for the server-only <see cref="WaveSystem"/> (Husk wave/threat director).
|
|
/// A bare world is seeded with NetworkTime + CycleState singletons and a director entity carrying
|
|
/// WaveDirector + WaveState + a WaveEnemyPrefab buffer (whose prefab is a real <c>Prefab</c>-tagged entity so
|
|
/// it is excluded from the alive-Husk query and Instantiate yields plain Husk instances). Pins: a due Lull
|
|
/// starts the next (escalating) wave; Spawning emits one Husk per interval; the director is gated off outside
|
|
/// Defend; a fully-spawned, cleared wave returns to Lull.
|
|
/// </summary>
|
|
public class WaveSystemTests
|
|
{
|
|
static (World world, SimulationSystemGroup group) MakeWorld(string name, uint serverTick, byte cyclePhase)
|
|
{
|
|
var world = new World(name);
|
|
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
|
group.AddSystemToUpdateList(world.GetOrCreateSystem<WaveSystem>());
|
|
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) });
|
|
var cyc = em.CreateEntity(typeof(CycleState));
|
|
em.SetComponentData(cyc, new CycleState { Phase = cyclePhase });
|
|
return (world, group);
|
|
}
|
|
|
|
static Entity MakeHuskPrefab(EntityManager em)
|
|
{
|
|
var e = em.CreateEntity(typeof(LocalTransform), typeof(EnemyTag));
|
|
em.AddComponent<Prefab>(e); // real prefab: excluded from EnemyTag queries; Instantiate strips the tag
|
|
return e;
|
|
}
|
|
|
|
static Entity MakeDirector(EntityManager em, Entity huskPrefab, byte phase, int waveNumber,
|
|
uint nextActionTick, int remainingToSpawn, int spawnCounter)
|
|
{
|
|
var e = em.CreateEntity(typeof(WaveDirector), typeof(WaveState));
|
|
em.SetComponentData(e, new WaveDirector
|
|
{
|
|
RingRadius = 10f, RingSlots = 12, BaseCount = 3, CountPerWave = 2,
|
|
SpawnIntervalTicks = 10, LullTicks = 5,
|
|
});
|
|
em.SetComponentData(e, new WaveState
|
|
{
|
|
Phase = phase, WaveNumber = waveNumber, NextActionTick = nextActionTick,
|
|
RemainingToSpawn = remainingToSpawn, SpawnCounter = spawnCounter,
|
|
});
|
|
var buf = em.AddBuffer<WaveEnemyPrefab>(e);
|
|
buf.Add(new WaveEnemyPrefab { Prefab = huskPrefab });
|
|
return e;
|
|
}
|
|
|
|
static int HuskCount(EntityManager em)
|
|
{
|
|
using var q = em.CreateEntityQuery(typeof(EnemyTag));
|
|
return q.CalculateEntityCount();
|
|
}
|
|
|
|
[Test]
|
|
public void Due_Lull_Starts_Wave_With_Escalating_Count()
|
|
{
|
|
var (world, group) = MakeWorld("WaveLullStart", serverTick: 100, cyclePhase: CyclePhase.Defend);
|
|
using (world)
|
|
{
|
|
var em = world.EntityManager;
|
|
var prefab = MakeHuskPrefab(em);
|
|
var dir = MakeDirector(em, prefab, WavePhase.Lull, waveNumber: 0, nextActionTick: 100, remainingToSpawn: 0, spawnCounter: 0);
|
|
|
|
group.Update();
|
|
|
|
var w = em.GetComponentData<WaveState>(dir);
|
|
Assert.AreEqual(WavePhase.Spawning, w.Phase, "A due lull starts spawning.");
|
|
Assert.AreEqual(1, w.WaveNumber, "Wave number advances.");
|
|
Assert.AreEqual(3, w.RemainingToSpawn, "Wave 1 count = BaseCount + (1-1)*CountPerWave = 3.");
|
|
Assert.AreEqual(0, HuskCount(em), "No Husk is spawned on the lull->spawning transition tick itself.");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Spawning_Emits_One_Husk_And_Decrements_Remaining()
|
|
{
|
|
var (world, group) = MakeWorld("WaveSpawnOne", serverTick: 100, cyclePhase: CyclePhase.Defend);
|
|
using (world)
|
|
{
|
|
var em = world.EntityManager;
|
|
var prefab = MakeHuskPrefab(em);
|
|
var dir = MakeDirector(em, prefab, WavePhase.Spawning, waveNumber: 1, nextActionTick: 100, remainingToSpawn: 3, spawnCounter: 0);
|
|
|
|
group.Update();
|
|
|
|
Assert.AreEqual(1, HuskCount(em), "One Husk spawns this interval.");
|
|
var w = em.GetComponentData<WaveState>(dir);
|
|
Assert.AreEqual(2, w.RemainingToSpawn, "RemainingToSpawn decrements.");
|
|
Assert.AreEqual(1, w.SpawnCounter, "SpawnCounter advances (drives ring slot + round-robin).");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Director_Is_Gated_Off_Outside_Defend()
|
|
{
|
|
var (world, group) = MakeWorld("WaveGated", serverTick: 100, cyclePhase: CyclePhase.Expedition);
|
|
using (world)
|
|
{
|
|
var em = world.EntityManager;
|
|
var prefab = MakeHuskPrefab(em);
|
|
var dir = MakeDirector(em, prefab, WavePhase.Lull, waveNumber: 0, nextActionTick: 100, remainingToSpawn: 0, spawnCounter: 0);
|
|
|
|
group.Update();
|
|
|
|
var w = em.GetComponentData<WaveState>(dir);
|
|
Assert.AreEqual(WavePhase.Lull, w.Phase, "Outside Defend the director does not run.");
|
|
Assert.AreEqual(0, w.WaveNumber, "Wave number stays put outside Defend.");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Fully_Spawned_Cleared_Wave_Returns_To_Lull()
|
|
{
|
|
var (world, group) = MakeWorld("WaveCleared", serverTick: 100, cyclePhase: CyclePhase.Defend);
|
|
using (world)
|
|
{
|
|
var em = world.EntityManager;
|
|
var prefab = MakeHuskPrefab(em);
|
|
var dir = MakeDirector(em, prefab, WavePhase.Spawning, waveNumber: 1, nextActionTick: 100, remainingToSpawn: 0, spawnCounter: 3);
|
|
|
|
group.Update();
|
|
|
|
Assert.AreEqual(WavePhase.Lull, em.GetComponentData<WaveState>(dir).Phase,
|
|
"A fully-spawned wave with no live Husks returns to Lull.");
|
|
}
|
|
}
|
|
}
|
|
}
|