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.

ChangeBeforeAfterRationale
Burn damage/turn58Negligible vs Act 2-3 enemies (150-1500 HP)
Wind AoE damage353 dmg trivial; wind is AoE so needs meaningful per-hit
Lightning damage812Single-target needs to compete with wind's AoE
Vulnerable duration1 turn2 turns1 turn gone before player could capitalize
Chill dmg reduction15%25%Ice non-freeze outcome too weak
Base elemental chance2%/die3%/dieToo rare; 5 dice now ~14% chance of at least one
Singularity (4-elem)0% damage120% damageRarest combo did literally zero damage
Floor HP scaling+15%/floor+10%/floorAct 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 scaling
  • solo_game_manager.gd: BASE_ELEM_CHANCE 0.02 -> 0.03
  • elemental_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.

KeystoneBeforeAfterRationale
Glass Cannon2x all damage1.75xDominated every other keystone
Escalation+3 chips, +0.15 mult/turn+2, +0.10Exponential scaling absurd by Turn 3
Merchant Prince-30% shop prices-20%Breaking economy with gold nodes
PyromaniacBurn 2xBurn 3xWith burn buffed to 8, 3x=24/turn. Competitive now
StormcallerCurse: -15 HPCurse: -1 rerollHP penalty too harsh for situational benefit
Eye of StormHeal 3, -15% comboHeal 5, -10% comboWeakest keystone, slightly better now
Golden TouchNo shop discount+10% shop discountWas strictly worse than Merchant Prince
  • skill_tree_manager.gd: all keystone getters updated
  • skill_tree_data.gd: all keystone descriptions updated
  • dice_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_property to tween_callback for guaranteed reset. Always reset to Vector2.ZERO before 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 + blocked from 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 log
  • hand_scorer.gd: exposes mirror_die and mirror_value in 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_idx moved to outer scope, mirror_die/mirror_value added to result dict
  • dice_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_TAUNTS dicts, _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:

CategoryFiles Deleted
Card systemcard_database.gd, card_effect_resolver.gd, deck_manager.gd, player_resources.gd
Resourcescard_data.gd, card_effect.gd, effect_modifier.gd, ai_condition.gd
Combatcombat_manager.gd (replaced by dice_combat_manager.gd)
Stategame_state_manager.gd (replaced by SoloGameManager)
UIcard_visual.gd, CardVisual.tscn, UnitTests.tscn
Teststest_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_pool removed from RunData + SoloGameManager
  • deck removed from PlayerRunData + SoloGameManager
  • threat_die removed from EnemyData + all 26 enemy entries in EnemyDatabase
  • ai_type + action_pattern removed from EnemyData + all 26 enemy entries
  • _condition() helper removed from EnemyDatabase
  • 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_indices array 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_size uniform (default 12px) controls cell size — bigger sprites get more cells, not bigger cells
  • crack_seed offsets 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)

BlessingEffectWired Into
Sharpened Edge+2 base dmg to all handsdice_combat_manager scoring
Stone Skin-1 damage from all enemy hits_apply_enemy_attack_damage()
Resonance+15% elemental combo damage_execute_combo()
Second WindHeal 3 HP at start of each combatinit_combat()
Lucky Seven10% chance rerolled die becomes 6dice_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 — UI await in 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
  • 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 _content in ScrollContainer with 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 of floor(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)

  • VScrollBar and HScrollBar styles added to game_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.gdshader with 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_frozen array tracks freeze type per die
  • HandScorer no 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.mp3 wired to AudioManager.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_hit shows 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_damage dict 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_target and pre_damage_living before 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.WHITE before 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 spr validity 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.tscn with 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, v replaces unicode
  • Unicode x fix — replaced × (U+00D7) with ASCII x in 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:

