Skip to content

feat: sprints 008-011 — pressure profiles, gem-graph map, combat stage, status effects#2

Open
ericfode wants to merge 77 commits intomainfrom
hermes/combat-ui-scaling-followthrough
Open

feat: sprints 008-011 — pressure profiles, gem-graph map, combat stage, status effects#2
ericfode wants to merge 77 commits intomainfrom
hermes/combat-ui-scaling-followthrough

Conversation

@ericfode
Copy link
Copy Markdown
Owner

@ericfode ericfode commented Apr 8, 2026

Summary

Merge the full hermes/combat-ui-scaling-followthrough branch into origin/main.

Included on this branch:

  • Sprint 008 — encounter pressure profiles + response cards
  • Sprint 009 — gem-graph floor traversal, persistence, conduit/circuit/seal objective work
  • Sprint 010 — combat stage visual overhaul, reward overlay, gem/icon HUD work
  • Sprint 011 — status effects, status cards/intents, and floor-objective follow-through
  • Consistency true-ups from the merged worktree branches

Latest follow-up in this push:

  • refresh six determinism expected baselines after intentional reward/status pool drift
  • relax the GSM reward-pool smoke assertion so it validates the contract instead of stale exact base IDs
  • update NEXT_AGENT.md and the combat-card Ralph loop board so autonomous follow-through starts from the repaired baseline

Validation

Passed locally:

  • python3 -m unittest tests.determinism.test_fixture_compare -v
  • python3 -m unittest tests.smoke.test_playable_prototype.PlayablePrototypeSmokeTests.test_reward_pool_keeps_gsm_cards_opt_in -v
  • python3 -m unittest discover -s tests -p 'test_*.py' -v
  • /opt/homebrew/bin/godot --headless --path /Users/ericfode/src/cardgame1 --quit-after 1

Notes

  • The upstream PR from this same branch remains open and was updated by this push.
  • The bounded combat-card cron loop is still enabled, so future autonomous slices may continue landing on this branch unless paused.

thrazznos and others added 30 commits April 5, 2026 13:10
…-followthrough

feat: land combat iteration, card UI scaling, and support docs
Add 4 distinct encounter pressure profiles (steady, burst, escalating,
attrition) that create mechanically different fights demanding different
sequencing plans. Each profile uses a different script mode from the
enemy encounters design doc.

- Add data-driven pressure profiles with fixed_cycle, state_reactive,
  and weighted_policy script modes
- Add energy_drain and force_discard enemy effect types to ERP
- Add 3 response tool cards (Heavy Guard, Quick Slash, Steady Hand)
- Update HUD to show telegraph text and profile name
- Add smoke probes validating all 4 profiles, attrition mechanics,
  and encounter variety metrics
- Re-baseline all 7 determinism fixtures for new RNG patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11 locked design decisions from Socratic session:
- Polyhedra projection topology with random node mods
- Full gem stack persistence + room affinity gems
- 6-slot stack cap (natural snowball limiter)
- Player-drafted constraints merged with card reward
- Gem slot loss as debt spiral penalty
- Toast + immediate combat room transitions

Sprint 009 scope: graph generator, gem persistence, floor
traversal controller, map UI, and validation coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Polyhedra-projected floor graphs with gem stack persistence:
- FloorGraph generator: tetrahedron (4-node), octahedron (6-node),
  cube (8-node) with random modifications and gem attunements
- GSM: 6-slot stack cap, save/restore for cross-room persistence,
  affinity gem grant, slot loss mechanic
- FloorController: state machine wrapping combat slice with
  room selection, gem persistence, and boss gate logic
- MapHudController: graph renderer with node colors, edge
  highlighting, gem stack widget, and select-then-commit interaction
- FloorRunner: orchestrates map->combat->map flow
- Floor scene (floor_run.tscn) embedding combat slice
- Smoke probes: graph determinism, gem persistence, stack cap

