Files
Project-M/Assets/_Project/Scripts/Simulation/World/RegionComponents.cs
T
kronic ba303e5fd0 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>
2026-07-07 23:29:43 -07:00

82 lines
4.2 KiB
C#

using Unity.Entities;
using Unity.Mathematics;
namespace ProjectM.Simulation
{
/// <summary>
/// Identifies which world REGION an entity belongs to. M6 splits the single server world into two
/// spatial regions at a large coordinate offset — the persistent home <see cref="RegionId.Base"/> and
/// the procedurally-arranged <see cref="RegionId.Expedition"/> — and uses per-connection GhostRelevancy
/// to replicate each region only to the connections whose player is currently in it. Server-side only
/// (NOT a [GhostField]; the server makes all relevancy decisions). Added to players on spawn and to
/// every region-scoped ghost the server spawns. Untagged ghosts are global (relevant to everyone).
/// </summary>
public struct RegionTag : IComponentData
{
/// <summary>Region id (see <see cref="RegionId"/>): 0 = base, 1 = expedition.</summary>
public byte Region;
}
/// <summary>Region ids for <see cref="RegionTag.Region"/> (a byte, not an enum, to keep server/Burst code trivial).</summary>
public static class RegionId
{
/// <summary>The persistent, shared home base.</summary>
public const byte Base = 0;
/// <summary>The procedural expedition field (offset far from the base on +X).</summary>
public const byte Expedition = 1;
}
/// <summary>
/// Deterministic mapping of a region id to its world-space origin. The base region keeps the existing
/// home-base coordinates; the expedition region lives at a large +X offset so the two never overlap in
/// the single shared PhysicsWorld. Pure (no RNG/wall-clock) — server-authoritative teleports and field
/// spawners resolve region positions through here.
/// </summary>
public static class RegionMath
{
/// <summary>World-space X offset of the expedition region (room sub-slot 0) from the base region.</summary>
public const float ExpeditionOffsetX = 1000f;
/// <summary>X stride between the two ping-pong room sub-slots — kept >= any sweep/AI/aggro range so two
/// 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
/// cleared one is torn down). THE single expedition coordinate authority: every expedition placement
/// (field scatter, enemy ring, party teleport) resolves through here.
/// </summary>
public static float3 ExpeditionRoomOrigin(float3 baseCenter, byte subSlot)
{
return baseCenter + new float3(ExpeditionOffsetX + subSlot * RoomStrideX, 0f, 0f);
}
/// <summary>World-space position of the room-exit PORTAL for sub-slot <paramref name="subSlot"/> — the single
/// client-derivable authority the HUD prompt AND the presentation beacon both resolve through (DR-046), so they
/// can't drift. = the room origin nudged by <see cref="Tuning.PortalOffsetZ"/> in Z.</summary>
public static float3 ExpeditionPortalPos(float3 baseCenter, byte subSlot)
{
float3 p = ExpeditionRoomOrigin(baseCenter, subSlot);
p.z += Tuning.PortalOffsetZ;
return p;
}
/// <summary>World-space origin of <paramref name="region"/>, given the base center (BaseGridMath.PlotCenter).
/// The expedition resolves to room sub-slot 0 (legacy call sites; room-aware systems pass the ACTIVE
/// sub-slot to <see cref="ExpeditionRoomOrigin"/> directly).</summary>
public static float3 RegionOrigin(byte region, float3 baseCenter)
{
return region == RegionId.Expedition
? ExpeditionRoomOrigin(baseCenter, 0)
: baseCenter;
}
}
}