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,51 @@
|
||||
---
|
||||
id: DR-031
|
||||
title: Base-local mining — mine at the base, any attack harvests, scheduled base sieges close the loop
|
||||
status: accepted
|
||||
date: 2026-06-11
|
||||
tags:
|
||||
- decision
|
||||
- design
|
||||
- economy
|
||||
- combat
|
||||
- harvesting
|
||||
- netcode
|
||||
- loop-cohesion
|
||||
permalink: gamevault/07-sessions/decisions/dr-031-base-mining-loop-cohesion
|
||||
---
|
||||
|
||||
# DR-031 — Base-local mining + any-attack harvest + scheduled base sieges
|
||||
|
||||
## Context
|
||||
|
||||
Session [[2026-06-11_Base_Mining_Cohesive_Loop]] · economy foundation [[DR-026_Inventory_Equipment_Progression_Foundation]] · region split [[DR-013_M6_Aether_Cycle_Region_Split]] · melee verb [[DR-030_MC4_Combo_Melee_Primary_Verb]] · world art [[DR-025_World_Environment_Redo_Natural_Frontier]].
|
||||
|
||||
Operator: combat got a lot of work, inventory/equipment got some, **but you couldn't mine resources at the base** — consolidate it into a cohesive playable loop and replace placeholder cube assets. Diagnosis: the loop's two halves were divorced. Resource nodes only spawned in the far Expedition region (`RegionMath.ExpeditionOffsetX = 1000`u), only while a player physically stood there, reachable solely by an **unsignposted** walk-in gate (nothing sends `RegionTransitRequest`), harvested by the MC-4-demoted ranged weapon. And the **only** wired siege source was post-expedition retaliation — so a base-only player draws **zero waves**, and all the dash/charger/melee combat never fires. Ran the standing **pre-code adversarial review** (caught 3 blockers) and a **post-code adversarial review** (caught 2 real defects, fixed).
|
||||
|
||||
## Decision
|
||||
|
||||
**1. Mining lives AT THE BASE** (operator fork; expedition becomes a dormant optional layer). A server-only `BaseFieldSpawnSystem` keeps `RegionTag{Base}` Ore nodes topped up to `TargetCount` in a `[Inner,Outer]` annulus around `BaseGridMath.PlotCenter`. Reuses the existing `ResourceNode` ghost (NO new replicated state) — a **distinct** `BaseFieldSpawner` singleton (reusing `ResourceFieldSpawner` would throw multiple-instances vs `ExpeditionFieldSystem`). Override the baked `RegionTag{Expedition}`→Base via **`SetComponent` not `AddComponent`** (Add throws; a node left Expedition-tagged is hidden from base players by `RegionRelevancy`). **Ore-only** — do NOT copy the expedition's Aether/Ore/Biomass round-robin (Ore is the sole build currency; scattering it breaks palette-affordability legibility). Determinism: RNG seeded from a monotonic `Epoch` (never the tick), cadence via `TickUtil.NonZero`+`NetworkTick.IsNewerThan`, first pass fires immediately, **uniform-in-radius** scatter (not area-weighted `sqrt` — that piles nodes on the outer wall).
|
||||
|
||||
**2. ANY attack harvests** (operator fork). Both the melee cleave and the ranged projectile deplete nodes. Melee harvest lives **strictly inside `MeleeComboSystem`'s server-only post-loop cleave block** (NEVER the predicted per-player foreach — interpolated node ghosts aren't rolled back on the client; harvesting there is a determinism/ownership violation + ledger double-credit risk). Writes `Remaining` back via `ecb.SetComponent` so the `[GhostField]` replicates and `WorldFeedbackSystem` chips fire on melee mining too.
|
||||
|
||||
**3. Harvest routing by node region (both melee + projectile):** **Base node → the shared `ResourceLedger` directly**; **Expedition / un-tagged → the harvesting player's PERSONAL `InventorySlot`** (spill-to-ledger). The build economy spends EXCLUSIVELY from the ledger (`BuildPlaceSystem`/`BuildSendSystem`/HUD), so routing base harvest there makes "mine → build" work with **zero `G`-deposit friction**; the expedition keeps DR-026's personal-haul. Read `RegionTag` via an **optional `ComponentLookup` (default missing → owner-inventory path)**, NOT a required query column — a required column silently drops un-tagged test fixtures + Expedition clutter (the 2026-06-08 partial-archetype class of bug). Only deplete a target if its yield actually landed somewhere (no zero-credit consume when no ledger exists).
|
||||
|
||||
**4. A base-resident siege source closes the loop** (the hidden blocker). Activated the **reserved Schedule source** in `ThreatDirectorSystem`: arms a `SizeBase + ScheduleSizePerWave*WaveNumber` siege every `ScheduleIntervalTicks` with NO expedition trip, deferring `NextScheduledTick` while a siege runs (a guaranteed calm/build window even on a long siege). Composes with the post-expedition source (guarded `Phase==Calm && PendingSiegeSize==0`). Chosen over the **Heat** source (mining accrues heat → arms siege) for predictability + zero harvest-coupling; Heat stays reserved-but-inert as the harder-braided alternative. Defaults: `Interval=2700` (45s), `PerWave=1`, baked on `CycleDirectorAuthoring` — all tunable.
|
||||
|
||||
**5. `ExpeditionFieldSystem` teardown region-filtered** to Expedition-only nodes — its old unfiltered destroy would wipe the new permanent base field the first time the expedition emptied (clutter teardown stays unfiltered: there is no base clutter).
|
||||
|
||||
**6. Real art by NEST, not root mesh-swap.** Swap each ghost prefab's visual: strip the root placeholder `MeshFilter`+`MeshRenderer`, **reset root `LocalScale` to 1** (Scale is a `[GhostField]` propagated by spawners — 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 yields a blank "Lit" material; the pre-made *prefabs* carry the atlas). Nesting is uniform for single- and multi-mesh (the Ballista is a 7-GameObject hierarchy → a root mesh-swap renders 1/7). GhostAuthoring stays root-only (no phantom ghost children); `LinkedEntityGroupAuthoring` auto-collects the child. PolygonFantasyKingdom throughout (one atlas). Automation machines (Harvester/Fabricator/Conveyor) **trimmed from the build palette** — code stays, no placeholder cube shown.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **The loop closes on one screen:** mine Ore at the base (any attack) → ledger fills → build Turret/Wall/Pylon (real ballista/palisade/crystal) → survive a scheduled wave → repeat. The combat work (dash/charger/melee) now actually triggers at the base.
|
||||
- 302/302 EditMode (8 new); live netcode Play clean (10 Ore crystals in a reachable ring, schedule sieges firing, 0 errors). **Operator hands-on feel-test is OPEN.**
|
||||
- **Two tuning forks left to the operator:** siege cadence (45s default) and the ore-ring radius (sits at ~24–26m, against the boundary, because the 32×32 build plot fills the centre — only that thin annulus is conflict-free; widen by shrinking the plot / widening the boundary / allowing nodes on the grass).
|
||||
- Brings forward gather→spend economy work the roadmap had gated behind the combat fun-gates ([[DR-028_Combat_Primary_Verb_Depth_First]]/[[combat-first-depth-before-breadth]]) — the operator's request was the reprioritization; the natural follow-on braids are EB-1 (machines can die) / EB-2 (felt spend) which give the mined Ore weight.
|
||||
|
||||
## Alternatives considered (rejected)
|
||||
|
||||
- **Fix the expedition round-trip instead** (signpost the gate, add a `RegionTransitRequest` sender) — keeps the region-split design but leaves the loop two-screen + less cohesive. Operator chose base-local.
|
||||
- **Heat siege source** instead of Schedule — more teachable (mining draws the attack) but unpredictable + can be gamed (don't mine → no waves) and needs harvest→heat plumbing. Reserved.
|
||||
- **Generalize `ExpeditionFieldSystem` with a region param** rather than a new `BaseFieldSpawnSystem` — rejected: the cadence-top-up model differs from the presence-edge model, and a shared singleton collides.
|
||||
- **A `HarvestMath.Deposit` helper** to dedup the two harvest paths — rejected as over-abstraction (the shared core is ~3 lines; the resolve-owner/stackMax differs per caller). Inlined.
|
||||
Reference in New Issue
Block a user