208 lines
13 KiB
Markdown
208 lines
13 KiB
Markdown
# Cross-GDD Review Report
|
||
|
||
> **Date**: 2026-04-29
|
||
> **Reviewer**: Consistency Agent + Game Design Holism Agent
|
||
> **GDDs Reviewed**: 5 (node-system.md, shop.md, tower-assembly.md, event-system.md, progression.md)
|
||
> **Systems Covered**: Node System, Shop System, Tower Assembly, Event System, Progression
|
||
|
||
---
|
||
|
||
## Consistency Issues
|
||
|
||
### Blocking (must resolve before architecture)
|
||
|
||
🔴 **[C1] Rule contradiction: "no partial rewards on loss" vs. Progression accepting gold on loss**
|
||
- **Documents**: `node-system.md` vs `progression.md`
|
||
- **Node System** (Edge Cases): "Any combat loss: Run ends in failure immediately. No partial rewards are awarded."
|
||
- **Progression** (SR2 + AC): `RecordRunEnd()` is called on every run end (win or loss). On loss: `totalRunsStarted++`, `totalGoldEarned += goldEarned`, `furthestNodeReached` updates, but NO unlock evaluation.
|
||
- **Contradiction**: "No partial rewards" implies gold is NOT awarded/recorded on loss. Progression explicitly expects `goldEarned` on loss runs.
|
||
- **Resolution needed**: Clarify the intended design. Two options:
|
||
- (A) Loss runs do NOT record gold in LifetimeStats → remove `totalGoldEarned += goldEarned` from loss path in Progression AC
|
||
- (B) Loss runs DO record gold stats (only unlocks are withheld) → Node System's "no partial rewards" must be clarified to mean "no unlocks" not "no gold tracking"
|
||
|
||
---
|
||
|
||
🔴 **[C2] One-directional dependency: Tower Assembly ↔ Progression**
|
||
- **Documents**: `tower-assembly.md` vs `progression.md`
|
||
- **Tower Assembly** (Dependencies): "Progression — May read aggregate tower assembly stats across runs. Pending Progression GDD."
|
||
- **Progression** (Dependencies): Lists Node System and Shop System as upstream. Does NOT list Tower Assembly.
|
||
- **Impact**: If Tower Assembly wanted to persist aggregate stats (e.g., "total towers assembled lifetime"), no interface contract exists. Progression has no `RecordTowerAssemblyStats()` method.
|
||
- **Resolution needed**: Either (A) Progression adds Tower Assembly as a soft upstream dependency with a defined read interface, or (B) Tower Assembly's reference to Progression is removed/clarified as speculative.
|
||
|
||
---
|
||
|
||
### Warnings
|
||
|
||
⚠️ **[C3] Bidirectionality gap: Node System → Tower Assembly (soft vs. hard mismatch)**
|
||
- **Documents**: `node-system.md` vs `tower-assembly.md`
|
||
- Node System marks Tower Assembly as **Soft/provisional** in its Dependencies.
|
||
- Tower Assembly marks Node System as **Hard** upstream.
|
||
- This is a directionality mismatch — the softer designation should flow one way consistently.
|
||
- **Not blocking**: Tower Assembly's treatment of Node System as hard is likely correct; Node System's soft marking may be residual uncertainty from before Tower Assembly was designed.
|
||
|
||
⚠️ **[C4] Shop System GDD status in Node System is stale**
|
||
- **Documents**: `node-system.md` (Open Questions #3)
|
||
- Node System marks Shop System GDD as "blocking — interface contract pending alignment."
|
||
- However, `shop.md` is now **Designed** (complete design exists).
|
||
- Node System's Open Question #3 flag is stale.
|
||
- **Resolution**: Node System Open Question #3 should be resolved now that Shop GDD exists.
|
||
|
||
⚠️ **[C5] Tuning Knob ownership: MaxPlayerGold shared across 3 GDDs**
|
||
- **Documents**: `shop.md`, `event-system.md`, `progression.md`
|
||
- All three reference `MaxPlayerGold = 9999` consistently — no value conflict.
|
||
- However, no single GDD formally declares itself the **owner** of this constant.
|
||
- Entity registry correctly sources it from `shop.md`.
|
||
- **Recommendation**: Document in Shop GDD that it is the authoritative owner of `MaxPlayerGold`.
|
||
|
||
---
|
||
|
||
## Game Design Issues
|
||
|
||
### Blocking
|
||
|
||
🔴 **[G1] Exponential boss HP vs. linear player power — indefinite gap creates a hard wall**
|
||
- **Documents**: `node-system.md` + `tower-assembly.md`
|
||
- `BossEffectiveHp = BaseHp × 2^(completedLoopCount)` — exponential in boss cycles survived
|
||
- Player tower stats: `statValue[i] = baseValue + perLevel × i` — linear, capped at 5 levels
|
||
- After ~5–7 boss cycles, boss HP doubles so fast that even max-rarity max-level towers cannot deal enough damage before the boss outscales
|
||
- The game becomes mathematically unbeatable at high loop counts regardless of build quality
|
||
- **This is not a difficulty curve — it is a hard wall**
|
||
- **Resolution needed**: Add a boss cycle cap, or add a player power catch-up mechanic (e.g., each boss cycle also grants a temporary attack buff, or boss HP scaling changes to logarithmic rather than exponential). Alternatively, redefine "VictoryType" to not involve looping, removing the exponential scaling trigger.
|
||
|
||
---
|
||
|
||
🔴 **[G2] Run End Screen cannot display gold earned this run — undefined data flow**
|
||
- **Documents**: `node-system.md` + `progression.md`
|
||
- Node System fires `RunEnd` → calls `Progression.RecordRunEnd(runStats)`
|
||
- Progression updates `LifetimeStats.totalGoldEarned += goldEarned`
|
||
- But `OnUnlockedEventArgs` carries only `UnlockResult[]` — NOT the run's gold amount
|
||
- Run End Victory screen should show "Gold earned this run" — but no interface provides this data
|
||
- **Resolution needed**: Either (A) `UnlockedEventArgs` carries a `goldEarnedThisRun` field, or (B) Node System provides gold data directly to Run End screen (bypass Progression), or (C) Run End screen queries `Progression.GetLastRunGold()` (add this method)
|
||
|
||
---
|
||
|
||
🔴 **[G3] Undefined behavior: does RecordRunEnd fire on loss runs?**
|
||
- **Documents**: `node-system.md` vs `progression.md`
|
||
- Progression SR2: "`RecordRunEnd()` is called on every run end (win or loss)"
|
||
- Node System Edge Case: "Any combat loss: Run ends in failure immediately" — no mention of calling `RecordRunEnd`
|
||
- Progression AC explicitly covers loss runs (stats update, no unlocks)
|
||
- But if Node System never calls `RecordRunEnd` on loss, Progression's loss-path AC is unreachable
|
||
- **Resolution needed**: Clarify: does Node System call `RecordRunEnd` on loss? If yes — Node System GDD must document this. If no — Progression's loss-run AC is unreachable and must be revised.
|
||
|
||
---
|
||
|
||
### Warnings
|
||
|
||
⚠️ **[G4] Unbounded gold accumulation — no sink between runs**
|
||
- **Documents**: `node-system.md` + `shop.md`
|
||
- Per full winning run: ~600 (combat) + 300 (boss level) + 200 (boss bonus) = ~1100 gold
|
||
- With `MaxPlayerGold = 9999`, a player needs ~9 wins to cap out
|
||
- Once at cap, excess gold from combat is discarded — additional survival is not rewarded
|
||
- No sink between runs: no repair, no permanent upgrades, no sacrifice mechanic
|
||
- **Risk**: Short optimized runs may be more rewarding than long successful ones; endgame economy becomes meaningless
|
||
- **Recommendation**: Consider a meta-gold sink (permanent repair station, unlock purchases, or cosmetic unlocks) to recycle late-game gold
|
||
|
||
⚠️ **[G5] Multiple systems claim to be the primary progression loop**
|
||
- **Documents**: All 5 GDDs
|
||
- Node System: "tactician executing a plan" — navigates the run
|
||
- Tower Assembly: "puzzle solver, cleverness" — builds power
|
||
- Shop: "tactical urgency, deliberate investment" — resource decisions
|
||
- Event: "narrative surprise, meaningful stakes" — variety
|
||
- Progression: "relentless collection" — meta motivation
|
||
- **Risk**: Without a declared primary loop, players optimize the wrong thing
|
||
- **Recommendation**: Explicitly designate Tower Assembly as the core tactical loop; Node System as the structural frame; Shop/Event as the decision points; Progression as the meta-reward
|
||
|
||
⚠️ **[G6] Component Tag stacking — potential dominant strategy**
|
||
- **Documents**: `tower-assembly.md`
|
||
- R7: No compatibility constraints between component types
|
||
- Tags aggregate with stack counts merging across 3 components
|
||
- Optimal strategy: collect 3 copies of the best tag, stack it
|
||
- **Risk**: The "puzzle" of tower assembly may have a trivially discoverable dominant solution
|
||
- **Recommendation**: Consider adding anti-synergy rules (e.g., same tag on 3 components reduces effectiveness) or explicit trade-offs to maintain build diversity
|
||
|
||
⚠️ **[G7] Lossy economy (sell ~50% of buy) discourages experimentation**
|
||
- **Documents**: `shop.md`
|
||
- Sell price = midpoint of `[MinPrice, MaxPrice]` ≈ 50% of average buy
|
||
- Repeated buy-sell cycles destroy gold
|
||
- Players hoard components rather than experiment
|
||
- **Risk**: Reduces strategic depth; players avoid build diversity
|
||
- **Recommendation**: Consider a higher sell ratio (e.g., 60–70%) or a component enhancement sink to make exploration more affordable
|
||
|
||
⚠️ **[G8] Event rewards use same rarity budget as shop — no distinct reward tier**
|
||
- **Documents**: `event-system.md` + `shop.md` (OQ5)
|
||
- `AddRandomComps` uses `InventoryGenerationComponent` with identical rarity budget to shop
|
||
- Events offer no higher-tier rewards than shop can provide
|
||
- **Risk**: Events feel mathematically equivalent to additional shop visits; "genuine dilemma" fantasy may be undermined
|
||
- **Recommendation**: Consider a separate event rarity budget (e.g., events drop 1 tier lower than equivalent shop purchase) to give events a distinct identity
|
||
|
||
⚠️ **[G9] Assembly Phase has 5 simultaneous information panels**
|
||
- **Documents**: `tower-assembly.md`
|
||
- During Assembly Phase: Inventory Grid + Tower Slots + Assembled Towers Panel + Combat Roster + Next Node Preview
|
||
- **Risk**: Cognitive overload for new players
|
||
- **Recommendation**: Consider a tabbed or sequenced interface rather than all panels visible simultaneously
|
||
|
||
---
|
||
|
||
## Cross-System Scenario Issues
|
||
|
||
**Scenario: Player completes a winning run — full RunEnd chain**
|
||
|
||
### Steps:
|
||
1. **Node System**: Combat victory at Boss node → fires `NodeCompleteEventArgs` with `CombatWon=true`
|
||
2. **Node System**: Calls `Progression.RecordRunEnd(runStats)` with `{goldEarned, nodesCompleted=10, bossDefeated=true, ...}`
|
||
3. **Progression**: Updates `LifetimeStats` → evaluates unlocks → fires `UnlockedEventArgs` with `UnlockResult[]`
|
||
4. **UI / Run End Screen**: Receives `OnUnlockedEventArgs` → shows toast for new unlocks
|
||
|
||
### Issues found:
|
||
|
||
🔴 **G2 (already flagged above)**: Run End screen cannot display gold earned this run
|
||
- Step 3: `UnlockedEventArgs` carries no `goldEarned` field
|
||
- Run End Victory screen should show gold earned — no data path defined
|
||
|
||
🔴 **G3 (already flagged above)**: Ambiguous whether loss runs call `RecordRunEnd`
|
||
- If loss path does not call `RecordRunEnd`, steps 2–3 never occur on loss
|
||
|
||
⚠️ **Scenario: Player reaches Boss but loses**
|
||
- Node System fires `RunEnd` with `bossDefeated=false`
|
||
- Does `RecordRunEnd` fire? (G3 ambiguity)
|
||
- If yes: LifetimeStats records `furthestNodeReached=9`, no unlocks
|
||
- If no: Partial run progress is lost, player gets no credit for reaching node 9
|
||
|
||
⚠️ **Scenario: Multiple unlocks fire simultaneously**
|
||
- Step 3: `UnlockResult[]` can contain multiple items
|
||
- Step 4: Toast popup shows all — good
|
||
- But no priority ordering if unlocks are from different categories (e.g., Difficulty + Theme simultaneously)
|
||
- **Info**: Not a blocker; worth documenting expected ordering
|
||
|
||
---
|
||
|
||
## GDDs Flagged for Revision
|
||
|
||
| GDD | Reason | Type | Priority |
|
||
|-----|--------|------|----------|
|
||
| `node-system.md` | C1/C3/C4: Stale Open Question #3, ambiguous loss behavior, "no partial rewards" contradicts Progression's loss-path AC | Consistency + Design Theory | **High** |
|
||
| `progression.md` | C1: Loss-run gold recording contradicts Node System's "no partial rewards"; G3: loss run behavior undefined | Consistency + Design Theory | **High** |
|
||
| `tower-assembly.md` | C2: Progression dependency is orphaned (one-directional); G6: Tag stacking dominant strategy risk | Consistency + Design Theory | Medium |
|
||
|
||
---
|
||
|
||
## Verdict: **CONCERNS**
|
||
|
||
Three blocking issues must be resolved before architecture begins:
|
||
1. **C1**: "No partial rewards" vs. Progression loss-path gold — design decision required
|
||
2. **G1**: Exponential boss HP vs. linear player power — hard wall makes boss unbeatable after ~cycle 5–7
|
||
3. **G2/G3**: Undefined data flow for Run End screen gold display + ambiguous loss run behavior
|
||
|
||
Warnings (G4–G9) are advisory and should be addressed before implementation but do not block architecture.
|
||
|
||
---
|
||
|
||
## Recommended Actions
|
||
|
||
1. **C1**: Decide: do loss runs record gold in LifetimeStats? Update both Node System and Progression GDDs accordingly
|
||
2. **G1**: Redesign boss scaling formula — change from exponential to logarithmic, or add player catch-up mechanic, or cap boss cycles
|
||
3. **G2**: Add `goldEarnedThisRun` to `UnlockedEventArgs`, OR have Node System provide gold directly to Run End screen
|
||
4. **G3**: Clarify whether Node System calls `RecordRunEnd` on loss. Update Progression SR2 and AC to match.
|
||
5. **C2**: Either add Tower Assembly as soft upstream to Progression (with interface), or remove the speculative reference from Tower Assembly GDD
|
||
6. **C4**: Resolve Node System Open Question #3 now that Shop GDD is complete
|