geometry-tower-defense/design/gdd/gdd-cross-review-2026-04-29...

13 KiB
Raw Blame History

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 ~57 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., 6070%) 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 23 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 57
  3. G2/G3: Undefined data flow for Run End screen gold display + ambiguous loss run behavior

Warnings (G4G9) are advisory and should be addressed before implementation but do not block architecture.


  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