Hygiene B3: single-sourcing & magic-number consolidation

- StructureCatalogAuthoring: WallCostOre -> WallCostBiomass (it bakes a Biomass cost; [FormerlySerializedAs] preserves the scene value).
- Harvester/Fabricator authoring: resource-id byte defaults reference ResourceId.Ore/.Charge instead of magic 2/4.
- RegionMath.RegionBoundaryX (= ExpeditionOffsetX*0.5) single-sources the region-flip X used by HudSystem + OnboardingSystem (was 500f in 3 places).
- CharacterComponent.DefaultGroundedSharpness single-sources the CC sharpness 15f (GetDefault, DashSystem, PlayerDeathStateSystem, PlayerCharacterAuthoring).
- InventorySlot [InternalBufferCapacity] references Tuning.InventoryMaxSlots.
- ConnectionMode enum -> byte-const class (project convention; removes the latent enum-in-Burst trap); field + one Seed() param become byte.
- Tuning.ChargerWindupTicks single-sources the Charger telegraph windup (EnemyBaker + TuningConfig.Defaults; was a bare 30 that could drift).
- (TicksPerSecond deliberately NOT added: no seconds->ticks conversion site exists; the tick-count fields are per-authoring designer tunables, so a const would be unreferenced.)

451/451 EditMode tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-07 23:29:43 -07:00
parent 589e712db3
commit ba303e5fd0
16 changed files with 37 additions and 21 deletions
@@ -11,17 +11,19 @@ namespace ProjectM.Simulation
/// <c>NetworkStreamRequestConnect</c> and clears <see cref="Requested"/>. Direct IP/LAN only for now —
/// Unity Relay is deferred to a later pass. Created per-world as a singleton.
/// </summary>
public enum ConnectionMode : byte
/// <summary>Connection intent values (byte-const, matching the project's op/id convention — not an enum, so
/// they stay safe if a consumer is ever [BurstCompile]d). Written to <see cref="ConnectionConfig.Mode"/>.</summary>
public static class ConnectionMode
{
None,
Host,
Join,
public const byte None = 0;
public const byte Host = 1;
public const byte Join = 2;
}
public struct ConnectionConfig : IComponentData
{
/// <summary>What to do with this world's network stream.</summary>
public ConnectionMode Mode;
public byte Mode;
/// <summary>Dotted IPv4 to connect to (Join). Ignored for Host (binds AnyIpv4).</summary>
public FixedString64Bytes Address;
@@ -74,7 +74,7 @@ namespace ProjectM.Simulation
RecoverTailTicks = 9f,
DashCooldownTicks = 36f, // tune: was 45 (0.75s) -> 0.60s, snappier horde-kiter cadence
DashSharpness = 200f,
ChargerWindupTicks = 30f,
ChargerWindupTicks = Tuning.ChargerWindupTicks,
ChargerLungeSpeed = 16f,
ChargerLungeDurationTicks = 18f,
ChargerWhiffStaggerTicks = 36f,
@@ -23,7 +23,7 @@ namespace ProjectM.Simulation
/// subscene MUST be re-baked (consistently in both worlds) or the connect handshake desyncs.
/// </summary>
[GhostComponent(OwnerSendType = SendToOwnerType.All)]
[InternalBufferCapacity(24)]
[InternalBufferCapacity(Tuning.InventoryMaxSlots)]
public struct InventorySlot : IBufferElementData
{
/// <summary>Item carried in this slot (0 = empty/unused; aligns with InventoryMath's 0-id no-op).</summary>
@@ -16,6 +16,10 @@ namespace ProjectM.Simulation
[Serializable]
public struct CharacterComponent : IComponentData
{
/// <summary>The CC's default grounded-movement smoothing sharpness — the single source for GetDefault, the
/// authoring default, DashSystem's base, and the death-state reset.</summary>
public const float DefaultGroundedSharpness = 15f;
/// <summary>How quickly RelativeVelocity is lerped toward the target velocity on the ground.</summary>
public float GroundedMovementSharpness;
@@ -26,7 +30,7 @@ namespace ProjectM.Simulation
{
return new CharacterComponent
{
GroundedMovementSharpness = 15f,
GroundedMovementSharpness = DefaultGroundedSharpness,
StepAndSlopeHandling = BasicStepAndSlopeHandlingParameters.GetDefault(),
};
}
@@ -32,7 +32,7 @@ namespace ProjectM.Simulation
// Feel knobs are LIVE-tunable via the TuningConfig singleton (MC-0): OnUpdate reads it each tick and falls
// back to TuningConfig.Defaults() when absent (release builds / EditMode), so behaviour is identical to the
// old baked consts. DefaultSharpness (the restore target) + SimTickRate stay compile-time (not tuned).
const float DefaultSharpness = 15f; // CharacterComponent.GetDefault() base
const float DefaultSharpness = CharacterComponent.DefaultGroundedSharpness; // CharacterComponent.GetDefault() base
const float SimTickRate = 60f;
[BurstCompile]
@@ -48,7 +48,7 @@ namespace ProjectM.Simulation
if (SystemAPI.HasComponent<CharacterComponent>(entity))
{
var cc = SystemAPI.GetComponent<CharacterComponent>(entity);
cc.GroundedMovementSharpness = 15f;
cc.GroundedMovementSharpness = CharacterComponent.DefaultGroundedSharpness;
SystemAPI.SetComponent(entity, cc);
}
}
@@ -51,6 +51,11 @@ namespace ProjectM.Simulation
/// under interp lag (>= ~250ms reaction + interp buffer; Slice 1 readability). 0/1 = near-instant (legacy).</summary>
public const int AttackWindupTicks = 22;
/// <summary>Charger lunge telegraph windup (ticks). Single source shared by EnemyBaker (the baked client
/// danger-ramp denominator) and TuningConfig.Defaults, mirroring the Grunt AttackWindupTicks pattern so the
/// baked telegraph can't drift from the server windup.</summary>
public const int ChargerWindupTicks = 30;
// ---- Production / automation (M7: Harvester/Conveyor/Fabricator) ----
/// <summary>Max production cycles a single machine awards in one process (bounds within-session
@@ -42,6 +42,10 @@ namespace ProjectM.Simulation
/// transiently-coexisting arenas can never interact in the shared PhysicsWorld.</summary>
public const float RoomStrideX = 500f;
/// <summary>Region-flip boundary X (half the expedition offset): a player/camera past this reads as the
/// +1000 expedition region. Single source for the HUD / onboarding / atmosphere region checks (DR-013).</summary>
public const float RegionBoundaryX = ExpeditionOffsetX * 0.5f;
/// <summary>
/// World-space origin of expedition room sub-slot <paramref name="subSlot"/> (0 or 1 — the run FSM
/// ping-pongs consecutive rooms between two offsets so the next room spawns at the idle slot while the