Fix: dying on an expedition soft-bricked the player — respawn now resets RegionTag to Base

PlayerRespawnSystem teleported a recovered player to base coords but never reset its server-only RegionTag
(every other region-mover flips RegionTag + Position together). So dying ON an expedition left you at base
still tagged Expedition: GhostRelevancy hid all base ghosts from you, base enemies ignored you, and the
expedition field/zone-director kept counting you as "still out there" (waves never stopped). No self-recovery.

- PlayerRespawnSystem: add RefRW<RegionTag> to the recovery query + set Region=Base alongside the reposition.
- Harden: drop the hard RequireForUpdate<PlayerSpawner> (a transiently-missing spawner could strand dead
  players downed forever) -> TryGetSingleton with a BaseAnchor fallback, early-return only if both are absent.
- PlayerRespawnSystemTests: add RegionTag to the harness + a regression (expedition death -> respawn at base
  with RegionTag reset to Base). 390/390 EditMode.

Investigation: combat-overhaul workflow wf_c6c87dc5-9c3 (death lane). Base-death case was already correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 22:44:33 -07:00
parent 8596cc74b1
commit 3c1b5c44cd
2 changed files with 39 additions and 7 deletions
@@ -44,6 +44,7 @@ namespace ProjectM.Tests
em.AddComponentData(e, new GhostOwner { NetworkId = networkId });
em.AddComponentData(e, new EffectiveCharacterStats { MaxHealth = maxHealth });
em.AddComponent<PlayerTag>(e);
em.AddComponentData(e, new RegionTag { Region = RegionId.Base });
return e;
}
@@ -103,5 +104,27 @@ namespace ProjectM.Tests
Assert.AreEqual(50f, em.GetComponentData<Health>(player).Current, 1e-4f, "Alive health is untouched.");
}
}
[Test]
public void Expedition_Death_Respawns_At_Base_And_Resets_RegionTag()
{
// Death-fix regression: a player who dies ON an expedition (RegionTag=Expedition) must respawn at base
// AND have its RegionTag reset to Base, or it soft-bricks (RegionRelevancy hides all base ghosts).
var (world, group) = MakeWorld("RespawnExpedition", serverTick: 200);
using (world)
{
var em = world.EntityManager;
var player = MakePlayer(em, health: 0f, maxHealth: 100f, respawnTick: 160,
delayTicks: 60, invulnTicks: 120, pos: new float3(1005, 1, 3), networkId: 1);
em.SetComponentData(player, new RegionTag { Region = RegionId.Expedition }); // died out on the sortie
group.Update();
Assert.AreEqual(RegionId.Base, em.GetComponentData<RegionTag>(player).Region,
"Death fix: respawn resets RegionTag to Base (else the player is stranded tagged Expedition).");
Assert.Less(em.GetComponentData<LocalTransform>(player).Position.x, 100f,
"And is repositioned from the expedition (x~1005) back to the base ring.");
}
}
}
}