All existing combat probes and determinism fixtures still pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CombatSliceRunner callbacks to FloorRunner on combat complete
- External GSM injection (use_external_gsm flag) for gem persistence
- Combat runner detects floor parent and skips auto-start
- Player loss routes back to map (TODO: run-over screen)
- Floor integration probe validates full loop: map->combat->reward->map
- Gem stack correctly persists between rooms (verified in Godot)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- FloorGraph assigns gem gate costs to 1-2 premium nodes per floor
- FloorController checks gate payment on room entry
- Shortfall triggers permanent gem slot loss (GSM reduce_cap)
- Gate cost indicators shown on map nodes
- Gates are always optional (free path to boss exists)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Constraint types (conduit/circuit/seal) attached to reward picks
- FloorController tracks conduit pattern and progress per floor
- Conduit pattern generated deterministically, length scales with depth
- Map HUD shows conduit banner with progress tracker
- Constraint history carried across floors in FloorRunner
- Floor start accepts constraint parameter from previous boss reward

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All Must Have + Should Have tasks done:
- Polyhedra graph generator (tetra/octa/cube)
- GSM 6-slot cap, persistence, affinity gems
- Floor traversal controller with combat integration
- Map UI with graph rendering and select-then-commit
- Gem gates with slot loss mechanic
- Constraint draft merged with card reward
- Conduit floor objective with pattern tracker
- Full integration verified in Godot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Single-click to enter rooms (removed confusing double-click)
- Bigger nodes (36px radius) with text labels (Combat/Boss/Rest/Event)
- Clear current-position double ring in gold
- Legal rooms get thick gold border, cleared rooms dim out
- _busy flag prevents multi-click during room transitions
- Gem stack bar with slot indicators at bottom
- Floor banner and instructions text
- Thicker legal edges, thinner non-legal edges
- Set main scene to floor_run.tscn

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Nodes doubled to 72px radius with 22px type labels and 18px affinity
- Brighter, more saturated color palette for node fills
- Bigger gem stack bar (22px text, 30x12 slot indicators)
- Floor banner 24px, instructions 20px
- Larger hit areas for click detection
- Everything minimum 2x previous size per feedback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sprint 010 replaces spreadsheet combat with a visual stage:
- Arena with player/enemy portraits facing off
- Card hand as overlapping fan overlaid at bottom
- Gem stack as icon textures (shared map/combat component)
- Coherent HUD frame across all screens
- Event room scene transitions
- No mechanic changes, pure layout/visual overhaul

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New CombatStageController replaces spreadsheet layout:
- Arena top 55%: player/enemy portraits facing off with HP bars
- Enemy intent as large centered text between portraits
- Card hand as overlapping fan at bottom with hover lift
- Gem stack as Ruby/Sapphire icon textures
- Compact event feed in arena corner
- Click cards to play, SPACE to pass, 1-5 for hotkeys
- Floor scene uses new combat_stage.tscn

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cards now have:
- Dark title bar with display name and cost badge circle
- Card art thumbnail from existing art assets
- Role marker badge below art
- Rules text split by bullet points
- Palette-colored border (red=attack, blue=defend, gold=utility)
- LOCKED footer bar for unplayable cards
- Hover lifts card 60px with 1.15x scale
- 320x440 base size (2x the previous 160x220)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Full reward screen with styled card rendering (280x380)
- Cards show title bar, cost badge, art thumbnail, rules text
- Click a card to select, SPACE to continue
- Hover lift on reward cards
- Selected card gets green border and SELECTED badge
- Victory title and summary text
- Dark scrim over arena during reward phase

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Card art was missing because _load_tex used ResourceLoader.exists()
which requires .import files. Switched to preload() for all art
assets (strike, defend, utility, ruby, sapphire, focus, placeholder)
and portrait/gem textures. Cards now render with proper art.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 4 distinct encounter pressure profiles (steady, burst, escalating,
attrition) that create mechanically different fights demanding different
sequencing plans. Each profile uses a different script mode from the
enemy encounters design doc.

- Add data-driven pressure profiles with fixed_cycle, state_reactive,
  and weighted_policy script modes
- Add energy_drain and force_discard enemy effect types to ERP
- Add 3 response tool cards (Heavy Guard, Quick Slash, Steady Hand)
- Update HUD to show telegraph text and profile name
- Add smoke probes validating all 4 profiles, attrition mechanics,
  and encounter variety metrics
- Re-baseline all 7 determinism fixtures for new RNG patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ericfode and others added 27 commits April 7, 2026 16:22
…onstant

Update TEXT_WARN from #ffcc44 to canonical #ffd36a to match combat stage
controller palette. Replace inline Color("#1a1520") with PANEL_BG constant
in gem stack bar empty slot rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mentation

Mark completed tasks (E1.T1-T5, E2.T1-T4, E3.T1-T2, E4.T1-T2) and add
deprecation note for combat_slice.tscn / combat_hud_controller.gd.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FloorRunner was directly accessing floor_controller.graph and
floor_controller.current_node (implementation details). Added
get_current_node_data(), get_state(), and get_rooms_cleared()
accessors to FloorController and replaced all 3 graph.get_node()
calls plus direct state/rooms_cleared reads in FloorRunner.

