Phase 1 B1/B2/B4-B7: separation, poise, boss lunge, party HP scale, run-failed banner, fire anim
B1 SEPARATION: pairwise soft-collision pass INSIDE EnemyAISystem (sole enemy-Position writer preserved; O(n^2) fine at MaxAlive<=14). Knocked/ lunging enemies keep committed motion but still push neighbours; the boss is never pushed; enemies yield a small personal radius around players (below melee range per review B1-1); displacement goes through the swept move so nothing shoves through walls. SeparationMaxSpeed = live knob. B2 POISE: the windup-cancel lived in the knockback CONTINUE branches (grunt/ charger/spitter). Threshold on the EXISTING KnockbackState.Speed channel: light melee (6) nudges without interrupting; the finisher (10.8) and cone (8) stagger as before. StaggerKnockbackSpeed = live knob (default 7). B4 BOSS LUNGE: a telegraphed gap-closer on its own cooldown when the target sits outside slam reach (repositioning - the slam stays the damage beat). BossState.PendingAttack (server-only byte) disambiguates the shared windup elapse (review-confirmed: naive reuse would SLAM on a lunge elapse); LungeState.UntilTick spans windup+travel so the existing IsLunging ghost bit replicates the tell, and the client draws a travel wedge instead of the slam ring while it is set. B5 PARTY HP SCALE: boss Health x(1 + 0.75/extra LIVING expedition player) at spawn (not RunParticipant - dead-respawned members park at base). Max is a GhostField so the bar stays truthful. B6 RUN-FAILED BANNER: silent wipes used to land players home with zero explanation. Client detects the (in-run)->Staging edge with a launch-cached Charge (NEVER Returning - a 1-tick transient that precedes the bank by a tick) and shows EXPEDITION FAILED for 6 s. B7 FIRE ANIM: the class ability finally moves the body - a stateless window from replicated AbilityCooldown.NextFireTick minus derived CooldownTicks (works for predicted local + interpolated remotes, no cached edges). 456/456 EditMode; live Play smoke: launch -> 5 kills credit the room clear through the corpse window -> boon window opens -> corpses expire clean, no exceptions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,11 @@ namespace ProjectM.Simulation
|
||||
// than a normal one; GoalReachedSystem also math.max(1, ...) at the use-site.
|
||||
public float FinalSiegeMultiplier;
|
||||
|
||||
// Phase 1 B1/B2 (design review wf_fd177263): poise threshold on the EXISTING KnockbackState.Speed channel
|
||||
// (light melee 6 nudges; finisher 6x1.8=10.8 and cone 8 stagger), and the separation pass's push-speed cap.
|
||||
public float StaggerKnockbackSpeed;
|
||||
public float SeparationMaxSpeed;
|
||||
|
||||
/// <summary>The baked feel defaults == the pre-MC-0 consts. Single source of truth for the fallback path.</summary>
|
||||
public static TuningConfig Defaults() => new TuningConfig
|
||||
{
|
||||
@@ -88,6 +93,8 @@ namespace ProjectM.Simulation
|
||||
CoreRegenIntervalTicks = 18f, // END-1: +1 integrity / 0.3s in Calm (~30s to refill 100 from 0)
|
||||
CoreOverrunDrainPct = 0.5f, // END-1: a breach costs half the shared ledger (soft-loss penalty)
|
||||
FinalSiegeMultiplier = 2.5f, // END-2: the final siege is ~2.5x the would-be-next normal siege
|
||||
StaggerKnockbackSpeed = 7f, // B2 poise: kb.Speed >= this interrupts windups/lunges; below = nudge only
|
||||
SeparationMaxSpeed = 3f, // B1: max separation push (units/s) so soft-collision can't fling
|
||||
};
|
||||
|
||||
/// <summary>Clamp a knob to its safe floor: tick knobs >= 1, value knobs >= 0. Used by every write path
|
||||
@@ -109,6 +116,8 @@ namespace ProjectM.Simulation
|
||||
case TuningKnob.StructureAggroWeight:
|
||||
case TuningKnob.CoreDamagePerHusk:
|
||||
case TuningKnob.CoreOverrunDrainPct:
|
||||
case TuningKnob.StaggerKnockbackSpeed:
|
||||
case TuningKnob.SeparationMaxSpeed:
|
||||
return math.max(0f, value);
|
||||
// tick knobs: >= 1 (a 0 tick count is degenerate; a 0 i-frame window divides-by-zero in DashSystem).
|
||||
// FinalSiegeMultiplier also lands here on purpose — a final siege should never be < 1x a normal one.
|
||||
@@ -147,6 +156,8 @@ namespace ProjectM.Simulation
|
||||
case TuningKnob.CoreRegenIntervalTicks: c.CoreRegenIntervalTicks = value; break;
|
||||
case TuningKnob.CoreOverrunDrainPct: c.CoreOverrunDrainPct = value; break;
|
||||
case TuningKnob.FinalSiegeMultiplier: c.FinalSiegeMultiplier = value; break;
|
||||
case TuningKnob.StaggerKnockbackSpeed: c.StaggerKnockbackSpeed = value; break;
|
||||
case TuningKnob.SeparationMaxSpeed: c.SeparationMaxSpeed = value; break;
|
||||
// unknown index -> no-op (matches the no-default switch convention in DebugCommandReceiveSystem)
|
||||
}
|
||||
}
|
||||
@@ -180,6 +191,8 @@ namespace ProjectM.Simulation
|
||||
case TuningKnob.CoreRegenIntervalTicks: return c.CoreRegenIntervalTicks;
|
||||
case TuningKnob.CoreOverrunDrainPct: return c.CoreOverrunDrainPct;
|
||||
case TuningKnob.FinalSiegeMultiplier: return c.FinalSiegeMultiplier;
|
||||
case TuningKnob.StaggerKnockbackSpeed: return c.StaggerKnockbackSpeed;
|
||||
case TuningKnob.SeparationMaxSpeed: return c.SeparationMaxSpeed;
|
||||
default: return 0f;
|
||||
}
|
||||
}
|
||||
@@ -211,6 +224,8 @@ namespace ProjectM.Simulation
|
||||
CoreRegenIntervalTicks = c.CoreRegenIntervalTicks,
|
||||
CoreOverrunDrainPct = c.CoreOverrunDrainPct,
|
||||
FinalSiegeMultiplier = c.FinalSiegeMultiplier,
|
||||
StaggerKnockbackSpeed = c.StaggerKnockbackSpeed,
|
||||
SeparationMaxSpeed = c.SeparationMaxSpeed,
|
||||
};
|
||||
|
||||
/// <summary>Reconstruct the full config from a wire snapshot (FULL state, not a delta).</summary>
|
||||
@@ -240,6 +255,8 @@ namespace ProjectM.Simulation
|
||||
CoreRegenIntervalTicks = r.CoreRegenIntervalTicks,
|
||||
CoreOverrunDrainPct = r.CoreOverrunDrainPct,
|
||||
FinalSiegeMultiplier = r.FinalSiegeMultiplier,
|
||||
StaggerKnockbackSpeed = r.StaggerKnockbackSpeed,
|
||||
SeparationMaxSpeed = r.SeparationMaxSpeed,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -270,9 +287,11 @@ namespace ProjectM.Simulation
|
||||
public const byte CoreRegenIntervalTicks = 21;
|
||||
public const byte CoreOverrunDrainPct = 22;
|
||||
public const byte FinalSiegeMultiplier = 23;
|
||||
public const byte StaggerKnockbackSpeed = 24;
|
||||
public const byte SeparationMaxSpeed = 25;
|
||||
|
||||
/// <summary>Knob count (overlay iteration bound).</summary>
|
||||
public const byte Count = 24;
|
||||
public const byte Count = 26;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -307,5 +326,7 @@ namespace ProjectM.Simulation
|
||||
public float CoreRegenIntervalTicks;
|
||||
public float CoreOverrunDrainPct;
|
||||
public float FinalSiegeMultiplier;
|
||||
public float StaggerKnockbackSpeed;
|
||||
public float SeparationMaxSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user