Dungeons & Dragons & Dices — Progress
Dungeons & Dragons & Dices — Progress
Last Updated: 2026-04-08e (balance pass, dead code cleanup, bug fixes, mirror relic UI, elite taunts, hub narration) Status: Full elemental + skill tree balance pass. 17 legacy multiplayer files deleted. GameStateManager autoload removed. Screen shake race condition fixed. Mirror relic shows wild die visually. Pre-fight taunts for elites/bosses. Hub ambient narration. Vulnerable damage now reflected in floating numbers.
Session 2026-04-08e — Balance Pass, Dead Code Cleanup, Bug Fixes, Quick Wins
Elemental Balance Pass (S10-08)
Research-driven rebalance of all elemental damage values, status durations, and proc chances.
| Change | Before | After | Rationale |
|---|---|---|---|
| Burn damage/turn | 5 | 8 | Negligible vs Act 2-3 enemies (150-1500 HP) |
| Wind AoE damage | 3 | 5 | 3 dmg trivial; wind is AoE so needs meaningful per-hit |
| Lightning damage | 8 | 12 | Single-target needs to compete with wind's AoE |
| Vulnerable duration | 1 turn | 2 turns | 1 turn gone before player could capitalize |
| Chill dmg reduction | 15% | 25% | Ice non-freeze outcome too weak |
| Base elemental chance | 2%/die | 3%/die | Too rare; 5 dice now ~14% chance of at least one |
| Singularity (4-elem) | 0% damage | 120% damage | Rarest combo did literally zero damage |
| Floor HP scaling | +15%/floor | +10%/floor | Act 3 boss was 1575 HP (394 DPS needed), now 1080 HP |
dice_combat_manager.gd: burn base, wind damage, lightning damage+vuln, chill reduction, floor scalingsolo_game_manager.gd: BASE_ELEM_CHANCE 0.02 -> 0.03elemental_system.gd: Singularity damage_pct 0.0 -> 1.20, removed unimplemented consume_statuses effect
Skill Tree Balance Pass (S10-09)
Nerfs to dominant keystones, buffs to underperforming ones.
| Keystone | Before | After | Rationale |
|---|---|---|---|
| Glass Cannon | 2x all damage | 1.75x | Dominated every other keystone |
| Escalation | +3 chips, +0.15 mult/turn | +2, +0.10 | Exponential scaling absurd by Turn 3 |
| Merchant Prince | -30% shop prices | -20% | Breaking economy with gold nodes |
| Pyromaniac | Burn 2x | Burn 3x | With burn buffed to 8, 3x=24/turn. Competitive now |
| Stormcaller | Curse: -15 HP | Curse: -1 reroll | HP penalty too harsh for situational benefit |
| Eye of Storm | Heal 3, -15% combo | Heal 5, -10% combo | Weakest keystone, slightly better now |
| Golden Touch | No shop discount | +10% shop discount | Was strictly worse than Merchant Prince |
skill_tree_manager.gd: all keystone getters updatedskill_tree_data.gd: all keystone descriptions updateddice_combat_manager.gd: Glass Cannon multiplier, Eye of Storm heal
Screen Shake Bug Fix
- Root cause: Race condition between concurrent
_screen_shake()calls during ATTACK actions (enemy lunge + player hit fire in parallel) - Fix: Final tween step changed from
tween_propertytotween_callbackfor guaranteed reset. Always reset toVector2.ZERObefore starting new shake. Phase boundary resets in_on_roll_phase()and_on_enemy_phase_ended() dice_combat_screen.gd:_screen_shake(),_on_roll_phase(),_on_enemy_phase_ended()
Vulnerable Damage Display Fix
- Root cause:
_animate_enemy_hit()received raw hand score, not post-vulnerability damage - Fix: Now computes
actual_hit_damage = hp_lost + blockedfrom damage_results. Shows "VULN!" suffix when vulnerability boosted damage - Battle log now shows "+N vuln" instead of vague "vuln/passive adjusted"
dice_combat_screen.gd: scoring display + battle loghand_scorer.gd: exposesmirror_dieandmirror_valuein evaluation result
Mirror Relic UI
- Wild die now shows purple tint + "=N" overlay label in bottom-right corner
- Tooltip: "WILD – Mirror makes this die count as N for hand evaluation"
hand_scorer.gd:best_mirror_idxmoved to outer scope,mirror_die/mirror_valueadded to result dictdice_combat_screen.gd: mirror label creation/cleanup in_preview_hand()
Pre-Fight Elite/Boss Taunts (S11-12)
- 7 elite taunts + 4 boss taunts as floating text at combat start
- Fades in (0.5s), holds (2.5s), fades out (0.8s)
- Logged to battle log
dice_combat_screen.gd:ELITE_TAUNTS,BOSS_TAUNTSdicts,_show_pre_fight_taunt()
First-Hub Ambient Narration (S11-13)
- One-time atmospheric line on first hub visit (4 random options)
- Slow fade in/hold/fade out near bottom of screen
- Persisted via TutorialOverlay seen tracking
hub_screen.gd:_show_ambient_narration()
Combat Start Battle Log
- Added missing combat start logging to
_init_from_solo_manager()(floor, enemy count, each enemy name/tier/HP/passive) - Was only present in
_init_quick_test()
Dead Code Cleanup (17 files deleted)
Legacy multiplayer card system completely removed:
| Category | Files Deleted |
|---|---|
| Card system | card_database.gd, card_effect_resolver.gd, deck_manager.gd, player_resources.gd |
| Resources | card_data.gd, card_effect.gd, effect_modifier.gd, ai_condition.gd |
| Combat | combat_manager.gd (replaced by dice_combat_manager.gd) |
| State | game_state_manager.gd (replaced by SoloGameManager) |
| UI | card_visual.gd, CardVisual.tscn, UnitTests.tscn |
| Tests | test_card_effects.gd, combat_test_harness.gd, test_runner_scene.gd, test_all.gd |
- GameStateManager autoload removed from
project.godot _init_quick_test()rewritten to create RunData inline (no GameStateManager dependency)
Dead Field Cleanup
rally_poolremoved fromRunData+SoloGameManagerdeckremoved fromPlayerRunData+SoloGameManagerthreat_dieremoved fromEnemyData+ all 26 enemy entries inEnemyDatabaseai_type+action_patternremoved fromEnemyData+ all 26 enemy entries_condition()helper removed fromEnemyDatabase- 7 dead enums removed from
enums.gd:CardCategory,TargetType,EffectType,ModifierType,CardRarity,AIType,ConditionType,GameState.LOBBY
Stale TODO Cleanup
8 items marked done that were already implemented:
- Music system (9 tracks, crossfade, per-act)
- Enemy idle animation (bob tween)
- Skill tree redesign (5/6 items: layout, icons, pathing, labels, tooltips)
- Parallax backgrounds (replaced with 9 static backgrounds)
- Hub_bg files (actively used, not intermediates)
- Shop combat design doc (exists)
- Custom pixel cursor description corrected (no asset exists)
Session 2026-04-08d — Deep Freeze Rework, New Intent, Shrine Blessings, Battle Log, Bug Fixes
Deep Freeze Rework
- Deep frozen dice excluded from combo detection — filtered out before
_identify_hand()in HandScorer - Deep frozen dice add their face value (crushed to 1) as flat bonus damage after multiplied score
- Deep frozen dice don't contribute elements — excluded from
get_active_elements()and_apply_single_element_effects() - Soft frozen dice still participate in combos and elements normally
- Tooltip updated: "Excluded from combos and elements. Value added as flat damage."
deep_frozen_indicesarray passed from combat manager to scorer
New Enemy Intent: STRIP_ELEMENT (Dispel)
- New
Enums.EnemyActionType.STRIP_ELEMENT— strips elemental status from N dice - Balanced: only activates if player has 5%+ total elemental chance per die. If below threshold, fallback to attack (val+4 damage)
- Intent icon shows attack instead of dispel when fallback applies
- UI animation: affected dice flash purple, shrink, glow removed
- Intent color: purple. Description: "Dispels N elemental dice"
- Added to Beholder (Act 1 boss, dispels 3) and Frost Wraith (elite, dispels 2)
- Wired into interference system: pending count, debuff warning, has_pending check
Deep Frozen Shader — Hexagonal Ice Crystal Pattern
- Replaced long diagonal crack lines with hexagonal ice lattice
- Uses proper hex grid math (cube coordinates) to tile hexagons at 1px edges
hex_sizeuniform (default 12px) controls cell size — bigger sprites get more cells, not bigger cellscrack_seedoffsets grid per die instance for unique patterns- Per-cell glint variation (~30% of cells shimmer brighter)
- Shimmer pulses along hex edges, not globally
6 New Shrine Blessings (14 total, was 8)
| Blessing | Effect | Wired Into |
|---|---|---|
| Sharpened Edge | +2 base dmg to all hands | dice_combat_manager scoring |
| Stone Skin | -1 damage from all enemy hits | _apply_enemy_attack_damage() |
| Resonance | +15% elemental combo damage | _execute_combo() |
| Second Wind | Heal 3 HP at start of each combat | init_combat() |
| Lucky Seven | 10% chance rerolled die becomes 6 | dice_pool.reroll() |
| Tax Collector | +2 gold after each combat | _end_combat() gold calc |
Battle Log Overhaul — Full Combat Tracking
Every combat event now logged for debugging and transparency:
- Combat start: floor, enemy count, each enemy name/tier/HP/passive
- Turn start: enemy status summary (HP, burn/freeze/vuln, block, stunned)
- Dice:
Rolled: [3,5,2,6,1],Rerolled: [3,4,2,6,5] (1 left) d1=held - Scoring: hand name, target name, base chips x mult, per-enemy damage (HP lost, blocked, vuln adjusted, killed)
- Elements: each effect with target name and damage
- Combos: name, tier, total damage, target names
- Interference: effect name, dice indices, old/new values
- Enemy actions: name, description, value
- Player hit: attacker name, damage, remaining HP
- Burn ticks: damage, target name, HP remaining, kill
- Manipulation: ability name, die index, old/new value
- Death: enemy name, HP at death
- Victory/Defeat: turn number, remaining HP
Scoring Breakdown — Grand Total with Elements/Combos
- Phase 5: Hand damage pops (white)
- Phase 6a: Hit lands, HP bars sync
- Phase 6b: If elemental damage > 0, score updates with blue "+N elemental" label
- Phase 6c: If combo triggered, score pops with gold "+N combo_name" label
- Phase 6d: If grand total differs from hand, final "Total: N" with big bounce
- Each step has distinct color so player sees where damage comes from
Burn Kill Bug Fix
- Root cause:
finish_enemy_phase()emitted burn signals +_end_combat(true)synchronously — UIawaitin death animation never ran - Fix: Split burn processing into UI-driven sequence:
- Manager:
process_burn_ticks()returns[{enemy_id, damage, killed}]without signals - Manager:
advance_after_burns()handles intent selection or victory - UI: animates each burn tick with VFX, syncs HP bars, plays death animation with await, then advances
- Manager:
- HP bars now decrease and dissolve plays before victory shows
Systematic Null Guards (Item Kill Crashes)
- Added
is_instance_valid(enemy_container)to all vulnerable entry points:_find_enemy_node(),_update_enemy_display(),_on_enemy_died()(before + after await)_on_shatter_damage(),_on_enemy_phase_ended(),_fire_element_effects_instant()_fire_single_element_vfx(),_fire_combo_instant()
- Prevents crashes when items (Shatter) or effects kill enemies during scene transitions
- Shatter kills now also sync HP bars + play death animation
Shop Discard Picker
- When inventory full and player clicks a consumable, shows discard picker overlay instead of dead-end text
- Dark overlay + centered panel with current consumables as clickable cards
- Click to discard + buy, or Cancel button to dismiss
Stats Panel Scrollable
- Wrapped
_contentinScrollContainerwith max 400px height - Scrolls when stats list exceeds screen space (many relics + skill tree bonuses)
Target Indicator Fix
- Moved "v TARGET" label from y=0 to y=-14, above intent icons (was hidden behind them)
Unicode → ASCII (Web Export)
- All player-facing
→replaced with "to" (SNIPE descriptions) - All player-facing
—(em dash) replaced with-across 15 strings in 7 files - Archetype descriptions, shop tips, map tooltips, intent descriptions, victory screen headers
Status Shader Pixel Scaling Fix
- All 5 status shaders now use
textureSize(TEXTURE, 0)instead offloor(UV * N) - Patterns stay 1px crisp regardless of sprite size — bigger sprites get more dots/lines/bands
- Burning: 1px embers on 4px grid, more embers on bigger sprites
- Frozen: 1px frost dots on 5px grid near edges
- Deep frozen: hexagonal ice crystal lattice (see above)
- Vulnerable: crack offsets scaled to sprite height (h * 0.15/0.5/0.7)
- Stunned: bands use pixel row height, every 6th row
Scrollbar Theme (1-bit Aesthetic)
VScrollBarandHScrollBarstyles added togame_theme.tres- Track: near-black with subtle border. Grabber: light grey → bright on hover → white on press
- 2px corner radius for clean pixel look
Hourglass Cooldown Overlay
- Ability cooldown no longer replaces icon with hourglass
- 14x14 yellow-tinted hourglass overlaid in bottom-right corner of ability icon
- Original ability icon stays visible underneath
Frozen Dice Elements (Soft Freeze)
- Soft frozen dice now correctly contribute their element to combos and single-element effects
- Only deep frozen dice are excluded from elements
Session 2026-04-08c — Playtest Feedback, Intent Display, Freeze Rework, UX
Enemy Intent Display (STS-style)
- 5 intent icons above enemy sprites, always visible (no hover needed):
- Dagger (red) = Attack, shows damage number
- Shield (blue) = Defend, shows block number
- Potion (green) = Heal, shows HP value
- D6 die (orange) = Dice interference (SNIPE, GRAVITY, CHAOS, FLIP_ALL, CURSE, EVENODD, RIG, LOCK_DIE)
- Key (purple) = Resource steal (STEAL_REROLL, LOCK_HOLD, BURN_DICE, STEAL_DIE, FORCE_REROLL)
- Hover on intent icon shows full description via combat tooltip
- Stunned enemies show no intent
- Sprite group pushed down 12px to make room for intent above
- 5 unique Kenney 1-bit icons, tracked in
docs/kenney-1bit-icon-usage.md
Freeze Rework — Soft vs Deep
- Soft Freeze (basic monsters): Value locked at current roll. Can't reroll/hold. Still counts in combos at full value. Light blue shader.
- Deep Freeze (elite/boss): Value crushed to 1. Can't reroll/hold. Still counts in combos (but 1 is nearly useless). New
deep_frozen.gdshaderwith heavy desaturation, frost crystal pattern, bright dual-wave shimmer. - Both types keep dice "in play" — frozen dice always matter, always part of your hand
DicePool.hard_frozenarray tracks freeze type per dieHandScorerno longer excludes frozen dice from combo detection- Updated tooltips: "FROZEN – Value locked" vs "DEEP FROZEN – Value crushed to 1"
- VFXTest scene updated with Deep Frozen shader preview
Victory Celebration
- Multi-color confetti burst (6 colors x 10 particles = 60 total) raining from top
- Gold screen flash (0.3 alpha, fades in 0.6s)
- Victory SFX (
victory.mp3wired toAudioManager.SFX.COMBAT_WIN) - HP bars sync immediately on kill via
_sync_enemy_hp_bars()— enemy shows 0 HP before death animation
Per-Enemy Floating Damage Numbers
- Combo hits: Each enemy in
targets_hitshows floating damage number (correct per-target amount for splash) - Element effects: Wind (3 AoE) and Lightning (8 to target) show per-enemy floating damage
- Wind multi-hits: Each staggered hit shows floating "-3" on all enemies
- New
_spawn_enemy_floating_damage()helper for consistent floating numbers per_target_damagedict added to combo_result in manager
Element/Combo VFX Plays on Kill
- Root cause:
score_hand()dealt hand damage first, killing target._get_target_enemy()returned null, so element/combo logic produced zero entries. - Fix: Capture
pre_damage_targetandpre_damage_livingbefore hand damage. Pass to_execute_combo()and_apply_single_element_effects(). Dead targets skip damage but still produce VFX entries.
Dissolve Death Animation Fixes
- Reset
spr.modulate = Color.WHITEbefore dissolve (hit flash tween could leave tint) - Kill idle bob tweens before dissolve starts
- Hide status particles + overlays in sprite_group during dissolve
- Fallback: create dissolve_mat on the fly if meta is missing
- Added
sprvalidity check to tween condition
Combo VFX Rebalance
- Particle counts reduced — dual: 2.5x→1.5x, triple: core 35→18 / ring 50→25 / sparks 25→12, singularity: core 80→40 / sparks 60→30
- Sprite VFX scaled up — dual: 2.5x→3.5x, triple: 3.5x→5.0x, singularity vortex: 5.0→6.5x, burst: 5.5→7.0x
- Net effect: bigger main VFX, less particle noise
Hub Rework
- Achievements moved to hub as TrophyStone interactable object (removed from main menu)
- Trophy stone node in
Hub.tscnwith outline hover shader - Achievements screen back button → hub (not main menu)
- Skill tree firefly particles — CPUParticles2D node parented to SkillTree in Hub.tscn
- Skill tree locked state improved — brighter base color (0.55→0.7) + gentle alpha pulse (0.6-1.0 over 3s)
- AI background generation config added for trophy_stone asset
Early Game Pacing
- Floor 2 guaranteed shop — players now hit combat (F0), combat (F1), shop (F2), combat (F3), shop (F4). Two early shops so new players see relics/consumables before floor 5.
UX Fixes (Playtest Feedback)
- "Chips" renamed to "dmg" — formula panel shows "5 dmg" instead of "5 chips", combat stats shows "Relic Dmg" / "Bonus Dmg"
- Non-combo dice flash during scoring — remaining dice flash together with subdued pulse + "+X bonus" label after combo dice pop
- Element label moved above dice — bigger (11px, 3px outline), positioned above die instead of tiny text below
- Reroll+ cooldown clarified — "(+1)" changed to "[1 turn]" with hourglass icon when on cooldown. Descriptions updated to mention "2 turn cooldown."
- Frozen dice tooltip added — hover shows "FROZEN – Value locked" or "DEEP FROZEN – Value crushed to 1"
- "Click to continue" bigger — cutscene continue text: size 10→16, brighter color, full opacity,
vreplaces unicode▼ - Unicode
xfix — replaced×(U+00D7) with ASCIIxin consumable descriptions + combat pending warnings
Playtest Feedback Form
- Google Apps Script (
tools/create_feedback_form.gs) generates full playtest feedback form (6 sections, 21 questions) - Discord announcement message drafted
Artist Reference Pack
- All 324 art assets copied to
artist_reference/in 14 categorized folders for handoff to artist
Status Shader Pixel Patterns
All 5 status effect shaders now have distinct pixel-art patterns for better visual readability:
| Shader | Pattern | Visual |
|---|---|---|
burning.gdshader | Ember pixels crawl upward from bottom | Bright orange/red dots concentrated at feet, shift each frame |
frozen.gdshader (soft) | Frost dots near sprite edges | Sparse blue-white pixels along border, like frost creeping in |
deep_frozen.gdshader (hard) | Bold crack lines, randomized per instance | Blue-white cracks cutting across, shimmer glints along them |
vulnerable.gdshader | 3 crack lines across sprite | Amber-gold jagged lines like broken armor, pulse with intensity |
stunned.gdshader | Horizontal static bands scrolling down | Bright pixel rows drifting downward, dazed TV interference |
- All patterns use
floor(UV * N)pixel grid snapping for chunky pixel-art look - Deep frozen crack positions randomized via
crack_seeduniform (set per die) - Burning embers weighted toward bottom of sprite via
smoothstep - Frozen frost dots concentrated near edges via edge distance calculation
Research: Dicey Elementalist
- Competitor analysis of Dicey Elementalist (mobile dice roguelike by Doublethink Games)
- Key finding: their enemy system is much simpler (~1 interference type vs our 11). Our game already more sophisticated.
- Design lesson: dice-as-mana (gating what you can do) is an interesting pattern for future elemental system evolution
- Intent display identified as #1 gap — now implemented
Session 2026-04-08b — Combat Fixes, Consumables, Shrine, Elemental Refactor
Consumable Handlers Wired
- Tempering: +10% elemental dice chance for combat (stored as dice_pool meta)
- Prism: All elemental dice count as every element for one turn (guarantees combo if 2+ elemental)
- Purify: Remove all debuffs — unfreeze frozen dice, restore 0-value burned/cursed dice
- All 9 active consumables now have handlers (was 6/9)
Death Animation Fixes
- Enemy swap on death:
_update_enemy_display()now blocked during_in_scoring_phaseand while_pending_death_enemiesis non-empty — survivors don't reposition mid-dissolve - Dissolve with status shaders: Death animation now forces dissolve shader directly via
dissolve_materialmeta, replacing any active status shader (burn/chill/freeze/vulnerable) - Fallback death anim: If dissolve shader missing, enemy shrinks + fades (0.4s) instead of just alpha fade
- Burn kill visibility: Fixed burn-killed enemies not disappearing — death sound played but node stayed
- Death sound duplicate: Added
_dead_animatedtracker — each enemy's death animation only plays once across turns - Relic pop SFX: Uses
DICE_REROLL(same as dice pops) for consistent escalating sequence
Ice Targeting Fix
- Ice/Fire/Lightning elemental effects now use same target as hand damage (captured before damage dealt)
- Was:
_get_target_enemy()re-queried after kill, targeting next living enemy instead of intended target
Wind Multi-Hit Working
wind_extra_hitsskill tree effect now wired into gameplay (was defined but never read)SkillTreeManager.get_wind_extra_hits()getter added- Each wind hit deals 3 damage to all enemies sequentially
- VFX: staggered bursts (0.4s apart) with per-hit screen shake + hit sound + floating text
- Full Tempest path: +4 extra wind hits = 15 damage AoE per wind die
Elemental Chance Centralized
- Single source of truth in
SoloGameManager:get_elemental_chance_per_die(element)— per-element chance including base + relics + shrine + skill tree + archetypeget_all_elemental_chances()— returns{fire, ice, wind, lightning}dictget_total_elemental_chance()— combined (for atheist conversion)is_atheist_no_elements()— helper
- Gameplay now per-element: Element-specific relics (Ember Core, Frost Shard, etc.) and skill tree nodes increase that specific element's chance, not global
- Stats panel uses centralized functions — no duplicate logic
- Per-element display: shows Fire/Ice/Wind/Bolt separately when chances differ
Shrine Expanded (4 → 8 blessings)
- New: Golden Offering (+15 gold), Iron Will (+5 max HP), Keen Eye (+1 max hold), Blessing of Greed (+2 interest cap)
- All new blessings wired with persistent run state (
shrine_max_hp_bonus,shrine_hold_bonus,shrine_interest_bonus) - Interest cap bonus wired into
dice_combat_managergold calculation - Fixed shrine always rolling same blessing — was using unseeded
randi(), now usesrun_seed + floor * 7
Stats Panel — Relic Bonuses
- Rerolls show relic modifier:
"2 (+1 relic)"for Second Chance,(-1)for Twin Dice/Full House Party - Dice count shows relic modifier:
"6d6 (+1 relic)"for Twin Dice - Relic Chips/Mult/Gold rows for flat passive relics (Collector's Pride, Mult Magnet, Greedy Fingers, Lucky Coin)
Skill Tree Rebalance — Reroll Reduction
- Removed 3 redundant +1 reroll nodes (max was +3 from tree alone + keystones)
- Nimble Fingers bridge: +1 reroll → +1 max hold
- Extra Rolls (Utility gateway): +1 reroll → 10% consumable save
- Steady Hand (Utility Path A): +1 reroll → +3 chips to all hands
- Max rerolls from tree now: +1 (Purist keystone) or +2 (Gambler keystone), down from +3 base + keystones
Audio & Ambient
- Hub: campfire ambient at 25% volume, no music duck
- Solo map: wind ambient (act-scaled: 15%/30%/50%), stops music
- Reward screen: stops music (was playing reward track)
- Wind sound registered in
AMBIENT_SOUNDSdict play_ambient()now acceptsvolume_scaleandshould_duckparameters
Other Fixes
- Elemental tutorial: Now persists via
TutorialOverlaysave file — shows only once ever (was per-session) - 0-value dice: Skipped in
_rebuild_dice_display— not visible or hoverable - Achievement popup: Tween bound to autoload (survives scene changes), hold time 5s, game theme applied
- GDScript warnings: Fixed 10+ warnings (static-on-instance, shadowed vars, UID mismatch, capture reassignment)
_confirm_panelbug: Moved from local var to class member insolo_map_screen.gd— abandon run toggle now works
Session 2026-04-08 — Web Export Fixes, Settings, Scoring Polish
Web Export — Audio Bus Crash Fix
- Root cause: Godot's JS audio backend can't resolve named buses ("SFX", "Music") — crashes with
invalid bus index "-1"insample_set_volumes_linear - Fix: All
AudioStreamPlayernodes route through "Master" bus on web (OS.has_feature("web")) - Desktop unchanged: Still uses separate SFX/Music buses
- Removed dead "UI" bus from
default_bus_layout.tres(was merged into SFX previously but layout not cleaned up) - Removed
_bus_uireferences fromAudioManager - Removed
_apply_bus_volume("UI", ...)fromSettingsManager - Added
is_instance_valid(_music_player)guard +CONNECT_DEFERREDonsettings_changedsignal to prevent "object freed while signal emitting" crash
Web Export — Settings Slider Freeze Fix
- Root cause:
HSlidergrabs mouse input for drag tracking; on web, mouse release event can get lost, freezing all UI input permanently - Fix: Web uses
[-]/[+]button controls instead of sliders (gated behindOS.has_feature("web")) - Desktop still uses normal
HSlidercontrols - Button group wrapped in expanding HBox to match slider width alignment
- Both
settings_screen.gdandoverlay_manager.gd(gear overlay) updated
Web Export — Scene Transition Black Screen Fix
- Root cause:
SceneManager.goto()usesawait get_tree().process_frameafterchange_scene_to_file()— can fail to resume on web, leaving_busy = truepermanently and_fade_rectstuck at full black - Fix: Replaced
process_frameawait withcreate_timer(0.05)(reliable on web) - Added
_processsafety valve: if_busystuck for >2s, force-resets and clears fade rect
Dice Hidden Before First Roll
- Dice buttons were visible and hoverable before clicking "Roll" in first combat
- Root cause:
_on_roll_phasecalled_rebuild_dice_display()which setdice_container.visible = true— butdice_pool.resultswas pre-populated with zeroes fromadd_die() - Fix: Added
_first_roll_doneflag — dice container stays hidden until_on_dice_rolledfires
Scoring Animation — Relic Pop SFX
- Relic activations during scoring breakdown now play escalating-pitch sound
- Uses
DICE_REROLLSFX (same as dice pops) so it feels like one continuous rising sequence - Pitch continues from where dice pops left off:
1.0 + (die_flash_index + relic_pop_index) * 0.12
Achievement Popup Polish
- Applied
game_theme.tresto achievement notification panel - Extended hold time from 3s to 5s before auto-dismiss fade
Audio Cleanup
main_menu.oggreplaced with ambient sound (user-selected)main_menu_original.oggretained as backup
Session 2026-04-07d — System Design Docs, Achievements, Polish
6 System Design Documents Written (2959 lines total)
| System | File | Lines | Review Status |
|---|---|---|---|
| Shop Combat & Karma | design/gdd/shop-combat-system.md | 336 | Designed |
| Player-Echo Boss ("The Curse") | design/gdd/player-echo-boss.md | 778 | Approved with notes |
| 2.5D Map Scene | design/gdd/map-scene-overhaul.md | 467 | Approved |
| Blind Effect | design/gdd/blind-effect.md | 425 | Needs minor revision |
| Enemy Intent Display | design/gdd/enemy-intent-display.md | 303 | Approved |
| Endless Mode | design/gdd/endless-mode.md | 650 | Designed |
Other Changes This Session
- Achievement system with 16 achievements + lifetime SP cap (100)
- Floor-scaled SP formula (deeper floors = more SP per floor)
- Ambient sounds for shop/shrine/trap/rest/event scenes (duck music + play ambient loop)
- Music zone system (exploration/combat/boss/menu/end — no track change within same zone)
- Settings gear overlay (accessible from any scene, bottom-right)
- Chill reworked: enemy deals 15% less damage (was: duplicate of vulnerable)
- VULN display bug fixed (double %%)
- Elemental chance per-die + skill tree rebalance
- All panels use game theme
- Coding standards updated (game theme panels, UI_CLICK on all buttons)
Session 2026-04-07c — Music, Combat Polish, VFX, UX Fixes
Music System
- 9 tracks from alkakrab (free commercial use): 3 combat (per act), 3 boss (per act), menu/hub, map/shop, reward/victory
- Per-act music:
AudioManager.get_combat_music(act, is_boss)picks the right track - Smart dedup: same track won't restart when transitioning between screens using same file
- OGG looping enabled, crossfade on track changes (1s)
- Music bus at -3dB, volume controlled by SettingsManager
- Music slider added back to Settings screen (Sound + Music + Speed)
Shield Blocked Damage Feedback
- Damage numbers always show even when blocked by shield
- Partially blocked: shows
"-X (-Y blocked)"in red - Fully blocked: shows
"BLOCKED! (-X)"in blue-grey - Shield break flash animation when damage punches through
- Big hits (15+): yellow-white flash + larger font
Burn VFX + Separate Damage
- New
burn_tickedsignal emitted when burn deals damage during status tick burn_tick.pngsprite (Part 4/177, row 0) plays on enemy when burn ticks- Floating orange "BURN -X" damage number (separate from hand scoring)
- Logged in battle log as distinct event
Lightning Hit VFX Overhaul
- Now spawns 3 bolts at randomized offsets (was 1) with scale 2.0-3.0x (was 1.5x)
- Extra bright blue-white modulate (1.8, 1.8, 2.5) for visibility
Death Animation Fix
_play_deferred_deaths()now awaits ALL death tweens'finishedsignal- Enemies only removed/repositioned after every dissolve completes
Battle Log Fix
- Replaced broken
ScrollContainer > RichTextLabel(fit_content=true)withRichTextLabel(scroll_active + scroll_following) - Now actually displays logged events with auto-scroll
Double Game Over Button Fix
- Added
_game_over_shownguard — only creates button once
Combat Tooltip Overhaul
- Fixed
PanelContaineranchored top-right (260px wide), game theme styled - Replaces default follow-mouse tooltips for: status icons, elemental status labels, dice
- 13px clear parchment-colored text on themed panel
_wire_combat_tip()helper for easy hookup
Map Tooltip Removal
- Disabled default tooltips on map nodes — legend panel is the info source
Abandon Run Button
- Bottom-left of solo map screen, red "Abandon Run" text
- Confirmation dialog: "Abandon this run? Progress will be lost. Skill points will still be awarded."
- Triggers normal defeat flow (cutscene + game over + skill point award)
Achievement System + Skill Point Rework
- New
AchievementManagerautoload (src/core/achievement_manager.gd) - Persists to
user://achievements.save - Lifetime SP cap: 100 — can't fill the whole tree (~55-65 nodes out of ~95 allocatable). Forces build specialization.
- Floor-scaled base SP — deeper floors worth more per floor:
- Floors 1-7: 0 SP (too early, no reward)
- Floors 8-10: 0.2 SP/floor (mid Act 1)
- Floors 11-14: 0.4 SP/floor (late Act 1)
- Floors 15-19: 0.5 SP/floor (Act 2)
- Floors 20+: 0.6 SP/floor (Act 3)
- Bosses: +2 SP each, combos: capped at +3
- Cap enforced in both
SkillTreeManager.award_run_points()andAchievementManager._award_sp() - UI shows lifetime progress: "+X Skill Points (Y/100)" on victory/game over
- 16 achievements total, front-loading ~55+ bonus SP across first 10-15 runs:
| Category | Achievement | Points | Trigger |
|---|---|---|---|
| Progression | Into the Wasteland | 5 | Clear Act 1 |
| Progression | The Throne Awaits | 5 | Clear Act 2 |
| Progression | Curse Broken | 8 | First victory |
| Archetype | Wanderer/Gambler/Bruiser/Trickster/Strategist/Elementalist/Atheist | 3 each (21 total) | Complete run as each |
| Skill | Elemental Awakening | 2 | First elemental combo |
| Skill | Perfect Roll | 3 | Five of a Kind |
| Skill | Impossible Odds | 5 | Six+ of a Kind |
| Challenge | Versatile Hero | 5 | Win with 3+ archetypes |
| Challenge | Naked Run | 5 | Win with 0 relics |
| Challenge | Untouched | 5 | Win at full HP |
| Challenge | Devastating Blow | 3 | 100+ damage single hand |
| Challenge | Speed Demon | 3 | Win under 10 minutes |
- In-combat checks: combo, hand type, damage — trigger immediately with toast notification
- End-of-run checks: progression, archetype, challenge — shown on victory/game over screen
- Skill point economy: base formula unchanged (1 + floors0.3 + bosses2 + combos), achievements add large one-time bonuses on top
- UI: achievements listed on victory/game over screens with SP values
- Foundation for Steam achievements —
ACHIEVEMENTSdict maps directly to Steam API
Elemental Chance Rework — Per-Die
- Changed from per-roll to per-die: each unheld die rolls independently for elemental chance
- Base chance reduced: 5% per roll > 2% per die
- With 5 dice: ~10% chance of at least one, ~1% of two+ (natural combos possible)
- Relic bonuses reduced to match: ember_core/frost_shard/wind_chime/spark_plug +1% per die (was +3%), elemental_lens +2% per die (was +5%)
- Elementalist passive: doubles chance per die (was: force pairs). Now gets ~4% per die base = ~19% chance per roll
- Atheist conversion: +1 chip per 2% chance (was per 5%)
- Guaranteed window (floors 3-5): still forces at least 1 elemental if none rolled naturally
- Stats panel updated: shows "Elem/Die" instead of "Elem Chance"
Skill Tree Unlock Rework
- Now unlocks on first skill point earned (was: first boss kill)
- Removed
UNLOCK_BOSSES_REQUIREDconstant - Hub tutorial tip: themed panel floats above SkillTree sprite when newly unlocked
- Locked skill tree: hoverable with outline glow, click shows "Complete a run to unlock!" warning
Web Font Safety
- Replaced all Unicode symbols with ASCII equivalents across all UI:
▼>v,🔥❄⚡>BURN/FROZEN/CHILL/VULN,🔒>[LOCKED],★>*,→>>,×>x,—>-
- All player-visible text now renders correctly in pixel fonts on web
Future Feature Ideas (saved to PROGRESS.md + game-concept-v3)
- Player-Echo Boss ("The Curse"): final boss is a previous player build
- 2.5D Map Scene: adventurer figure with interactable backpack/map scroll
- Shop Combat & Karma: rob the merchant with cross-run consequences
Known Issues
- Enemy intent not visible on enemy sprites (only on hover) — needs STS-style intent icons above heads
Session 2026-04-07b — Backgrounds, Scoring, Combat Fixes, UX Overhaul
Background System Rework
- Removed parallax layers (mid/near) and perspective shader — single static background per theme
- 9 new 1-bit backgrounds generated via FLUX.1-schnell with chunky pixel post-processing (3x downscale, 2.5x contrast, 4-tone posterize):
- Act 1 (The Depths):
dungeon(stone hallway + torch),catacombs(skull niches + candle),cavern(stalactites + moss) - Act 2 (The Wasteland):
wasteland(dead tree + cracked earth),ruins(broken arch + rubble),battlefield(swords + banner) - Act 3 (The Throne):
throne(pillars + throne),cathedral(gothic arches + rose window),infernal(obsidian + chain)
- Act 1 (The Depths):
- Random per run:
SoloGameManager._run_bg_themespicks one from each act's pool at run start - Reward screen now shows the act background with dark overlay (was solid color)
parallax_bg.gdsimplified to single image loader with fallback chainbg_test_scene.gdsimplified — Left/Right cycles all 9 themes- Old layer folders deleted (dungeon/, catacombs/, infernal/, wasteland/, throne/)
- Perspective shader deleted
Scoring Rework — All Dice Count
- New formula:
(hand_chips + combo_dice) × multiplier + remaining_dice - Combo dice (part of the hand) get the multiplier benefit
- Non-combo dice add their face value flat (no multiplier)
- Example: Pair of 3s with [3,3,5,6,2] →
(5+3+3)×1.0 + 5+6+2 = 24(was 11) - Makes every die matter without inflating multiplier scaling
Burn Kill Bug Fix
- Enemies that died from burn damage weren't triggering death properly
dice_combat_manager.gd: Aftertick_statuses(), now emitsenemy_died_signalif enemy died from burn- Checks
all_enemies_dead()immediately after burn ticks — prevents dead enemies from selecting intents or attacking
Precise Alpha Hitboxes
- New
AlphaHitRectcomponent (src/ui/components/alpha_hit_rect.gd): invisible Control that overrides_has_point()to check texture pixel alpha - Handles aspect-centered stretching math — maps Control coordinates to texture pixel space
- Applied to: hub objects (DungeonGate, SkillTree, Campfire, MapScroll), rest campfire, shrine altar, enemy sprites in combat
- Only opaque pixels register as hover/click — transparent areas pass through
Map Legend Panel
- Hover any map node → info panel appears on the right with:
- AI-generated legend art (256×256, 1-bit style) spanning full panel width
- Node title in matching color
- Multi-line description explaining the node type
- 9 legend art pieces generated (
assets/art/map/legend/) - Hidden by default, shown on hover, hidden on mouse exit
Battle Log
- "Log" button in combat top bar (next to skip button)
- Scrollable panel on the right side showing turn-by-turn events:
- Turn starts, hand scored (name + damage), enemy actions, enemy deaths, stuns
- Toggle on/off with the button
Reward Screen — View Map Button
- "View Map" button in top-right corner
- Opens map in view-only mode (nodes unclickable)
- "Back to Rewards" button in bottom-left to return
SoloGameManager.map_view_onlyflag controls behavior
Map Tooltip Theme Consistency
map_node.trestooltip now usessimple.png9-patch from game theme (was monochrome_packed atlas)- Font color and size aligned to game theme values
Archetype Sort Order
get_all()now sorts dynamically: unlocked archetypes first, then by unlock threshold- Registration order: Wanderer + Gambler (always) → Bruiser + Trickster (15 pts) → Strategist + Elementalist (30 pts) → Atheist (50 pts)
Enemy Status Tooltips
- Hovering burn/freeze/chill/vulnerable labels on enemies shows description + turns remaining
- Status icon tooltips for stunned/shield/enrage with stack counts
Combat UI Fixes
- Removed "Incoming:" prefix from pending warning label
- Frozen dice no longer play reroll animation (added to skip_indices)
- Nudge/Set Die picker dismissed on Score or Reroll (was lingering if unused)
- Missing
_on_enemy_diedhandler added (was connected but undefined — caused silent error)
Deleted Old Scenes
Combat.tscnandMap.tscn— leftover multiplayer scenes referencing deleted scripts (caused UID errors on startup)
Game Concept v3 Updated
- Section 15: Full relic catalog table (58 relics organized by category)
- Section 16: Future considerations updated with shop combat/karma system, blind effect
Session 2026-04-07 — Skill Tree Rework, Archetype Rework, UX Polish
Skill Tree Complete Redesign (3-Ring Constellation)
Replaced the old 89-node radial web with a 146-node 3-ring structure with choice forks, cross-branch bridges, and 17 keystones.
New Structure
- Inner Ring: 4 smalls + 1 gateway notable per branch (32+8 = 40 nodes)
- Middle Ring: Fork into Path A / Path B per branch, each with 2 smalls + 1 notable (48+16 = 64 nodes)
- Outer Ring: 2 smalls + 1 keystone per path (32+16 = 48 nodes)
- Bridges: 8 notable nodes connecting adjacent branches at gateway level
- Center: Origin + Elementalist keystone
- Choice Lock: Allocating any node on Path A permanently locks Path B (and vice versa). Respec clears locks.
17 Keystones (2 per branch + Elementalist)
| Branch | Path A Keystone | Path B Keystone |
|---|---|---|
| ICE | Permafrost (freeze +2, ice +50%, no fire) | Absolute Zero (ice combo 2x, instant freeze, -1 reroll) |
| FIRE | Pyromaniac (all fire, burn 2x, no other elements) | Firestorm (burns spread, fire combo +25%, -15 HP) |
| WIND | Gale Force (wind +2 hits, +15%, -1 reroll) | Eye of Storm (20% dodge, wind heals 3, -15% combo) |
| LIGHTNING | Stormcaller (chain +2, +20%, -15 HP) | Overcharge (triple +12 dmg, pair +4, -20% elem chance) |
| HP | Thick Hide (+30 HP, heal 5, -20% dmg) | Blood Rage (below 50%: +50% dmg +1 reroll, -20 HP) |
| DAMAGE | Glass Cannon (2x dmg, HP halved) | Escalation (+3 chips/+0.15 mult per turn, turn 1 -25%) |
| UTILITY | Purist (no elements, 1.5x hands, +1 reroll) | Gambler (+2 rerolls, 15% elemental, -20% combo) |
| ECONOMY | Merchant Prince (-30% shop, +10 gold elite/boss) | Golden Touch (+5 interest cap, +5 gold/boss, -1 reroll) |
New Node Effect Types
dodge_chance: % to avoid enemy attacks entirely (Wind/Zephyr path)damage_reduction: flat damage reduction per hit (HP/Fortress path)on_triple_damage: bonus damage on triples (Lightning/Surge path)streak_mult: +mult per consecutive scoring turn (Damage/Momentum path)lucky_reroll_chance: % die becomes 6 on reroll (Utility/Chaos path)max_hold_bonus: +max holds (Utility/Control path)interest_cap_bonus: +interest cap (Economy/Investor path)below_hp_mult: +mult below 50% HP (HP/Berserker path)
Cross-Branch Bridges (8)
Notable nodes connecting adjacent branches — incentivize diverse builds: Frost Guard (+8 HP), Tempered Steel (heal 3/combat), Searing Strikes (+3 chips), Swift Strike (+0.15 mult), Nimble Fingers (+1 reroll), Overclocked (+5% lightning), Shock Value (+2 gold/combat), Cold Commerce (+5% ice)
Point Economy Reduction (~60%)
- Old:
1 + floors*0.5 + bosses*3 + min(combos, 5)→ full clear ≈ 22 pts - New:
1 + floors*0.3 + bosses*2 + min(combos, 3)→ full clear ≈ 13 pts - Max allocatable per build: ~172 pts (choices prevent filling both paths)
- Full build takes ~13+ runs
Visual Redesign (1-bit Aesthetic)
- Canvas: 3200×3200 (was 2400×2400)
- Unallocated nodes: greyish dark tones (user request)
- Choice-locked nodes: very dark, nearly invisible (dead paths)
- Allocated nodes: warm parchment white with subtle branch color tint
- Available nodes: mid-warm grey with pulse glow
- Connections: dotted pixel lines (locked), solid gold (allocated)
- Branch labels: small text at gateway ring
- Parchment/dark background palette throughout
- Default zoom: 0.8 (shows more of tree)
Manager Updates
locked_choices: Dictionarytracks committed fork sidesis_choice_locked(node_id)API for UI- Save version bumped to 3 (auto-migrates old saves with point refund)
- Respec clears choice locks
- 15+ new bonus getters for new effect types
Gameplay Wiring
_apply_enemy_attack_damage(): dodge chance + damage reduction- Combat start:
get_heal_per_combat()replaces hardcoded thick_hide check - Scoring: Escalation stacking, streak mult, on-triple/pair damage, Blood Rage, turn 1 penalty
- Rerolls: Blood Rage below-HP reroll bonus
- Max holds: skill tree hold bonus
- Interest: cap bonus + reduced threshold from Investor path
- Combos: Absolute Zero ice 2x, Firestorm fire +25% + burn spread, Eye of Storm heal
- Shop: fixed double-counting merchant prince discount
- Combat stats panel: shows dodge, damage reduction, max hold bonus
Hub Map Scroll Fix
hub_map_scroll.pngdownscaled from 1365×768 to 910×512 with nearest-neighbor pixelation- Now matches the chunky 1-bit pixel density of other hub sprites (dungeon gate, campfire, skill tree)
- Outline shader now proportionally thicker
All Node Icons (1-bit)
- 17 keystone icons (10 existing + 7 new from Kenney 1-bit pack)
- 9 branch icons for small/notable nodes: droplet (ice), torch (fire), wind swirl, mana star (lightning), heart (hp), fist (damage), gear (utility), coin (economy), star (center/bridges)
- Icons tinted dark on light fill (allocated), semi-transparent on locked nodes
- Icon usage tracked in
docs/kenney-1bit-icon-usage.md
Archetype Rework — No Starting Relics, Mechanical Identity
Removed all starting relics. Each archetype now changes HOW you play, not just damage numbers.
| Archetype | Mechanic | Unlock |
|---|---|---|
| Wanderer | Shops always relic. +1 reroll/3 relics (max +3). | Always |
| Gambler | 4 rerolls, 15% chance each curses a die to 1. | Always |
| Bruiser | 6 dice, cannot hold (max_holds=0). 100 HP. | 15 skill pts |
| Trickster | 3 holds + 1 free Flip/turn. 65 HP. | 15 skill pts |
| Strategist | 1 free Set Die/turn. 1 reroll. Puzzle solver. | 30 skill pts |
| Elementalist | Elements from start, always pairs, +15% combo. | 30 skill pts |
| Atheist | No elements ever. Elem chance → +1 chip/5%. | 50 skill pts |
archetype_data.gd:is_unlocked()checks total allocated skill points vs thresholdmanipulation_manager.gd: free Set Die/Flip as per-turn innate abilitiesdice_pool.gd: Gambler risky reroll (15% curse to 1)dice_combat_manager.gd: Atheist elem-to-chips conversionarchetype_select_screen.gd: locked archetypes greyed with unlock requirement- Removed all archetype relic scoring from
hand_scorer.gd
Skill Tree UX Overhaul
- Single-click allocate/deallocate — no inspect panel, no allocate button
- Hover tooltip shows everything: name, type, cost, description, status, action hint, cascade warning
- Cascade deallocation: removing a node also removes all nodes that become unreachable from center
- Free respec: respec costs 0 points, button removed (just click nodes to toggle)
- Inspect panel removed — tree viewport uses full screen width
- SkillTree.tscn scene-authored nodes (was 100% code-built)
- Game theme applied to tooltip panel
Map Node Colors
- Available nodes tinted by type (combat=red, elite=purple, boss=orange, rest=green, shop=blue, etc.)
- Current node: golden, visited: grey, locked: dark/faded
node_type_colorsdict (already defined) now wired into rendering
Hit Flash Rework
- Changed from red
(1,0,0,0.3)to warm yellow(1.0,0.7,0.15,0.5)— much more visible - Brighter (0.5 alpha) and yellowish per user request
Journey Log
- Run row panels now use game theme (
polis_sanctuary9-patch) instead of code-built StyleBoxFlat
Shop Bug Fix
- Fixed double-counting Merchant Prince discount (was applied in both manager and shop screen)
- Fixed
ICON_HEART→ICON_HPreference error
Settings Cleanup
- Removed Music slider (no music files exist — bus was empty)
- Merged UI sounds into SFX bus (UI players now route to "SFX" instead of "UI")
- Renamed "SFX" slider to "Sound" — controls all audio
- Settings now: 2 sliders (Sound + Speed), down from 4
Session 2026-04-06c — Archetypes, Stats Panel, Onboarding, Polish
6 Dice Archetypes
Full character selection system before each run. Each archetype has unique stats, signature relic, and passive mechanics.
| # | Name | HP | Rerolls | Dice | Signature Relic | Key Passive |
|---|---|---|---|---|---|---|
| 1 | The Gambler | 70 | 3 | 5 | Loaded Dice (pairs +12, unmatched -2) | 20% lucky reroll chance |
| 2 | The Strategist | 85 | 1 | 6 | Sequence Master (+3/die in longest straight) | Lock 1 die per combat |
| 3 | The Bruiser | 100 | 2 | 5 | Go Big (6s +8, 5s +4, triple-6 doubles chips) | High HP tank |
| 4 | The Trickster | 65 | 3 | 5 | Penny Pincher (1s/2s +5 chips + 1 gold) | Hold 3 dice, free Flip/turn |
| 5 | The Elementalist | 75 | 2 | 5 | Elemental Lens (+5% chance, +20% combo dmg) | Elemental pairs, unlocked from start |
| 6 | The Wanderer | 80 | 2 | 5 | Adaptable (+3 chips/unique, +2 gold/unique) | Shops always have relic, +1 reroll/relic (max 3) |
src/gameplay/archetype_data.gd— static definitions for all 6src/ui/archetype_select_screen.gd+ArchetypeSelect.tscn— selection screen with game theme panels, 1-bit character icons (Kenney)src/core/solo_game_manager.gd—start_new_run()takes archetype_id, applies stats/relic/consumable/gold/passives- Wired into: hand_scorer (6 archetype relic effects), dice_combat_manager (dice count, rerolls, max holds, elemental pairs, combo bonus, gold gen), shop (wanderer relic guarantee), dice_pool (archetype dice count)
- Hub DungeonGate → archetype select → run start
- Solo map shows archetype icon + name in top bar
- Game concept v3 updated with archetype section (5b)
Combat Stats Panel
CombatStatsPanelcomponent — collapsible panel using game theme- Shows: archetype name + icon, signature relic, relic count, gold, rerolls, max holds, dice count, elemental chance, combo bonus, skill tree bonuses
- Key stats have 1-bit icons (heart, coin, gem, star)
- Toggle button uses game theme Button style
Tutorial/Onboarding System
TutorialOverlayautoload — tooltip-style popups at key moments- Persists seen tips to
user://tutorial.save(each tip shows once ever) - First combat tip: explains dice rolling, holding, scoring, enemy turns
- First shop tip: explains relics vs consumables, gold scarcity
- Dim background + "Got it" dismiss button
Relic Scarcity (replaces removed relic cap)
- Reward screen: relic appears 30% after basic, 70% after elite, 100% after boss (was guaranteed)
- Shop: 60% chance relic appears (was guaranteed)
- When no relic: gold option or extra consumable instead
- Removed mult soft cap (x8.0) — broken builds can go infinite if you find the relics
Scene Polish
- Shop: items use game theme panels with colored borders, warm-tinted icons, card styling
- Shrine: altar cutout with outline shader hover, recolored to cold blue-grey, click-to-accept
- Rest: new screen with campfire cutout, outline hover, ember/spark particles, click-to-rest
- All scenes converted to proper .tscn with scene-authored nodes (bg, particles, content areas)
- Scripts use
%ContentVBoxinstead of building layout from code
Edge-Case Audit (8 fixes)
- 3 death checks: curse damage at combat start, Blood Pact after win, Double Down self-damage
- 2 empty pool guards: shop and reward screen consumable lists
- 3 array bounds guards: dice_pool roll/reroll/element arrays
Other
- Scene transitions:
fade=truedefault on allSceneManager.goto()calls - 6 archetype icons from Kenney 1-bit pack (joker face, knight helmet, viking helmet, hood, crown, meeple)
- Shrine altar recolored to match cold background tones
- Shop background regenerated (mysterious merchant in dungeon)
generate_backgrounds.py— added--scenesflag for scene background generation
Session 2026-04-06b — Balance Pass, Boss Rework, Scene Immersion
Balance Retuning (research-driven)
Researched difficulty curves from GDC talks (Slay the Spire metrics-driven design), Balatro scaling analysis, and roguelike economy best practices. Applied findings:
Gold Economy Reduction (~40%)
- Basic combat gold: 8 → 5
- Elite combat gold: 20 → 12
- Boss combat gold: 40 → 25
- Base gold per combat: 8 → 3
- Interest mechanic added: +1 gold per 15 held after combat, capped at +3. Creates Balatro-style "hold vs spend" tension.
Shop Price Increases (~60-80%)
- Common relic: 50 → 80, Uncommon: 90 → 150, Rare: 140 → 250
- Common consumable: 15 → 25, Uncommon: 30 → 50, Rare: 50 → 80
- Heal: 30 → 40
- Target: player affords 25-35% of shop items per visit (was ~80-100%)
Base Scoring Nerf (~15-20% chip reduction)
All hand chips reduced. Examples: Pair 5→4, Three of a Kind 10→8, Full House 14→12, Five of a Kind 25→22. Multipliers unchanged — relics are where scaling comes from.
Enemy Scaling Per Act
| Stat | Act 1 | Act 2 | Act 3 |
|---|---|---|---|
| HP multiplier | 1.0x | 1.4x | 1.8x |
| Damage bonus | +0 | +2 | +4 |
| Shield bonus | +0 | +3 | +6 |
Applied at combat start via enemy meta, on top of existing floor scaling.
Boss Rework
Per-Act Boss Assignment
- Act 1: Beholder (lich) — disruptor, 190 HP, snipe + chaos + steal rerolls
- Act 2: Shadow Kraken (hydra) — bruiser, 220 HP, heavy attacks + flip + lock + defend
- Act 3: Arch Demon (dragon) — full toolkit, 250 HP, burn + chaos + flip + heavy attacks
Previously all acts used dragon. Now each boss has distinct identity.
Boss Passive Abilities (unique mechanics)
Each boss invalidates a common strategy:
- Beholder — "All-Seeing Eye": Immune to Freeze. Can't be cheesed with ice combos.
- Shadow Kraken — "Thick Hide": Absorbs first 5 damage per hit (min 1 through). Punishes chip damage, rewards burst.
- Arch Demon — "Enrage": +2 attack per turn elapsed. Kill fast or die.
Passives shown below boss name in combat UI.
Boss Add Policy
- Act 1: Solo boss (learning experience)
- Act 2: 50% chance of 1 basic add
- Act 3: Always 1 elite add
Relic Slot Cap (8 max)
- Hard limit of 8 relics, inspired by Balatro's 5-joker cap (scaled for 50+ relic pool)
- Reward screen shows "Relics full — must swap one" warning with swap picker UI
- Shop blocks relic purchase when at cap with "Relics full!" message
- Forces build identity: can't just stack everything
Scene Immersion
All non-combat screens enhanced with AI-generated backgrounds + particles + flavor text.
AI-Generated Scene Backgrounds (assets/art/scene_bg/)
5 backgrounds via FLUX.1-schnell + postprocess_hub_bg() pipeline. Added --scenes flag to generate_backgrounds.py.
shop_bg.png— merchant shop interior, shelves, lanterns, hooded shopkeepershrine_bg.png— stone altar in mossy cave with mystical glowtrap_bg.png— dungeon corridor with skulls, archway, torchevent_bg.png— torch-lit secret chamber with shelvesrest_bg.png— campfire in cave alcove (reserved for future rest screen)
Shop
- AI background (dimmed merchant interior)
- Floating dust mote particles + candle glow spots
- Shopkeeper flavor text
Shrine
- AI background (mossy cave altar)
- Green sparkle particles + ambient glow halo
- Flavor text + enhanced star icon
Trap
- AI background (persists through all trap phases: choice, roll, result)
- Danger spark particles + floating debris
- Warning icon, stronger title treatment
Hidden Room (Event)
- AI background (torch-lit secret chamber)
- Mystery dust particles + warm glow
- Discovery flavor text
Other Fixes
- Removed redundant
save_to_disk()calls inskill_tree_screen.gd(allocate + respec) - Added HSlider/VSlider theme to
game_theme.tres(1-bit style matching other UI)
Session 2026-04-06 — Storytelling, Hub Redesign, Systems, Polish
New Autoloads
RunHistory(src/core/run_history.gd) — persists run outcomes touser://run_history.save(max 50). Records outcome, act/floor, duration, HP, gold, relics.SettingsManager(src/core/settings_manager.gd) — SFX/Music/UI volume + animation speed. Applies to AudioServer buses. Persists touser://settings.save.CutsceneManager(src/core/cutscene_manager.gd) —play(id, lines, on_done),play_once(),has_seen(). Persists seen dict touser://narrative.save. Overlay parents toget_tree().root.
Storytelling & Cutscenes (Sprint 11)
- Narrative frame: You are the Diceturner — last of a lineage who reads fate in knucklebones. Descend through 3 bound wards (The Depths → The Wasteland → The Throne) to break a usurper's hold.
Narrative(src/core/narrative.gd) — 5 dialog scripts as class constants:INTRO_FIRST_RUN— first run, hub start (play_once)ACT1_TO_ACT2— after Act I boss (First Warden)ACT2_TO_ACT3— after Act II boss (Second Warden, mother reference)VICTORY_EPILOGUE— after Throne clearedDEFEAT_EPITAPH— every game over
CutsceneOverlay(src/ui/components/cutscene_overlay.gd+.tscn) — full-screen backdrop, bottom-center dialog panel (880×180), typewriter reveal (45 chars/sec), click/ESC/right-click controls.- Wired in
SoloGameManager: intro on first run, act transitions after bosses, epilogue/epitaph on win/loss. Callbacks navigate after cutscene finishes.
Hub Redesign
- AI-generated camp background (
hub_background.png) — unified scene with all objects painted together. - 4 hand-edited sprite cutouts as TextureRect children in
Hub.tscn: DungeonGate, SkillTree, Campfire, MapScroll. - Outline-on-hover via
outline.gdshaderwith newoutline_alphauniform. Each sprite has own ShaderMaterial. - Skill tree locked state: grey modulate (0.55),
MOUSE_FILTER_IGNORE→ no glow, no click. Unlocks after first boss. - Campfire particles: chunky embers (6 @ 6-9 scale) + bright sparks (4 @ 4-6 scale), wide emission rects.
hub_screen.gdslimmed to ~140 lines — wires behavior to scene-authored nodes.- 16 unused old hub icons deleted (hub_char_knight, hub_char_wizard, hub_tome, etc.).
New Screens
- Journey Log (
src/ui/journey_log_screen.gd+JourneyLog.tscn) — scrollable newest-first run list, V/X outcome badge, HP/gold/duration/act/floor summary, relic icon strip with tooltips. Accessible from Hub MapScroll. - Settings (
src/ui/settings_screen.gd+Settings.tscn) — 4 sliders (SFX/Music/UI volume + animation speed). Accessible from main menu.
Critical Bug Fix: Keystone Naming
15 call sites across dice_combat_manager.gd, solo_game_manager.gd, and shop_screen.gd used un-prefixed names ("pyromaniac" instead of "ks_pyromaniac"). has_keystone() always returned false. All skill tree keystones were non-functional. Fixed with ks_ prefix across all call sites.
Combat UI Fixes
- Turn counter centering: TopBar restructured into 3 equal-weight sections (LeftSide/PhaseLabel/RightSide). Phase label stays centered regardless of streak/incoming labels.
- VFX target fix: Elemental & combo VFX now center on enemy sprite, not root Control. New
_get_enemy_sprite_center()helper. - Death animation shuffle: Dying enemies reparented from HBox to scene root, preserving screen position during dissolve.
- Regular monster outline removed: Red "danger" outline reserved for elite (gold) and boss (red) only.
- Formula panel migrated: from GDScript-created to scene-authored nodes in
DiceCombat.tscn.
Skip Button & Animation
AnimConfig.skip_mode: bool+SKIP_DELAY = 0.02.delay()returns SKIP_DELAY when active.- ⏩ toggle button in combat TopBar RightSide.
Dice Roll Animation Unified
- New
DiceRollAnimationcomponent (src/ui/components/dice_roll_animation.gd). - Trap screen disarm roll now uses same config + SFX as combat.
Balance Changes
- Greedy Fingers nerf: was
+0.1 mult per gold (max +3.0), now+0.1 mult per 10 gold (max +1.5).
Other Fixes
- Discard picker: smaller cards (130×120), 48px consumable icon at top, fits 6 slots in 1280px.
- Victory/GameOver relic display: vertical labels →
GridContainer(columns=10)of 32px icons with tooltips. - Shatter targeting: now hits player-selected target enemy (▼ TARGET indicator) instead of hardcoded first.
- OverlayManager migration:
SoloGameManager.add_relic()firesOverlayManager.toast().
Multiplayer Code Stripped
Deleted: src/networking/ (3 files), src/ui/lobby_screen.gd, combat_screen.gd, reward_screen.gd, map_screen.gd, 4 scenes (Lobby/Combat/Map/Reward.tscn). Removed 3 autoloads from project.godot.
Map Icon Polish
- Base-5 + S8 icons recolored to uniform dark taupe
(85, 70, 55)(rest.png stays red). - White interior fill stripped from S8 icons.
map_visual.gdmodulate tinting removed — icons render at natural color.
Singularity VFX Rewrite (v2)
- 2-phase sprite animation: vortex shrinks+spins → burst explodes outward.
- Uses AtlasTexture frame stepping via
_play_strip()and_animate_singularity(). - Central flash + radial spark particles at the collapse moment.
Session 2026-04-05 — Hub, Autoloads, Skill Tree Polish, Bug Fixes
Architecture (new autoloads)
SceneManager(src/core/scene_manager.gd) — symbolic scene keys dict + optional fade. All 27 scatteredchange_scene_to_filecalls migrated toSceneManager.goto(&"<key>").OverlayManager(src/core/overlay_manager.gd) — CanvasLayer(100) withtoast(),flash(),fade_out/in(),shake_scene(). Built but not yet migrated to; existing_screen_shakein combat still in place.
Hub Scene ("The Adventurer's Hearth")
src/ui/hub_screen.gd + assets/scenes/Hub.tscn. Immersive camp — every interactable is a worldspace object (campfire, wall torches, map scroll, tome, chest, door). 3 character slots around fire: The Adventurer (unlocked, uses hero.png) + 2 locked silhouette placeholders (knight/wizard). Hover shows name+tagline at top and inspect bar at bottom.
- Main menu "Start Run" → Hub
- Victory/Game Over "Try Again/Return" → Hub
- Skill Tree "Back" → Hub
- Hub map scroll → start run, tome → skill tree (gated), chest → placeholder, door → main
Skill Tree
- Screen redesign (
src/ui/skill_tree_screen.gd): scroll-wheel/pinch zoom (0.3x–2.0x at cursor), drag pan, Mac trackpadInputEventPanGesture/InputEventMagnifyGesturesupport, hover tooltip + right-side inspect panel with Allocate button. Keystone hexagons now render with 1-bit icons on light fill for contrast. - Transform bug fix: draw used
origin=_panbut hit-detection and zoom-at-cursor treated pan aspan*zoom. Unified formula toscreen = canvas * zoom + pan. - Gating: new
unlocked: boolinSkillTreeManager(persisted, save version→2). Auto-unlocks after clearing ≥1 Act boss. Main-menu button and Hub tome hidden until unlocked. First-unlock banner on victory/game-over.
1-bit Icon Integration
All new systems now use Kenney 1-bit icons from ~/Downloads/Game Dev Assets/1-bit_icons/Sprites/.
- 12 elemental relic icons →
assets/art/icons/(ember_core, frost_shard, wind_chime, spark_plug, elemental_lens, combo_amplifier, prismatic_die, resonance_crystal, status_spreader, catalyst, chaos_die, loaded_d4) + registered insprite_map.gd - 4 S8 map node icons →
assets/art/map/(trap, temple, hidden_room, shrine). Untinted to match existing tilesheet icons. - 10 skill tree keystone icons →
assets/art/skill_tree/ - Hub objects →
assets/art/ui/hub_*.png(campfire, torch, door, tome, chest, map_scroll, char_knight, char_wizard)
Theme Update (assets/theme/game_theme.tres)
- Buttons:
star_fleet.png9-slice (cleaner thanassembly) - PanelContainer:
polis_sanctuary.png(architectural) - TooltipPanel:
simple.png(minimal)
Shaders & VFX
- New
stunned.gdshader— heavy grayscale + darken + yellow pulse. Combat stun now uses this (was modulate-only before — easily missed). - Rewrote
vulnerable.gdshader— red outline was reading as boss/elite. Now amber tint + diagonal hatch pattern. Combat overlay alpha = 0.65 so base sprite shows through. - Dissolve shader — brighter edge (width 0.08→0.14, 2.2× multiplier)
- VFX sprite brightness —
sprite_vfx.gdappliesmodulate = Color(1.6,1.6,1.6)to all spritesheet VFX - Lightning tuning — dialed back to support sprite (amount 42→14, scale 10–16→5–8, bolt width 4.5→3.0)
- Shield flash — replaced blue ColorRect with actual
shield_icon.pngsprite (pops in, settles, pulses out) - Sting VFX — replaced with pack #52 (Part 5/230.png) row 3, tinted toxic yellow-green baked into image
Singularity VFX Rewrite
New sprite: extracted Part 1/04 row 2 (magenta curls, 14 frames), tinted dark purple/indigo baked into assets/art/effects/vfx_picks/singularity.png.
New particle effect — 2-phase black hole (ElementalVFX.create_singularity_hit):
- Phase 1 (0–0.55s): 80 dark-purple particles born on 80px ring, orbit velocity 1.6–2.4 + radial_accel -260/-380 → vortex suck-inward.
- Phase 2 (0.4s+): 24 giant white-flash particles at center + 90 radial-burst particles (velocity 160–320, color ramp white→magenta→purple).
- Deferred start via
container.ready.connect+SceneTreeTimer(0.4)._spawn_combo_hit_vfxin combat now switches particle types by tier (singularity=new vortex, triple=triple_combo, dual=dual_combo, single=element_hit). Tier-4 cleanup bumped to 2.2s.
Map Icon Extraction (tilesheet → PNG with outlines)
Cut old tilesheet-based map icons (combat/elite/boss/shop/rest) out of tilesheet_transparent.png into individual PNGs under assets/art/map/. Baked 1px cream outline (245, 230, 200) into each, plus the 4 S8 icons (trap/temple/hidden_room/shrine). map_visual.gd now prefers assets/art/map/<type>.png (with fallback to tilesheet atlas). Rationale: the neighbor-sampling outline shader can't work across atlas boundaries, and per-icon PNGs are easier to iterate on.
Pixel-Shader Pack (removed)
Pack didn't fit the art style. Folder assets/art/pixel-shader/ and all test buttons in VFXTest.tscn removed.
Elemental Gating
10 elemental relics + 2 elemental consumables (prism, tempering) stay hidden from shops/rewards/events/shrines until player sees first elemental die (SoloGameManager.elemental_tutorial_seen). Helpers: RelicDatabase.filter_by_elemental_unlock(), ConsumableDatabase.filter_by_elemental_unlock(). Applied in shop_screen, dice_reward_screen, event_screen, shrine_screen.
Combat Bug Fixes
- Damage number on wrong enemy — UI re-queried
_get_target_enemy()at display time; if target died from that damage, returned next living. Now usesresult["damage_results"][0].enemy_id(authoritative). - Dissolve effect not showing —
_update_enemy_display()(called mid-scoring) was queue-freeing dead enemies before_animate_enemy_death()could grab them. Now preserves nodes whereenemy.is_alive == false. Death anim now runs dissolve solo first (0.55s) then short fade (0.25s). - Status duration off-by-one — newly-applied statuses were ticked immediately at end of current turn, so vulnerable/chill expired before player's next scoring. Added
_just_appliedgrace-tick flag inenemy_instance.gd. - Freeze only stunning once — freeze relied on one-shot
stunnedflag. Nowenemy.has_status("freeze")also triggers skip gate every turn. - Enemy lunge direction — horizontal (right→left). Now vertical toward player (
position:y). - Attack action text — was
"Attack! -6 HP"(confusing). Now just"Attack!". - Hit-animation delay — was ~2.5s after intent. Enemy action now runs detached while player_hit fires after 0.18s (~0.7s total).
is_instance_validguards at every await. has_nodeon RefCounted (dice_combat_manager.gd) — usesEngine.get_main_loop().root.has_node().- Multiplayer RPC crash (
combat_screen.gd) — guarded withmultiplayer_peer.get_connection_status()check. - Elemental tutorial popup — dice particles rendered on top. Hosted on own CanvasLayer(layer=50). Removed "hold elemental die to keep element" line.
Known Gaps / Next Session
- Multiplayer code not yet stripped (
game_server.gd,combat_screen.gd,lobby_screen.gd,room_manager.gd,network_manager.gd) - OverlayManager built but nothing migrated to it yet
- Keystone naming inconsistency — data uses
ks_xxx, manyhas_keystone()callers drop theks_prefix (always returns false). Pre-existing. - Character 2 & 3 are cosmetic placeholders, no gameplay differences defined
- Playtest — user to test dissolve fix, damage-number target fix, singularity vortex, outlined map icons, skill tree gating, hub flow
Game Overview
Balatro-inspired dice roguelike. Roll dice, make Yahtzee hands, deal damage. Collect relics that warp scoring rules. Enemies interfere with your dice. Per-enemy turn limit — run out of turns and the monster hits you (lose 1 life). 15-floor dungeon with STS-style branching map.
Core feel: Roll → manipulate → score → watch relics chain → enemy interferes → next turn.
What's Built
Core Game Loop
- 5d6 dice rolling, hold (max 2), reroll (2 base, persists for entire encounter)
- 19 hand types supporting 5-8+ dice
- Per-enemy turn limit (3 basic / 3 elite / 4 boss)
- Lives system (3 lives, 0 = game over)
- Scoring breakdown animation (Balatro-style)
- Hand preview shows damage before scoring
- Frozen dice excluded from hand evaluation and scoring
- Dice not clickable before rolling
- Lock icons cleared during scoring animation
- Reroll pips dynamically grow when Reroll+ adds extra rerolls
Hand Types (19 total)
| Hand | Chips | Mult | Min Dice |
|---|---|---|---|
| High Die | 2 | ×1.0 | 1 |
| Pair | 5 | ×1.0 | 2 |
| Two Pair | 8 | ×1.0 | 4 |
| Three of a Kind | 10 | ×1.5 | 3 |
| Straight | 12 | ×1.5 | 5 |
| Full House (3+2) | 14 | ×2.0 | 5 |
| Three Pair | 16 | ×2.0 | 6 |
| Long Straight | 18 | ×2.5 | 6 |
| Two Trips (3+3) | 20 | ×2.5 | 6 |
| Four of a Kind | 18 | ×2.5 | 4 |
| Full Keep (4+2) | 22 | ×3.0 | 6 |
| Four Pair | 24 | ×3.0 | 8 |
| Five of a Kind | 25 | ×4.0 | 5 |
| Royal Keep (5+2) | 30 | ×4.5 | 7 |
| Double Quad (4+4) | 35 | ×5.0 | 8 |
| Royal House (5+3) | 38 | ×5.5 | 8 |
| Six of a Kind | 40 | ×6.0 | 6 |
| Seven of a Kind | 55 | ×8.0 | 7 |
| Eight of a Kind | 75 | ×10.0 | 8 |
Enemy System
- 15 basic, 7 elite, 4 boss (26 total) with descriptive sprite names
- Outline shader: gold pulse (elite), red pulse (boss) — regular monster outline removed
- Dissolve shader on death (swapped from outline)
- CPUParticles2D embers, idle bob, ground shadow
- HP bar: 1px outline, color-coded, 20px tall, 14px font, shield icon left of bar
Enemy Interference (always bad for player)
| Effect | What It Does |
|---|---|
| SNIPE | Highest die → 1, frozen |
| CHAOS | N dice scrambled to different low values (1-3) |
| WEAKEN | N dice lose 2 |
| GRAVITY | Cap all dice at 3 |
| BURN | Remove die for 2 turns (then returns) |
| STEAL_REROLL | Fewer rerolls |
| LOCK_HOLD | Can't hold dice |
| STEAL_DIE | Remove die permanently |
| CURSE | All dice lose N |
| EVENODD | Even -2, Odd -1 |
| RIG | Scramble all (no 3+ matching) |
Map System (STS-style)
- 4 paths across 5-column × 15-floor grid with ancestor gap constraint
- Room types assigned after topology with floor rules
- Guaranteed floors: F0=combat, F4=shop, F8=rest, F9=shop, F13=rest, F14=boss
- Shop rate increased to 16% on mid-late floors (was 8%)
- No elite before F5, no elite→elite or rest→rest sequences
- Map flipped (F0 bottom, boss top), scrollbar hidden
- Parchment scroll background (pixelized 9-patch)
- All nodes always revealed, route color by column position
Manipulation System
- 2 innate abilities (Nudge ±1, Reroll+)
- 7 consumable types (Set Die, Clone, Flip, Add Die, Reforge, Salvage, Shatter)
- Nudge/value picker: floating overlay with dark panel (no layout push)
- d8+ dice disabled (Transmute, Chaos Die, Loaded d4 commented out)
Content & Economy
- 50 relics (45 base + 5 utility) + 8 Joker relics
- Reward screen: relic or consumable only (gold removed from pool)
- Gold auto-awarded after combat by enemy tier: Basic +8, Elite +20, Boss +40
- Gold shown on reward screen title: "Choose a reward (+16 gold)"
- Guaranteed shop floors (F4, F9) + 16% shop rate on other floors
- Shop requires left click (hover-to-buy bug fixed)
Visual & Theme
- Monochrome game theme: black/white/grey panels (simple.png dark 9-patch)
- Fonts: Munro (body), Alagard (headers), Press Start 2P (damage numbers)
- 59 pixel art icons for all relics, consumables, innate abilities
- Parallax backgrounds: 4 themes (dungeon/catacombs/infernal/throne), far.png only
- Menu background: generated dungeon entrance (skull archway + torches)
- Splash screen: dungeon altar with floating dice
- Game icon: dark fantasy die (256×256)
- Formula reference panel: signboard icon, slide-out hand scoring table
- Frozen dice shader: desaturate + blue tint + ice shimmer on die face texture
- Bottom panel: flat dark bar, bright section labels (blue/green/gold), separators
- Map nodes: empty button style, tilesheet icons, nearest filtering
- Inventory slots: nearest-neighbor filtered icons
- No dark overlay on combat background (0% opacity)
- CPUParticles2D everywhere (no GPUParticles2D)
- Looping tweens: set_loops(50) + tracked/killed on rebuild
Combat Balance
- Chips reduced ~40%, multipliers reduced ~25%
- Boss HP: 160-200 range (down from 260-320)
- Boss damage caps: 70-80 (down from 100-120)
- Rerolls persist entire encounter (not reset per turn)
- Burn duration: 2 turns (was permanent for combat)
- Turn display: "Turn X / Y" properly calculated
Narrative & Cutscenes
- CutsceneManager autoload (play, play_once, has_seen, persistence)
- CutsceneOverlay with typewriter reveal, input blocking
- 5 narrative beats (intro, 2 act transitions, victory epilogue, defeat epitaph)
- Narrative frame: Diceturner lineage, 3-act ward structure
Persistence & Settings
- RunHistory autoload — run outcomes, 50 max, save/load
- SettingsManager autoload — volume (SFX/Music/UI) + animation speed
- Journey Log screen — scrollable run history with relic icons
- Settings screen — 4 sliders, accessible from main menu
Hub ("The Adventurer's Camp")
- AI-generated unified camp background
- 4 interactable sprites: DungeonGate, SkillTree, Campfire, MapScroll
- Outline-on-hover via outline_alpha shader uniform
- Campfire particles (embers + sparks)
- Skill tree locked state (grey, no interaction until first boss)
Audio
- AudioManager singleton with 24 SFX events
Animation
- Skip button (⏩) in combat — AnimConfig.skip_mode toggles all delays to 0.02s
- DiceRollAnimation component shared between combat and trap screens
Tools
-
tools/generate_backgrounds.py— FLUX.1-schnell via HF free API + post-processing -
tools/fix_parallax_transparency.py— mid/near layer transparency -
tools/setup_sd.sh— SD backend setup (HuggingFace default) -
tools/bg_test_scene.gd+BGTest.tscn— layer toggle/scale preview -
.envsupport for API keys (auto-loaded)
Known Bugs
-
mirrorrelic UI: show wild die value visually — purple tint + "=N" overlay + tooltip on transformed die -
Mid/near parallax layers have artifacts— parallax removed, single static backgrounds per act -
Old hub_bg intermediates— files are actively used by Hub.tscn (not intermediates)
TODO — What's Needed for a Full Game
Balance & Tuning (Sprint 10 remaining)
- S10-07: Monster balance pass (HP/damage curves across 3 acts) — act scaling + boss rework
- S10-08: Elemental balance pass — burn 5->8, wind 3->5, lightning 8->12, chill 15%->25%, vuln 1->2 turns, base chance 2%->3%, Singularity 0%->120% damage, floor scaling 15%->10%
- S10-09: Skill tree balance pass — Glass Cannon 2x->1.75x, Escalation +3/+0.15->+2/+0.10, Merchant Prince -30%->-20%, Pyromaniac burn 2x->3x, Stormcaller curse HP->reroll, Eye of Storm heal 3->5 + penalty 15%->10%, Golden Touch +10% shop discount
- S10-10: Economy balance pass (gold income vs shop prices, relic rarity distribution) — gold reduced ~40%, prices up ~60-80%
- S10-12: Edge-case audit — 8 safety guards added (death checks, empty pools, array bounds)
- Play 5+ full runs and tune based on findings
Storytelling Polish (Sprint 11 nice-to-haves)
- S11-11: Shrine lore blurbs (reuses CutsceneOverlay)
- S11-12: Pre-fight elite taunts — floating taunt text at combat start for 7 elites + 4 bosses
- S11-13: First-Hub ambient narration — one-time atmospheric text on first hub visit
Content Gaps
- Dice archetypes — 6 playable characters with unique relics, stats, passives + selection screen
- More consumable types (current 7 may need expansion)
- Character portraits for cutscene speakers (portrait key reserved in line dicts)
-
Parallax backgrounds— replaced with 9 single static backgrounds (3 per act)
Skill Tree Redesign
- Rethink visual layout — 146-node 3-ring constellation with choice forks
- Better node art / icons per branch — 17 keystone icons + 9 branch icons (Kenney 1-bit)
- Clearer pathing visualization — solid gold (allocated), dotted (locked), choice lock indicators
- Mobile/touch-friendly node sizing and spacing
- Branch labels and legend on the tree itself
- Tooltip and inspect panel polish — single-click allocate, hover tooltip with full info
Scene Transitions & Cutscenes
- Scene transition animations — fade default on all SceneManager.goto() calls (0.22s)
- Wire transitions into SceneManager.goto() — fade=true is now the default
- Cutscene visual polish (character portraits, background art per beat)
- Cutscene skip/fast-forward improvements (hold to skip, speed slider)
- Act transition cutscenes with unique art (not just text overlay)
- Boss intro cutscenes (camera pan, name card, dramatic entrance)
Game Feel & Polish
- Enemy idle animation — idle bob tween on sprite_group
- Custom pixel cursor (no asset created yet)
- Tutorial/onboarding — TutorialOverlay autoload with persisted tips at first combat + first shop
- Sound design pass — ambient audio per act, more varied SFX
- Music — 9 tracks (per-act combat + boss + menu/hub/map/reward), crossfade, music bus
2.5D Map Scene Overhaul
- Adventurer figure in center of solo map screen — backpack, map scroll in left hand
- Map scroll is interactable: click to open normal map with scroll-unroll animation
- Backpack is interactable: click to view current build (relics, consumables, stats, archetype)
- 2.5D node objectives shown as scene objects based on generated map:
- Hooded merchant figure (shop), stone altar (shrine), armored elite, boss silhouette, hidden door, campfire (rest), trap glyph
- Interactable — clicking them is an alternative to clicking map nodes directly
- Objects update as player progresses through the floor
Shop Combat & Karma System
- "Rob" option at shop — triggers combat against the Merchant (unique enemy)
- Win: take all shop items free. Lose: game over (or heavy penalty)
- Can also "Run" (skip the shop entirely)
- Karma system (cross-run persistence like skill tree):
- Track robbery count in save file
- Escalating consequences: price increases, merchant bodyguards, fully-armed merchant
- Possible redemption path (paying full price reduces karma debt)
- Merchant as unique enemy with shop-themed interference
- Design doc:
design/gdd/shop-combat-system.md
Player-Echo Boss System ("The Curse")
- After final boss kill: store the player's current build (archetype, relics, consumables, skill tree snapshot, dice count)
- Final boss rework: the Act 3 boss IS a previous player build
- Boss has its own dice pool and rolls Yahtzee hands against the player
- Boss uses stored relics/keystones as passive abilities
- Boss also has normal interference actions (mess with player's dice)
- First playthrough uses a pre-built "default" boss build
- Narrative frame: a mysterious god curses the hero — upon victory, the hero becomes the next dungeon's final boss. Endless cycle of self-improvement.
- Cutscene: unique final boss intro revealing "this was once a hero like you" — show the stored build's archetype, relics
- Victory cutscene: the curse takes hold, hero transforms, "your build is now the boss"
- Future expansion: community boss builds (share/import builds as bosses), "fight the gods" endgame beyond the curse cycle
Endgame & Replayability
- Endless mode (post-victory scaling)
- Unlockable content (new relics/consumables gated behind achievements)
- Run statistics / meta-progression tracking
- Leaderboard (local at minimum)
Technical Debt
- Dead code cleanup — 17 legacy multiplayer files deleted (card system, combat_manager, GameStateManager, deck_manager, card_visual, test harnesses)
- Dead enum cleanup — removed CardCategory, TargetType, EffectType, ModifierType, CardRarity, AIType, ConditionType, LOBBY state
- Dead field cleanup — removed threat_die, ai_type, action_pattern, rally_pool, deck from data classes + enemy_database
- GameStateManager autoload removed from project.godot
- Architecture decision records (docs/architecture/ doesn't exist)
- Gameplay resource system design doc (src/gameplay/resources/ undocumented)
Platform & Release
- Web export (Compatibility renderer configured but untested)
- Desktop builds (Windows/Mac/Linux)
- Steam page / itch.io page
- Accessibility features (remapping, text scaling, colorblind modes)
- Localization infrastructure (currently English-only)