Also replaced stringly-typed "floor_complete" comparison with
FLOOR_CONTROLLER_SCRIPT.STATE_FLOOR_COMPLETE constant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all inline Color() and layout constants from CombatStageController
into a new UITheme class (src/ui/theme.gd) with static constants.
This establishes a single canonical palette for the Dungeon Steward UI,
eliminating color drift across the 3 UI controller files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sion

The map system implementation diverged from the original layered-acyclic
spec during Sprint 009. This adds a revision section documenting the live
polyhedra topology (tetrahedron/octahedron/cube), gem attunement per node,
gem stack persistence, 6-slot cap, gem gates with slot-loss debt, floor
objective variants (conduit/circuit/seal), and constraint-card draft merge.

The original Core Rules are annotated as superseded for topology but still
valid for traversal contracts.

Refs: design/gdd/systems/gem-graph-map-decisions.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add RESULT_IN_PROGRESS, RESULT_PLAYER_WIN, RESULT_PLAYER_LOSE, REWARD_NONE,
REWARD_PRESENTED, REWARD_APPLIED, REWARD_CLOSED constants to CombatSliceRunner.
Replace all raw magic string comparisons across combat_slice_runner.gd,
combat_stage_controller.gd, floor_runner.gd, and map_hud_controller.gd with
references to these constants and FloorController.STATE_* constants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…followthrough

# Conflicts:
#	src/ui/combat_stage/combat_stage_controller.gd
…followthrough

# Conflicts:
#	src/ui/map_hud/map_hud_controller.gd
…followthrough