ShaderPatternVisual
burning.gdshaderEmber pixels crawl upward from bottomBright orange/red dots concentrated at feet, shift each frame
frozen.gdshader (soft)Frost dots near sprite edgesSparse blue-white pixels along border, like frost creeping in
deep_frozen.gdshader (hard)Bold crack lines, randomized per instanceBlue-white cracks cutting across, shimmer glints along them
vulnerable.gdshader3 crack lines across spriteAmber-gold jagged lines like broken armor, pulse with intensity
stunned.gdshaderHorizontal static bands scrolling downBright 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_seed uniform (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_phase and while _pending_death_enemies is non-empty — survivors don't reposition mid-dissolve
  • Dissolve with status shaders: Death animation now forces dissolve shader directly via dissolve_material meta, 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_animated tracker — 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_hits skill 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 + archetype
    • get_all_elemental_chances() — returns {fire, ice, wind, lightning} dict
    • get_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_manager gold calculation
  • Fixed shrine always rolling same blessing — was using unseeded randi(), now uses run_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_SOUNDS dict
  • play_ambient() now accepts volume_scale and should_duck parameters

Other Fixes

  • Elemental tutorial: Now persists via TutorialOverlay save 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_panel bug: Moved from local var to class member in solo_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" in sample_set_volumes_linear
  • Fix: All AudioStreamPlayer nodes 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_ui references from AudioManager
  • Removed _apply_bus_volume("UI", ...) from SettingsManager
  • Added is_instance_valid(_music_player) guard + CONNECT_DEFERRED on settings_changed signal to prevent "object freed while signal emitting" crash

Web Export — Settings Slider Freeze Fix

  • Root cause: HSlider grabs 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 behind OS.has_feature("web"))
  • Desktop still uses normal HSlider controls
  • Button group wrapped in expanding HBox to match slider width alignment
  • Both settings_screen.gd and overlay_manager.gd (gear overlay) updated

Web Export — Scene Transition Black Screen Fix

  • Root cause: SceneManager.goto() uses await get_tree().process_frame after change_scene_to_file() — can fail to resume on web, leaving _busy = true permanently and _fade_rect stuck at full black
  • Fix: Replaced process_frame await with create_timer(0.05) (reliable on web)
  • Added _process safety valve: if _busy stuck 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_phase called _rebuild_dice_display() which set dice_container.visible = true — but dice_pool.results was pre-populated with zeroes from add_die()
  • Fix: Added _first_roll_done flag — dice container stays hidden until _on_dice_rolled fires

