LANTERN purge B3+B5: delete the cycle/core/win-lose spine + onboarding; save epoch v7
Deletes CyclePhaseSystem, GoalReachedSystem, CoreDamage/CoreRestore, ThreatDirector, CoreIntegrity/GoalProgress/RunPhase/RunOutcome/ThreatState components, CoreVisualFeedbackSystem, and the whole Client/Onboarding slice (+6 test files). Keepers reworked: RunDirectorSystem (UpdateBefore attr + launch guard + goal/threat bank removed; sole SaveRequest raiser now), CycleDirectorSpawnSystem (ledger/meta host only), WaveSystem UNGATED (waves run wherever a WaveDirector is baked), EnemyAISystem core-fallback stripped, AmbientAudioSystem reworked (bed + run cues; no CycleState gate), MusicSystem RunInfo-only, HudSystem big trim (goal meter, core bar, siege banner, terminal banner, outcome flash, onboarding hook all gone), MetaShop/ClassPrep/AimReticle siege gates dropped, DebugOverlay/ops re-meant (SpawnWave=force next wave, EndSiege=quiet arena; SetCalm/AdvanceGoal/SetHeat retired, bytes reserved), TuningConfig Core knobs retired (ids 20-23 reserved), StorageMath.DrainFraction deleted, HowToPlay copy rewritten. Save epoch v7 (fresh epoch, operator-approved): SaveData drops goal/core/outcome + conveyor/machine-IO fields; MinLoadableVersion=7; PendingSave/PendingStructure trimmed; RollTerminalCampaignForward deleted; SaveStructureScan signature slimmed. 390 tests green; Play world-creation clean (player + waves live, no exceptions). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,13 +85,7 @@ namespace ProjectM.Server
|
||||
decoyPositions.Add(dx.ValueRO.Position);
|
||||
}
|
||||
|
||||
// END-1: the Engine Core is a FALLBACK target. When no living player/structure remains, undefended
|
||||
// Husks march on the base heart (PlotCenter) so the base can be overrun instead of the swarm idling.
|
||||
bool coreAlive = SystemAPI.HasSingleton<BaseAnchor>()
|
||||
&& SystemAPI.TryGetSingleton<CoreIntegrity>(out var coreInteg) && coreInteg.Current > 0;
|
||||
float3 corePos = coreAlive ? BaseGridMath.PlotCenter(SystemAPI.GetSingleton<BaseAnchor>()) : float3.zero;
|
||||
|
||||
if (playerEntities.Length == 0 && structureEntities.Length == 0 && decoyEntities.Length == 0 && !coreAlive)
|
||||
if (playerEntities.Length == 0 && structureEntities.Length == 0 && decoyEntities.Length == 0)
|
||||
{
|
||||
playerEntities.Dispose();
|
||||
playerPositions.Dispose();
|
||||
@@ -137,7 +131,6 @@ namespace ProjectM.Server
|
||||
{
|
||||
float3 pos = xform.ValueRO.Position;
|
||||
byte huskRegion = region.ValueRO.Region;
|
||||
bool huskCoreAlive = coreAlive && huskRegion == RegionId.Base;
|
||||
|
||||
// Knockback overrides seek/strike for its window — EnemyAISystem stays the SOLE writer of Position.
|
||||
var kb = knockback.ValueRO;
|
||||
@@ -178,12 +171,10 @@ namespace ProjectM.Server
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tgtIdx < 0 && !huskCoreAlive)
|
||||
continue; // no decoy, no player/structure, and no Core -> nothing to seek
|
||||
targetEntity = tgtIdx < 0 ? Entity.Null
|
||||
: (tgtIsStruct ? structureEntities[tgtIdx] : playerEntities[tgtIdx]);
|
||||
targetPos = tgtIdx < 0 ? corePos
|
||||
: (tgtIsStruct ? structurePositions[tgtIdx] : playerPositions[tgtIdx]);
|
||||
if (tgtIdx < 0)
|
||||
continue; // no decoy, no player/structure -> nothing to seek
|
||||
targetEntity = tgtIsStruct ? structureEntities[tgtIdx] : playerEntities[tgtIdx];
|
||||
targetPos = tgtIsStruct ? structurePositions[tgtIdx] : playerPositions[tgtIdx];
|
||||
}
|
||||
|
||||
// Seek: stop just inside strike range so the Husk holds position to attack.
|
||||
@@ -271,7 +262,6 @@ namespace ProjectM.Server
|
||||
{
|
||||
float3 pos = xform.ValueRO.Position;
|
||||
byte cHuskRegion = region.ValueRO.Region;
|
||||
bool cHuskCoreAlive = coreAlive && cHuskRegion == RegionId.Base;
|
||||
|
||||
// 1. Knockback wins (and cancels any in-flight lunge so Position keeps a single writer).
|
||||
var kb = knockback.ValueRO;
|
||||
@@ -296,12 +286,10 @@ namespace ProjectM.Server
|
||||
|
||||
// EB-1 fortress aggro: same weighted target selection as the Grunt pass (shared helper).
|
||||
EnemyAIMath.PickWeightedNearest(pos, playerPositions, playerRegions, structurePositions, structureRegions, cHuskRegion, structAggro, out bool cIsStruct, out int cIdx);
|
||||
if (cIdx < 0 && !cHuskCoreAlive)
|
||||
if (cIdx < 0)
|
||||
continue;
|
||||
Entity cTargetEntity = cIdx < 0 ? Entity.Null
|
||||
: (cIsStruct ? structureEntities[cIdx] : playerEntities[cIdx]);
|
||||
float3 cTargetPos = cIdx < 0 ? corePos
|
||||
: (cIsStruct ? structurePositions[cIdx] : playerPositions[cIdx]);
|
||||
Entity cTargetEntity = cIsStruct ? structureEntities[cIdx] : playerEntities[cIdx];
|
||||
float3 cTargetPos = cIsStruct ? structurePositions[cIdx] : playerPositions[cIdx];
|
||||
|
||||
// 2. Lunge active: travel the locked direction; damage on contact, or stagger on a wall-stop whiff.
|
||||
var lg = lunge.ValueRO;
|
||||
@@ -414,7 +402,6 @@ namespace ProjectM.Server
|
||||
{
|
||||
float3 pos = xform.ValueRO.Position;
|
||||
byte sRegion = region.ValueRO.Region;
|
||||
bool sCoreAlive = coreAlive && sRegion == RegionId.Base;
|
||||
|
||||
// 1. Knockback overrides everything (sole Position writer preserved).
|
||||
var kb = knockback.ValueRO;
|
||||
@@ -434,14 +421,12 @@ namespace ProjectM.Server
|
||||
knockback.ValueRW.UntilTick = 0;
|
||||
}
|
||||
|
||||
// 2. Target (region-scoped shared helper); Core fallback like the Grunt/Charger passes.
|
||||
// 2. Target (region-scoped shared helper); no target -> idle.
|
||||
EnemyAIMath.PickWeightedNearest(pos, playerPositions, playerRegions, structurePositions, structureRegions, sRegion, structAggro, out bool sIsStruct, out int sIdx);
|
||||
if (sIdx < 0 && !sCoreAlive)
|
||||
if (sIdx < 0)
|
||||
continue;
|
||||
Entity sTargetEntity = sIdx < 0 ? Entity.Null
|
||||
: (sIsStruct ? structureEntities[sIdx] : playerEntities[sIdx]);
|
||||
float3 sTargetPos = sIdx < 0 ? corePos
|
||||
: (sIsStruct ? structurePositions[sIdx] : playerPositions[sIdx]);
|
||||
Entity sTargetEntity = sIsStruct ? structureEntities[sIdx] : playerEntities[sIdx];
|
||||
float3 sTargetPos = sIsStruct ? structurePositions[sIdx] : playerPositions[sIdx];
|
||||
|
||||
// 3. Range-band movement: advance if too far, retreat if too close, hold in-band. Face the target.
|
||||
var sp = spitter.ValueRO;
|
||||
@@ -504,7 +489,7 @@ namespace ProjectM.Server
|
||||
float sDist = math.length(sToTarget);
|
||||
bool sInBand = math.abs(sDist - sp.PreferredRange) <= sp.RangeTolerance;
|
||||
bool sCornered = sDist <= sp.CorneredRange;
|
||||
if (sReady && (sInBand || sCornered) && (sTargetEntity != Entity.Null || sCoreAlive))
|
||||
if (sReady && (sInBand || sCornered))
|
||||
{
|
||||
uint wTicks = (uint)math.max(1, sp.WindupTicks);
|
||||
windup.ValueRW.WindUpUntilTick = TickUtil.NonZero(now + wTicks);
|
||||
@@ -625,9 +610,8 @@ namespace ProjectM.Server
|
||||
}
|
||||
|
||||
EnemyAIMath.PickWeightedNearest(npos, playerPositions, playerRegions, structurePositions, structureRegions, nRegion, structAggro, out bool nIsStruct, out int nIdx);
|
||||
bool nCoreAlive = coreAlive && nRegion == RegionId.Base;
|
||||
bool hasTarget = nIdx >= 0 || nCoreAlive;
|
||||
float3 nTarget = nIdx < 0 ? corePos : (nIsStruct ? structurePositions[nIdx] : playerPositions[nIdx]);
|
||||
bool hasTarget = nIdx >= 0;
|
||||
float3 nTarget = nIdx < 0 ? npos : (nIsStruct ? structurePositions[nIdx] : playerPositions[nIdx]);
|
||||
bool wantsToClose = hasTarget && !committed
|
||||
&& math.distance(npos.xz, nTarget.xz) > nstats.ValueRO.AttackRange * 1.15f;
|
||||
|
||||
|
||||
@@ -39,9 +39,6 @@ namespace ProjectM.Server
|
||||
if (!serverTick.IsValid)
|
||||
return;
|
||||
uint now = serverTick.TickIndexForValidTick;
|
||||
// Player-driven loop: the base-defense wave only spawns during a Siege.
|
||||
if (SystemAPI.TryGetSingleton<CycleState>(out var cycle) && cycle.Phase != CyclePhase.Siege)
|
||||
return;
|
||||
|
||||
var director = SystemAPI.GetSingleton<WaveDirector>();
|
||||
var directorEntity = SystemAPI.GetSingletonEntity<WaveDirector>();
|
||||
|
||||
Reference in New Issue
Block a user