Files
kronic a74b761363 Dev tool: switch player class (Warrior/Ranger) at runtime for testing
Editor-only class swap via the existing scalar dev-RPC family (new DebugOp.SetClass): F1/F2 keybind (ClassSwitchHotkeySystem), DebugOverlay '- Class -' buttons, and DebugCommandSendSystem.SetWarrior/SetRanger/SetClass statics. Server (DebugCommandReceiveSystem) swaps class in place on the spawned player: strips+re-seeds the ClassTraits StatModifier seeds, swaps the AbilityRef Fire slot, resets the ability cooldown, and heals a LIVING player to the new max (dead players skip the heal so respawn isn't raced). Server-authoritative + prediction-correct (same buffer-mutation path as GrantUpgrade); wire type unchanged so the RpcCollection hash is unaffected.

ClassTraits gains a shared Seeds core (spawn + swap can't drift), ClassSeedCount, IsClassSeed, a DynamicBuffer AppendSeeds overload, and Reapply. +3 EditMode tests (exact-count round-trip, value-equality fold, boundary/foreign-mod preservation); 351/351 green; Warrior<->Ranger round-trip Play-validated (server+client agree).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 21:23:33 -07:00

73 lines
3.4 KiB
C#

using Unity.NetCode;
namespace ProjectM.Simulation
{
/// <summary>
/// One-shot dev-tools command from a client to the server (the DebugOverlay / execute_code). A scalar-only
/// <see cref="IRpcCommand"/> (byte opcode + two ints, the project's scalar-RPC convention) so a single wire
/// type covers every dev action via <see cref="DebugOp"/>. **Unconditional (no #if)** — the reflection-built
/// RpcCollection hash must match across release/dev peers, so only the SEND/RECEIVE systems + overlay are
/// #if-gated, never this type. The server applies it authoritatively in DebugCommandReceiveSystem, so the dev
/// buttons exercise the real paths and work over a live connection too.
/// </summary>
public struct DebugCommandRequest : IRpcCommand
{
/// <summary>Which action to perform (see <see cref="DebugOp"/>).</summary>
public byte Op;
/// <summary>First scalar argument (size / item id / region id / amount — per-op).</summary>
public int ArgA;
/// <summary>Second scalar argument (count — per-op).</summary>
public int ArgB;
}
/// <summary>Opcodes for <see cref="DebugCommandRequest.Op"/> (bytes — never an enum on the wire).</summary>
public static class DebugOp
{
/// <summary>Arm + immediately fire a siege of ArgA Husks (ArgA = size).</summary>
public const byte SpawnWave = 0;
/// <summary>Collapse the current siege (cull Husks, stop spawning, clear pending) -> back to Calm.</summary>
public const byte EndSiege = 1;
/// <summary>Cull every living Husk now (leaves the phase alone).</summary>
public const byte ClearEnemies = 2;
/// <summary>Hard-reset the run-state to Calm (clears any siege/pending).</summary>
public const byte SetCalm = 3;
/// <summary>Deposit ArgB of resource ArgA (a <see cref="ResourceId"/>) into the shared ledger.</summary>
public const byte GrantResource = 4;
/// <summary>Grow the sender's damage upgrade by one tier (a debug StatModifier).</summary>
public const byte GrantUpgrade = 5;
/// <summary>Teleport the sender to region ArgA (a <see cref="RegionId"/>).</summary>
public const byte Teleport = 6;
/// <summary>Toggle the sender's <see cref="DebugGodMode"/> (damage immunity).</summary>
public const byte ToggleGod = 7;
/// <summary>Heal the sender to full.</summary>
public const byte Heal = 8;
/// <summary>Kill the sender (Health -> 0; the normal death/respawn loop takes over).</summary>
public const byte KillPlayer = 9;
/// <summary>Add ArgA to the long-arc goal charge.</summary>
public const byte AdvanceGoal = 10;
/// <summary>Set ThreatState.Heat to ArgA (inert until the Heat source ships).</summary>
public const byte SetHeat = 11;
/// <summary>Set the <see cref="TuningKnob"/> ArgA to ArgB/1000f (live dash/Charger feel-tuning; MC-0).</summary>
public const byte SetTuning = 12;
/// <summary>Swap the sender's class to ArgA (a <see cref="CharacterId"/> byte: Warrior=2 / Ranger=3).
/// Strips the old class trait seeds, re-seeds the new ones, swaps the Fire ability, and heals a living
/// player to the new class's max. Editor-only dev tool (class-switch); 0 / unknown -> Warrior.</summary>
public const byte SetClass = 13;
}
}