Scoring Animation — Relic Pop SFX

  • Relic activations during scoring breakdown now play escalating-pitch sound
  • Uses DICE_REROLL SFX (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.tres to achievement notification panel
  • Extended hold time from 3s to 5s before auto-dismiss fade

Audio Cleanup

  • main_menu.ogg replaced with ambient sound (user-selected)
  • main_menu_original.ogg retained as backup

Session 2026-04-07d — System Design Docs, Achievements, Polish

6 System Design Documents Written (2959 lines total)

SystemFileLinesReview Status
Shop Combat & Karmadesign/gdd/shop-combat-system.md336Designed
Player-Echo Boss ("The Curse")design/gdd/player-echo-boss.md778Approved with notes
2.5D Map Scenedesign/gdd/map-scene-overhaul.md467Approved
Blind Effectdesign/gdd/blind-effect.md425Needs minor revision
Enemy Intent Displaydesign/gdd/enemy-intent-display.md303Approved
Endless Modedesign/gdd/endless-mode.md650Designed

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_ticked signal emitted when burn deals damage during status tick
  • burn_tick.png sprite (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' finished signal
  • Enemies only removed/repositioned after every dissolve completes

Battle Log Fix

  • Replaced broken ScrollContainer > RichTextLabel(fit_content=true) with RichTextLabel(scroll_active + scroll_following)
  • Now actually displays logged events with auto-scroll

Double Game Over Button Fix

  • Added _game_over_shown guard — only creates button once

Combat Tooltip Overhaul

  • Fixed PanelContainer anchored 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 AchievementManager autoload (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() and AchievementManager._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:
CategoryAchievementPointsTrigger
ProgressionInto the Wasteland5Clear Act 1
ProgressionThe Throne Awaits5Clear Act 2
ProgressionCurse Broken8First victory
ArchetypeWanderer/Gambler/Bruiser/Trickster/Strategist/Elementalist/Atheist3 each (21 total)Complete run as each
SkillElemental Awakening2First elemental combo
SkillPerfect Roll3Five of a Kind
SkillImpossible Odds5Six+ of a Kind
ChallengeVersatile Hero5Win with 3+ archetypes
ChallengeNaked Run5Win with 0 relics
ChallengeUntouched5Win at full HP
ChallengeDevastating Blow3100+ damage single hand
ChallengeSpeed Demon3Win 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 achievementsACHIEVEMENTS dict 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_REQUIRED constant
  • 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)
  • Random per run: SoloGameManager._run_bg_themes picks one from each act's pool at run start
  • Reward screen now shows the act background with dark overlay (was solid color)
  • parallax_bg.gd simplified to single image loader with fallback chain
  • bg_test_scene.gd simplified — 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: After tick_statuses(), now emits enemy_died_signal if enemy died from burn
  • Checks all_enemies_dead() immediately after burn ticks — prevents dead enemies from selecting intents or attacking

Precise Alpha Hitboxes

  • New AlphaHitRect component (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_only flag controls behavior

Map Tooltip Theme Consistency

  • map_node.tres tooltip now uses simple.png 9-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_died handler added (was connected but undefined — caused silent error)

Deleted Old Scenes

  • Combat.tscn and Map.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)

BranchPath A KeystonePath B Keystone
ICEPermafrost (freeze +2, ice +50%, no fire)Absolute Zero (ice combo 2x, instant freeze, -1 reroll)
FIREPyromaniac (all fire, burn 2x, no other elements)Firestorm (burns spread, fire combo +25%, -15 HP)
WINDGale Force (wind +2 hits, +15%, -1 reroll)Eye of Storm (20% dodge, wind heals 3, -15% combo)
LIGHTNINGStormcaller (chain +2, +20%, -15 HP)Overcharge (triple +12 dmg, pair +4, -20% elem chance)
HPThick Hide (+30 HP, heal 5, -20% dmg)Blood Rage (below 50%: +50% dmg +1 reroll, -20 HP)
DAMAGEGlass Cannon (2x dmg, HP halved)Escalation (+3 chips/+0.15 mult per turn, turn 1 -25%)
UTILITYPurist (no elements, 1.5x hands, +1 reroll)Gambler (+2 rerolls, 15% elemental, -20% combo)
ECONOMYMerchant 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: Dictionary tracks committed fork sides
  • is_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.png downscaled 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.

ArchetypeMechanicUnlock
WandererShops always relic. +1 reroll/3 relics (max +3).Always
Gambler4 rerolls, 15% chance each curses a die to 1.Always
Bruiser6 dice, cannot hold (max_holds=0). 100 HP.15 skill pts
Trickster3 holds + 1 free Flip/turn. 65 HP.15 skill pts
Strategist1 free Set Die/turn. 1 reroll. Puzzle solver.30 skill pts
ElementalistElements from start, always pairs, +15% combo.30 skill pts
AtheistNo elements ever. Elem chance → +1 chip/5%.50 skill pts
  • archetype_data.gd: is_unlocked() checks total allocated skill points vs threshold
  • manipulation_manager.gd: free Set Die/Flip as per-turn innate abilities
  • dice_pool.gd: Gambler risky reroll (15% curse to 1)
  • dice_combat_manager.gd: Atheist elem-to-chips conversion
  • archetype_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_colors dict (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_sanctuary 9-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_HEARTICON_HP reference 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.

#NameHPRerollsDiceSignature RelicKey Passive
1The Gambler7035Loaded Dice (pairs +12, unmatched -2)20% lucky reroll chance
2The Strategist8516Sequence Master (+3/die in longest straight)Lock 1 die per combat
3The Bruiser10025Go Big (6s +8, 5s +4, triple-6 doubles chips)High HP tank
4The Trickster6535Penny Pincher (1s/2s +5 chips + 1 gold)Hold 3 dice, free Flip/turn
5The Elementalist7525Elemental Lens (+5% chance, +20% combo dmg)Elemental pairs, unlocked from start
6The Wanderer8025Adaptable (+3 chips/unique, +2 gold/unique)Shops always have relic, +1 reroll/relic (max 3)
  • src/gameplay/archetype_data.gd — static definitions for all 6
  • src/ui/archetype_select_screen.gd + ArchetypeSelect.tscn — selection screen with game theme panels, 1-bit character icons (Kenney)
  • src/core/solo_game_manager.gdstart_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

  • CombatStatsPanel component — 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

  • TutorialOverlay autoload — 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 %ContentVBox instead 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=true default on all SceneManager.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 --scenes flag 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

StatAct 1Act 2Act 3
HP multiplier1.0x1.4x1.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 shopkeeper
  • shrine_bg.png — stone altar in mossy cave with mystical glow
  • trap_bg.png — dungeon corridor with skulls, archway, torch
  • event_bg.png — torch-lit secret chamber with shelves
  • rest_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 in skill_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 to user://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 to user://settings.save.
  • CutsceneManager (src/core/cutscene_manager.gd) — play(id, lines, on_done), play_once(), has_seen(). Persists seen dict to user://narrative.save. Overlay parents to get_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 cleared
    • DEFEAT_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.gdshader with new outline_alpha uniform. 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.gd slimmed 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 DiceRollAnimation component (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() fires OverlayManager.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.gd modulate 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 scattered change_scene_to_file calls migrated to SceneManager.goto(&"<key>").
  • OverlayManager (src/core/overlay_manager.gd) — CanvasLayer(100) with toast(), flash(), fade_out/in(), shake_scene(). Built but not yet migrated to; existing _screen_shake in 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 trackpad InputEventPanGesture/InputEventMagnifyGesture support, 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=_pan but hit-detection and zoom-at-cursor treated pan as pan*zoom. Unified formula to screen = canvas * zoom + pan.
  • Gating: new unlocked: bool in SkillTreeManager (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 iconsassets/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 in sprite_map.gd
  • 4 S8 map node iconsassets/art/map/ (trap, temple, hidden_room, shrine). Untinted to match existing tilesheet icons.
  • 10 skill tree keystone iconsassets/art/skill_tree/
  • Hub objectsassets/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.png 9-slice (cleaner than assembly)
  • 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 brightnesssprite_vfx.gd applies modulate = 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.png sprite (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_vfx in 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 uses result["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 where enemy.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_applied grace-tick flag in enemy_instance.gd.
  • Freeze only stunning once — freeze relied on one-shot stunned flag. Now enemy.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_valid guards at every await.
  • has_node on RefCounted (dice_combat_manager.gd) — uses Engine.get_main_loop().root.has_node().
  • Multiplayer RPC crash (combat_screen.gd) — guarded with multiplayer_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, many has_keystone() callers drop the ks_ 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)

HandChipsMultMin Dice
High Die2×1.01
Pair5×1.02
Two Pair8×1.04
Three of a Kind10×1.53
Straight12×1.55
Full House (3+2)14×2.05
Three Pair16×2.06
Long Straight18×2.56
Two Trips (3+3)20×2.56
Four of a Kind18×2.54
Full Keep (4+2)22×3.06
Four Pair24×3.08
Five of a Kind25×4.05
Royal Keep (5+2)30×4.57
Double Quad (4+4)35×5.08
Royal House (5+3)38×5.58
Six of a Kind40×6.06
Seven of a Kind55×8.07
Eight of a Kind75×10.08

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)

EffectWhat It Does
SNIPEHighest die → 1, frozen
CHAOSN dice scrambled to different low values (1-3)
WEAKENN dice lose 2
GRAVITYCap all dice at 3
BURNRemove die for 2 turns (then returns)
STEAL_REROLLFewer rerolls
LOCK_HOLDCan't hold dice
STEAL_DIERemove die permanently
CURSEAll dice lose N
EVENODDEven -2, Odd -1
RIGScramble 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
  • .env support for API keys (auto-loaded)

Known Bugs

  • mirror relic 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)
Built with LogoFlowershow