Slice 1: combat readability + HUD declutter (DR-038)
Four playtest do-now wins: - Enemy health bars: pooled world-space Canvas, on-damage-sticky + fade, always-on <25% HP (CombatFeedbackSystem; no new replication). - Telegraph fix: new baked client-safe EnemyTelegraph sizes the danger-cone ramp per enemy (0->1 ending at impact, fixes the Charger plateau); windup 18->22; a windup scale-pulse. - Build-mode toggle: BuildPaletteState.PaletteOpen hides the palette by default, Tab / gamepad-Y toggles, with a discovery chip (HudSystem/BuildSendSystem). - Charger committed-lunge tell: [GhostEnabledBit] IsLunging derived once/tick from LungeState (the Dead idiom); the danger cone persists through the lunge. 345/345 EditMode (+3 IsLunging derive tests); Play-validated: ghost-hash change did not break the handshake, bake correct (telegraph on all enemies, IsLunging baked-disabled on the Charger, replicated to client), no runtime errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,8 @@ namespace ProjectM.Tests
|
||||
em.AddComponentData(e, new KnockbackState());
|
||||
em.AddComponentData(e, new AttackWindup());
|
||||
em.AddComponentData(e, new LungeState());
|
||||
em.AddComponent<IsLunging>(e);
|
||||
em.SetComponentEnabled<IsLunging>(e, false); // baked DISABLED on the real Charger (spawns not-lunging)
|
||||
em.AddComponent<EnemyTag>(e);
|
||||
return e;
|
||||
}
|
||||
@@ -124,5 +126,65 @@ namespace ProjectM.Tests
|
||||
"The recoiling Charger moved along its knockback direction (-X), not its lunge direction.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Commit_Enables_IsLunging()
|
||||
{
|
||||
var (world, group) = MakeWorld("ChargerIsLungingCommit", 200);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
MakePlayer(em, new float3(3, 1, 0));
|
||||
var charger = MakeCharger(em, new float3(0, 1, 0));
|
||||
em.SetComponentData(charger, new AttackWindup { WindUpUntilTick = 200 }); // elapses this tick -> commit
|
||||
Assert.IsFalse(em.IsComponentEnabled<IsLunging>(charger), "Charger spawns not-lunging (baked DISABLED).");
|
||||
|
||||
group.Update(); // tick 200: commit the lunge
|
||||
|
||||
Assert.AreNotEqual(0u, em.GetComponentData<LungeState>(charger).UntilTick, "Sanity: the lunge committed.");
|
||||
Assert.IsTrue(em.IsComponentEnabled<IsLunging>(charger),
|
||||
"The replicated mid-lunge cue is ENABLED while a committed lunge is live (.WithPresent visits the disabled entity to write the bit).");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Whiff_Disables_IsLunging()
|
||||
{
|
||||
var (world, group) = MakeWorld("ChargerIsLungingWhiff", 206);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
MakePlayer(em, new float3(-10, 1, 0));
|
||||
var charger = MakeCharger(em, new float3(0, 1, 0));
|
||||
em.SetComponentData(charger, new LungeState { Dir = new float2(1, 0), Speed = 16f, UntilTick = 205 }); // expiring
|
||||
em.SetComponentEnabled<IsLunging>(charger, true); // was mid-lunge
|
||||
|
||||
group.Update(); // tick 206 > 205 -> overshoot whiff clears the lunge
|
||||
|
||||
Assert.AreEqual(0u, em.GetComponentData<LungeState>(charger).UntilTick, "Sanity: the whiffed lunge cleared.");
|
||||
Assert.IsFalse(em.IsComponentEnabled<IsLunging>(charger), "The cue clears the tick the lunge ends (whiff).");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Knockback_Disables_IsLunging()
|
||||
{
|
||||
var (world, group) = MakeWorld("ChargerIsLungingKnockback", 305);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
MakePlayer(em, new float3(10, 1, 0));
|
||||
var charger = MakeCharger(em, new float3(0, 1, 0));
|
||||
em.SetComponentData(charger, new LungeState { Dir = new float2(1, 0), Speed = 16f, UntilTick = 320 });
|
||||
em.SetComponentData(charger, new KnockbackState { Dir = new float2(-1, 0), Speed = 10f, UntilTick = 315 });
|
||||
em.SetComponentEnabled<IsLunging>(charger, true); // mid-lunge before the knockback
|
||||
|
||||
group.Update(); // tick 305: knockback cancels the lunge (UntilTick -> 0) via the mid-body continue path
|
||||
|
||||
Assert.AreEqual(0u, em.GetComponentData<LungeState>(charger).UntilTick, "Sanity: knockback cancelled the lunge.");
|
||||
Assert.IsFalse(em.IsComponentEnabled<IsLunging>(charger),
|
||||
"The cue clears when knockback cancels the lunge (covers the mid-body continue exit path).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user