Docs: base-mining cohesive-loop session log + DR-031; CLAUDE.md base-local loop
Session log + DR-031 (base-local mining, any-attack harvest, scheduled base sieges, Synty asset swap) capturing the diagnosis, locked operator forks, both adversarial reviews, and the tuning knobs. CLAUDE.md: base-local loop is now the model (BaseFieldSpawnSystem + harvest region-routing + ThreatDirector Schedule source); net-neutral condensation of M7/biome/HUD reference bullets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
---
|
||||
date: 2026-06-11
|
||||
type: session
|
||||
tags:
|
||||
- session
|
||||
- economy
|
||||
- combat
|
||||
- harvesting
|
||||
- netcode
|
||||
- assets
|
||||
- loop-cohesion
|
||||
permalink: gamevault/07-sessions/2026/2026-06-11-base-mining-cohesive-loop
|
||||
---
|
||||
|
||||
# Base-local mining — consolidate combat + economy into one cohesive playable loop
|
||||
|
||||
> Operator: "A lot of work has been done on combat & some on inventory & equipment; currently you cannot mine any resources with the base. Consolidate all the changes so it's a cohesive playable loop. Also get rid of any placeholder assets for ores/turrets/anything else — you have plenty of real SM to choose from." Full [[dots-dev]] Feature track under **ultracode** (pre-code adversarial design review + post-code adversarial review, both workflowed). Locked → [[DR-031_Base_Mining_Loop_Cohesion]].
|
||||
|
||||
## Diagnosis — why you couldn't mine at the base
|
||||
The combat half and the economy half were **structurally divorced**, and the economy half was effectively unreachable:
|
||||
- **Resource nodes only spawned in the Expedition region** (`RegionMath.ExpeditionOffsetX = 1000`u) and only while a player physically stood out there (`ExpeditionFieldSystem` keys off per-player presence). **Nothing was harvestable at the home base.**
|
||||
- The **only** path to the Expedition is an unsignposted walk-in `ExpeditionGate` — and **nothing in client code sends `RegionTransitRequest`** (no button, no prompt).
|
||||
- Harvest was **projectile-only**, and MC-4 ([[DR-030_MC4_Combo_Melee_Primary_Verb]]) demoted ranged to the *secondary* button.
|
||||
- **The hidden loop-breaker:** the ONLY wired siege source was post-expedition retaliation (`ThreatDirectorSystem` arms only on `ThreatState.PendingReturns`, set only by `ExpeditionGateSystem` on a base return). In a base-only loop nobody ever returns → **no siege ever arms → zero waves → all the dash/charger/melee combat work never triggers.**
|
||||
- Everything was rendered as **placeholder cubes**.
|
||||
|
||||
## Locked forks (operator)
|
||||
1. **Mine at the base** (not the expedition round-trip). 2. **Any attack harvests** (melee cleave AND ranged). 3. **Natural/stone-frontier art**.
|
||||
|
||||
## What shipped
|
||||
### A — Base-local mining (server-only, reuses the node ghost; NO new replicated state)
|
||||
- New `BaseFieldSpawner`/`BaseFieldRuntime` components + `BaseFieldSpawnerAuthoring` (one in the Gameplay subscene, prefab = the existing `ResourceNode.prefab`).
|
||||
- New `BaseFieldSpawnSystem` (plain server group): tops the live `RegionTag{Base}` node count up to `TargetCount` (10) on a `NetworkTick` cadence; scatters **uniform-in-radius** in the annulus `[InnerRadius, OuterRadius]` (23.5–27) around `BaseGridMath.PlotCenter`; overrides each instance via **`SetComponent` (not Add)** to `RegionTag{Base}` + `ResourceId.Ore` (Ore-only — the build currency, no round-robin). First pass fires immediately; RNG seeded from a monotonic `Epoch` (never the tick). Runtime-spawned → dodges the prespawn handshake.
|
||||
|
||||
### B — Any-attack harvest
|
||||
- `MeleeComboSystem`'s **server-only** cleave block now also gathers `ResourceNode`/`BlightClutter` in the cone, deposits, writes `Remaining` back (so `WorldFeedbackSystem` chips fire on melee mining), destroys at-most-once. **Region routing mirrors the projectile path:** Base node → shared `ResourceLedger` directly; Expedition/un-tagged → the swinging player's PERSONAL `InventorySlot` (via an `OwnerId`→player map), spill-to-ledger. Only deplete if the yield actually landed (no zero-credit consume).
|
||||
- `ResourceHarvestSystem` (projectile) routes Base-region nodes straight to the ledger via an **optional** `ComponentLookup<RegionTag>` (NOT a required query column — that would drop the un-tagged test fixtures + Expedition clutter); default un-tagged → owner-inventory path (preserves `InventoryHarvestTests`).
|
||||
- **Why base→ledger:** the build economy spends EXCLUSIVELY from the ledger (`BuildPlaceSystem`/`BuildSendSystem`/HUD all read it). Routing base harvest there makes "mine → build" work with zero `G`-deposit friction; the expedition keeps the DR-026 personal-haul.
|
||||
|
||||
### F — Base siege cadence (the loop-closer)
|
||||
- Activated the reserved **Schedule** source in `ThreatDirectorSystem`: arms a `SizeBase + ScheduleSizePerWave*WaveNumber` siege every `ScheduleIntervalTicks` with NO expedition trip; defers `NextScheduledTick` while a siege runs (guaranteed calm/build window even on a long siege). Composes with the post-expedition source (guarded by `Phase==Calm && PendingSiegeSize==0`). New `ThreatConfig.ScheduleSizePerWave`; `CycleDirectorAuthoring` bakes `ScheduleEnabled=true`, `Interval=2700` (45s), `PerWave=1`.
|
||||
|
||||
### Blocker fix — ExpeditionFieldSystem teardown
|
||||
- Its occupied→empty teardown destroyed EVERY `ResourceNode` unfiltered → would wipe the new permanent base field the first time the expedition emptied. Region-filtered the node destroy to `Expedition`-only (clutter stays unfiltered — there is no base clutter).
|
||||
|
||||
### C — Real art (swap mesh+material; ghost/authoring/LinkedEntityGroup untouched)
|
||||
Mechanism: `PrefabUtility.LoadPrefabContents` → strip the root placeholder `MeshFilter`+`MeshRenderer`, reset root `LocalScale` to 1 (Scale is a `[GhostField]` propagated by spawners/BuildPlaceSystem — cosmetic scale goes on the nested child), **nest the Synty model as a "Model" child**, strip its colliders, assign the pack atlas to bare FBXs (Synty FBX import gives a blank "Lit" material — the pre-made *prefabs* carry the atlas) → `SaveAsPrefabAsset`. Uniform nest works for single- and multi-mesh (the Ballista is 7 GameObjects → a root mesh-swap would render 1/7).
|
||||
|
||||
| Prefab | → Synty (PolygonFantasyKingdom) |
|
||||
|---|---|
|
||||
| ResourceNode | `SM_Prop_Crystal_01` (crystal, +atlas) |
|
||||
| Turret | `SiegeEngines/SM_Wep_Ballista_Mounted_01` |
|
||||
| Wall | `SM_Prop_Spike_Wall_01` (palisade) |
|
||||
| Pylon | `SM_Prop_Crystal_01` (tall beacon) |
|
||||
| Storage | `Furniture/SM_Prop_Chest_01` |
|
||||
| BlightClutter | `Environments/SM_Env_Rock_Chunk_01` |
|
||||
| UpgradePickup | `SM_Prop_Crystal_01` (gem, floating) |
|
||||
|
||||
Automation machines (Harvester/Fabricator/Conveyor) **trimmed from the build palette** (catalog refs nulled on the subscene `StructureCatalogAuthoring`) — code stays, no placeholder cube ever shown. Palette is now `[Turret, Wall, Pylon]`.
|
||||
|
||||
### HUD
|
||||
- Replaced the stale "deploy through the gate" copy with phase-aware loop guidance: Calm → "MINE THE CRYSTALS — any attack harvests Ore, then BUILD"; Siege → "DEFEND THE BASE — hold the line".
|
||||
|
||||
## Adversarial reviews (both workflowed under ultracode)
|
||||
- **Pre-code** (6 lenses → synthesis): caught all 3 blockers BEFORE coding — the ExpeditionFieldSystem teardown wipe, the missing base siege source, and base-harvest-must-credit-the-ledger — plus the optional-ComponentLookup trap, the SetComponent-not-Add override, the measured annulus radii, the nest-not-root-swap for the 7-part Ballista. **This is why the slice landed clean.**
|
||||
- **Post-code** (3 lenses → adversarial verify → synthesis): found 2 real defects I'd introduced when I simplified the melee path: (1) MAJOR — melee deposited EXPEDITION yields into the shared base ledger (no region routing); (2) MINOR — a node was consumed even with no ledger (silent loss). **Both fixed** + two new regression tests.
|
||||
|
||||
## Validation
|
||||
- **EditMode: 302/302** (294 prior + 8 new: BaseFieldSpawn ×3, ExpeditionTeardown ×1, Schedule ×2, MeleeHarvest base/expedition ×2). Zero console errors at every compile.
|
||||
- **Play (live netcode, focused editor):** server world has **10 Ore crystal nodes**, `RegionTag.Base`, radii 23.7–26.1; `ThreatConfig.ScheduleEnabled=1`; catalog `[Turret,Wall,Pylon]`; crystals render textured (cyan, not magenta/white) in a reachable ring; schedule sieges fire (loop cycles calm↔siege); **0 console errors/exceptions**.
|
||||
- **Operator hands-on feel test is OPEN:** walk to a crystal → swing → watch Ore climb → build a ballista → survive a wave.
|
||||
|
||||
## Tuning knobs surfaced (sensible defaults shipped)
|
||||
- **Siege cadence** `ScheduleIntervalTicks=2700` (45s). Bump to 3600–5400 for 60–90s calm/build windows if waves feel too frequent.
|
||||
- **Ore-ring placement** sits at radius ~24–26 (just inside the boundary) because the 32×32 build plot fills the centre and the boundary is ~28.7 — only that thin annulus is conflict-free. If it feels edge-stuck: shrink the plot, widen the boundary ring, or allow some nodes on the grass plot (placeable-on; occupancy only scans `PlacedStructure`).
|
||||
- **Siege source** chose timed **Schedule**; **Heat** (`HeatPerHarvest` — mining draws the attack) is the reserved alternative that braids the halves harder.
|
||||
|
||||
## Files
|
||||
- New: `Simulation/Economy/BaseFieldSpawner.cs`, `Authoring/Economy/BaseFieldSpawnerAuthoring.cs`, `Server/Economy/BaseFieldSpawnSystem.cs`; tests `BaseFieldSpawnSystemTests.cs`, `ExpeditionFieldTeardownTests.cs`.
|
||||
- Modified: `Simulation/Player/MeleeComboSystem.cs` (server-only node harvest + region routing), `Server/Economy/ResourceHarvestSystem.cs` (region routing), `Server/World/ThreatDirectorSystem.cs` (schedule source), `Simulation/World/ThreatComponents.cs` (`ScheduleSizePerWave`), `Authoring/World/CycleDirectorAuthoring.cs` (schedule bake), `Server/Economy/ExpeditionFieldSystem.cs` (teardown region-filter), `Client/Presentation/HudSystem.cs` (loop copy); tests `ThreatDirectorSystemTests.cs`, `MeleeComboTests.cs`.
|
||||
- Assets: 7 prefabs re-meshed to Synty; `Subscenes/Gameplay.unity` (BaseFieldSpawner placed, catalog trimmed); `Prefabs/CycleDirector.prefab` (schedule baked).
|
||||
|
||||
## Next-session intent
|
||||
Operator runs the hands-on feel test (mine → build → survive). Tune the siege cadence + ore-ring to taste. If the loop feels good, the obvious next braids are **EB-1 (machines can die)** / **EB-2 (felt spend — turret ammo from harvest)** which give the mined Ore weight, or wiring `PolygonParticleFX` into the harvest/death VFX. The expedition path remains dormant-but-functional (gather code intact); reviving it would want a signposted gate + a `RegionTransitRequest` sender.
|
||||
Reference in New Issue
Block a user