096c62862f
8 new EditMode tests (302 total, all green): BaseFieldSpawnSystem (target count + Base/Ore + cadence + top-up), ExpeditionFieldSystem teardown preserves the base field, ThreatDirector schedule arming, and melee harvest routing (base->ledger, expedition->personal inventory) which guards the cross-region leak the post-impl review caught. See DR-031. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
3.1 KiB
C#
58 lines
3.1 KiB
C#
using NUnit.Framework;
|
|
using ProjectM.Server;
|
|
using ProjectM.Simulation;
|
|
using Unity.Core;
|
|
using Unity.Entities;
|
|
using Unity.Mathematics;
|
|
using Unity.Transforms;
|
|
|
|
namespace ProjectM.Tests
|
|
{
|
|
/// <summary>
|
|
/// Regression test for the <see cref="ExpeditionFieldSystem"/> teardown region-filter. When the last player
|
|
/// leaves the expedition the field is cleared — but that teardown must destroy ONLY RegionTag{Expedition}
|
|
/// nodes, never the permanent RegionTag{Base} home-base mining field. Before the fix the unfiltered teardown
|
|
/// wiped every ResourceNode on the empty edge (a despawn storm beside base players that broke the core loop).
|
|
/// </summary>
|
|
public class ExpeditionFieldTeardownTests
|
|
{
|
|
[Test]
|
|
public void Expedition_Empty_Edge_Destroys_Only_Expedition_Nodes_Base_Field_Survives()
|
|
{
|
|
var world = new World("ExpeditionTeardown");
|
|
using (world)
|
|
{
|
|
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
|
group.AddSystemToUpdateList(world.GetOrCreateSystem<ExpeditionFieldSystem>());
|
|
group.SortSystems();
|
|
world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f));
|
|
var em = world.EntityManager;
|
|
|
|
// Cycle director: was occupied last tick, nobody out there now => the occupied->empty edge fires.
|
|
var cycle = em.CreateEntity(typeof(CycleState), typeof(CycleRuntime));
|
|
em.SetComponentData(cycle, new CycleState { Phase = CyclePhase.Calm, CycleNumber = 1 });
|
|
em.SetComponentData(cycle, new CycleRuntime { PrevExpeditionOccupied = 1 });
|
|
|
|
// Spawner singleton (required); null prefab so the spawn branch is inert.
|
|
var spawnerE = em.CreateEntity(typeof(ResourceFieldSpawner));
|
|
em.SetComponentData(spawnerE, new ResourceFieldSpawner { Prefab = Entity.Null, Count = 5, Radius = 10f });
|
|
|
|
var baseNode = em.CreateEntity(typeof(LocalTransform), typeof(ResourceNode), typeof(RegionTag));
|
|
em.SetComponentData(baseNode, LocalTransform.FromPosition(new float3(20, 0, 0)));
|
|
em.SetComponentData(baseNode, new ResourceNode { ResourceId = ResourceId.Ore, Remaining = 30, HarvestPerHit = 5f });
|
|
em.SetComponentData(baseNode, new RegionTag { Region = RegionId.Base });
|
|
|
|
var expNode = em.CreateEntity(typeof(LocalTransform), typeof(ResourceNode), typeof(RegionTag));
|
|
em.SetComponentData(expNode, LocalTransform.FromPosition(new float3(1020, 0, 0)));
|
|
em.SetComponentData(expNode, new ResourceNode { ResourceId = ResourceId.Aether, Remaining = 30, HarvestPerHit = 5f });
|
|
em.SetComponentData(expNode, new RegionTag { Region = RegionId.Expedition });
|
|
|
|
group.Update();
|
|
|
|
Assert.IsTrue(em.Exists(baseNode), "The permanent base mining field survives the expedition teardown.");
|
|
Assert.IsFalse(em.Exists(expNode), "Only the expedition node is cleared when the last player leaves.");
|
|
}
|
|
}
|
|
}
|
|
}
|