Continued
This commit is contained in:
@@ -139,6 +139,10 @@ The M6 core-loop slice (Expedition→Defend→Build) + the base/expedition world
|
||||
- **Run an adversarial design-review Workflow (3 critics: netcode/relevancy, determinism/prediction, reuse/scope → synthesize) BEFORE coding a netcode-heavy slice** — for M6 Stage 2 it caught every one of the above pre-implementation (relevancy trap, singleton collision, dt-trap, double-destroy, lazy-create hazard).
|
||||
- **`manage_gameobject create` `component_properties` SILENTLY DROPS enum + Vector3 fields** (it set object-refs and simple scalars, but baked authoring enums/`Vector3` stayed at their C# defaults — two gates baked identical, one worked only by coincidence). **Always set those via a follow-up `manage_components set_property` (with a `properties` dict) and VERIFY through the `mcpforunity://scene/gameobject/{id}/component/{Type}` resource** (or, for a ghost, by reading the baked component in `execute_code` after Play). Same caveat applies to `manage_prefabs modify_contents` `component_properties`. Per-renderer color via `manage_material set_renderer_color` defaults to a runtime **PropertyBlock that does NOT persist into Play** — create a material asset (`manage_material create`) and `assign_material_to_renderer`, or use a prefab-stage assign, for colors that survive a domain reload.
|
||||
- **Walk-in region gates (M6 visibility pass):** a baked `ExpeditionGate{FromRegion,ToRegion,Radius,ArrivalPos}` entity (visible primitive, collider stripped so you pass through) + a server `ExpeditionGateSystem` (plain group, `[UpdateAfter(CyclePhaseSystem)]`) proximity-transits a player whose `RegionTag` matches `FromRegion` (flip RegionTag + teleport to `ArrivalPos`, offset from the destination gate so no re-trigger). Returning to base mid-Expedition expires the cycle timer → Defend ("timer cap + early return"). The expedition is a *place* = cosmetic ground/pillars in **SampleScene** at the +1000 offset (classic URP, like SyntyWorld), not the DOTS subscene; gameplay nodes/gates are the baked subscene entities.
|
||||
- **Build/automation foundation (M6 Stage 3, the M7 contract):** generic `PlacedStructure{[GhostField] byte Type; int2 Cell (server-only); uint NextTick; uint LastProcessedTick}` on an ownerless interpolated ghost (`RegionTag{Base}`, world-owned, runtime-spawned). **Bake the two tick fields NOW** — the turret reuses `NextTick` as its fire cooldown, and they are the deterministic-offline-catch-up linchpin M7 needs and that can't be reconstructed retroactively. Only `Type` replicates (client derives `Cell` from `LocalTransform` via `BaseGridMath.WorldToCell`). Data-driven `StructureCatalog` buffer (`{byte Type; Entity Prefab; byte CostResourceId; int CostAmount}`, modeled on `AbilityPrefabElement`); M7 adds a recipe column additively. **Occupancy is DERIVED** by scanning live structure ghosts into a Temp `NativeHashSet<int2>` (structures are the source of truth — restart/replay-safe), NEVER a mutable buffer on the immutable baked `BaseAnchor`.
|
||||
- **Co-op placement atomicity:** `BuildPlaceSystem` commits the `StorageMath.Withdraw` + cell-reservation **IN-PLACE inside the RPC foreach** (only the `Instantiate` goes through the ECB) — the `StorageOpReceiveSystem` idiom — so two same-tick `BuildPlaceRequest`s for one cell can't both pass (validated: → exactly one structure + one withdraw). RPC carries `int CellX/CellZ` scalars, not `int2` (scalar-only RPC precedent).
|
||||
- **Buildable turret = hitscan = reversed `EnemyAISystem`:** snapshot living Husks, nearest-in-same-region-within-Range, on the `NextTick` cooldown append a direct `DamageEvent{Damage, SourceNetworkId=-1}` → reuses `HealthApplyDamageSystem` (despawns at HP≤0). NO projectile → no tunnelling, no friendly-fire/team model. Plain server group `[UpdateAfter(PredictedSimulationSystemGroup)]`.
|
||||
- **Resource-gated ability tiers reuse `StatModifier` — no new replicated component.** `AbilityUpgradeSystem` spends Aether and grows ONE `StatModifier{Target=Damage, Op=PercentAdd, SourceId=<sentinel>}` on the player (**replace-by-SourceId** so the `[InternalBufferCapacity(8)]` buffer stays bounded — repeated upgrades grow one row, not append); `StatRecomputeSystem` folds it into `EffectiveAbilityStats.Damage` on both worlds (the `UpgradePickup` path). `GoalProgress{[GhostField] int Charge, Target}` lives on the global CycleDirector ghost, single-writer in `CyclePhaseSystem`. **Disk-persistence writer is deferred to post-M7** (in-session-only state, per DR-008); freeze the save schema + bake the structure tick fields now so it's additive. See [[DR-014_M6_Build_Structures_Automation_Foundation]].
|
||||
|
||||
## Bootstrap & worlds
|
||||
|
||||
|
||||
Reference in New Issue
Block a user