# Conflicts:
#	src/bootstrap/floor_runner.gd
#	src/ui/combat_stage/combat_stage_controller.gd
Merged worktree branches for:
- UITheme shared constants (thrazznos#8)
- Map HUD theme alignment (thrazznos#6)
- TextureLoader utility (thrazznos#5)
- FloorController encapsulation (thrazznos#7)
- Centralized string constants (thrazznos#10)
- Map-pathing design doc update (thrazznos#9)
- Sprint 010 taskboard fix (#1)

All smoke probes pass after merge. Godot class cache rebuilt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the _load_texture method out of CombatHudController into a
standalone TextureLoader class with a static try_load() method.
This eliminates duplication when future UI controllers need the
same ResourceLoader-then-Image fallback logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sion (thrazznos#9)

The map system implementation diverged from the original layered-acyclic
spec during Sprint 009. This adds a revision section documenting the live
polyhedra topology (tetrahedron/octahedron/cube), gem attunement per node,
gem stack persistence, 6-slot cap, gem gates with slot-loss debt, floor
objective variants (conduit/circuit/seal), and constraint-card draft merge.

The original Core Rules are annotated as superseded for topology but still
valid for traversal contracts.

Refs: design/gdd/systems/gem-graph-map-decisions.md

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hrazznos#10)

Add RESULT_IN_PROGRESS, RESULT_PLAYER_WIN, RESULT_PLAYER_LOSE, REWARD_NONE,
REWARD_PRESENTED, REWARD_APPLIED, REWARD_CLOSED constants to CombatSliceRunner.
Replace all raw magic string comparisons across combat_slice_runner.gd,
combat_stage_controller.gd, floor_runner.gd, and map_hud_controller.gd with
references to these constants and FloorController.STATE_* constants.


(cherry picked from commit 9f5e8f8)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hrazznos#7)

Co-authored-by: thrazznos <thrazznos@users.noreply.github.com>
Co-authored-by: thrazznos <thrazznos@users.noreply.github.com>
Co-authored-by: thrazznos <thrazznos@users.noreply.github.com>
Sprint 011 adds:
- Status effect system (poison/weakness/strength/vulnerability)
- Data-driven status definitions with registry and tracker
- Status-applying cards and enemy intents
- Combat stage status display with duration counters
- Circuit and seal floor objectives (completing map variety trio)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ility

Sprint 011 E1-E3:
- StatusRegistry: data-driven status definitions from JSON
- StatusTracker: apply/stack/tick/expire with damage multipliers
- ERP: apply_status effect type for card and enemy effects
- 4 new cards: Venom Strike, Battle Cry, Expose Weakness, Gem Venom Burst
- Attrition profile gains poison intents, burst gains vulnerability on charge
- Combat stage shows status strips below portraits
- Poison ticks at turn start, strength/weakness/vulnerability expire by duration
- Status multipliers applied to both player and enemy damage calculations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sprint 011 E4:
- Circuit: target gem color sequence, progress tracker, penalty counter
  on wrong-color room visit
- Seal: 3 mandatory seal nodes with gem costs, boss locked until all
  broken, seal_broken tracking per node
- Boss gate locked when seals are active and unbroken
- Map HUD unified _draw_objective_banner showing conduit/circuit/seal
  state with progress indicators
- Generator creates circuit sequences and distributes seal nodes
  deterministically from RNG

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Status effects (poison/weakness/strength/vulnerability) and
circuit/seal floor objectives all implemented and tested.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runs the same floor twice with identical seed, verifies:
- Gem stack state matches on entry/exit for each room
- Combat results identical across replays
- Final floor state hash matches
- Gems persist correctly between rooms

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…aling-followthrough

# Conflicts:
#	data/cards/catalog_v1.json
#	data/encounters/pressure_profiles_v1.json
#	production/sprints/sprint-010-taskboard.md
#	src/bootstrap/combat_slice_runner.gd
#	src/bootstrap/floor_runner.gd
#	src/core/erp/erp.gd
#	src/core/map/floor_controller.gd
#	src/ui/combat_stage/combat_stage_controller.gd
#	src/ui/map_hud/map_hud_controller.gd
- refresh six determinism expected baselines after intentional reward/status content changes\n- relax the GSM opt-in smoke assertion to check the pool contract instead of a stale exact trio\n- update NEXT_AGENT and the combat-card loop board with the repair context
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ef8e18f33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +196 to +198
graph.mark_cleared(current_node)
rooms_cleared += 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent defeated combats from clearing floor nodes

complete_combat() marks the current node cleared and increments rooms_cleared before checking outcome, and FloorRunner.on_combat_complete() calls this even when combat_result is player_lose. In a loss scenario, the run still advances map progression (including potential floor completion), which breaks encounter gating and lets failed fights count as completed rooms.

Useful? React with 👍 / 👎.

Comment thread src/core/map/floor_controller.gd Outdated
Comment on lines +297 to +300
for n_id in neighbors:
var node: Dictionary = graph.get_node(n_id)
if not bool(node.get("cleared", false)):
uncleared.append(n_id)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude locked boss node from legal-move list

When the seal constraint is active, _get_uncleared_legal_moves() appends every uncleared neighbor, including the exit node, before applying the boss-lock rule. Because the exit is already present, the later guarded append path is skipped, so adjacent players can still select the boss while is_boss_locked() is true.

Useful? React with 👍 / 👎.

Comment on lines +392 to +394
if affinity != "neutral" and affinity != "" and gsm != null:
gsm.consume_top(affinity)
seals_broken += 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require successful gem spend before breaking a seal

Seal progress increments unconditionally after calling gsm.consume_top(affinity), but the consume result is ignored. If the stack is empty or the top gem does not match, the seal is still marked broken, letting players clear mandatory seal objectives without paying the documented gem cost.

Useful? React with 👍 / 👎.

- filter unaffordable gem-gated rooms out of map legal_moves\n- reject select_room with ERR_GEM_GATE_UNAFFORDABLE\n- add smoke coverage for gem gate blocking\n- update map-pathing docs and NEXT_AGENT handoff
@ericfode
Copy link
Copy Markdown
Owner Author

ericfode commented Apr 8, 2026

Latest push: hard-gate unaffordable gem-gated rooms.

What changed:

  • FloorController now filters unaffordable gated rooms out of legal_moves
  • select_room() now rejects them with ERR_GEM_GATE_UNAFFORDABLE
  • room entry no longer consumes stack cap on shortfall
  • added smoke coverage in tests/smoke/run_gem_gate_block_probe.gd and test_unaffordable_gem_gates_block_room_entry
  • updated map-pathing.md, gem-graph-map-decisions.md, and NEXT_AGENT.md

Validation:

  • python3 -m unittest tests.smoke.test_playable_prototype.PlayablePrototypeSmokeTests.test_unaffordable_gem_gates_block_room_entry -v
  • python3 -m unittest discover -s tests -p 'test_*.py' -v
  • /opt/homebrew/bin/godot --headless --path /Users/ericfode/src/cardgame1 --quit-after 1

All green locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants