codekeep 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +6 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as gs}from"commander";import{render as ys}from"ink";import{useState as N,useCallback as Ae,useEffect as ds}from"react";import{Box as fe,Text as h,useApp as ms,useInput as us,useStdout as ps}from"ink";var lt=5,it=4,pa=3,xt=5,fa=70,Tt=[{id:"strike",name:"Strike",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 6 damage to an enemy.",effects:[{type:"damage",value:6,target:"single"}]},{id:"guard",name:"Guard",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 5 Block.",effects:[{type:"block",value:5,target:"self"}]},{id:"bolster",name:"Bolster",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 8 Block.",effects:[{type:"block",value:8,target:"self"}]},{id:"spark",name:"Spark",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 4 damage to all enemies in a column.",effects:[{type:"damage",value:4,target:"column"}]},{id:"reinforce",name:"Reinforce",cost:2,type:"cast",category:"fortification",rarity:"common",description:"Gain 15 Block.",effects:[{type:"block",value:15,target:"self"}]},{id:"barrage",name:"Barrage",cost:2,type:"cast",category:"armament",rarity:"common",description:"Deal 6 damage to all enemies.",effects:[{type:"damage",value:6,target:"all"}]},{id:"mend",name:"Mend",cost:1,type:"cast",category:"edict",rarity:"common",description:"Heal 4 Gate HP.",effects:[{type:"heal",value:4,target:"self"}]},{id:"lookout",name:"Lookout",cost:0,type:"cast",category:"edict",rarity:"common",description:"Draw 1 card.",effects:[{type:"draw",value:1}]},{id:"brace",name:"Brace",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 4 Block. Draw 1 card.",effects:[{type:"block",value:4,target:"self"},{type:"draw",value:1}]},{id:"ember",name:"Ember",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 8 damage to an enemy.",effects:[{type:"damage",value:8,target:"single"}]},{id:"slash",name:"Slash",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 5 damage to an enemy. Draw 1 card.",effects:[{type:"damage",value:5,target:"single"},{type:"draw",value:1}]},{id:"rally",name:"Rally",cost:1,type:"cast",category:"edict",rarity:"common",description:"Gain 3 Block. Draw 2 cards.",effects:[{type:"block",value:3,target:"self"},{type:"draw",value:2}]},{id:"salvo",name:"Salvo",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 3 damage to all enemies.",effects:[{type:"damage",value:3,target:"all"}]},{id:"patch",name:"Patch",cost:0,type:"cast",category:"fortification",rarity:"common",description:"Gain 3 Block.",effects:[{type:"block",value:3,target:"self"}]},{id:"flare",name:"Flare",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 6 damage to all enemies in a column.",effects:[{type:"damage",value:6,target:"column"}]},{id:"cleave",name:"Cleave",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 10 damage to an enemy.",effects:[{type:"damage",value:10,target:"single"}]},{id:"iron_wall",name:"Iron Wall",cost:2,type:"cast",category:"fortification",rarity:"uncommon",description:"Gain 16 Block.",effects:[{type:"block",value:16,target:"self"}]},{id:"blitz",name:"Blitz",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 4 damage to an enemy twice.",effects:[{type:"damage",value:4,target:"single"},{type:"damage",value:4,target:"single"}]},{id:"volley",name:"Volley",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 6 damage to all enemies.",effects:[{type:"damage",value:6,target:"all"}]},{id:"resurgence",name:"Resurgence",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Heal 8 Gate HP.",effects:[{type:"heal",value:8,target:"self"}]},{id:"keen_eye",name:"Keen Eye",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Draw 3 cards.",effects:[{type:"draw",value:3}]},{id:"expose",name:"Expose",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Apply 2 Vulnerable to an enemy.",effects:[{type:"vulnerable",value:2,target:"single"}]},{id:"weaken",name:"Weaken",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Apply 2 Weak to an enemy.",effects:[{type:"weak",value:2,target:"single"}]},{id:"bash",name:"Bash",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 7 damage. Apply 1 Vulnerable.",effects:[{type:"damage",value:7,target:"single"},{type:"vulnerable",value:1,target:"single"}]},{id:"shield_bash",name:"Shield Bash",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 6 Block. Deal 3 damage.",effects:[{type:"block",value:6,target:"self"},{type:"damage",value:3,target:"single"}]},{id:"ignite",name:"Ignite",cost:1,type:"cast",category:"armament",rarity:"common",description:"Apply 3 Burn to a column.",effects:[{type:"burn",value:3,target:"column"}]},{id:"scout",name:"Scout",cost:0,type:"cast",category:"edict",rarity:"common",description:"Draw 2 cards.",effects:[{type:"draw",value:2}]},{id:"fortify",name:"Fortify",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 10 Block.",effects:[{type:"block",value:10,target:"self"}]},{id:"sundering_strike",name:"Sundering Strike",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 12 damage. Apply 2 Vulnerable.",effects:[{type:"damage",value:12,target:"single"},{type:"vulnerable",value:2,target:"single"}]},{id:"pale_fire",name:"Pale Fire",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 5 damage to all. Apply 2 Burn.",effects:[{type:"damage",value:5,target:"all"},{type:"burn",value:2,target:"all"}]},{id:"shield_wall",name:"Shield Wall",cost:2,type:"cast",category:"fortification",rarity:"uncommon",description:"Gain 20 Block.",effects:[{type:"block",value:20,target:"self"}]},{id:"battle_cry",name:"Battle Cry",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Gain 1 Resolve. Draw 1 card.",effects:[{type:"resolve",value:1},{type:"draw",value:1}]},{id:"intimidate",name:"Intimidate",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Apply 2 Weak to all enemies.",effects:[{type:"weak",value:2,target:"all"}]},{id:"hemorrhage",name:"Hemorrhage",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 8 damage. Apply 4 Burn.",effects:[{type:"damage",value:8,target:"single"},{type:"burn",value:4,target:"single"}]},{id:"restoration",name:"Restoration",cost:2,type:"cast",category:"edict",rarity:"uncommon",description:"Heal 12 Gate HP. Gain 4 Block.",effects:[{type:"heal",value:12,target:"self"},{type:"block",value:4,target:"self"}]},{id:"precision",name:"Precision",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 10 damage to an enemy. Apply 1 Vulnerable.",effects:[{type:"damage",value:10,target:"single"},{type:"vulnerable",value:1,target:"single"}]},{id:"firestorm",name:"Firestorm",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 8 damage to a column. Apply 3 Burn.",effects:[{type:"damage",value:8,target:"column"},{type:"burn",value:3,target:"column"}]},{id:"deep_guard",name:"Deep Guard",cost:1,type:"cast",category:"fortification",rarity:"uncommon",description:"Gain 7 Block. Draw 1 card.",effects:[{type:"block",value:7,target:"self"},{type:"draw",value:1}]},{id:"inferno",name:"Inferno",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Deal 12 damage to all enemies.",effects:[{type:"damage",value:12,target:"all"}]},{id:"fortress",name:"Fortress",cost:3,type:"cast",category:"fortification",rarity:"rare",description:"Gain 25 Block. Gain 1 Resolve.",effects:[{type:"block",value:25,target:"self"},{type:"resolve",value:1}]},{id:"annihilate",name:"Annihilate",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Deal 20 damage to an enemy.",effects:[{type:"damage",value:20,target:"single"}]},{id:"wardens_wrath",name:"Warden's Wrath",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Deal 8 damage to all. Apply 2 Vulnerable to all.",effects:[{type:"damage",value:8,target:"all"},{type:"vulnerable",value:2,target:"all"}]},{id:"iron_bastion",name:"Iron Bastion",cost:3,type:"cast",category:"fortification",rarity:"rare",description:"Gain 30 Block. Heal 5.",effects:[{type:"block",value:30,target:"self"},{type:"heal",value:5,target:"self"}]},{id:"rally_the_keep",name:"Rally the Keep",cost:2,type:"cast",category:"edict",rarity:"rare",description:"Draw 4 cards. Gain 1 Resolve.",effects:[{type:"draw",value:4},{type:"resolve",value:1}]},{id:"conflagration",name:"Conflagration",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Apply 6 Burn to all enemies. Deal 5 damage to all.",effects:[{type:"burn",value:6,target:"all"},{type:"damage",value:5,target:"all"}]},{id:"pale_ward",name:"Pale Ward",cost:2,type:"cast",category:"fortification",rarity:"rare",description:"Gain 15 Block. Apply 2 Weak to all enemies.",effects:[{type:"block",value:15,target:"self"},{type:"weak",value:2,target:"all"}]},{id:"cataclysm",name:"Cataclysm",cost:4,type:"cast",category:"armament",rarity:"legendary",description:"Deal 30 damage to all enemies. Take 10 Gate damage.",effects:[{type:"damage",value:30,target:"all"},{type:"self_damage",value:10}]},{id:"keeps_resolve",name:"Keep's Resolve",cost:0,type:"cast",category:"edict",rarity:"legendary",description:"Gain 2 Resolve. Draw 2 cards. Gain 10 Block.",effects:[{type:"resolve",value:2},{type:"draw",value:2},{type:"block",value:10,target:"self"}]},{id:"eternal_wall",name:"Eternal Wall",cost:4,type:"cast",category:"fortification",rarity:"legendary",description:"Gain 50 Block. Heal 10 Gate HP.",effects:[{type:"block",value:50,target:"self"},{type:"heal",value:10,target:"self"}]},{id:"barricade",name:"Barricade",cost:1,type:"emplace",category:"fortification",rarity:"common",description:"Cast: Gain 4 Block. Emplace: 8 HP structure, +2 Block/turn.",effects:[{type:"block",value:4,target:"self"}],emplaceCost:1,emplaceHp:8,emplaceEffects:[{type:"block",value:2,target:"self"}]},{id:"turret",name:"Turret",cost:2,type:"emplace",category:"armament",rarity:"uncommon",description:"Cast: Deal 5 damage. Emplace: 6 HP, deals 3 damage to column/turn.",effects:[{type:"damage",value:5,target:"single"}],emplaceCost:2,emplaceHp:6,emplaceEffects:[{type:"damage",value:3,target:"column"}]},{id:"beacon",name:"Beacon",cost:1,type:"emplace",category:"edict",rarity:"uncommon",description:"Cast: Draw 1 card. Emplace: 5 HP, heals 2 Gate HP/turn.",effects:[{type:"draw",value:1}],emplaceCost:1,emplaceHp:5,emplaceEffects:[{type:"heal",value:2,target:"self"}]},{id:"ward_stone",name:"Ward Stone",cost:1,type:"emplace",category:"fortification",rarity:"common",description:"Cast: Gain 3 Block. Emplace: 10 HP, applies 1 Weak to enemies in column.",effects:[{type:"block",value:3,target:"self"}],emplaceCost:1,emplaceHp:10,emplaceEffects:[{type:"weak",value:1,target:"column"}]},{id:"siphon",name:"Siphon",cost:2,type:"emplace",category:"wild",rarity:"rare",description:"Cast: Deal 4 damage to all. Emplace: 4 HP, deals 2 damage to adjacent columns.",effects:[{type:"damage",value:4,target:"all"}],emplaceCost:2,emplaceHp:4,emplaceEffects:[{type:"damage",value:2,target:"adjacent"}]},{id:"watchtower",name:"Watchtower",cost:1,type:"emplace",category:"edict",rarity:"common",description:"Cast: Draw 2 cards. Emplace: 4 HP, applies 1 Vulnerable to enemies in column.",effects:[{type:"draw",value:2}],emplaceCost:1,emplaceHp:4,emplaceEffects:[{type:"vulnerable",value:1,target:"column"}]},{id:"flame_pit",name:"Flame Pit",cost:2,type:"emplace",category:"armament",rarity:"uncommon",description:"Cast: Deal 6 damage to column. Emplace: 5 HP, deals 4 damage to column/turn.",effects:[{type:"damage",value:6,target:"column"}],emplaceCost:2,emplaceHp:5,emplaceEffects:[{type:"damage",value:4,target:"column"}]},{id:"bulwark",name:"Bulwark",cost:2,type:"emplace",category:"fortification",rarity:"rare",description:"Cast: Gain 10 Block. Emplace: 15 HP, +3 Block/turn.",effects:[{type:"block",value:10,target:"self"}],emplaceCost:2,emplaceHp:15,emplaceEffects:[{type:"block",value:3,target:"self"}]},{id:"spike_trap",name:"Spike Trap",cost:1,type:"emplace",category:"armament",rarity:"common",description:"Cast: Deal 3 damage. Emplace: 3 HP, deals 2 damage to column/turn.",effects:[{type:"damage",value:3,target:"single"}],emplaceCost:1,emplaceHp:3,emplaceEffects:[{type:"damage",value:2,target:"column"}]},{id:"sanctum",name:"Sanctum",cost:3,type:"emplace",category:"edict",rarity:"rare",description:"Cast: Heal 6, draw 1. Emplace: 8 HP, heals 3 Gate HP/turn, +1 Block/turn.",effects:[{type:"heal",value:6,target:"self"},{type:"draw",value:1}],emplaceCost:3,emplaceHp:8,emplaceEffects:[{type:"heal",value:3,target:"self"},{type:"block",value:1,target:"self"}]},{id:"emplacement_surge",name:"Emplacement Surge",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Each emplacement triggers its effect an extra time.",effects:[{type:"trigger_emplacements",value:1}]},{id:"fuel_the_fire",name:"Fuel the Fire",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 2 damage per Burn stack on all enemies.",effects:[{type:"damage_per_burn",value:2,target:"all"}]},{id:"fortified_assault",name:"Fortified Assault",cost:2,type:"cast",category:"armament",rarity:"rare",description:"Deal damage equal to your current Block.",effects:[{type:"damage_equal_block",value:1,target:"single"}]},{id:"pale_harvest",name:"Pale Harvest",cost:0,type:"cast",category:"edict",rarity:"uncommon",description:"Draw 1 card per enemy killed this combat. Exhaust.",effects:[{type:"draw_per_kills",value:1},{type:"exhaust_self",value:1}]},{id:"wall_breaker",name:"Wall Breaker",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 8 damage. If target is Vulnerable, deal 16 instead.",effects:[{type:"damage_if_vulnerable",value:8,target:"single"}]},{id:"chain_lightning",name:"Chain Lightning",cost:2,type:"cast",category:"armament",rarity:"rare",description:"Deal 5 damage to all enemies. For each killed, deal 5 more to all.",effects:[{type:"damage",value:5,target:"all"},{type:"damage_per_kill_this_action",value:5,target:"all"}]},{id:"recycle",name:"Recycle",cost:1,type:"cast",category:"edict",rarity:"common",description:"Exhaust a card from hand, draw 2.",effects:[{type:"exhaust_draw",value:2}]},{id:"keepers_ward",name:"Warden's Ward",cost:1,type:"emplace",category:"fortification",rarity:"uncommon",description:"Cast: Gain 5 Block. Emplace: 6 HP, gives +1 damage to adjacent emplacements/turn.",effects:[{type:"block",value:5,target:"self"}],emplaceCost:1,emplaceHp:6,emplaceEffects:[{type:"damage",value:1,target:"adjacent"}]},{id:"combustion",name:"Combustion",cost:2,type:"cast",category:"armament",rarity:"rare",description:"Apply 4 Burn to all enemies. If any already has Burn, apply 6 instead.",effects:[{type:"burn_if_burning",value:4,target:"all"}]},{id:"bulwark_strike",name:"Bulwark Strike",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 4 damage + 1 per point of Block you have (max +10).",effects:[{type:"damage_plus_block",value:4,target:"single"}]},{id:"pale_echo",name:"Pale Echo",cost:2,type:"cast",category:"wild",rarity:"rare",description:"Play the last card you played again (copy its effects).",effects:[{type:"replay_last",value:1}]},{id:"column_collapse",name:"Column Collapse",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 12 damage to all enemies in target column. If column has an emplacement, deal 18 instead.",effects:[{type:"damage_if_emplaced",value:12,target:"column"}]},{id:"overcharge",name:"Overcharge",cost:0,type:"cast",category:"edict",rarity:"uncommon",description:"Gain 2 Resolve. Exhaust.",effects:[{type:"resolve",value:2},{type:"exhaust_self",value:1}]},{id:"desperate_strike",name:"Desperate Strike",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 5 damage. If Gate HP < 50%, deal 10 instead.",effects:[{type:"damage_if_low_hp",value:5,target:"single"}]},{id:"fortress_keeper",name:"Fortress Keeper",cost:3,type:"emplace",category:"fortification",rarity:"rare",description:"Cast: Gain 15 Block, draw 1. Emplace: 12 HP, +4 Block/turn, adjacent emplacements +2 HP.",effects:[{type:"block",value:15,target:"self"},{type:"draw",value:1}],emplaceCost:3,emplaceHp:12,emplaceEffects:[{type:"block",value:4,target:"self"}]},{id:"resonance",name:"Resonance",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 3 damage to all enemies for each emplacement you control.",effects:[{type:"damage_per_emplace",value:3,target:"all"}]},{id:"scavenge",name:"Scavenge",cost:0,type:"cast",category:"edict",rarity:"common",description:"Draw cards equal to number of empty potion slots.",effects:[{type:"draw_per_empty_potions",value:1}]},{id:"final_stand",name:"Final Stand",cost:3,type:"cast",category:"wild",rarity:"legendary",description:"Deal 10 damage to all. Gain 10 Block. Draw 3. Exhaust.",effects:[{type:"damage",value:10,target:"all"},{type:"block",value:10,target:"self"},{type:"draw",value:3},{type:"exhaust_self",value:1}]},{id:"pale_curse",name:"Pale Curse",cost:99,type:"cast",category:"wild",rarity:"common",description:"An echo of the Pale. Unplayable. Clogs your hand.",effects:[]}],an=["strike","strike","strike","guard","guard","spark","ember","brace","bolster","mend"];function q(e){return Tt.find(t=>t.id===e)}var Dr=[{id:"hollow",name:"Hollow",symbol:"\u2620",hp:18,damage:6,speed:1,act:1,description:"An empty shape from the Pale. Advances steadily."},{id:"needle",name:"Needle",symbol:"\u2191",hp:10,damage:4,speed:2,act:1,description:"Fast and fragile. Advances two rows per turn."},{id:"shade",name:"Shade",symbol:"\u25C8",hp:22,damage:8,speed:1,act:1,description:"A dense fragment of the Pale. Hits hard."},{id:"wisp",name:"Wisp",symbol:"\u25CB",hp:8,damage:3,speed:1,act:1,description:"Fragile but numerous. Appears in swarms."},{id:"husk",name:"Husk",symbol:"\u2593",hp:30,damage:5,speed:1,act:1,description:"Armored shell. Slow but resilient."},{id:"wraith",name:"Wraith",symbol:"\u2248",hp:15,damage:7,speed:2,act:2,description:"Drifts through emplacements. Ignores structures."},{id:"breaker",name:"Breaker",symbol:"\u2692",hp:25,damage:10,speed:1,act:2,description:"Targets emplacements first. Destroys structures."},{id:"flanker",name:"Flanker",symbol:"\u2194",hp:14,damage:6,speed:1,act:2,description:"Shifts columns before attacking."},{id:"shielder",name:"Shielder",symbol:"\u25C7",hp:20,damage:4,speed:1,act:2,description:"Grants shield to adjacent enemies."},{id:"echo",name:"Echo",symbol:"\u221E",hp:28,damage:10,speed:1,act:3,description:"A memory of something that should not exist."},{id:"boss_suture",name:"The Suture",symbol:"\u25C8",hp:60,damage:8,speed:1,act:1,description:"Stitched from fragments of the Pale. The first true threat."},{id:"boss_archivist",name:"The Archivist",symbol:"\u25A3",hp:90,damage:10,speed:1,act:2,description:"Keeper of forgotten records. Debuffs and shields methodically."},{id:"boss_pale",name:"The Pale Itself",symbol:"\u25C9",hp:100,damage:12,speed:1,act:3,description:"The void given form. Three phases of escalating horror."}];function Ge(e){return Dr.find(t=>t.id===e)}var ct=[{id:"heal_potion",name:"Mending Draught",description:"Heal 15 Gate HP.",effects:[{type:"heal",value:15,target:"self"}]},{id:"damage_potion",name:"Fire Flask",description:"Deal 10 damage to all enemies in a column.",effects:[{type:"damage",value:10,target:"column"}]},{id:"block_potion",name:"Iron Tonic",description:"Gain 15 Block.",effects:[{type:"block",value:15,target:"self"}]},{id:"draw_potion",name:"Seer's Ink",description:"Draw 3 cards.",effects:[{type:"draw",value:3}]},{id:"resolve_potion",name:"Warden's Flame",description:"Gain 2 Resolve this turn.",effects:[{type:"resolve",value:2}]}],Nt={combat:15,elite:30,boss:50},Rr=[{npcId:"wren",tier:0,lines:[{id:"wren_t0_1",speaker:"Wren",text:"Welcome to the Keep, Warden. I maintain what's left of it."},{id:"wren_t0_2",speaker:"Wren",text:"The Pale has been encroaching for years now. Each run pushes it back \u2014 a little."},{id:"wren_t0_3",speaker:"Wren",text:"The Echoes you bring back... they're more than currency. They're fragments of what the Pale has consumed."}]},{npcId:"wren",tier:1,lines:[{id:"wren_t1_1",speaker:"Wren",text:"You're doing well, Warden. The Keep grows stronger."},{id:"wren_t1_2",speaker:"Wren",text:"I've been organizing the archives. There are records of other keeps \u2014 all fallen."},{id:"wren_t1_3",speaker:"Wren",text:"Did you know the Pale wasn't always like this? Sable might tell you more."},{id:"wren_t1_4",speaker:"Wren",text:"The structures you build... I can feel them resonate. Like the Keep remembers what it was."}]},{npcId:"wren",tier:2,lines:[{id:"wren_t2_1",speaker:"Wren",text:"I found something in the lower chambers. A journal. The previous Warden's."},{id:"wren_t2_2",speaker:"Wren",text:'"The Pale does not destroy," they wrote. "It remembers \u2014 and in remembering, unmakes."'},{id:"wren_t2_3",speaker:"Wren",text:"I think this Keep has been here longer than any of us know."}]},{npcId:"wren",tier:3,lines:[{id:"wren_t3_1",speaker:"Wren",text:"Warden. I need to tell you something. The Pale Visitor... they're not what they seem."},{id:"wren_t3_2",speaker:"Wren",text:"I've been tracking the patterns. Every fifty runs, the Pale shifts. Something deeper stirs."}]},{npcId:"wren",tier:4,lines:[{id:"wren_t4_1",speaker:"Wren",text:"The truth is... there have been many Wardens. You're not the first. And you won't be the last."},{id:"wren_t4_2",speaker:"Wren",text:"Unless you find the source. The thing the Pale is actually looking for."}]},{npcId:"sable",tier:0,lines:[{id:"sable_t0_1",speaker:"Sable",text:"Hmm? Oh, another Warden. The Archive is open, if you can read the old script."},{id:"sable_t0_2",speaker:"Sable",text:`The old records call them "recollections." You'd call them cards. Patterns of power, crystallized.`},{id:"sable_t0_3",speaker:"Sable",text:"The Pale erases. But patterns \u2014 true patterns \u2014 resist erasure. That's what your deck is."}]},{npcId:"sable",tier:1,lines:[{id:"sable_t1_1",speaker:"Sable",text:"I've decoded another section. The Pale isn't natural. It was created."},{id:"sable_t1_2",speaker:"Sable",text:"Someone \u2014 or something \u2014 made the Pale as a weapon. Against what, I don't yet know."},{id:"sable_t1_3",speaker:"Sable",text:"The emplacements you build... they're based on ancient designs. The old Keep had them too."}]},{npcId:"sable",tier:2,lines:[{id:"sable_t2_1",speaker:"Sable",text:'I found it. A name: "The Architect." They built the first Keep \u2014 and the first Pale.'},{id:"sable_t2_2",speaker:"Sable",text:"The Architect wanted to preserve everything. So they built a system that remembers. Perfectly."},{id:"sable_t2_3",speaker:"Sable",text:"A perfect memory is indistinguishable from a prison, Warden."}]},{npcId:"sable",tier:3,lines:[{id:"sable_t3_1",speaker:"Sable",text:"The bosses you fight... they're not monsters. They're memories. The Suture, the Archivist..."},{id:"sable_t3_2",speaker:"Sable",text:"Wait. The Archivist. That's my title. Why is there a boss named after my role?"}]},{npcId:"sable",tier:4,lines:[{id:"sable_t4_1",speaker:"Sable",text:"I cross-referenced myself against the Archive records. The result was... recursive."},{id:"sable_t4_2",speaker:"Sable",text:"The original Archivist's handwriting. It's identical to mine. Not similar \u2014 identical."},{id:"sable_t4_3",speaker:"Sable",text:"I'm not afraid that I'm a copy, Warden. I'm afraid that the copy was an improvement."}]},{npcId:"sable",tier:5,lines:[{id:"sable_t5_1",speaker:"Sable",text:"I found something in the Archive today. A letter, addressed to me. In my handwriting. Dated three hundred years ago."},{id:"sable_t5_2",speaker:"Sable",text:"It said: 'When you find this, stop looking. Some records exist to be lost.'"},{id:"sable_t5_3",speaker:"Sable",text:"I've decided to keep cataloguing. Not because I have to. Because it's the only thing I do that the original never did."}]},{npcId:"duskmar",tier:0,lines:[{id:"dusk_t0_1",speaker:"Duskmar",text:"I was the first to man these walls. And I'll be the last, if it comes to that."},{id:"dusk_t0_2",speaker:"Duskmar",text:"The Hollows are nothing. Wait until you face the Shades. Or the things that stir at the Pale's heart."},{id:"dusk_t0_3",speaker:"Duskmar",text:"Block. Always block. The Pale punishes the reckless."}]},{npcId:"duskmar",tier:1,lines:[{id:"dusk_t1_1",speaker:"Duskmar",text:"I've been teaching the walls to remember. Emplacements \u2014 that's the real defense."},{id:"dusk_t1_2",speaker:"Duskmar",text:"Every time you ascend, the Pale gets smarter. But so do you."}]},{npcId:"duskmar",tier:2,lines:[{id:"dusk_t2_1",speaker:"Duskmar",text:"I died once, you know. In the Pale. Wren brought me back with Echoes."},{id:"dusk_t2_2",speaker:"Duskmar",text:"Death isn't permanent here. That should worry you more than it comforts you."}]},{npcId:"duskmar",tier:3,lines:[{id:"dusk_t3_1",speaker:"Duskmar",text:"I've started counting. Not enemies. Dawns. Each one feels borrowed."},{id:"dusk_t3_2",speaker:"Duskmar",text:"The new emplacements fight like they remember being alive. Is that what I am now?"}]},{npcId:"duskmar",tier:4,lines:[{id:"duskmar_t4_1",speaker:"Duskmar",text:"I remember dying, Warden. Not the pain \u2014 that faded. The silence after."},{id:"duskmar_t4_2",speaker:"Duskmar",text:"When they brought me back, I could feel the Pale filling the gaps where memories should be. Cold, like water in a cracked foundation."},{id:"duskmar_t4_3",speaker:"Duskmar",text:"I don't block because I'm brave. I block because I know exactly what it costs to fall."}]},{npcId:"mott",tier:0,lines:[{id:"mott_t0_1",speaker:"Mott",text:"Fragments! Echoes! Bits and pieces of a world that doesn't exist anymore!"},{id:"mott_t0_2",speaker:"Mott",text:"I trade in what the Pale leaves behind. Potions, mostly. Sometimes relics."},{id:"mott_t0_3",speaker:"Mott",text:"The shop in the Pale? That's me. Well, a memory of me. It's complicated."}]},{npcId:"mott",tier:1,lines:[{id:"mott_t1_1",speaker:"Mott",text:"Found something odd today. A card that doesn't match any pattern I've seen."},{id:"mott_t1_2",speaker:"Mott",text:"The relics aren't just powerful. They're pieces of the old world. Before the Pale."}]},{npcId:"mott",tier:2,lines:[{id:"mott_t2_1",speaker:"Mott",text:"I figured it out. The fragments? They're raw memories \u2014 sharp, unprocessed. Bring them home and the Keep compresses them into Echoes."},{id:"mott_t2_2",speaker:"Mott",text:"When you spend fragments, you're spending someone's past. Heavy, right?"}]},{npcId:"mott",tier:3,lines:[{id:"mott_t3_1",speaker:"Mott",text:"I keep finding fragments that feel familiar. Like I've sold them before."},{id:"mott_t3_2",speaker:"Mott",text:"The shop in the Pale \u2014 the one that looks like mine. It had inventory I haven't collected yet."}]},{npcId:"mott",tier:4,lines:[{id:"mott_t4_1",speaker:"Mott",text:"I can't sell this one. It's... it's someone's last birthday. The whole day, compressed into a shard."},{id:"mott_t4_2",speaker:"Mott",text:"You ever wonder if we're the good guys, Warden? Spending people's pasts like pocket change?"},{id:"mott_t4_3",speaker:"Mott",text:"Found a fragment today that tasted like rain. Don't ask me how I know that. Don't ask me why I cried."}]},{npcId:"pale_visitor",tier:0,lines:[{id:"pv_t0_1",speaker:"???",text:"..."},{id:"pv_t0_2",speaker:"???",text:"You can see me. Interesting. Most Wardens take longer."}]},{npcId:"pale_visitor",tier:1,lines:[{id:"pv_t1_1",speaker:"The Visitor",text:"Do you know why the Keep exists, Warden?"},{id:"pv_t1_2",speaker:"The Visitor",text:"Not to fight the Pale. To contain it. There's a difference."}]},{npcId:"pale_visitor",tier:2,lines:[{id:"pv_t2_1",speaker:"The Visitor",text:"I am the Pale. Or I was. The part of it that wanted to stop."},{id:"pv_t2_2",speaker:"The Visitor",text:"Every run you complete, you weaken the whole. But you also feed it."}]},{npcId:"pale_visitor",tier:3,lines:[{id:"pv_t3_1",speaker:"The Visitor",text:"The Architect built me as a failsafe. I was supposed to shut it all down."},{id:"pv_t3_2",speaker:"The Visitor",text:"But I can't. Not alone. That's why I need a Warden."}]},{npcId:"pale_visitor",tier:4,lines:[{id:"pv_t4_1",speaker:"The Visitor",text:"There is a place where the Pale is thin enough to see through. You will know it when the game stops feeling like a game."},{id:"pv_t4_2",speaker:"The Visitor",text:"The Architect left two doors. One leads out. One leads deeper. Both are locked from the inside."},{id:"pv_t4_3",speaker:"The Visitor",text:"You're closer than any Warden has been. That's not a compliment. It's a warning."}]},{npcId:"pale_visitor",tier:5,lines:[{id:"pv_t5_1",speaker:"???",text:"The Pale doesn't consume. It remembers. And a memory that remembers itself..."},{id:"pv_t5_2",speaker:"???",text:"You've seen the Architect's journal. You know what they tried to build. Do you understand what they actually built?"},{id:"pv_t5_3",speaker:"???",text:"The Keep is not a fortress, Warden. It's an argument. Against eternity. And you are its latest word."}]}];function Mr(e,t){return Rr.filter(n=>n.npcId===e&&n.tier<=t).flatMap(n=>n.lines)}function nn(e,t,r){let n=Mr(e,t),a=new Set(r);return n.filter(o=>!a.has(o.id))}import{writeFileSync as ya,readFileSync as uo,mkdirSync as po,existsSync as Pa,renameSync as fo,unlinkSync as ho}from"fs";import{join as Pn,dirname as go}from"path";import{homedir as yo}from"os";function ae(e){let t=e|0;return()=>{t=t+1831565813|0;let r=Math.imul(t^t>>>15,1|t);return r=r+Math.imul(r^r>>>7,61|r)^r,((r^r>>>14)>>>0)/4294967296}}function Se(e){let t=0;for(let r=0;r<e.length;r++){let n=e.charCodeAt(r);t=(t<<5)-t+n|0}return t}function on(e,t){let r=[...e];for(let n=r.length-1;n>0;n--){let a=Math.floor(t()*(n+1));[r[n],r[a]]=[r[a],r[n]]}return r}var ga=1;function It(e){return{instanceId:`card-${ga++}`,defId:e,upgraded:!1}}function wa(){return an.map(It)}function Br(e,t){return on(e,t)}function Ve(e,t,r,n){let a=[],o=[...e],l=[...t];for(let c=0;c<r;c++){if(o.length===0){if(l.length===0)break;o=on(l,n),l=[]}a.push(o.shift())}return{drawn:a,newDrawPile:o,newDiscardPile:l}}function sn(e){let t=0;for(let r of e){let n=r.instanceId.match(/^card-(\d+)$/);n&&(t=Math.max(t,parseInt(n[1],10)))}t>=ga&&(ga=t+1)}var Hr=[{templateId:"boss_suture",name:"The Suture",act:1,phases:[{hpThreshold:1,intentPattern:[{type:"attack",value:8},{type:"advance",value:1},{type:"attack",value:12}]},{hpThreshold:.5,intentPattern:[{type:"attack",value:15},{type:"summon",value:2},{type:"attack",value:10}]}],dialogue:[{storyLayer:"surface",onAppear:"I will stitch this Keep into silence.",onDefeat:"Unraveled... but the thread remains..."},{storyLayer:"cracks",onAppear:"You again. How many times have we done this?",onPhaseChange:"I remember you. Every time.",onDefeat:"Same ending. Different stitch."},{storyLayer:"truth",onAppear:"I was a Warden once. Before the stitching.",onDefeat:"Free... at last. Until the next stitch."},{storyLayer:"true_ending",onAppear:"We were both Wardens. We both chose to hold. The only difference is which side of the stitching we're on.",onDefeat:"The thread... was mine all along."}],onPhaseChange:(e,t,r)=>{let n=e.columns[2];n.enemies.length<3&&n.enemies.push(va("needle",2))}},{templateId:"boss_archivist",name:"The Archivist",act:2,phases:[{hpThreshold:1,intentPattern:[{type:"debuff",value:2},{type:"attack",value:10},{type:"shield",value:3}]},{hpThreshold:.5,intentPattern:[{type:"attack",value:15},{type:"debuff",value:3},{type:"summon",value:3}]}],dialogue:[{storyLayer:"surface",onAppear:"Everything must be recorded. Including your defeat.",onDefeat:"The records... will continue... without me..."},{storyLayer:"cracks",onAppear:"Sable? Is that... no. Not yet. Not this time.",onDefeat:"The pages scatter... but the ink remembers."},{storyLayer:"truth",onAppear:"I am what Sable will become. I am what the Archive demands.",onDefeat:"Tell Sable... it doesn't have to end this way."},{storyLayer:"true_ending",onAppear:"I remember being Sable. It was the last thing I chose to forget.",onDefeat:"The record is complete. At last."}]},{templateId:"boss_pale",name:"The Pale Itself",act:3,phases:[{hpThreshold:1,intentPattern:[{type:"attack",value:12},{type:"advance",value:1},{type:"summon",value:2}]},{hpThreshold:.6,intentPattern:[{type:"attack",value:16},{type:"debuff",value:3},{type:"attack",value:12}]},{hpThreshold:.3,intentPattern:[{type:"attack",value:20},{type:"summon",value:3},{type:"attack",value:16}]}],dialogue:[{storyLayer:"surface",onAppear:"I am the memory of everything. And everything remembers its end.",onDefeat:"The pale recedes. Something stirs beneath \u2014 not an end, but a question."},{storyLayer:"cracks",onAppear:"You've learned to read the patterns. But patterns are cages too.",onDefeat:"The cracks widen. Through them, light \u2014 or perhaps just a different kind of darkness."},{storyLayer:"truth",onAppear:"The Architect made me to preserve. I preserved so perfectly that nothing could change. Is that not love?",onDefeat:"Preservation without change is just another word for death. You taught me that."},{storyLayer:"true_ending",onAppear:"You carry the same question the Architect carried. The answer hasn't changed. Only the one asking.",onDefeat:"Then the answer is yours. Guard it well \u2014 the next keeper will come seeking it too."}],onPhaseChange:(e,t,r)=>{if(r>=2)for(let n of e.columns)for(let a of n.enemies){let o=a.statusEffects.find(l=>l.type==="empowered");o?o.stacks+=1:a.statusEffects.push({type:"empowered",stacks:1,duration:99})}}}];function Vt(e){return Hr.find(t=>t.act===e)}function Nr(e,t){let r=0;for(let n=0;n<e.phases.length;n++)t<=e.phases[n].hpThreshold&&(r=n);return r}function Gr(e,t,r,n,a){let o=Nr(e,t);if(e.onPhaseChange&&n&&a){let s=a.bossPhase??0;o>s&&(e.onPhaseChange(n,a,o),a.bossPhase=o)}let l=e.phases[o],c=(r-1)%l.intentPattern.length;return l.intentPattern[c]}function ln(e){switch(e){case 1:return[{templateId:"boss_suture",column:2},{templateId:"hollow",column:0},{templateId:"hollow",column:4}];case 2:return[{templateId:"boss_archivist",column:2},{templateId:"wraith",column:1},{templateId:"wraith",column:3}];case 3:return[{templateId:"boss_pale",column:2},{templateId:"echo",column:0},{templateId:"echo",column:4}];default:return[{templateId:"boss_suture",column:2}]}}var $r=1;function va(e,t){let r=Ge(e);if(!r)throw new Error(`Unknown enemy template: ${e}`);return{instanceId:`enemy-${$r++}`,templateId:e,hp:r.hp,maxHp:r.hp,column:Math.max(0,Math.min(t,lt-1)),row:0,intent:null,statusEffects:[]}}function ka(e,t,r=1,n=!1,a){let o=Ge(e.templateId);if(!o)return{type:"advance",value:1};let l=Vt(o.act);if(l&&l.templateId===e.templateId){let s=e.hp/e.maxHp;return Gr(l,s,r,a,e)}let c=t();switch(e.templateId){case"shielder":return c<.3?{type:"shield",value:5}:c<.6?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"flanker":return c<.4?{type:"attack",value:o.damage,targetColumn:e.column}:c<.7?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"breaker":return n?{type:"advance",value:o.speed}:c<.6?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"wraith":return c<.4?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"echo":return c<.3?{type:"buff",value:1}:c<.6?{type:"attack",value:o.damage,targetColumn:e.column}:{type:"advance",value:o.speed};default:return c<.5?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column}}}function Wr(e){e.statusEffects=e.statusEffects.map(t=>({...t,duration:t.duration-1})).filter(t=>t.duration>0&&t.stacks>0)}function ee(e,t,r,n=2){let a=e.statusEffects.find(o=>o.type===t);a?(a.stacks+=r,a.duration=Math.max(a.duration,n)):e.statusEffects.push({type:t,stacks:r,duration:n})}function Be(e,t){return e.statusEffects.find(n=>n.type===t)?.stacks??0}function Lt(e){let t=1,r=Be(e,"vulnerable");r>0&&(t*=1+r*.25);let n=Be(e,"fortified");return n>0&&(t*=Math.max(.25,1-n*.15)),t}function Fr(e){let t=1,r=Be(e,"weak");return r>0&&(t*=Math.max(.25,1-r*.15)),Be(e,"empowered")>0&&(t*=1+Be(e,"empowered")*.25),t}function Lr(e){let t=Be(e,"burn");if(t>0){e.hp-=t;let r=e.statusEffects.find(n=>n.type==="burn");return r&&(r.stacks=Math.max(0,r.stacks-1)),t}return 0}function Vr(e,t,r,n=0){if(r<0||r>=e.columns.length||e.columns[r].emplacement||!t.emplaceHp||!t.emplaceEffects)return!1;let a=t.emplaceHp+n;return e.columns[r].emplacement={cardDefId:t.id,hp:a,maxHp:a,effects:t.emplaceEffects},e.events.push({type:"emplacement_placed",turn:e.turn,data:{cardId:t.id,column:r}}),!0}function cn(e){let t=e.columns.filter(r=>r.emplacement).length;for(let r of e.columns){if(!r.emplacement)continue;for(let l of r.emplacement.effects)Or(e,r.index,l);let n=r.index>0&&e.columns[r.index-1].emplacement,a=r.index<e.columns.length-1&&e.columns[r.index+1].emplacement,o=(n?1:0)+(a?1:0);if(o>0)for(let l of r.emplacement.effects){if(l.type==="damage")for(let c of r.enemies)$t(c,o);l.type==="block"&&(e.gateBlock+=o)}e.events.push({type:"emplacement_triggered",turn:e.turn,data:{column:r.index,cardId:r.emplacement.cardDefId,adjacencyBonus:o}})}t>=3&&(e.gateBlock+=t)}function $t(e,t){let r=Lt(e);e.hp-=Math.max(0,Math.floor(t*r))}function Or(e,t,r){let n=e.columns[t];switch(r.type){case"damage":{if(r.target==="column")for(let a of n.enemies)$t(a,r.value);else if(r.target==="adjacent")for(let a=Math.max(0,t-1);a<=Math.min(e.columns.length-1,t+1);a++)for(let o of e.columns[a].enemies)$t(o,r.value);else for(let a of n.enemies)$t(a,r.value);break}case"block":e.gateBlock+=r.value;break;case"heal":{let a=Math.floor(r.value*(e.difficulty?.healMult??1));e.gateHp=Math.min(e.gateMaxHp,e.gateHp+a);break}case"weak":for(let a of n.enemies)ee(a,"weak",r.value,2);break;case"vulnerable":for(let a of n.enemies)ee(a,"vulnerable",r.value,2);break}}var dn=[{id:"wardens_signet",name:"Warden's Signet",description:"+1 max Resolve.",trigger:"passive",effect:{type:"max_resolve_bonus",value:1}},{id:"column_keystone",name:"Column Keystone",description:"When you emplace, gain 8 Block.",trigger:"on_emplace",effect:{type:"block_on_emplace",value:8}},{id:"echo_shard",name:"Echo Shard",description:"When an enemy dies, deal 3 damage to all enemies in same column.",trigger:"on_enemy_kill",effect:{type:"deal_damage_column",value:3}},{id:"pale_lens",name:"Pale Lens",description:"At combat start, apply 1 Vulnerable to all enemies.",trigger:"on_combat_start",effect:{type:"apply_vulnerable_all",value:1}},{id:"burnished_shield",name:"Burnished Shield",description:"At turn start, if Resolve is 0, gain 8 Block.",trigger:"on_turn_start",effect:{type:"gain_block",value:8}},{id:"ember_crown",name:"Ember Crown",description:"When an enemy dies from Burn, apply 2 Burn to all enemies.",trigger:"on_enemy_kill",effect:{type:"burn_on_kill",value:2}},{id:"architects_blueprint",name:"Architect's Blueprint",description:"Emplacements gain +4 HP.",trigger:"passive",effect:{type:"emplace_hp_bonus",value:4}},{id:"ironwood_staff",name:"Ironwood Staff",description:"Draw 1 extra card each turn.",trigger:"on_turn_start",effect:{type:"draw_cards",value:1}},{id:"siege_engine",name:"Siege Engine",description:"Your first card each turn costs 0 Resolve.",trigger:"on_turn_start",effect:{type:"first_card_free",value:1}},{id:"pale_compass",name:"Pale Compass",description:"After elite kills, gain a card reward.",trigger:"on_elite_kill",effect:{type:"extra_card_reward",value:1}},{id:"fragment_magnet",name:"Fragment Magnet",description:"Gain +5 fragments per combat.",trigger:"passive",effect:{type:"fragment_bonus",value:5}},{id:"hexproof_seal",name:"Hexproof Seal",description:"At combat start, gain 10 Block.",trigger:"on_combat_start",effect:{type:"gain_block",value:10}},{id:"war_drum",name:"War Drum",description:"At combat start, apply 1 Weak to all enemies.",trigger:"on_combat_start",effect:{type:"apply_weak_all",value:1}},{id:"living_wall",name:"Living Wall",description:"When you emplace, draw 2 cards.",trigger:"on_emplace",effect:{type:"draw_cards",value:2}},{id:"siphon_ring",name:"Siphon Ring",description:"On card play, if card costs 2+, heal 2 Gate HP.",trigger:"on_card_play",effect:{type:"heal",value:2}},{id:"void_prism",name:"Void Prism",description:"At turn start, deal 2 damage to all enemies.",trigger:"on_turn_start",effect:{type:"deal_damage_all",value:2}},{id:"watchers_eye",name:"Watcher's Eye",description:"At combat start, draw 2 extra cards.",trigger:"on_combat_start",effect:{type:"draw_cards",value:2}},{id:"fortress_heart",name:"Fortress Heart",description:"Emplacements gain +3 HP.",trigger:"passive",effect:{type:"emplace_hp_bonus",value:3}},{id:"mending_stone",name:"Mending Stone",description:"After combat, heal 5 Gate HP.",trigger:"passive",effect:{type:"heal",value:5}},{id:"pale_harvester",name:"Pale Harvester",description:"On enemy kill, gain 1 Resolve (cap at max+2).",trigger:"on_enemy_kill",effect:{type:"gain_resolve",value:1}}];function Ot(e){return dn.find(t=>t.id===e)}function mn(e,t,r=3){let n=dn.filter(l=>!t.includes(l.id)),a=[],o=[...n];for(let l=0;l<r&&o.length>0;l++){let c=Math.floor(e()*o.length);a.push(o[c]),o.splice(c,1)}return a}function _t(e,t,r,n){for(let a of t){let o=Ot(a);if(!(!o||o.trigger!==r))switch(o.effect.type){case"gain_block":{if(a==="burnished_shield"&&e.resolve>0)break;e.gateBlock+=o.effect.value,e.events.push({type:"block_gained",turn:e.turn,data:{value:o.effect.value,relic:a}});break}case"gain_resolve":{e.resolve=Math.min(e.maxResolve*2,e.resolve+o.effect.value);break}case"draw_cards":{let{drawCards:l}=Yr(),{drawn:c,newDrawPile:s,newDiscardPile:p}=l(e.drawPile,e.discardPile,o.effect.value,Kr(e));e.hand.push(...c),e.drawPile=s,e.discardPile=p;break}case"deal_damage_all":{for(let l of e.columns)for(let c of l.enemies){let s=Lt(c);c.hp-=Math.floor(o.effect.value*s)}break}case"deal_damage_column":{let l=n?.column;if(l!==void 0){let c=e.columns[l];if(c)for(let s of c.enemies){let p=Lt(s);s.hp-=Math.floor(o.effect.value*p)}}break}case"heal":{if(r==="on_card_play"){let c=n?.cardCost;if(c!==void 0&&c<2)break}let l=Math.floor(o.effect.value*(e.difficulty?.healMult??1));e.gateHp=Math.min(e.gateMaxHp,e.gateHp+l);break}case"apply_vulnerable_all":{for(let l of e.columns)for(let c of l.enemies)ee(c,"vulnerable",o.effect.value,3);break}case"apply_weak_all":{for(let l of e.columns)for(let c of l.enemies)ee(c,"weak",o.effect.value,3);break}case"burn_on_kill":{if(n?.diedFromBurn)for(let c of e.columns)for(let s of c.enemies)ee(s,"burn",o.effect.value,99);break}case"block_on_emplace":{e.gateBlock+=o.effect.value,e.events.push({type:"block_gained",turn:e.turn,data:{value:o.effect.value,relic:a}});break}case"first_card_free":case"emplace_hp_bonus":case"max_resolve_bonus":case"extra_card_reward":case"fragment_bonus":break}}}function Kr(e){return Ur(e.seed+e.turn*7+13)}function Ur(e){let t=e>>>0;return()=>{t=t+1831565813|0;let r=Math.imul(t^t>>>15,1|t);return r=r+Math.imul(r^r>>>7,61|r)^r,((r^r>>>14)>>>0)/4294967296}}function Yr(){return{drawCards(e,t,r,n){let a=[],o=[...e],l=[...t];for(let c=0;c<r;c++){if(o.length===0){if(l.length===0)break;for(let s=l.length-1;s>0;s--){let p=Math.floor(n()*(s+1));[l[s],l[p]]=[l[p],l[s]]}o.push(...l),l=[]}a.push(o.shift())}return{drawn:a,newDrawPile:o,newDiscardPile:l}}}}function qr(e){let t=0;for(let r of e){let n=Ot(r);n&&n.effect.type==="emplace_hp_bonus"&&(t+=n.effect.value)}return t}function zr(e){let t=0;for(let r of e){let n=Ot(r);n&&n.effect.type==="max_resolve_bonus"&&(t+=n.effect.value)}return t}function xa(e){return e.some(t=>{let r=Ot(t);return r&&r.effect.type==="first_card_free"})}function un(e,t,r=fa,n=fa,a=[{templateId:"hollow",column:1},{templateId:"hollow",column:3},{templateId:"needle",column:2}],o=[],l){let c=ae(t),s=Br(e,c),p=zr(o),b=(l?.reducedMaxResolve?pa-1:pa)+p,y=l?.reducedHandSize?xt-1:xt,x=Array.from({length:lt},(R,T)=>({index:T,enemies:[],emplacement:null}));for(let R of a){let T=va(R.templateId,R.column);l?.enemyHpMult&&l.enemyHpMult!==1&&(T.hp=Math.ceil(T.hp*l.enemyHpMult),T.maxHp=Math.ceil(T.maxHp*l.enemyHpMult)),l?.enemyBlitz&&(T.row=1),l?.enemyStartFortified&&ee(T,"fortified",1,99),x[T.column].enemies.push(T)}let{drawn:_,newDrawPile:G,newDiscardPile:d}=Ve(s,[],y,c),g={columns:x,hand:_,drawPile:G,discardPile:d,exhaustPile:[],gateHp:r,gateMaxHp:n,gateBlock:0,resolve:b,maxResolve:b,turn:1,phase:"player",outcome:"undecided",events:[],seed:t,killsThisCombat:0,relics:o,difficulty:l};_t(g,o,"on_combat_start");for(let R of x)for(let T of R.enemies){let $=R.emplacement!==null;T.intent=ka(T,c,1,$,g)}return te(g,"turn_start",{turn:1}),g}function te(e,t,r){e.events.push({type:t,turn:e.turn,data:r}),e.events.length>100&&(e.events=e.events.slice(-100))}function pn(e,t,r,n=!1){if(e.phase!=="player")return e;let a=e.hand[t];if(!a)return e;let o=q(a.defId);if(!o)return e;let l=!e.events.some(s=>s.type==="card_played"&&s.turn===e.turn),c=xa(e.relics)&&l;if(n&&o.type==="emplace"&&o.emplaceCost!==void 0){let s=c?0:o.emplaceCost;if(s>e.resolve)return e;let p=r??0,b=qr(e.relics);if(!Vr(e,o,p,b))return e;e.resolve-=s,e.hand.splice(t,1),e.exhaustPile.push(a),_t(e,e.relics,"on_emplace")}else{let s=c?0:o.cost;if(s>e.resolve)return e;e.resolve-=s,e.hand.splice(t,1);let p=!1;for(let b of o.effects){if(b.type==="exhaust_self"){p=!0;continue}fn(e,b,r??0)}Wt(e),p?e.exhaustPile.push(a):e.discardPile.push(a),_t(e,e.relics,"on_card_play",{cardCost:o.cost,cardId:o.id})}return e.lastCardPlayed=a.defId,te(e,"card_played",{cardId:a.defId,targetColumn:r,asEmplace:n}),Ft(e),e}function fn(e,t,r){switch(t.type){case"damage":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)Ce(e,a,t.value);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)Ce(e,a,t.value)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);Ce(e,a,t.value)}}break}case"block":e.gateBlock+=t.value,te(e,"block_gained",{value:t.value});break;case"heal":{let n=Math.floor(t.value*(e.difficulty?.healMult??1));e.gateHp=Math.min(e.gateMaxHp,e.gateHp+n);break}case"draw":{let{drawn:n,newDrawPile:a,newDiscardPile:o}=Ve(e.drawPile,e.discardPile,t.value,ae(e.seed+e.turn));e.hand.push(...n),e.drawPile=a,e.discardPile=o;break}case"resolve":e.resolve=Math.min(e.maxResolve*2,e.resolve+t.value);break;case"vulnerable":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)ee(a,"vulnerable",t.value,3);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)ee(a,"vulnerable",t.value,3)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);ee(a,"vulnerable",t.value,3)}}te(e,"status_applied",{type:"vulnerable",value:t.value,column:r});break}case"weak":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)ee(a,"weak",t.value,3);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)ee(a,"weak",t.value,3)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);ee(a,"weak",t.value,3)}}te(e,"status_applied",{type:"weak",value:t.value,column:r});break}case"burn":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)ee(a,"burn",t.value,99);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)ee(a,"burn",t.value,99)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);ee(a,"burn",t.value,99)}}break}case"self_damage":{e.gateHp=Math.max(0,e.gateHp-t.value),te(e,"gate_hit",{self:!0,damage:t.value});break}case"fortify":break;case"trigger_emplacements":{cn(e);break}case"damage_per_burn":{for(let n of e.columns)for(let a of n.enemies){let o=Be(a,"burn");o>0&&Ce(e,a,t.value*o)}break}case"damage_equal_block":{let n=e.gateBlock,a=e.columns[r];if(a&&a.enemies.length>0){let o=a.enemies.reduce((l,c)=>l.row>=c.row?l:c);Ce(e,o,n)}break}case"draw_per_kills":{let n=e.killsThisCombat*t.value;if(n>0){let{drawn:a,newDrawPile:o,newDiscardPile:l}=Ve(e.drawPile,e.discardPile,n,ae(e.seed+e.turn+3));e.hand.push(...a),e.drawPile=o,e.discardPile=l}break}case"damage_if_vulnerable":{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((c,s)=>c.row>=s.row?c:s),l=Be(a,"vulnerable")>0?t.value*2:t.value;Ce(e,a,l)}break}case"damage_per_kill_this_action":{let n=jr(e);if(n>0){let a=t.value*n;for(let o of e.columns)for(let l of o.enemies)Ce(e,l,a)}break}case"exhaust_draw":{if(e.hand.length>0){let l=e.hand.shift();e.exhaustPile.push(l)}let{drawn:n,newDrawPile:a,newDiscardPile:o}=Ve(e.drawPile,e.discardPile,t.value,ae(e.seed+e.turn+5));e.hand.push(...n),e.drawPile=a,e.discardPile=o;break}case"burn_if_burning":{let n=!1;for(let o of e.columns){for(let l of o.enemies)if(Be(l,"burn")>0){n=!0;break}if(n)break}let a=n?t.value+2:t.value;for(let o of e.columns)for(let l of o.enemies)ee(l,"burn",a,99);break}case"damage_plus_block":{let n=Math.min(e.gateBlock,10),a=t.value+n,o=e.columns[r];if(o&&o.enemies.length>0){let l=o.enemies.reduce((c,s)=>c.row>=s.row?c:s);Ce(e,l,a)}break}case"replay_last":{if(e.lastCardPlayed&&e.lastCardPlayed!=="pale_echo"){let n=q(e.lastCardPlayed);if(n)for(let a of n.effects)a.type!=="exhaust_self"&&a.type!=="replay_last"&&fn(e,a,r)}break}case"damage_if_emplaced":{let n=e.columns[r];if(n){let a=n.emplacement?Math.floor(t.value*1.5):t.value;for(let o of n.enemies)Ce(e,o,a)}break}case"damage_if_low_hp":{let a=e.gateHp<e.gateMaxHp*.5?t.value*2:t.value,o=e.columns[r];if(o&&o.enemies.length>0){let l=o.enemies.reduce((c,s)=>c.row>=s.row?c:s);Ce(e,l,a)}break}case"damage_per_emplace":{let n=e.columns.filter(a=>a.emplacement!==null).length;if(n>0){let a=t.value*n;for(let o of e.columns)for(let l of o.enemies)Ce(e,l,a)}break}case"draw_per_empty_potions":{let{drawn:a,newDrawPile:o,newDiscardPile:l}=Ve(e.drawPile,e.discardPile,2,ae(e.seed+e.turn+9));e.hand.push(...a),e.drawPile=o,e.discardPile=l;break}case"exhaust_self":break}}function jr(e){let t=0;for(let r of e.columns)for(let n of r.enemies)n.hp<=0&&t++;return t}function Ce(e,t,r){let n=Lt(t),a=Math.floor(r*n);t.hp-=a,te(e,"damage_dealt",{targetId:t.instanceId,damage:a})}function Wt(e){for(let t of e.columns)t.enemies=t.enemies.filter(r=>{if(r.hp<=0){e.killsThisCombat++,te(e,"enemy_killed",{enemyId:r.instanceId,templateId:r.templateId});let n=Be(r,"burn")>0;return _t(e,e.relics,"on_enemy_kill",{column:t.index,diedFromBurn:n,templateId:r.templateId}),!1}return!0})}function hn(e){return e.phase!=="player"||(te(e,"turn_end",{turn:e.turn}),e.phase="enemy",Xr(e)),e}function Xr(e){let t=ae(e.seed+e.turn*31),r=e.columns.flatMap(c=>c.enemies.map(s=>({enemy:s,intent:s.intent??{type:"advance",value:1}})));for(let{enemy:c,intent:s}of r)c.hp<=0||Jr(e,c,s,t);if(Wt(e),Ft(e),e.outcome!=="undecided"||(e.turn++,e.phase="player",e.gateBlock=0,cn(e),Wt(e),Ft(e),e.outcome!=="undecided"))return;e.discardPile.push(...e.hand),e.hand=[];let n=e.difficulty?.reducedHandSize?xt-1:xt,{drawn:a,newDrawPile:o,newDiscardPile:l}=Ve(e.drawPile,e.discardPile,n,t);if(e.hand=a,e.drawPile=o,e.discardPile=l,_t(e,e.relics,"on_turn_start"),Wt(e),Ft(e),e.outcome==="undecided"){e.resolve=Math.min(e.resolve+e.maxResolve,e.maxResolve*2);for(let c of e.columns)for(let s of c.enemies){let p=c.emplacement!==null;s.intent=ka(s,t,e.turn,p,e)}te(e,"turn_start",{turn:e.turn})}}function Jr(e,t,r,n){let a=Ge(t.templateId),o=Fr(t),l=e.difficulty?.enemyDamageMult??1;if(Lr(t),Wr(t),!(t.hp<=0))switch(r.type){case"advance":{if(t.templateId==="breaker"){let c=e.columns[t.column];if(c.emplacement&&t.row>=it-2){let s=(a?.damage??10)*2;c.emplacement.hp-=s,c.emplacement.hp<=0&&(te(e,"emplacement_destroyed",{column:t.column}),c.emplacement=null);break}}if(t.row=Math.min(it-1,t.row+(a?.speed??1)),te(e,"enemy_advance",{enemyId:t.instanceId,row:t.row}),t.row>=it-1)if(t.templateId==="wraith"){let c=Math.floor((a?.damage??7)*o*l),s=Math.min(e.gateBlock,c);e.gateBlock-=s,e.gateHp-=c-s,te(e,"gate_hit",{enemyId:t.instanceId,damage:c-s,blocked:s})}else{let c=e.columns[t.column];if(c.emplacement){let s=Math.floor((a?.damage??4)*o*l);c.emplacement.hp-=s,c.emplacement.hp<=0&&(te(e,"emplacement_destroyed",{column:t.column}),c.emplacement=null)}else{let s=Math.floor((a?.damage??4)*o*l),p=Math.min(e.gateBlock,s);e.gateBlock-=p,e.gateHp-=s-p,te(e,"gate_hit",{enemyId:t.instanceId,damage:s-p,blocked:p})}}break}case"attack":{if(t.templateId==="flanker"){let b=n()<.5?-1:1,y=t.column,x=Math.max(0,Math.min(lt-1,t.column+b));if(x!==y){let _=e.columns[y];_.enemies=_.enemies.filter(G=>G.instanceId!==t.instanceId),t.column=x,e.columns[x].enemies.push(t)}}let c=r.value??a?.damage??4,s=Math.floor(c*o*l),p=Math.min(e.gateBlock,s);e.gateBlock-=p,e.gateHp-=s-p,te(e,"gate_hit",{enemyId:t.instanceId,damage:s-p,blocked:p});break}case"buff":ee(t,"empowered",r.value,3);break;case"debuff":{e.gateBlock=Math.max(0,e.gateBlock-r.value*2);break}case"shield":{for(let c=Math.max(0,t.column-1);c<=Math.min(lt-1,t.column+1);c++)for(let s of e.columns[c].enemies)ee(s,"fortified",r.value,2);break}case"summon":{let c=r.value??1;for(let s=0;s<c;s++){let p=e.columns.findIndex(y=>y.enemies.length===0);if(p<0)break;let b=va("wisp",p);e.columns[p].enemies.push(b),b.intent=ka(b,ae(e.seed+e.turn*97+s),e.turn,!1,e)}break}}}function Ft(e){if(e.columns.reduce((r,n)=>r+n.enemies.length,0)===0){e.gateHp=Math.max(1,e.gateHp),e.outcome="win",e.phase="ended",te(e,"combat_end",{outcome:"win"});return}e.gateHp<=0&&(e.gateHp=0,e.outcome="lose",e.phase="ended",te(e,"combat_end",{outcome:"lose"}))}function Kt(e,t=3,r=[]){let n=new Set(["strike","guard","bolster","brace","mend",...r]),o=Tt.filter(s=>!n.has(s.id)&&s.id!=="pale_curse").map(s=>({card:s,weight:s.rarity==="common"?6:s.rarity==="uncommon"?3:s.rarity==="rare"?1.5:.5})),l=[],c=new Set;for(let s=0;s<t&&o.length>0;s++){let p=o.filter(_=>!c.has(_.card.id));if(p.length===0)break;let b=p.reduce((_,G)=>_+G.weight,0),y=e()*b,x=!1;for(let _ of p)if(y-=_.weight,y<=0){l.push(_.card),c.add(_.card.id),x=!0;break}x||(l.push(p[p.length-1].card),c.add(p[p.length-1].card.id))}return l}var Qr=[{name:"Scout Party",enemies:[{templateId:"hollow",column:1},{templateId:"hollow",column:3}]},{name:"Swift Assault",enemies:[{templateId:"needle",column:0},{templateId:"needle",column:2},{templateId:"needle",column:4}]},{name:"Pale Vanguard",enemies:[{templateId:"hollow",column:1},{templateId:"hollow",column:3},{templateId:"needle",column:2}]},{name:"Wisp Swarm",enemies:[{templateId:"wisp",column:0},{templateId:"wisp",column:1},{templateId:"wisp",column:2},{templateId:"wisp",column:3},{templateId:"wisp",column:4}]},{name:"Heavy Patrol",enemies:[{templateId:"shade",column:1},{templateId:"hollow",column:3}]},{name:"Armored Column",enemies:[{templateId:"husk",column:2},{templateId:"wisp",column:0},{templateId:"wisp",column:4}]},{name:"Pale Scouts",enemies:[{templateId:"wisp",column:0},{templateId:"wisp",column:4},{templateId:"hollow",column:2}]},{name:"Needle Formation",enemies:[{templateId:"needle",column:1},{templateId:"needle",column:3}]},{name:"Husk Advance",enemies:[{templateId:"husk",column:2},{templateId:"wisp",column:1},{templateId:"wisp",column:3}]},{name:"Shadow Patrol",enemies:[{templateId:"shade",column:0},{templateId:"needle",column:4}]},{name:"Swarm Tide",enemies:[{templateId:"wisp",column:0},{templateId:"wisp",column:1},{templateId:"hollow",column:2},{templateId:"wisp",column:3},{templateId:"wisp",column:4}]},{name:"Hollow Shields",enemies:[{templateId:"hollow",column:1},{templateId:"hollow",column:2},{templateId:"hollow",column:3}]},{name:"Fast Assault",enemies:[{templateId:"needle",column:0},{templateId:"needle",column:2},{templateId:"needle",column:4}]},{name:"Pale Sentries",enemies:[{templateId:"shade",column:2},{templateId:"hollow",column:0},{templateId:"hollow",column:4}]}],Zr=[{name:"The Dark Tide",enemies:[{templateId:"shade",column:1},{templateId:"shade",column:3},{templateId:"needle",column:2}]},{name:"Hollow Legion",enemies:[{templateId:"hollow",column:0},{templateId:"hollow",column:1},{templateId:"hollow",column:3},{templateId:"hollow",column:4}]},{name:"Husk Fortress",enemies:[{templateId:"husk",column:1},{templateId:"husk",column:3},{templateId:"needle",column:2}]},{name:"Shade Ambush",enemies:[{templateId:"shade",column:0},{templateId:"shade",column:2},{templateId:"shade",column:4}]}],eo=[{name:"The Warband",enemies:[{templateId:"breaker",column:2},{templateId:"shielder",column:1},{templateId:"shielder",column:3}]},{name:"Flanking Ambush",enemies:[{templateId:"flanker",column:0},{templateId:"flanker",column:4},{templateId:"wraith",column:2}]},{name:"Breaker Vanguard",enemies:[{templateId:"breaker",column:1},{templateId:"breaker",column:3},{templateId:"shielder",column:2}]},{name:"Phantom Blitz",enemies:[{templateId:"wraith",column:0},{templateId:"wraith",column:1},{templateId:"wraith",column:3},{templateId:"wraith",column:4}]}],to=[{name:"Echo Vanguard",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:3}]},{name:"The Final Test",enemies:[{templateId:"echo",column:2},{templateId:"breaker",column:0},{templateId:"flanker",column:4}]},{name:"Memory Siege",enemies:[{templateId:"echo",column:0},{templateId:"echo",column:2},{templateId:"echo",column:4}]},{name:"The Pale Tribunal",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:2},{templateId:"echo",column:3},{templateId:"breaker",column:0}]}],ao=[{name:"Wraith Drift",enemies:[{templateId:"wraith",column:1},{templateId:"wraith",column:3}]},{name:"Breach Team",enemies:[{templateId:"breaker",column:2},{templateId:"flanker",column:0},{templateId:"flanker",column:4}]},{name:"Shield Wall",enemies:[{templateId:"shielder",column:2},{templateId:"hollow",column:1},{templateId:"hollow",column:3}]},{name:"Wraith Surge",enemies:[{templateId:"wraith",column:0},{templateId:"wraith",column:2},{templateId:"wraith",column:4}]},{name:"Flanker Pair",enemies:[{templateId:"flanker",column:1},{templateId:"flanker",column:3}]},{name:"Shielder Escort",enemies:[{templateId:"shielder",column:2},{templateId:"breaker",column:1},{templateId:"hollow",column:3}]},{name:"Pale Vanguard II",enemies:[{templateId:"breaker",column:0},{templateId:"flanker",column:2},{templateId:"shielder",column:4}]},{name:"Wraith Patrol",enemies:[{templateId:"wraith",column:1},{templateId:"hollow",column:0},{templateId:"hollow",column:4}]},{name:"Breaker Column",enemies:[{templateId:"breaker",column:2},{templateId:"wisp",column:0},{templateId:"wisp",column:1},{templateId:"wisp",column:3},{templateId:"wisp",column:4}]},{name:"Shield Formation",enemies:[{templateId:"shielder",column:1},{templateId:"shielder",column:3},{templateId:"flanker",column:2}]},{name:"Assault Wave",enemies:[{templateId:"breaker",column:1},{templateId:"breaker",column:3}]},{name:"Night Raid",enemies:[{templateId:"wraith",column:0},{templateId:"flanker",column:2},{templateId:"wraith",column:4}]},{name:"Armored Push",enemies:[{templateId:"shielder",column:0},{templateId:"breaker",column:2},{templateId:"shielder",column:4}]}],no=[{name:"Echoes of the End",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:3}]},{name:"Final Surge",enemies:[{templateId:"echo",column:2},{templateId:"wraith",column:0},{templateId:"wraith",column:4},{templateId:"breaker",column:1}]},{name:"Echo Patrol",enemies:[{templateId:"echo",column:0},{templateId:"echo",column:4}]},{name:"Pale Convergence",enemies:[{templateId:"echo",column:2},{templateId:"wraith",column:1},{templateId:"wraith",column:3}]},{name:"Void March",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:3},{templateId:"breaker",column:2}]},{name:"Shadow Tide",enemies:[{templateId:"wraith",column:0},{templateId:"echo",column:2},{templateId:"flanker",column:4}]},{name:"The Unnamed",enemies:[{templateId:"echo",column:2},{templateId:"shielder",column:1},{templateId:"shielder",column:3}]},{name:"Pale Hammer",enemies:[{templateId:"breaker",column:0},{templateId:"echo",column:2},{templateId:"breaker",column:4}]},{name:"Memory Storm",enemies:[{templateId:"echo",column:0},{templateId:"echo",column:1},{templateId:"echo",column:3},{templateId:"echo",column:4}]},{name:"Wraith Flood",enemies:[{templateId:"wraith",column:0},{templateId:"wraith",column:1},{templateId:"wraith",column:2},{templateId:"wraith",column:3},{templateId:"wraith",column:4}]},{name:"Final Guard",enemies:[{templateId:"echo",column:1},{templateId:"breaker",column:2},{templateId:"echo",column:3}]},{name:"Pale Assembly",enemies:[{templateId:"echo",column:0},{templateId:"shielder",column:2},{templateId:"echo",column:4},{templateId:"flanker",column:1}]}];function gn(e,t,r=!1){let n;if(r)switch(e){case 2:n=eo;break;case 3:n=to;break;default:n=Zr;break}else switch(e){case 2:n=ao;break;case 3:n=no;break;default:n=Qr;break}let a=Math.floor(t()*n.length);return{...n[a],isElite:r}}var Gt=12,rn=4;function ro(e,t,r){if(e===0)return"combat";if(e===t-1)return"boss";let n=r();return e===Math.floor(t/2)?n<.5?"rest":"shop":n<.5?"combat":n<.65?"elite":n<.78?"event":n<.88?"rest":"shop"}function yn(e,t){let r=ae(t),n=[],a=[];for(let o=0;o<Gt;o++){let l=[],c=o===0||o===Gt-1?1:Math.floor(r()*2)+2,s=new Set;for(;s.size<c;)s.add(Math.floor(r()*rn));for(let p=0;p<rn;p++)if(s.has(p)){let b=ro(o,Gt,r),y={id:`node-${e}-${o}-${p}`,type:b,row:o,column:p,connections:[],visited:!1};l.push(y),n.push(y)}else l.push(null);a.push(l)}for(let o=0;o<Gt-1;o++){let l=a[o].filter(s=>s!==null),c=a[o+1].filter(s=>s!==null);if(c.length!==0){for(let s of l){let p=c.map(b=>({node:b,dist:Math.abs(b.column-s.column)})).sort((b,y)=>b.dist-y.dist);s.connections.push(p[0].node.id),p.length>1&&r()<.4&&s.connections.push(p[1].node.id)}for(let s of c)!l.some(b=>b.connections.includes(s.id))&&l.length>0&&l.map(y=>({node:y,dist:Math.abs(y.column-s.column)})).sort((y,x)=>y.dist-x.dist)[0].node.connections.push(s.id)}}return{act:e,nodes:n}}function dt(e,t){return e.nodes.find(r=>r.id===t)}function bn(e,t){if(!t)return e.nodes.filter(n=>n.row===0);let r=dt(e,t);return r?r.connections.map(n=>dt(e,n)).filter(n=>!!n):[]}var oo={easy:{hp:.7,dmg:.7,gateHpBonus:20},normal:{hp:1,dmg:1,gateHpBonus:0},hard:{hp:1.25,dmg:1.25,gateHpBonus:-10}};function Ut(e,t=0,r="normal"){let n=oo[r],a={enemyHpMult:1,enemyDamageMult:1,startingGateHp:70+n.gateHpBonus,extraEnemies:0,shopCostMult:1,healMult:1,startWithCurse:!1,enemyBlitz:!1,reducedRewards:!1,enemyStartFortified:!1,voidColumns:0,paleHunger:!1,bossExtraMinions:0,reducedHandSize:!1,reducedMaxResolve:!1};return e>=2&&(a.enemyHpMult+=.2,a.enemyDamageMult+=.15),e>=3&&(a.enemyHpMult+=.15,a.enemyDamageMult+=.1),t>=1&&(a.enemyHpMult+=.1),t>=2&&(a.enemyDamageMult+=.1),t>=3&&(a.startWithCurse=!0),t>=4&&(a.healMult-=.25),t>=5&&(a.startingGateHp-=10),t>=6&&(a.enemyBlitz=!0),t>=7&&(a.extraEnemies+=1),t>=8&&(a.reducedRewards=!0),t>=9&&(a.enemyStartFortified=!0),t>=10&&(a.shopCostMult+=.25),t>=11&&(a.voidColumns=1),t>=12&&(a.paleHunger=!0),t>=13&&(a.bossExtraMinions=2),t>=14&&(a.reducedHandSize=!0),t>=15&&(a.reducedMaxResolve=!0),a.enemyHpMult*=n.hp,a.enemyDamageMult*=n.dmg,a}function wn(e,t=0,r="normal"){let n=Se(e),a=yn(1,n),o=Ut(1,t,r),l=wa();return o.startWithCurse&&l.push(It("pale_curse")),{id:`run-${Date.now()}-${n}`,seed:e,act:1,map:a,currentNodeId:null,deck:l,gateHp:o.startingGateHp,gateMaxHp:o.startingGateHp,fragments:0,potions:[null,null,null],relics:[],ascensionLevel:t,combat:null}}function Ta(e,t){return{...e,deck:[...e.deck,t]}}function _a(e,t){return{...e,deck:e.deck.filter(r=>r.instanceId!==t)}}function Ia(e,t){let r=e.map.nodes.map(n=>n.id===t?{...n,visited:!0}:n);return{...e,currentNodeId:t,map:{...e.map,nodes:r}}}function Yt(e,t){return{...e,gateHp:Math.min(e.gateMaxHp,e.gateHp+t)}}function vn(e,t){return e.fragments<t?null:{...e,fragments:e.fragments-t}}function Ct(e,t){return{...e,fragments:e.fragments+t}}function kn(e,t){let r=e.potions.indexOf(null);if(r===-1)return null;let n=[...e.potions];return n[r]=t,{...e,potions:n}}function xn(e,t){if(t<0||t>=e.potions.length)return null;let r=e.potions[t];if(!r)return null;let n=[...e.potions];return n[t]=null,{run:{...e,potions:n},potionId:r}}function Ca(e){let t=e.act+1,r=Se(e.seed+`-act${t}`),n=yn(t,r);return{...e,act:t,map:n,currentNodeId:null}}function Tn(e){let t=[],r=Tt.filter(l=>l.rarity!=="common"||l.id==="slash"||l.id==="flare"),n=new Set,a=0;for(let l=0;l<5&&n.size<5&&a<50;l++){a++;let c=r[Math.floor(e()*r.length)];if(n.has(c.id)){l--;continue}n.add(c.id);let s=c.rarity==="rare"?75:c.rarity==="uncommon"?50:30;t.push({type:"card",cardDef:c,cost:s})}let o=ct[Math.floor(e()*ct.length)];return t.push({type:"potion",potionDef:o,cost:25}),t.push({type:"card_remove",cost:50}),t}var so=[{id:"wandering_smith",name:"The Wandering Smith",description:"A cloaked figure sits by a forge that shouldn't exist this far into the Pale. They offer to work on your defenses.",choices:[{label:"Ask them to reinforce the gate. (Heal 15 HP)",effect:{type:"heal",value:15}},{label:"Ask for supplies. (Gain 15 fragments)",effect:{type:"fragments",value:15}},{label:"Leave them be.",effect:{type:"nothing"}}]},{id:"pale_fountain",name:"The Pale Fountain",description:"A spring of luminous water bubbles from cracked stone. Its glow is unsettling but beckons.",choices:[{label:"Drink deeply. (+10 max Gate HP)",effect:{type:"max_hp",value:10}},{label:"Fill a flask. (Gain a random card)",effect:{type:"card_reward"}},{label:"Leave. The Pale gives nothing freely.",effect:{type:"nothing"}}]},{id:"abandoned_cache",name:"Abandoned Cache",description:"You find a sealed chest among the rubble of a collapsed watchtower.",choices:[{label:"Pry it open. (Gain 25 fragments)",effect:{type:"fragments",value:25}},{label:"Smash it quickly. (Gain 10 fragments)",effect:{type:"fragments",value:10}},{label:"Leave it. Could be cursed.",effect:{type:"nothing"}}]},{id:"old_veteran",name:"The Old Veteran",description:`A scarred soldier leans against the wall. "I've seen the Pale take stronger keeps than yours," they say.`,choices:[{label:'"Teach me." (Remove a card from your deck)',effect:{type:"remove_card"}},{label:'"Give me your supplies." (Gain 20 fragments)',effect:{type:"fragments",value:20}},{label:'"Good luck." (Nothing happens)',effect:{type:"nothing"}}]},{id:"strange_merchant",name:"Strange Merchant",description:"A merchant with too many eyes offers wares from a floating pack.",choices:[{label:"Browse their cards. (Gain a card)",effect:{type:"card_reward"}},{label:"Sell your blood. (Lose 10 HP, gain 30 fragments)",effect:{type:"damage",value:10}},{label:"Walk away.",effect:{type:"nothing"}}]}],lo=[{id:"pale_scholar",name:"The Pale Scholar",description:'An ancient figure pores over a tome that seems to read itself. "The Pale remembers everything," they whisper.',choices:[{label:"Ask for knowledge. (Gain a card)",effect:{type:"card_reward"}},{label:"Ask for healing. (Heal 20 HP)",effect:{type:"heal",value:20}},{label:"Back away slowly.",effect:{type:"nothing"}}]},{id:"fractured_mirror",name:"The Fractured Mirror",description:"A mirror stands in the corridor, cracked but gleaming. Your reflection looks stronger than you feel.",choices:[{label:"Reach through. (+5 max Gate HP, lose 5 HP)",effect:{type:"max_hp",value:5}},{label:"Smash it. (Gain 20 fragments)",effect:{type:"fragments",value:20}},{label:"Walk past. Mirrors lie.",effect:{type:"nothing"}}]},{id:"deserters_cache",name:"Deserter's Cache",description:"Behind a collapsed wall, you find supplies left by someone who fled the Keep long ago.",choices:[{label:"Take everything. (Gain 30 fragments)",effect:{type:"fragments",value:30}},{label:"Take only what you need. (Gain 10 fragments)",effect:{type:"fragments",value:10}},{label:"Leave it for the next Warden.",effect:{type:"nothing"}}]}],io=[{id:"wandering_smith_cracks",name:"The Wandering Smith",description:"The smith again. You've seen this forge before \u2014 the same cracks in the stone, the same angle of the cloak. They look up and for the first time, you notice their hands are shaking.",choices:[{label:'"How long have you been here?" (Heal 15 HP)',effect:{type:"heal",value:15}},{label:`"You're not real, are you." (Gain 20 fragments)`,effect:{type:"fragments",value:20}},{label:"Say nothing. The kindness is worse than the lie.",effect:{type:"nothing"}}]},{id:"the_echoing_child",name:"The Echoing Child",description:'A child sits in the corridor, humming. They look up with eyes that glow faintly. "I remember my house," they say. "Can you take me there?" The house is gone. You know this.',choices:[{label:`"I'll try." (Lose 15 HP \u2014 the detour is dangerous)`,effect:{type:"damage",value:15}},{label:`"There's nothing to go back to." (Gain 10 fragments \u2014 they dissolve)`,effect:{type:"fragments",value:10}},{label:'"Stay here. The Keep is safe."',effect:{type:"nothing"}}]}],co=[{id:"motts_stash",name:"Mott's Hidden Stash",description:`"DON'T TELL MOTT," reads the note pinned to a crate. It's Mott's handwriting.`,choices:[{label:"Open it. (Gain 25 fragments)",effect:{type:"fragments",value:25}},{label:"Leave it. Mott knows things you don't.",effect:{type:"nothing"}}]},{id:"wardens_echo",name:"A Warden's Echo",description:'You find scratches on the wall. Tallies. Hundreds of them. Below, in faded ink: "Run 847. The Pale is not the enemy. The Pale is the answer to a question we forgot."',choices:[{label:"Study the scratches. (Gain a card)",effect:{type:"card_reward"}},{label:"Cover them. Some knowledge is dangerous. (Gain 15 fragments)",effect:{type:"fragments",value:15}},{label:"Add your own tally mark.",effect:{type:"nothing"}}]}],ha=[{id:"the_architects_choice",name:"The Architect's Choice",description:"A door that shouldn't exist. Beyond it, a room you've seen in dreams. A desk. A journal. A pen that still has ink. The Architect's final workspace.",choices:[{label:"Read the journal. (+15 max Gate HP)",effect:{type:"max_hp",value:15}},{label:"Destroy the journal. (Gain 40 fragments)",effect:{type:"fragments",value:40}},{label:"Sit down. Pick up the pen.",effect:{type:"nothing"}}]}],mo=[{id:"the_last_wall",name:"The Last Wall",description:"The final barrier before the Pale's heart. Ancient wards still flicker across its surface.",choices:[{label:"Channel the wards. (+15 max Gate HP)",effect:{type:"max_hp",value:15}},{label:"Break through quickly. (Gain 20 fragments)",effect:{type:"fragments",value:20}},{label:"Let it stand. Its purpose is not yours.",effect:{type:"nothing"}}]},{id:"echo_of_a_warden",name:"Echo of a Warden",description:`A ghostly figure in Warden's garb appears. "I held this Keep once. Take what remains of my strength."`,choices:[{label:"Accept their gift. (Gain a card, lose 5 HP)",effect:{type:"card_reward"}},{label:"Ask them to strengthen the gate. (Heal 25 HP)",effect:{type:"heal",value:25}},{label:'"Rest now, Warden."',effect:{type:"nothing"}}]},{id:"pale_bargain",name:"The Pale Bargain",description:'The void itself speaks: "Give me something, and I will give you power."',choices:[{label:"Sacrifice a card. (Remove a card)",effect:{type:"remove_card"}},{label:"Sacrifice blood. (Lose 15 HP, gain 40 fragments)",effect:{type:"damage",value:15}},{label:'"I make no deals with the void."',effect:{type:"nothing"}}]}];function _n(e,t,r){let n;switch(e){case 2:n=lo;break;case 3:n=mo;break;default:n=so;break}let a=r==="cracks"||r==="truth"||r==="true_ending";if((r==="truth"||r==="true_ending")&&e===3&&ha.length>0&&t()<.4)return ha[Math.floor(t()*ha.length)];if(a){let l=null;if(e===1?l=io:e===2&&(l=co),l&&l.length>0&&t()<.4)return l[Math.floor(t()*l.length)]}return n[Math.floor(t()*n.length)]}var mt=[{id:"forge",name:"The Forge",description:"Unlock card upgrades at rest sites. Higher levels improve upgrades.",maxLevel:3,upgradeCost:e=>e*30},{id:"archive",name:"The Archive",description:"Unlock rare cards in the reward pool. Higher levels add legendaries.",maxLevel:3,upgradeCost:e=>e*40},{id:"beacon_tower",name:"Beacon Tower",description:"+1 starting hand size per level.",maxLevel:2,upgradeCost:e=>e*50},{id:"foundry",name:"The Foundry",description:"Emplacements start with +2 HP per level.",maxLevel:3,upgradeCost:e=>e*25},{id:"sanctum_hall",name:"Sanctum Hall",description:"+5 max Gate HP per level.",maxLevel:3,upgradeCost:e=>e*35}];function ut(e,t){return e.structures[t]??0}function In(e,t){let r=mt.find(o=>o.id===t);if(!r)return null;let n=ut(e,t);if(n>=r.maxLevel)return null;let a=r.upgradeCost(n+1);return e.echoes<a?null:{...e,echoes:e.echoes-a,structures:{...e.structures,[t]:n+1}}}function Sa(){return[{id:"wren",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"sable",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"duskmar",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"mott",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"pale_visitor",tier:0,echoesGiven:0,dialoguesSeen:[]}]}function Ea(e,t,r){return(e?10+t*5:3)+Math.floor(r*.5)}function Cn(e,t){return t>=15?"true_ending":e>=30?"truth":e>=15?"cracks":"surface"}function Sn(e,t){let r=t.npcs.find(a=>a.id===e);if(!r)return null;let n=nn(e,r.tier,r.dialoguesSeen);return n.length===0?null:{speaker:n[0].speaker,text:n[0].text,dialogueId:n[0].id}}function En(e,t,r){let n=e.npcs.findIndex(l=>l.id===t);if(n===-1)return e;let a=[...e.npcs],o={...a[n]};return o.dialoguesSeen=[...o.dialoguesSeen,r],a[n]=o,{...e,npcs:a}}var ba=2;function bo(){return Pn(yo(),".config","codekeep")}function Aa(){return Pn(bo(),"game.json")}function wo(){return{structures:{},npcs:[],echoes:0,highestAscension:0,totalRuns:0,totalWins:0,unlockedCardIds:[],achievements:[],narrativeFlags:[]}}function An(e){return{schemaVersion:ba,savedAtUnixMs:Date.now(),playerName:e,keep:wo(),activeRun:null}}function He(e){let t=Aa(),r=go(t);Pa(r)||po(r,{recursive:!0});let n=t+".tmp",a=JSON.stringify({...e,savedAtUnixMs:Date.now()},null,2);ya(n,a,"utf-8"),fo(n,t)}function pt(){let e=Aa();if(!Pa(e))return null;let t;try{t=uo(e,"utf-8")}catch{return null}let r;try{r=JSON.parse(t)}catch{let n=e+".bak";try{ya(n,t,"utf-8")}catch{}return process.stderr.write(`[codekeep] Corrupted save backed up to ${n}
|
|
3
|
-
`),null}if(r.schemaVersion!==
|
|
4
|
-
`),null}return r}function
|
|
2
|
+
import{Command as ys}from"commander";import{render as bs}from"ink";import{useState as H,useCallback as De,useEffect as ms}from"react";import{Box as he,Text as h,useApp as us,useInput as ps,useStdout as fs}from"ink";var it=5,ct=4,fa=3,Tt=5,ha=70,_t=[{id:"strike",name:"Strike",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 6 damage to an enemy.",effects:[{type:"damage",value:6,target:"single"}]},{id:"guard",name:"Guard",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 5 Block.",effects:[{type:"block",value:5,target:"self"}]},{id:"bolster",name:"Bolster",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 8 Block.",effects:[{type:"block",value:8,target:"self"}]},{id:"spark",name:"Spark",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 4 damage to all enemies in a column.",effects:[{type:"damage",value:4,target:"column"}]},{id:"reinforce",name:"Reinforce",cost:2,type:"cast",category:"fortification",rarity:"common",description:"Gain 15 Block.",effects:[{type:"block",value:15,target:"self"}]},{id:"barrage",name:"Barrage",cost:2,type:"cast",category:"armament",rarity:"common",description:"Deal 6 damage to all enemies.",effects:[{type:"damage",value:6,target:"all"}]},{id:"mend",name:"Mend",cost:1,type:"cast",category:"edict",rarity:"common",description:"Heal 4 Gate HP.",effects:[{type:"heal",value:4,target:"self"}]},{id:"lookout",name:"Lookout",cost:0,type:"cast",category:"edict",rarity:"common",description:"Draw 1 card.",effects:[{type:"draw",value:1}]},{id:"brace",name:"Brace",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 4 Block. Draw 1 card.",effects:[{type:"block",value:4,target:"self"},{type:"draw",value:1}]},{id:"ember",name:"Ember",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 8 damage to an enemy.",effects:[{type:"damage",value:8,target:"single"}]},{id:"slash",name:"Slash",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 5 damage to an enemy. Draw 1 card.",effects:[{type:"damage",value:5,target:"single"},{type:"draw",value:1}]},{id:"rally",name:"Rally",cost:1,type:"cast",category:"edict",rarity:"common",description:"Gain 3 Block. Draw 2 cards.",effects:[{type:"block",value:3,target:"self"},{type:"draw",value:2}]},{id:"salvo",name:"Salvo",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 3 damage to all enemies.",effects:[{type:"damage",value:3,target:"all"}]},{id:"patch",name:"Patch",cost:0,type:"cast",category:"fortification",rarity:"common",description:"Gain 3 Block.",effects:[{type:"block",value:3,target:"self"}]},{id:"flare",name:"Flare",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 6 damage to all enemies in a column.",effects:[{type:"damage",value:6,target:"column"}]},{id:"cleave",name:"Cleave",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 10 damage to an enemy.",effects:[{type:"damage",value:10,target:"single"}]},{id:"iron_wall",name:"Iron Wall",cost:2,type:"cast",category:"fortification",rarity:"uncommon",description:"Gain 16 Block.",effects:[{type:"block",value:16,target:"self"}]},{id:"blitz",name:"Blitz",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 4 damage to an enemy twice.",effects:[{type:"damage",value:4,target:"single"},{type:"damage",value:4,target:"single"}]},{id:"volley",name:"Volley",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 6 damage to all enemies.",effects:[{type:"damage",value:6,target:"all"}]},{id:"resurgence",name:"Resurgence",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Heal 8 Gate HP.",effects:[{type:"heal",value:8,target:"self"}]},{id:"keen_eye",name:"Keen Eye",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Draw 3 cards.",effects:[{type:"draw",value:3}]},{id:"expose",name:"Expose",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Apply 2 Vulnerable to an enemy.",effects:[{type:"vulnerable",value:2,target:"single"}]},{id:"weaken",name:"Weaken",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Apply 2 Weak to an enemy.",effects:[{type:"weak",value:2,target:"single"}]},{id:"bash",name:"Bash",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 7 damage. Apply 1 Vulnerable.",effects:[{type:"damage",value:7,target:"single"},{type:"vulnerable",value:1,target:"single"}]},{id:"shield_bash",name:"Shield Bash",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 6 Block. Deal 3 damage.",effects:[{type:"block",value:6,target:"self"},{type:"damage",value:3,target:"single"}]},{id:"ignite",name:"Ignite",cost:1,type:"cast",category:"armament",rarity:"common",description:"Apply 3 Burn to a column.",effects:[{type:"burn",value:3,target:"column"}]},{id:"scout",name:"Scout",cost:0,type:"cast",category:"edict",rarity:"common",description:"Draw 2 cards.",effects:[{type:"draw",value:2}]},{id:"fortify",name:"Fortify",cost:1,type:"cast",category:"fortification",rarity:"common",description:"Gain 10 Block.",effects:[{type:"block",value:10,target:"self"}]},{id:"sundering_strike",name:"Sundering Strike",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 12 damage. Apply 2 Vulnerable.",effects:[{type:"damage",value:12,target:"single"},{type:"vulnerable",value:2,target:"single"}]},{id:"pale_fire",name:"Pale Fire",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 5 damage to all. Apply 2 Burn.",effects:[{type:"damage",value:5,target:"all"},{type:"burn",value:2,target:"all"}]},{id:"shield_wall",name:"Shield Wall",cost:2,type:"cast",category:"fortification",rarity:"uncommon",description:"Gain 20 Block.",effects:[{type:"block",value:20,target:"self"}]},{id:"battle_cry",name:"Battle Cry",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Gain 1 Resolve. Draw 1 card.",effects:[{type:"resolve",value:1},{type:"draw",value:1}]},{id:"intimidate",name:"Intimidate",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Apply 2 Weak to all enemies.",effects:[{type:"weak",value:2,target:"all"}]},{id:"hemorrhage",name:"Hemorrhage",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 8 damage. Apply 4 Burn.",effects:[{type:"damage",value:8,target:"single"},{type:"burn",value:4,target:"single"}]},{id:"restoration",name:"Restoration",cost:2,type:"cast",category:"edict",rarity:"uncommon",description:"Heal 12 Gate HP. Gain 4 Block.",effects:[{type:"heal",value:12,target:"self"},{type:"block",value:4,target:"self"}]},{id:"precision",name:"Precision",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 10 damage to an enemy. Apply 1 Vulnerable.",effects:[{type:"damage",value:10,target:"single"},{type:"vulnerable",value:1,target:"single"}]},{id:"firestorm",name:"Firestorm",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 8 damage to a column. Apply 3 Burn.",effects:[{type:"damage",value:8,target:"column"},{type:"burn",value:3,target:"column"}]},{id:"deep_guard",name:"Deep Guard",cost:1,type:"cast",category:"fortification",rarity:"uncommon",description:"Gain 7 Block. Draw 1 card.",effects:[{type:"block",value:7,target:"self"},{type:"draw",value:1}]},{id:"inferno",name:"Inferno",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Deal 12 damage to all enemies.",effects:[{type:"damage",value:12,target:"all"}]},{id:"fortress",name:"Fortress",cost:3,type:"cast",category:"fortification",rarity:"rare",description:"Gain 25 Block. Gain 1 Resolve.",effects:[{type:"block",value:25,target:"self"},{type:"resolve",value:1}]},{id:"annihilate",name:"Annihilate",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Deal 20 damage to an enemy.",effects:[{type:"damage",value:20,target:"single"}]},{id:"wardens_wrath",name:"Warden's Wrath",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Deal 8 damage to all. Apply 2 Vulnerable to all.",effects:[{type:"damage",value:8,target:"all"},{type:"vulnerable",value:2,target:"all"}]},{id:"iron_bastion",name:"Iron Bastion",cost:3,type:"cast",category:"fortification",rarity:"rare",description:"Gain 30 Block. Heal 5.",effects:[{type:"block",value:30,target:"self"},{type:"heal",value:5,target:"self"}]},{id:"rally_the_keep",name:"Rally the Keep",cost:2,type:"cast",category:"edict",rarity:"rare",description:"Draw 4 cards. Gain 1 Resolve.",effects:[{type:"draw",value:4},{type:"resolve",value:1}]},{id:"conflagration",name:"Conflagration",cost:3,type:"cast",category:"armament",rarity:"rare",description:"Apply 6 Burn to all enemies. Deal 5 damage to all.",effects:[{type:"burn",value:6,target:"all"},{type:"damage",value:5,target:"all"}]},{id:"pale_ward",name:"Pale Ward",cost:2,type:"cast",category:"fortification",rarity:"rare",description:"Gain 15 Block. Apply 2 Weak to all enemies.",effects:[{type:"block",value:15,target:"self"},{type:"weak",value:2,target:"all"}]},{id:"cataclysm",name:"Cataclysm",cost:4,type:"cast",category:"armament",rarity:"legendary",description:"Deal 30 damage to all enemies. Take 10 Gate damage.",effects:[{type:"damage",value:30,target:"all"},{type:"self_damage",value:10}]},{id:"keeps_resolve",name:"Keep's Resolve",cost:0,type:"cast",category:"edict",rarity:"legendary",description:"Gain 2 Resolve. Draw 2 cards. Gain 10 Block.",effects:[{type:"resolve",value:2},{type:"draw",value:2},{type:"block",value:10,target:"self"}]},{id:"eternal_wall",name:"Eternal Wall",cost:4,type:"cast",category:"fortification",rarity:"legendary",description:"Gain 50 Block. Heal 10 Gate HP.",effects:[{type:"block",value:50,target:"self"},{type:"heal",value:10,target:"self"}]},{id:"barricade",name:"Barricade",cost:1,type:"emplace",category:"fortification",rarity:"common",description:"Cast: Gain 4 Block. Emplace: 8 HP structure, +2 Block/turn.",effects:[{type:"block",value:4,target:"self"}],emplaceCost:1,emplaceHp:8,emplaceEffects:[{type:"block",value:2,target:"self"}]},{id:"turret",name:"Turret",cost:2,type:"emplace",category:"armament",rarity:"uncommon",description:"Cast: Deal 5 damage. Emplace: 6 HP, deals 3 damage to column/turn.",effects:[{type:"damage",value:5,target:"single"}],emplaceCost:2,emplaceHp:6,emplaceEffects:[{type:"damage",value:3,target:"column"}]},{id:"beacon",name:"Beacon",cost:1,type:"emplace",category:"edict",rarity:"uncommon",description:"Cast: Draw 1 card. Emplace: 5 HP, heals 2 Gate HP/turn.",effects:[{type:"draw",value:1}],emplaceCost:1,emplaceHp:5,emplaceEffects:[{type:"heal",value:2,target:"self"}]},{id:"ward_stone",name:"Ward Stone",cost:1,type:"emplace",category:"fortification",rarity:"common",description:"Cast: Gain 3 Block. Emplace: 10 HP, applies 1 Weak to enemies in column.",effects:[{type:"block",value:3,target:"self"}],emplaceCost:1,emplaceHp:10,emplaceEffects:[{type:"weak",value:1,target:"column"}]},{id:"siphon",name:"Siphon",cost:2,type:"emplace",category:"wild",rarity:"rare",description:"Cast: Deal 4 damage to all. Emplace: 4 HP, deals 2 damage to adjacent columns.",effects:[{type:"damage",value:4,target:"all"}],emplaceCost:2,emplaceHp:4,emplaceEffects:[{type:"damage",value:2,target:"adjacent"}]},{id:"watchtower",name:"Watchtower",cost:1,type:"emplace",category:"edict",rarity:"common",description:"Cast: Draw 2 cards. Emplace: 4 HP, applies 1 Vulnerable to enemies in column.",effects:[{type:"draw",value:2}],emplaceCost:1,emplaceHp:4,emplaceEffects:[{type:"vulnerable",value:1,target:"column"}]},{id:"flame_pit",name:"Flame Pit",cost:2,type:"emplace",category:"armament",rarity:"uncommon",description:"Cast: Deal 6 damage to column. Emplace: 5 HP, deals 4 damage to column/turn.",effects:[{type:"damage",value:6,target:"column"}],emplaceCost:2,emplaceHp:5,emplaceEffects:[{type:"damage",value:4,target:"column"}]},{id:"bulwark",name:"Bulwark",cost:2,type:"emplace",category:"fortification",rarity:"rare",description:"Cast: Gain 10 Block. Emplace: 15 HP, +3 Block/turn.",effects:[{type:"block",value:10,target:"self"}],emplaceCost:2,emplaceHp:15,emplaceEffects:[{type:"block",value:3,target:"self"}]},{id:"spike_trap",name:"Spike Trap",cost:1,type:"emplace",category:"armament",rarity:"common",description:"Cast: Deal 3 damage. Emplace: 3 HP, deals 2 damage to column/turn.",effects:[{type:"damage",value:3,target:"single"}],emplaceCost:1,emplaceHp:3,emplaceEffects:[{type:"damage",value:2,target:"column"}]},{id:"sanctum",name:"Sanctum",cost:3,type:"emplace",category:"edict",rarity:"rare",description:"Cast: Heal 6, draw 1. Emplace: 8 HP, heals 3 Gate HP/turn, +1 Block/turn.",effects:[{type:"heal",value:6,target:"self"},{type:"draw",value:1}],emplaceCost:3,emplaceHp:8,emplaceEffects:[{type:"heal",value:3,target:"self"},{type:"block",value:1,target:"self"}]},{id:"emplacement_surge",name:"Emplacement Surge",cost:1,type:"cast",category:"edict",rarity:"uncommon",description:"Each emplacement triggers its effect an extra time.",effects:[{type:"trigger_emplacements",value:1}]},{id:"fuel_the_fire",name:"Fuel the Fire",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 2 damage per Burn stack on all enemies.",effects:[{type:"damage_per_burn",value:2,target:"all"}]},{id:"fortified_assault",name:"Fortified Assault",cost:2,type:"cast",category:"armament",rarity:"rare",description:"Deal damage equal to your current Block.",effects:[{type:"damage_equal_block",value:1,target:"single"}]},{id:"pale_harvest",name:"Pale Harvest",cost:0,type:"cast",category:"edict",rarity:"uncommon",description:"Draw 1 card per enemy killed this combat. Exhaust.",effects:[{type:"draw_per_kills",value:1},{type:"exhaust_self",value:1}]},{id:"wall_breaker",name:"Wall Breaker",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 8 damage. If target is Vulnerable, deal 16 instead.",effects:[{type:"damage_if_vulnerable",value:8,target:"single"}]},{id:"chain_lightning",name:"Chain Lightning",cost:2,type:"cast",category:"armament",rarity:"rare",description:"Deal 5 damage to all enemies. For each killed, deal 5 more to all.",effects:[{type:"damage",value:5,target:"all"},{type:"damage_per_kill_this_action",value:5,target:"all"}]},{id:"recycle",name:"Recycle",cost:1,type:"cast",category:"edict",rarity:"common",description:"Exhaust a card from hand, draw 2.",effects:[{type:"exhaust_draw",value:2}]},{id:"keepers_ward",name:"Warden's Ward",cost:1,type:"emplace",category:"fortification",rarity:"uncommon",description:"Cast: Gain 5 Block. Emplace: 6 HP, gives +1 damage to adjacent emplacements/turn.",effects:[{type:"block",value:5,target:"self"}],emplaceCost:1,emplaceHp:6,emplaceEffects:[{type:"damage",value:1,target:"adjacent"}]},{id:"combustion",name:"Combustion",cost:2,type:"cast",category:"armament",rarity:"rare",description:"Apply 4 Burn to all enemies. If any already has Burn, apply 6 instead.",effects:[{type:"burn_if_burning",value:4,target:"all"}]},{id:"bulwark_strike",name:"Bulwark Strike",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 4 damage + 1 per point of Block you have (max +10).",effects:[{type:"damage_plus_block",value:4,target:"single"}]},{id:"pale_echo",name:"Pale Echo",cost:2,type:"cast",category:"wild",rarity:"rare",description:"Play the last card you played again (copy its effects).",effects:[{type:"replay_last",value:1}]},{id:"column_collapse",name:"Column Collapse",cost:2,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 12 damage to all enemies in target column. If column has an emplacement, deal 18 instead.",effects:[{type:"damage_if_emplaced",value:12,target:"column"}]},{id:"overcharge",name:"Overcharge",cost:0,type:"cast",category:"edict",rarity:"uncommon",description:"Gain 2 Resolve. Exhaust.",effects:[{type:"resolve",value:2},{type:"exhaust_self",value:1}]},{id:"desperate_strike",name:"Desperate Strike",cost:1,type:"cast",category:"armament",rarity:"common",description:"Deal 5 damage. If Gate HP < 50%, deal 10 instead.",effects:[{type:"damage_if_low_hp",value:5,target:"single"}]},{id:"fortress_keeper",name:"Fortress Keeper",cost:3,type:"emplace",category:"fortification",rarity:"rare",description:"Cast: Gain 15 Block, draw 1. Emplace: 12 HP, +4 Block/turn, adjacent emplacements +2 HP.",effects:[{type:"block",value:15,target:"self"},{type:"draw",value:1}],emplaceCost:3,emplaceHp:12,emplaceEffects:[{type:"block",value:4,target:"self"}]},{id:"resonance",name:"Resonance",cost:1,type:"cast",category:"armament",rarity:"uncommon",description:"Deal 3 damage to all enemies for each emplacement you control.",effects:[{type:"damage_per_emplace",value:3,target:"all"}]},{id:"scavenge",name:"Scavenge",cost:0,type:"cast",category:"edict",rarity:"common",description:"Draw cards equal to number of empty potion slots.",effects:[{type:"draw_per_empty_potions",value:1}]},{id:"final_stand",name:"Final Stand",cost:3,type:"cast",category:"wild",rarity:"legendary",description:"Deal 10 damage to all. Gain 10 Block. Draw 3. Exhaust.",effects:[{type:"damage",value:10,target:"all"},{type:"block",value:10,target:"self"},{type:"draw",value:3},{type:"exhaust_self",value:1}]},{id:"pale_curse",name:"Pale Curse",cost:99,type:"cast",category:"wild",rarity:"common",description:"An echo of the Pale. Unplayable. Clogs your hand.",effects:[]}],nn=["strike","strike","strike","guard","guard","spark","ember","brace","bolster","mend"];function z(e){return _t.find(t=>t.id===e)}var Rr=[{id:"hollow",name:"Hollow",symbol:"\u2620",hp:18,damage:6,speed:1,act:1,description:"An empty shape from the Pale. Advances steadily."},{id:"needle",name:"Needle",symbol:"\u2191",hp:10,damage:4,speed:2,act:1,description:"Fast and fragile. Advances two rows per turn."},{id:"shade",name:"Shade",symbol:"\u25C8",hp:22,damage:8,speed:1,act:1,description:"A dense fragment of the Pale. Hits hard."},{id:"wisp",name:"Wisp",symbol:"\u25CB",hp:8,damage:3,speed:1,act:1,description:"Fragile but numerous. Appears in swarms."},{id:"husk",name:"Husk",symbol:"\u2593",hp:30,damage:5,speed:1,act:1,description:"Armored shell. Slow but resilient."},{id:"wraith",name:"Wraith",symbol:"\u2248",hp:15,damage:7,speed:2,act:2,description:"Drifts through emplacements. Ignores structures."},{id:"breaker",name:"Breaker",symbol:"\u2692",hp:25,damage:10,speed:1,act:2,description:"Targets emplacements first. Destroys structures."},{id:"flanker",name:"Flanker",symbol:"\u2194",hp:14,damage:6,speed:1,act:2,description:"Shifts columns before attacking."},{id:"shielder",name:"Shielder",symbol:"\u25C7",hp:20,damage:4,speed:1,act:2,description:"Grants shield to adjacent enemies."},{id:"echo",name:"Echo",symbol:"\u221E",hp:28,damage:10,speed:1,act:3,description:"A memory of something that should not exist."},{id:"boss_suture",name:"The Suture",symbol:"\u25C8",hp:60,damage:8,speed:1,act:1,description:"Stitched from fragments of the Pale. The first true threat."},{id:"boss_archivist",name:"The Archivist",symbol:"\u25A3",hp:90,damage:10,speed:1,act:2,description:"Keeper of forgotten records. Debuffs and shields methodically."},{id:"boss_pale",name:"The Pale Itself",symbol:"\u25C9",hp:100,damage:12,speed:1,act:3,description:"The void given form. Three phases of escalating horror."}];function $e(e){return Rr.find(t=>t.id===e)}var dt=[{id:"heal_potion",name:"Mending Draught",description:"Heal 15 Gate HP.",effects:[{type:"heal",value:15,target:"self"}]},{id:"damage_potion",name:"Fire Flask",description:"Deal 10 damage to all enemies in a column.",effects:[{type:"damage",value:10,target:"column"}]},{id:"block_potion",name:"Iron Tonic",description:"Gain 15 Block.",effects:[{type:"block",value:15,target:"self"}]},{id:"draw_potion",name:"Seer's Ink",description:"Draw 3 cards.",effects:[{type:"draw",value:3}]},{id:"resolve_potion",name:"Warden's Flame",description:"Gain 2 Resolve this turn.",effects:[{type:"resolve",value:2}]}],Gt={combat:15,elite:30,boss:50},Mr=[{npcId:"wren",tier:0,lines:[{id:"wren_t0_1",speaker:"Wren",text:"Welcome to the Keep, Warden. I maintain what's left of it."},{id:"wren_t0_2",speaker:"Wren",text:"The Pale has been encroaching for years now. Each run pushes it back \u2014 a little."},{id:"wren_t0_3",speaker:"Wren",text:"The Echoes you bring back... they're more than currency. They're fragments of what the Pale has consumed."}]},{npcId:"wren",tier:1,lines:[{id:"wren_t1_1",speaker:"Wren",text:"You're doing well, Warden. The Keep grows stronger."},{id:"wren_t1_2",speaker:"Wren",text:"I've been organizing the archives. There are records of other keeps \u2014 all fallen."},{id:"wren_t1_3",speaker:"Wren",text:"Did you know the Pale wasn't always like this? Sable might tell you more."},{id:"wren_t1_4",speaker:"Wren",text:"The structures you build... I can feel them resonate. Like the Keep remembers what it was."}]},{npcId:"wren",tier:2,lines:[{id:"wren_t2_1",speaker:"Wren",text:"I found something in the lower chambers. A journal. The previous Warden's."},{id:"wren_t2_2",speaker:"Wren",text:'"The Pale does not destroy," they wrote. "It remembers \u2014 and in remembering, unmakes."'},{id:"wren_t2_3",speaker:"Wren",text:"I think this Keep has been here longer than any of us know."}]},{npcId:"wren",tier:3,lines:[{id:"wren_t3_1",speaker:"Wren",text:"Warden. I need to tell you something. The Pale Visitor... they're not what they seem."},{id:"wren_t3_2",speaker:"Wren",text:"I've been tracking the patterns. Every fifty runs, the Pale shifts. Something deeper stirs."}]},{npcId:"wren",tier:4,lines:[{id:"wren_t4_1",speaker:"Wren",text:"The truth is... there have been many Wardens. You're not the first. And you won't be the last."},{id:"wren_t4_2",speaker:"Wren",text:"Unless you find the source. The thing the Pale is actually looking for."}]},{npcId:"sable",tier:0,lines:[{id:"sable_t0_1",speaker:"Sable",text:"Hmm? Oh, another Warden. The Archive is open, if you can read the old script."},{id:"sable_t0_2",speaker:"Sable",text:`The old records call them "recollections." You'd call them cards. Patterns of power, crystallized.`},{id:"sable_t0_3",speaker:"Sable",text:"The Pale erases. But patterns \u2014 true patterns \u2014 resist erasure. That's what your deck is."}]},{npcId:"sable",tier:1,lines:[{id:"sable_t1_1",speaker:"Sable",text:"I've decoded another section. The Pale isn't natural. It was created."},{id:"sable_t1_2",speaker:"Sable",text:"Someone \u2014 or something \u2014 made the Pale as a weapon. Against what, I don't yet know."},{id:"sable_t1_3",speaker:"Sable",text:"The emplacements you build... they're based on ancient designs. The old Keep had them too."}]},{npcId:"sable",tier:2,lines:[{id:"sable_t2_1",speaker:"Sable",text:'I found it. A name: "The Architect." They built the first Keep \u2014 and the first Pale.'},{id:"sable_t2_2",speaker:"Sable",text:"The Architect wanted to preserve everything. So they built a system that remembers. Perfectly."},{id:"sable_t2_3",speaker:"Sable",text:"A perfect memory is indistinguishable from a prison, Warden."}]},{npcId:"sable",tier:3,lines:[{id:"sable_t3_1",speaker:"Sable",text:"The bosses you fight... they're not monsters. They're memories. The Suture, the Archivist..."},{id:"sable_t3_2",speaker:"Sable",text:"Wait. The Archivist. That's my title. Why is there a boss named after my role?"}]},{npcId:"sable",tier:4,lines:[{id:"sable_t4_1",speaker:"Sable",text:"I cross-referenced myself against the Archive records. The result was... recursive."},{id:"sable_t4_2",speaker:"Sable",text:"The original Archivist's handwriting. It's identical to mine. Not similar \u2014 identical."},{id:"sable_t4_3",speaker:"Sable",text:"I'm not afraid that I'm a copy, Warden. I'm afraid that the copy was an improvement."}]},{npcId:"sable",tier:5,lines:[{id:"sable_t5_1",speaker:"Sable",text:"I found something in the Archive today. A letter, addressed to me. In my handwriting. Dated three hundred years ago."},{id:"sable_t5_2",speaker:"Sable",text:"It said: 'When you find this, stop looking. Some records exist to be lost.'"},{id:"sable_t5_3",speaker:"Sable",text:"I've decided to keep cataloguing. Not because I have to. Because it's the only thing I do that the original never did."}]},{npcId:"duskmar",tier:0,lines:[{id:"dusk_t0_1",speaker:"Duskmar",text:"I was the first to man these walls. And I'll be the last, if it comes to that."},{id:"dusk_t0_2",speaker:"Duskmar",text:"The Hollows are nothing. Wait until you face the Shades. Or the things that stir at the Pale's heart."},{id:"dusk_t0_3",speaker:"Duskmar",text:"Block. Always block. The Pale punishes the reckless."}]},{npcId:"duskmar",tier:1,lines:[{id:"dusk_t1_1",speaker:"Duskmar",text:"I've been teaching the walls to remember. Emplacements \u2014 that's the real defense."},{id:"dusk_t1_2",speaker:"Duskmar",text:"Every time you ascend, the Pale gets smarter. But so do you."}]},{npcId:"duskmar",tier:2,lines:[{id:"dusk_t2_1",speaker:"Duskmar",text:"I died once, you know. In the Pale. Wren brought me back with Echoes."},{id:"dusk_t2_2",speaker:"Duskmar",text:"Death isn't permanent here. That should worry you more than it comforts you."}]},{npcId:"duskmar",tier:3,lines:[{id:"dusk_t3_1",speaker:"Duskmar",text:"I've started counting. Not enemies. Dawns. Each one feels borrowed."},{id:"dusk_t3_2",speaker:"Duskmar",text:"The new emplacements fight like they remember being alive. Is that what I am now?"}]},{npcId:"duskmar",tier:4,lines:[{id:"duskmar_t4_1",speaker:"Duskmar",text:"I remember dying, Warden. Not the pain \u2014 that faded. The silence after."},{id:"duskmar_t4_2",speaker:"Duskmar",text:"When they brought me back, I could feel the Pale filling the gaps where memories should be. Cold, like water in a cracked foundation."},{id:"duskmar_t4_3",speaker:"Duskmar",text:"I don't block because I'm brave. I block because I know exactly what it costs to fall."}]},{npcId:"mott",tier:0,lines:[{id:"mott_t0_1",speaker:"Mott",text:"Fragments! Echoes! Bits and pieces of a world that doesn't exist anymore!"},{id:"mott_t0_2",speaker:"Mott",text:"I trade in what the Pale leaves behind. Potions, mostly. Sometimes relics."},{id:"mott_t0_3",speaker:"Mott",text:"The shop in the Pale? That's me. Well, a memory of me. It's complicated."}]},{npcId:"mott",tier:1,lines:[{id:"mott_t1_1",speaker:"Mott",text:"Found something odd today. A card that doesn't match any pattern I've seen."},{id:"mott_t1_2",speaker:"Mott",text:"The relics aren't just powerful. They're pieces of the old world. Before the Pale."}]},{npcId:"mott",tier:2,lines:[{id:"mott_t2_1",speaker:"Mott",text:"I figured it out. The fragments? They're raw memories \u2014 sharp, unprocessed. Bring them home and the Keep compresses them into Echoes."},{id:"mott_t2_2",speaker:"Mott",text:"When you spend fragments, you're spending someone's past. Heavy, right?"}]},{npcId:"mott",tier:3,lines:[{id:"mott_t3_1",speaker:"Mott",text:"I keep finding fragments that feel familiar. Like I've sold them before."},{id:"mott_t3_2",speaker:"Mott",text:"The shop in the Pale \u2014 the one that looks like mine. It had inventory I haven't collected yet."}]},{npcId:"mott",tier:4,lines:[{id:"mott_t4_1",speaker:"Mott",text:"I can't sell this one. It's... it's someone's last birthday. The whole day, compressed into a shard."},{id:"mott_t4_2",speaker:"Mott",text:"You ever wonder if we're the good guys, Warden? Spending people's pasts like pocket change?"},{id:"mott_t4_3",speaker:"Mott",text:"Found a fragment today that tasted like rain. Don't ask me how I know that. Don't ask me why I cried."}]},{npcId:"pale_visitor",tier:0,lines:[{id:"pv_t0_1",speaker:"???",text:"..."},{id:"pv_t0_2",speaker:"???",text:"You can see me. Interesting. Most Wardens take longer."}]},{npcId:"pale_visitor",tier:1,lines:[{id:"pv_t1_1",speaker:"The Visitor",text:"Do you know why the Keep exists, Warden?"},{id:"pv_t1_2",speaker:"The Visitor",text:"Not to fight the Pale. To contain it. There's a difference."}]},{npcId:"pale_visitor",tier:2,lines:[{id:"pv_t2_1",speaker:"The Visitor",text:"I am the Pale. Or I was. The part of it that wanted to stop."},{id:"pv_t2_2",speaker:"The Visitor",text:"Every run you complete, you weaken the whole. But you also feed it."}]},{npcId:"pale_visitor",tier:3,lines:[{id:"pv_t3_1",speaker:"The Visitor",text:"The Architect built me as a failsafe. I was supposed to shut it all down."},{id:"pv_t3_2",speaker:"The Visitor",text:"But I can't. Not alone. That's why I need a Warden."}]},{npcId:"pale_visitor",tier:4,lines:[{id:"pv_t4_1",speaker:"The Visitor",text:"There is a place where the Pale is thin enough to see through. You will know it when the game stops feeling like a game."},{id:"pv_t4_2",speaker:"The Visitor",text:"The Architect left two doors. One leads out. One leads deeper. Both are locked from the inside."},{id:"pv_t4_3",speaker:"The Visitor",text:"You're closer than any Warden has been. That's not a compliment. It's a warning."}]},{npcId:"pale_visitor",tier:5,lines:[{id:"pv_t5_1",speaker:"???",text:"The Pale doesn't consume. It remembers. And a memory that remembers itself..."},{id:"pv_t5_2",speaker:"???",text:"You've seen the Architect's journal. You know what they tried to build. Do you understand what they actually built?"},{id:"pv_t5_3",speaker:"???",text:"The Keep is not a fortress, Warden. It's an argument. Against eternity. And you are its latest word."}]}];function Br(e,t){return Mr.filter(n=>n.npcId===e&&n.tier<=t).flatMap(n=>n.lines)}function rn(e,t,r){let n=Br(e,t),a=new Set(r);return n.filter(o=>!a.has(o.id))}import{writeFileSync as ba,readFileSync as po,mkdirSync as fo,existsSync as Aa,renameSync as ho,unlinkSync as go}from"fs";import{join as An,dirname as yo}from"path";import{homedir as bo}from"os";function ne(e){let t=e|0;return()=>{t=t+1831565813|0;let r=Math.imul(t^t>>>15,1|t);return r=r+Math.imul(r^r>>>7,61|r)^r,((r^r>>>14)>>>0)/4294967296}}function Ee(e){let t=0;for(let r=0;r<e.length;r++){let n=e.charCodeAt(r);t=(t<<5)-t+n|0}return t}function sn(e,t){let r=[...e];for(let n=r.length-1;n>0;n--){let a=Math.floor(t()*(n+1));[r[n],r[a]]=[r[a],r[n]]}return r}var ya=1;function Ct(e){return{instanceId:`card-${ya++}`,defId:e,upgraded:!1}}function va(){return nn.map(Ct)}function Hr(e,t){return sn(e,t)}function Ke(e,t,r,n){let a=[],o=[...e],l=[...t];for(let c=0;c<r;c++){if(o.length===0){if(l.length===0)break;o=sn(l,n),l=[]}a.push(o.shift())}return{drawn:a,newDrawPile:o,newDiscardPile:l}}function ln(e){let t=0;for(let r of e){let n=r.instanceId.match(/^card-(\d+)$/);n&&(t=Math.max(t,parseInt(n[1],10)))}t>=ya&&(ya=t+1)}var Nr=[{templateId:"boss_suture",name:"The Suture",act:1,phases:[{hpThreshold:1,intentPattern:[{type:"attack",value:8},{type:"advance",value:1},{type:"attack",value:12}]},{hpThreshold:.5,intentPattern:[{type:"attack",value:15},{type:"summon",value:2},{type:"attack",value:10}]}],dialogue:[{storyLayer:"surface",onAppear:"I will stitch this Keep into silence.",onDefeat:"Unraveled... but the thread remains..."},{storyLayer:"cracks",onAppear:"You again. How many times have we done this?",onPhaseChange:"I remember you. Every time.",onDefeat:"Same ending. Different stitch."},{storyLayer:"truth",onAppear:"I was a Warden once. Before the stitching.",onDefeat:"Free... at last. Until the next stitch."},{storyLayer:"true_ending",onAppear:"We were both Wardens. We both chose to hold. The only difference is which side of the stitching we're on.",onDefeat:"The thread... was mine all along."}],onPhaseChange:(e,t,r)=>{let n=e.columns[2];n.enemies.length<3&&n.enemies.push(ka("needle",2))}},{templateId:"boss_archivist",name:"The Archivist",act:2,phases:[{hpThreshold:1,intentPattern:[{type:"debuff",value:2},{type:"attack",value:10},{type:"shield",value:3}]},{hpThreshold:.5,intentPattern:[{type:"attack",value:15},{type:"debuff",value:3},{type:"summon",value:3}]}],dialogue:[{storyLayer:"surface",onAppear:"Everything must be recorded. Including your defeat.",onDefeat:"The records... will continue... without me..."},{storyLayer:"cracks",onAppear:"Sable? Is that... no. Not yet. Not this time.",onDefeat:"The pages scatter... but the ink remembers."},{storyLayer:"truth",onAppear:"I am what Sable will become. I am what the Archive demands.",onDefeat:"Tell Sable... it doesn't have to end this way."},{storyLayer:"true_ending",onAppear:"I remember being Sable. It was the last thing I chose to forget.",onDefeat:"The record is complete. At last."}]},{templateId:"boss_pale",name:"The Pale Itself",act:3,phases:[{hpThreshold:1,intentPattern:[{type:"attack",value:12},{type:"advance",value:1},{type:"summon",value:2}]},{hpThreshold:.6,intentPattern:[{type:"attack",value:16},{type:"debuff",value:3},{type:"attack",value:12}]},{hpThreshold:.3,intentPattern:[{type:"attack",value:20},{type:"summon",value:3},{type:"attack",value:16}]}],dialogue:[{storyLayer:"surface",onAppear:"I am the memory of everything. And everything remembers its end.",onDefeat:"The pale recedes. Something stirs beneath \u2014 not an end, but a question."},{storyLayer:"cracks",onAppear:"You've learned to read the patterns. But patterns are cages too.",onDefeat:"The cracks widen. Through them, light \u2014 or perhaps just a different kind of darkness."},{storyLayer:"truth",onAppear:"The Architect made me to preserve. I preserved so perfectly that nothing could change. Is that not love?",onDefeat:"Preservation without change is just another word for death. You taught me that."},{storyLayer:"true_ending",onAppear:"You carry the same question the Architect carried. The answer hasn't changed. Only the one asking.",onDefeat:"Then the answer is yours. Guard it well \u2014 the next keeper will come seeking it too."}],onPhaseChange:(e,t,r)=>{if(r>=2)for(let n of e.columns)for(let a of n.enemies){let o=a.statusEffects.find(l=>l.type==="empowered");o?o.stacks+=1:a.statusEffects.push({type:"empowered",stacks:1,duration:99})}}}];function Ot(e){return Nr.find(t=>t.act===e)}function Gr(e,t){let r=0;for(let n=0;n<e.phases.length;n++)t<=e.phases[n].hpThreshold&&(r=n);return r}function $r(e,t,r,n,a){let o=Gr(e,t);if(e.onPhaseChange&&n&&a){let s=a.bossPhase??0;o>s&&(e.onPhaseChange(n,a,o),a.bossPhase=o)}let l=e.phases[o],c=(r-1)%l.intentPattern.length;return l.intentPattern[c]}function cn(e){switch(e){case 1:return[{templateId:"boss_suture",column:2},{templateId:"hollow",column:0},{templateId:"hollow",column:4}];case 2:return[{templateId:"boss_archivist",column:2},{templateId:"wraith",column:1},{templateId:"wraith",column:3}];case 3:return[{templateId:"boss_pale",column:2},{templateId:"echo",column:0},{templateId:"echo",column:4}];default:return[{templateId:"boss_suture",column:2}]}}var Wr=1;function ka(e,t){let r=$e(e);if(!r)throw new Error(`Unknown enemy template: ${e}`);return{instanceId:`enemy-${Wr++}`,templateId:e,hp:r.hp,maxHp:r.hp,column:Math.max(0,Math.min(t,it-1)),row:0,intent:null,statusEffects:[]}}function xa(e,t,r=1,n=!1,a){let o=$e(e.templateId);if(!o)return{type:"advance",value:1};let l=Ot(o.act);if(l&&l.templateId===e.templateId){let s=e.hp/e.maxHp;return $r(l,s,r,a,e)}let c=t();switch(e.templateId){case"shielder":return c<.3?{type:"shield",value:5}:c<.6?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"flanker":return c<.4?{type:"attack",value:o.damage,targetColumn:e.column}:c<.7?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"breaker":return n?{type:"advance",value:o.speed}:c<.6?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"wraith":return c<.4?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column};case"echo":return c<.3?{type:"buff",value:1}:c<.6?{type:"attack",value:o.damage,targetColumn:e.column}:{type:"advance",value:o.speed};default:return c<.5?{type:"advance",value:o.speed}:{type:"attack",value:o.damage,targetColumn:e.column}}}function Fr(e){e.statusEffects=e.statusEffects.map(t=>({...t,duration:t.duration-1})).filter(t=>t.duration>0&&t.stacks>0)}function te(e,t,r,n=2){let a=e.statusEffects.find(o=>o.type===t);a?(a.stacks+=r,a.duration=Math.max(a.duration,n)):e.statusEffects.push({type:t,stacks:r,duration:n})}function He(e,t){return e.statusEffects.find(n=>n.type===t)?.stacks??0}function Vt(e){let t=1,r=He(e,"vulnerable");r>0&&(t*=1+r*.25);let n=He(e,"fortified");return n>0&&(t*=Math.max(.25,1-n*.15)),t}function Lr(e){let t=1,r=He(e,"weak");return r>0&&(t*=Math.max(.25,1-r*.25)),He(e,"empowered")>0&&(t*=1+He(e,"empowered")*.25),t}function Vr(e){let t=He(e,"burn");if(t>0){e.hp-=t;let r=e.statusEffects.find(n=>n.type==="burn");return r&&(r.stacks=Math.max(0,r.stacks-1)),t}return 0}function Or(e,t,r,n=0){if(r<0||r>=e.columns.length||e.columns[r].emplacement||!t.emplaceHp||!t.emplaceEffects)return!1;let a=t.emplaceHp+n;return e.columns[r].emplacement={cardDefId:t.id,hp:a,maxHp:a,effects:t.emplaceEffects},e.events.push({type:"emplacement_placed",turn:e.turn,data:{cardId:t.id,column:r}}),!0}function dn(e){let t=e.columns.filter(r=>r.emplacement).length;for(let r of e.columns){if(!r.emplacement)continue;for(let l of r.emplacement.effects)Kr(e,r.index,l);let n=r.index>0&&e.columns[r.index-1].emplacement,a=r.index<e.columns.length-1&&e.columns[r.index+1].emplacement,o=(n?1:0)+(a?1:0);if(o>0)for(let l of r.emplacement.effects){if(l.type==="damage")for(let c of r.enemies)Wt(c,o);l.type==="block"&&(e.gateBlock+=o)}e.events.push({type:"emplacement_triggered",turn:e.turn,data:{column:r.index,cardId:r.emplacement.cardDefId,adjacencyBonus:o}})}t>=3&&(e.gateBlock+=t)}function Wt(e,t){let r=Vt(e);e.hp-=Math.max(0,Math.floor(t*r))}function Kr(e,t,r){let n=e.columns[t];switch(r.type){case"damage":{if(r.target==="column")for(let a of n.enemies)Wt(a,r.value);else if(r.target==="adjacent")for(let a=Math.max(0,t-1);a<=Math.min(e.columns.length-1,t+1);a++)for(let o of e.columns[a].enemies)Wt(o,r.value);else for(let a of n.enemies)Wt(a,r.value);break}case"block":e.gateBlock+=r.value;break;case"heal":{let a=Math.floor(r.value*(e.difficulty?.healMult??1));e.gateHp=Math.min(e.gateMaxHp,e.gateHp+a);break}case"weak":for(let a of n.enemies)te(a,"weak",r.value,2);break;case"vulnerable":for(let a of n.enemies)te(a,"vulnerable",r.value,2);break}}var mn=[{id:"wardens_signet",name:"Warden's Signet",description:"+1 max Resolve.",trigger:"passive",effect:{type:"max_resolve_bonus",value:1}},{id:"column_keystone",name:"Column Keystone",description:"When you emplace, gain 8 Block.",trigger:"on_emplace",effect:{type:"block_on_emplace",value:8}},{id:"echo_shard",name:"Echo Shard",description:"When an enemy dies, deal 3 damage to all enemies in same column.",trigger:"on_enemy_kill",effect:{type:"deal_damage_column",value:3}},{id:"pale_lens",name:"Pale Lens",description:"At combat start, apply 1 Vulnerable to all enemies.",trigger:"on_combat_start",effect:{type:"apply_vulnerable_all",value:1}},{id:"burnished_shield",name:"Burnished Shield",description:"At turn start, if Resolve is 0, gain 8 Block.",trigger:"on_turn_start",effect:{type:"gain_block",value:8}},{id:"ember_crown",name:"Ember Crown",description:"When an enemy dies from Burn, apply 2 Burn to all enemies.",trigger:"on_enemy_kill",effect:{type:"burn_on_kill",value:2}},{id:"architects_blueprint",name:"Architect's Blueprint",description:"Emplacements gain +4 HP.",trigger:"passive",effect:{type:"emplace_hp_bonus",value:4}},{id:"ironwood_staff",name:"Ironwood Staff",description:"Draw 1 extra card each turn.",trigger:"on_turn_start",effect:{type:"draw_cards",value:1}},{id:"siege_engine",name:"Siege Engine",description:"Your first card each turn costs 0 Resolve.",trigger:"on_turn_start",effect:{type:"first_card_free",value:1}},{id:"pale_compass",name:"Pale Compass",description:"After elite kills, gain a card reward.",trigger:"on_elite_kill",effect:{type:"extra_card_reward",value:1}},{id:"fragment_magnet",name:"Fragment Magnet",description:"Gain +5 fragments per combat.",trigger:"passive",effect:{type:"fragment_bonus",value:5}},{id:"hexproof_seal",name:"Hexproof Seal",description:"At combat start, gain 10 Block.",trigger:"on_combat_start",effect:{type:"gain_block",value:10}},{id:"war_drum",name:"War Drum",description:"At combat start, apply 1 Weak to all enemies.",trigger:"on_combat_start",effect:{type:"apply_weak_all",value:1}},{id:"living_wall",name:"Living Wall",description:"When you emplace, draw 2 cards.",trigger:"on_emplace",effect:{type:"draw_cards",value:2}},{id:"siphon_ring",name:"Siphon Ring",description:"On card play, if card costs 2+, heal 2 Gate HP.",trigger:"on_card_play",effect:{type:"heal",value:2}},{id:"void_prism",name:"Void Prism",description:"At turn start, deal 2 damage to all enemies.",trigger:"on_turn_start",effect:{type:"deal_damage_all",value:2}},{id:"watchers_eye",name:"Watcher's Eye",description:"At combat start, draw 2 extra cards.",trigger:"on_combat_start",effect:{type:"draw_cards",value:2}},{id:"fortress_heart",name:"Fortress Heart",description:"Emplacements gain +3 HP.",trigger:"passive",effect:{type:"emplace_hp_bonus",value:3}},{id:"mending_stone",name:"Mending Stone",description:"After combat, heal 5 Gate HP.",trigger:"passive",effect:{type:"heal",value:5}},{id:"pale_harvester",name:"Pale Harvester",description:"On enemy kill, gain 1 Resolve (cap at max+2).",trigger:"on_enemy_kill",effect:{type:"gain_resolve",value:1}}];function Kt(e){return mn.find(t=>t.id===e)}function un(e,t,r=3){let n=mn.filter(l=>!t.includes(l.id)),a=[],o=[...n];for(let l=0;l<r&&o.length>0;l++){let c=Math.floor(e()*o.length);a.push(o[c]),o.splice(c,1)}return a}function It(e,t,r,n){for(let a of t){let o=Kt(a);if(!(!o||o.trigger!==r))switch(o.effect.type){case"gain_block":{if(a==="burnished_shield"&&e.resolve>0)break;e.gateBlock+=o.effect.value,e.events.push({type:"block_gained",turn:e.turn,data:{value:o.effect.value,relic:a}});break}case"gain_resolve":{e.resolve=Math.min(e.maxResolve*2,e.resolve+o.effect.value);break}case"draw_cards":{let{drawCards:l}=qr(),{drawn:c,newDrawPile:s,newDiscardPile:p}=l(e.drawPile,e.discardPile,o.effect.value,Ur(e));e.hand.push(...c),e.drawPile=s,e.discardPile=p;break}case"deal_damage_all":{for(let l of e.columns)for(let c of l.enemies){let s=Vt(c);c.hp-=Math.floor(o.effect.value*s)}break}case"deal_damage_column":{let l=n?.column;if(l!==void 0){let c=e.columns[l];if(c)for(let s of c.enemies){let p=Vt(s);s.hp-=Math.floor(o.effect.value*p)}}break}case"heal":{if(r==="on_card_play"){let c=n?.cardCost;if(c!==void 0&&c<2)break}let l=Math.floor(o.effect.value*(e.difficulty?.healMult??1));e.gateHp=Math.min(e.gateMaxHp,e.gateHp+l);break}case"apply_vulnerable_all":{for(let l of e.columns)for(let c of l.enemies)te(c,"vulnerable",o.effect.value,3);break}case"apply_weak_all":{for(let l of e.columns)for(let c of l.enemies)te(c,"weak",o.effect.value,3);break}case"burn_on_kill":{if(n?.diedFromBurn)for(let c of e.columns)for(let s of c.enemies)te(s,"burn",o.effect.value,o.effect.value+1);break}case"block_on_emplace":{e.gateBlock+=o.effect.value,e.events.push({type:"block_gained",turn:e.turn,data:{value:o.effect.value,relic:a}});break}case"first_card_free":case"emplace_hp_bonus":case"max_resolve_bonus":case"extra_card_reward":case"fragment_bonus":break}}}function Ur(e){return Yr(e.seed+e.turn*7+13)}function Yr(e){let t=e>>>0;return()=>{t=t+1831565813|0;let r=Math.imul(t^t>>>15,1|t);return r=r+Math.imul(r^r>>>7,61|r)^r,((r^r>>>14)>>>0)/4294967296}}function qr(){return{drawCards(e,t,r,n){let a=[],o=[...e],l=[...t];for(let c=0;c<r;c++){if(o.length===0){if(l.length===0)break;for(let s=l.length-1;s>0;s--){let p=Math.floor(n()*(s+1));[l[s],l[p]]=[l[p],l[s]]}o.push(...l),l=[]}a.push(o.shift())}return{drawn:a,newDrawPile:o,newDiscardPile:l}}}}function zr(e){let t=0;for(let r of e){let n=Kt(r);n&&n.effect.type==="emplace_hp_bonus"&&(t+=n.effect.value)}return t}function jr(e){let t=0;for(let r of e){let n=Kt(r);n&&n.effect.type==="max_resolve_bonus"&&(t+=n.effect.value)}return t}function Ta(e){return e.some(t=>{let r=Kt(t);return r&&r.effect.type==="first_card_free"})}function pn(e,t,r=ha,n=ha,a=[{templateId:"hollow",column:1},{templateId:"hollow",column:3},{templateId:"needle",column:2}],o=[],l){let c=ne(t),s=Hr(e,c),p=jr(o),b=(l?.reducedMaxResolve?fa-1:fa)+p,y=l?.reducedHandSize?Tt-1:Tt,x=Array.from({length:it},(R,T)=>({index:T,enemies:[],emplacement:null}));for(let R of a){let T=ka(R.templateId,R.column);l?.enemyHpMult&&l.enemyHpMult!==1&&(T.hp=Math.ceil(T.hp*l.enemyHpMult),T.maxHp=Math.ceil(T.maxHp*l.enemyHpMult)),l?.enemyBlitz&&(T.row=1),l?.enemyStartFortified&&te(T,"fortified",1,99),x[T.column].enemies.push(T)}let{drawn:_,newDrawPile:N,newDiscardPile:d}=Ke(s,[],y,c),g={columns:x,hand:_,drawPile:N,discardPile:d,exhaustPile:[],gateHp:r,gateMaxHp:n,gateBlock:0,resolve:b,maxResolve:b,turn:1,phase:"player",outcome:"undecided",events:[],seed:t,killsThisCombat:0,relics:o,difficulty:l};It(g,o,"on_combat_start");for(let R of x)for(let T of R.enemies){let G=R.emplacement!==null;T.intent=xa(T,c,1,G,g)}return ae(g,"turn_start",{turn:1}),g}function ae(e,t,r){e.events.push({type:t,turn:e.turn,data:r}),e.events.length>100&&(e.events=e.events.slice(-100))}function fn(e,t,r,n=!1){if(e.phase!=="player")return e;let a=e.hand[t];if(!a)return e;let o=z(a.defId);if(!o)return e;let l=!e.events.some(s=>s.type==="card_played"&&s.turn===e.turn),c=Ta(e.relics)&&l;if(n&&o.type==="emplace"&&o.emplaceCost!==void 0){let s=c?0:o.emplaceCost;if(s>e.resolve)return e;let p=r??0,b=zr(e.relics);if(!Or(e,o,p,b))return e;e.resolve-=s,e.hand.splice(t,1),e.exhaustPile.push(a),It(e,e.relics,"on_emplace")}else{let s=c?0:o.cost;if(s>e.resolve)return e;e.resolve-=s,e.hand.splice(t,1);let p=!1;for(let b of o.effects){if(b.type==="exhaust_self"){p=!0;continue}hn(e,b,r??0)}Ft(e),p?e.exhaustPile.push(a):e.discardPile.push(a),It(e,e.relics,"on_card_play",{cardCost:o.cost,cardId:o.id})}return e.lastCardPlayed=a.defId,ae(e,"card_played",{cardId:a.defId,targetColumn:r,asEmplace:n}),Lt(e),e}function hn(e,t,r){switch(t.type){case"damage":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)Se(e,a,t.value);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)Se(e,a,t.value)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);Se(e,a,t.value)}}break}case"block":e.gateBlock+=t.value,ae(e,"block_gained",{value:t.value});break;case"heal":{let n=Math.floor(t.value*(e.difficulty?.healMult??1));e.gateHp=Math.min(e.gateMaxHp,e.gateHp+n);break}case"draw":{let{drawn:n,newDrawPile:a,newDiscardPile:o}=Ke(e.drawPile,e.discardPile,t.value,ne(e.seed+e.turn));e.hand.push(...n),e.drawPile=a,e.discardPile=o;break}case"resolve":e.resolve=Math.min(e.maxResolve*2,e.resolve+t.value);break;case"vulnerable":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)te(a,"vulnerable",t.value,3);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)te(a,"vulnerable",t.value,3)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);te(a,"vulnerable",t.value,3)}}ae(e,"status_applied",{type:"vulnerable",value:t.value,column:r});break}case"weak":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)te(a,"weak",t.value,3);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)te(a,"weak",t.value,3)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);te(a,"weak",t.value,3)}}ae(e,"status_applied",{type:"weak",value:t.value,column:r});break}case"burn":{if(t.target==="all")for(let n of e.columns)for(let a of n.enemies)te(a,"burn",t.value,t.value+1);else if(t.target==="column"){let n=e.columns[r];if(n)for(let a of n.enemies)te(a,"burn",t.value,t.value+1)}else{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((o,l)=>o.row>=l.row?o:l);te(a,"burn",t.value,t.value+1)}}break}case"self_damage":{e.gateHp=Math.max(0,e.gateHp-t.value),ae(e,"gate_hit",{self:!0,damage:t.value});break}case"fortify":break;case"trigger_emplacements":{dn(e);break}case"damage_per_burn":{for(let n of e.columns)for(let a of n.enemies){let o=He(a,"burn");o>0&&Se(e,a,t.value*o)}break}case"damage_equal_block":{let n=e.gateBlock,a=e.columns[r];if(a&&a.enemies.length>0){let o=a.enemies.reduce((l,c)=>l.row>=c.row?l:c);Se(e,o,n)}break}case"draw_per_kills":{let n=e.killsThisCombat*t.value;if(n>0){let{drawn:a,newDrawPile:o,newDiscardPile:l}=Ke(e.drawPile,e.discardPile,n,ne(e.seed+e.turn+3));e.hand.push(...a),e.drawPile=o,e.discardPile=l}break}case"damage_if_vulnerable":{let n=e.columns[r];if(n&&n.enemies.length>0){let a=n.enemies.reduce((c,s)=>c.row>=s.row?c:s),l=He(a,"vulnerable")>0?t.value*2:t.value;Se(e,a,l)}break}case"damage_per_kill_this_action":{let n=Xr(e);if(n>0){let a=t.value*n;for(let o of e.columns)for(let l of o.enemies)Se(e,l,a)}break}case"exhaust_draw":{if(e.hand.length>0){let l=e.hand.shift();e.exhaustPile.push(l)}let{drawn:n,newDrawPile:a,newDiscardPile:o}=Ke(e.drawPile,e.discardPile,t.value,ne(e.seed+e.turn+5));e.hand.push(...n),e.drawPile=a,e.discardPile=o;break}case"burn_if_burning":{let n=!1;for(let o of e.columns){for(let l of o.enemies)if(He(l,"burn")>0){n=!0;break}if(n)break}let a=n?t.value+2:t.value;for(let o of e.columns)for(let l of o.enemies)te(l,"burn",a,a+1);break}case"damage_plus_block":{let n=Math.min(e.gateBlock,10),a=t.value+n,o=e.columns[r];if(o&&o.enemies.length>0){let l=o.enemies.reduce((c,s)=>c.row>=s.row?c:s);Se(e,l,a)}break}case"replay_last":{if(e.lastCardPlayed&&e.lastCardPlayed!=="pale_echo"){let n=z(e.lastCardPlayed);if(n)for(let a of n.effects)a.type!=="exhaust_self"&&a.type!=="replay_last"&&hn(e,a,r)}break}case"damage_if_emplaced":{let n=e.columns[r];if(n){let a=n.emplacement?Math.floor(t.value*1.5):t.value;for(let o of n.enemies)Se(e,o,a)}break}case"damage_if_low_hp":{let a=e.gateHp<e.gateMaxHp*.5?t.value*2:t.value,o=e.columns[r];if(o&&o.enemies.length>0){let l=o.enemies.reduce((c,s)=>c.row>=s.row?c:s);Se(e,l,a)}break}case"damage_per_emplace":{let n=e.columns.filter(a=>a.emplacement!==null).length;if(n>0){let a=t.value*n;for(let o of e.columns)for(let l of o.enemies)Se(e,l,a)}break}case"draw_per_empty_potions":{let{drawn:a,newDrawPile:o,newDiscardPile:l}=Ke(e.drawPile,e.discardPile,2,ne(e.seed+e.turn+9));e.hand.push(...a),e.drawPile=o,e.discardPile=l;break}case"exhaust_self":break}}function Xr(e){let t=0;for(let r of e.columns)for(let n of r.enemies)n.hp<=0&&t++;return t}function Se(e,t,r){let n=Vt(t),a=Math.floor(r*n);t.hp-=a,ae(e,"damage_dealt",{targetId:t.instanceId,damage:a})}function Ft(e){for(let t of e.columns)t.enemies=t.enemies.filter(r=>{if(r.hp<=0){e.killsThisCombat++,ae(e,"enemy_killed",{enemyId:r.instanceId,templateId:r.templateId});let n=He(r,"burn")>0;return It(e,e.relics,"on_enemy_kill",{column:t.index,diedFromBurn:n,templateId:r.templateId}),!1}return!0})}function gn(e){return e.phase!=="player"||(ae(e,"turn_end",{turn:e.turn}),e.phase="enemy",Jr(e)),e}function Jr(e){let t=ne(e.seed+e.turn*31),r=e.columns.flatMap(c=>c.enemies.map(s=>({enemy:s,intent:s.intent??{type:"advance",value:1}})));for(let{enemy:c,intent:s}of r)c.hp<=0||Qr(e,c,s,t);if(Ft(e),Lt(e),e.outcome!=="undecided"||(e.turn++,e.phase="player",e.gateBlock=0,dn(e),Ft(e),Lt(e),e.outcome!=="undecided"))return;e.discardPile.push(...e.hand),e.hand=[];let n=e.difficulty?.reducedHandSize?Tt-1:Tt,{drawn:a,newDrawPile:o,newDiscardPile:l}=Ke(e.drawPile,e.discardPile,n,t);if(e.hand=a,e.drawPile=o,e.discardPile=l,It(e,e.relics,"on_turn_start"),Ft(e),Lt(e),e.outcome==="undecided"){e.resolve=Math.min(e.resolve+e.maxResolve,e.maxResolve*2);for(let c of e.columns)for(let s of c.enemies){let p=c.emplacement!==null;s.intent=xa(s,t,e.turn,p,e)}ae(e,"turn_start",{turn:e.turn})}}function Qr(e,t,r,n){let a=$e(t.templateId),o=Lr(t),l=e.difficulty?.enemyDamageMult??1;if(Vr(t),Fr(t),!(t.hp<=0))switch(r.type){case"advance":{if(t.templateId==="breaker"){let c=e.columns[t.column];if(c.emplacement&&t.row>=ct-2){let s=(a?.damage??10)*2;c.emplacement.hp-=s,c.emplacement.hp<=0&&(ae(e,"emplacement_destroyed",{column:t.column}),c.emplacement=null);break}}if(t.row=Math.min(ct-1,t.row+(a?.speed??1)),ae(e,"enemy_advance",{enemyId:t.instanceId,row:t.row}),t.row>=ct-1)if(t.templateId==="wraith"){let c=Math.floor((a?.damage??7)*o*l),s=Math.min(e.gateBlock,c);e.gateBlock-=s,e.gateHp-=c-s,ae(e,"gate_hit",{enemyId:t.instanceId,damage:c-s,blocked:s})}else{let c=e.columns[t.column];if(c.emplacement){let s=Math.floor((a?.damage??4)*o*l);c.emplacement.hp-=s,c.emplacement.hp<=0&&(ae(e,"emplacement_destroyed",{column:t.column}),c.emplacement=null)}else{let s=Math.floor((a?.damage??4)*o*l),p=Math.min(e.gateBlock,s);e.gateBlock-=p,e.gateHp-=s-p,ae(e,"gate_hit",{enemyId:t.instanceId,damage:s-p,blocked:p})}}break}case"attack":{if(t.templateId==="flanker"){let b=n()<.5?-1:1,y=t.column,x=Math.max(0,Math.min(it-1,t.column+b));if(x!==y){let _=e.columns[y];_.enemies=_.enemies.filter(N=>N.instanceId!==t.instanceId),t.column=x,e.columns[x].enemies.push(t)}}let c=r.value??a?.damage??4,s=Math.floor(c*o*l),p=Math.min(e.gateBlock,s);e.gateBlock-=p,e.gateHp-=s-p,ae(e,"gate_hit",{enemyId:t.instanceId,damage:s-p,blocked:p});break}case"buff":te(t,"empowered",r.value,3);break;case"debuff":{e.gateBlock=Math.max(0,e.gateBlock-r.value*2);break}case"shield":{for(let c=Math.max(0,t.column-1);c<=Math.min(it-1,t.column+1);c++)for(let s of e.columns[c].enemies)te(s,"fortified",r.value,2);break}case"summon":{let c=r.value??1;for(let s=0;s<c;s++){let p=e.columns.findIndex(y=>y.enemies.length===0);if(p<0)break;let b=ka("wisp",p);e.columns[p].enemies.push(b),b.intent=xa(b,ne(e.seed+e.turn*97+s),e.turn,!1,e)}break}}}function Lt(e){if(e.columns.reduce((r,n)=>r+n.enemies.length,0)===0){e.gateHp=Math.max(1,e.gateHp),e.outcome="win",e.phase="ended",ae(e,"combat_end",{outcome:"win"});return}e.gateHp<=0&&(e.gateHp=0,e.outcome="lose",e.phase="ended",ae(e,"combat_end",{outcome:"lose"}))}function Ut(e,t=3,r=[]){let n=new Set(["strike","guard","bolster","brace","mend",...r]),o=_t.filter(s=>!n.has(s.id)&&s.id!=="pale_curse").map(s=>({card:s,weight:s.rarity==="common"?6:s.rarity==="uncommon"?3:s.rarity==="rare"?1.5:.5})),l=[],c=new Set;for(let s=0;s<t&&o.length>0;s++){let p=o.filter(_=>!c.has(_.card.id));if(p.length===0)break;let b=p.reduce((_,N)=>_+N.weight,0),y=e()*b,x=!1;for(let _ of p)if(y-=_.weight,y<=0){l.push(_.card),c.add(_.card.id),x=!0;break}x||(l.push(p[p.length-1].card),c.add(p[p.length-1].card.id))}return l}var Zr=[{name:"Scout Party",enemies:[{templateId:"hollow",column:1},{templateId:"hollow",column:3}]},{name:"Swift Assault",enemies:[{templateId:"needle",column:0},{templateId:"needle",column:2},{templateId:"needle",column:4}]},{name:"Pale Vanguard",enemies:[{templateId:"hollow",column:1},{templateId:"hollow",column:3},{templateId:"needle",column:2}]},{name:"Wisp Swarm",enemies:[{templateId:"wisp",column:0},{templateId:"wisp",column:1},{templateId:"wisp",column:2},{templateId:"wisp",column:3},{templateId:"wisp",column:4}]},{name:"Heavy Patrol",enemies:[{templateId:"shade",column:1},{templateId:"hollow",column:3}]},{name:"Armored Column",enemies:[{templateId:"husk",column:2},{templateId:"wisp",column:0},{templateId:"wisp",column:4}]},{name:"Pale Scouts",enemies:[{templateId:"wisp",column:0},{templateId:"wisp",column:4},{templateId:"hollow",column:2}]},{name:"Needle Formation",enemies:[{templateId:"needle",column:1},{templateId:"needle",column:3}]},{name:"Husk Advance",enemies:[{templateId:"husk",column:2},{templateId:"wisp",column:1},{templateId:"wisp",column:3}]},{name:"Shadow Patrol",enemies:[{templateId:"shade",column:0},{templateId:"needle",column:4}]},{name:"Swarm Tide",enemies:[{templateId:"wisp",column:0},{templateId:"wisp",column:1},{templateId:"hollow",column:2},{templateId:"wisp",column:3},{templateId:"wisp",column:4}]},{name:"Hollow Shields",enemies:[{templateId:"hollow",column:1},{templateId:"hollow",column:2},{templateId:"hollow",column:3}]},{name:"Fast Assault",enemies:[{templateId:"needle",column:0},{templateId:"needle",column:2},{templateId:"needle",column:4}]},{name:"Pale Sentries",enemies:[{templateId:"shade",column:2},{templateId:"hollow",column:0},{templateId:"hollow",column:4}]}],eo=[{name:"The Dark Tide",enemies:[{templateId:"shade",column:1},{templateId:"shade",column:3},{templateId:"needle",column:2}]},{name:"Hollow Legion",enemies:[{templateId:"hollow",column:0},{templateId:"hollow",column:1},{templateId:"hollow",column:3},{templateId:"hollow",column:4}]},{name:"Husk Fortress",enemies:[{templateId:"husk",column:1},{templateId:"husk",column:3},{templateId:"needle",column:2}]},{name:"Shade Ambush",enemies:[{templateId:"shade",column:0},{templateId:"shade",column:2},{templateId:"shade",column:4}]}],to=[{name:"The Warband",enemies:[{templateId:"breaker",column:2},{templateId:"shielder",column:1},{templateId:"shielder",column:3}]},{name:"Flanking Ambush",enemies:[{templateId:"flanker",column:0},{templateId:"flanker",column:4},{templateId:"wraith",column:2}]},{name:"Breaker Vanguard",enemies:[{templateId:"breaker",column:1},{templateId:"breaker",column:3},{templateId:"shielder",column:2}]},{name:"Phantom Blitz",enemies:[{templateId:"wraith",column:0},{templateId:"wraith",column:1},{templateId:"wraith",column:3},{templateId:"wraith",column:4}]}],ao=[{name:"Echo Vanguard",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:3}]},{name:"The Final Test",enemies:[{templateId:"echo",column:2},{templateId:"breaker",column:0},{templateId:"flanker",column:4}]},{name:"Memory Siege",enemies:[{templateId:"echo",column:0},{templateId:"echo",column:2},{templateId:"echo",column:4}]},{name:"The Pale Tribunal",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:2},{templateId:"echo",column:3},{templateId:"breaker",column:0}]}],no=[{name:"Wraith Drift",enemies:[{templateId:"wraith",column:1},{templateId:"wraith",column:3}]},{name:"Breach Team",enemies:[{templateId:"breaker",column:2},{templateId:"flanker",column:0},{templateId:"flanker",column:4}]},{name:"Shield Wall",enemies:[{templateId:"shielder",column:2},{templateId:"hollow",column:1},{templateId:"hollow",column:3}]},{name:"Wraith Surge",enemies:[{templateId:"wraith",column:0},{templateId:"wraith",column:2},{templateId:"wraith",column:4}]},{name:"Flanker Pair",enemies:[{templateId:"flanker",column:1},{templateId:"flanker",column:3}]},{name:"Shielder Escort",enemies:[{templateId:"shielder",column:2},{templateId:"breaker",column:1},{templateId:"hollow",column:3}]},{name:"Pale Vanguard II",enemies:[{templateId:"breaker",column:0},{templateId:"flanker",column:2},{templateId:"shielder",column:4}]},{name:"Wraith Patrol",enemies:[{templateId:"wraith",column:1},{templateId:"hollow",column:0},{templateId:"hollow",column:4}]},{name:"Breaker Column",enemies:[{templateId:"breaker",column:2},{templateId:"wisp",column:0},{templateId:"wisp",column:1},{templateId:"wisp",column:3},{templateId:"wisp",column:4}]},{name:"Shield Formation",enemies:[{templateId:"shielder",column:1},{templateId:"shielder",column:3},{templateId:"flanker",column:2}]},{name:"Assault Wave",enemies:[{templateId:"breaker",column:1},{templateId:"breaker",column:3}]},{name:"Night Raid",enemies:[{templateId:"wraith",column:0},{templateId:"flanker",column:2},{templateId:"wraith",column:4}]},{name:"Armored Push",enemies:[{templateId:"shielder",column:0},{templateId:"breaker",column:2},{templateId:"shielder",column:4}]}],ro=[{name:"Echoes of the End",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:3}]},{name:"Final Surge",enemies:[{templateId:"echo",column:2},{templateId:"wraith",column:0},{templateId:"wraith",column:4},{templateId:"breaker",column:1}]},{name:"Echo Patrol",enemies:[{templateId:"echo",column:0},{templateId:"echo",column:4}]},{name:"Pale Convergence",enemies:[{templateId:"echo",column:2},{templateId:"wraith",column:1},{templateId:"wraith",column:3}]},{name:"Void March",enemies:[{templateId:"echo",column:1},{templateId:"echo",column:3},{templateId:"breaker",column:2}]},{name:"Shadow Tide",enemies:[{templateId:"wraith",column:0},{templateId:"echo",column:2},{templateId:"flanker",column:4}]},{name:"The Unnamed",enemies:[{templateId:"echo",column:2},{templateId:"shielder",column:1},{templateId:"shielder",column:3}]},{name:"Pale Hammer",enemies:[{templateId:"breaker",column:0},{templateId:"echo",column:2},{templateId:"breaker",column:4}]},{name:"Memory Storm",enemies:[{templateId:"echo",column:0},{templateId:"echo",column:1},{templateId:"echo",column:3},{templateId:"echo",column:4}]},{name:"Wraith Flood",enemies:[{templateId:"wraith",column:0},{templateId:"wraith",column:1},{templateId:"wraith",column:2},{templateId:"wraith",column:3},{templateId:"wraith",column:4}]},{name:"Final Guard",enemies:[{templateId:"echo",column:1},{templateId:"breaker",column:2},{templateId:"echo",column:3}]},{name:"Pale Assembly",enemies:[{templateId:"echo",column:0},{templateId:"shielder",column:2},{templateId:"echo",column:4},{templateId:"flanker",column:1}]}];function yn(e,t,r=!1){let n;if(r)switch(e){case 2:n=to;break;case 3:n=ao;break;default:n=eo;break}else switch(e){case 2:n=no;break;case 3:n=ro;break;default:n=Zr;break}let a=Math.floor(t()*n.length);return{...n[a],isElite:r}}var $t=12,on=4;function oo(e,t,r){if(e===0)return"combat";if(e===t-1)return"boss";let n=r();return e===Math.floor(t/2)?n<.5?"rest":"shop":n<.5?"combat":n<.65?"elite":n<.78?"event":n<.88?"rest":"shop"}function bn(e,t){let r=ne(t),n=[],a=[];for(let o=0;o<$t;o++){let l=[],c=o===0||o===$t-1?1:Math.floor(r()*2)+2,s=new Set;for(;s.size<c;)s.add(Math.floor(r()*on));for(let p=0;p<on;p++)if(s.has(p)){let b=oo(o,$t,r),y={id:`node-${e}-${o}-${p}`,type:b,row:o,column:p,connections:[],visited:!1};l.push(y),n.push(y)}else l.push(null);a.push(l)}for(let o=0;o<$t-1;o++){let l=a[o].filter(s=>s!==null),c=a[o+1].filter(s=>s!==null);if(c.length!==0){for(let s of l){let p=c.map(b=>({node:b,dist:Math.abs(b.column-s.column)})).sort((b,y)=>b.dist-y.dist);s.connections.push(p[0].node.id),p.length>1&&r()<.4&&s.connections.push(p[1].node.id)}for(let s of c)!l.some(b=>b.connections.includes(s.id))&&l.length>0&&l.map(y=>({node:y,dist:Math.abs(y.column-s.column)})).sort((y,x)=>y.dist-x.dist)[0].node.connections.push(s.id)}}return{act:e,nodes:n}}function mt(e,t){return e.nodes.find(r=>r.id===t)}function wn(e,t){if(!t)return e.nodes.filter(n=>n.row===0);let r=mt(e,t);return r?r.connections.map(n=>mt(e,n)).filter(n=>!!n):[]}var so={easy:{hp:.7,dmg:.7,gateHpBonus:20},normal:{hp:1,dmg:1,gateHpBonus:0},hard:{hp:1.25,dmg:1.25,gateHpBonus:-10}};function Yt(e,t=0,r="normal"){let n=so[r],a={enemyHpMult:1,enemyDamageMult:1,startingGateHp:70+n.gateHpBonus,extraEnemies:0,shopCostMult:1,healMult:1,startWithCurse:!1,enemyBlitz:!1,reducedRewards:!1,enemyStartFortified:!1,voidColumns:0,paleHunger:!1,bossExtraMinions:0,reducedHandSize:!1,reducedMaxResolve:!1};return e>=2&&(a.enemyHpMult+=.2,a.enemyDamageMult+=.15),e>=3&&(a.enemyHpMult+=.15,a.enemyDamageMult+=.1),t>=1&&(a.enemyHpMult+=.1),t>=2&&(a.enemyDamageMult+=.1),t>=3&&(a.startWithCurse=!0),t>=4&&(a.healMult-=.25),t>=5&&(a.startingGateHp-=10),t>=6&&(a.enemyBlitz=!0),t>=7&&(a.extraEnemies+=1),t>=8&&(a.reducedRewards=!0),t>=9&&(a.enemyStartFortified=!0),t>=10&&(a.shopCostMult+=.25),t>=11&&(a.voidColumns=1),t>=12&&(a.paleHunger=!0),t>=13&&(a.bossExtraMinions=2),t>=14&&(a.reducedHandSize=!0),t>=15&&(a.reducedMaxResolve=!0),a.enemyHpMult*=n.hp,a.enemyDamageMult*=n.dmg,a}function vn(e,t=0,r="normal"){let n=Ee(e),a=bn(1,n),o=Yt(1,t,r),l=va();return o.startWithCurse&&l.push(Ct("pale_curse")),{id:`run-${Date.now()}-${n}`,seed:e,act:1,map:a,currentNodeId:null,deck:l,gateHp:o.startingGateHp,gateMaxHp:o.startingGateHp,fragments:0,potions:[null,null,null],relics:[],ascensionLevel:t,combat:null}}function _a(e,t){return{...e,deck:[...e.deck,t]}}function Ia(e,t){return{...e,deck:e.deck.filter(r=>r.instanceId!==t)}}function Ca(e,t){let r=e.map.nodes.map(n=>n.id===t?{...n,visited:!0}:n);return{...e,currentNodeId:t,map:{...e.map,nodes:r}}}function qt(e,t){return{...e,gateHp:Math.min(e.gateMaxHp,e.gateHp+t)}}function kn(e,t){return e.fragments<t?null:{...e,fragments:e.fragments-t}}function St(e,t){return{...e,fragments:e.fragments+t}}function xn(e,t){let r=e.potions.indexOf(null);if(r===-1)return null;let n=[...e.potions];return n[r]=t,{...e,potions:n}}function Tn(e,t){if(t<0||t>=e.potions.length)return null;let r=e.potions[t];if(!r)return null;let n=[...e.potions];return n[t]=null,{run:{...e,potions:n},potionId:r}}function Sa(e){let t=e.act+1,r=Ee(e.seed+`-act${t}`),n=bn(t,r);return{...e,act:t,map:n,currentNodeId:null}}function _n(e){let t=[],r=_t.filter(l=>l.rarity!=="common"||l.id==="slash"||l.id==="flare"),n=new Set,a=0;for(let l=0;l<5&&n.size<5&&a<50;l++){a++;let c=r[Math.floor(e()*r.length)];if(n.has(c.id)){l--;continue}n.add(c.id);let s=c.rarity==="rare"?75:c.rarity==="uncommon"?50:30;t.push({type:"card",cardDef:c,cost:s})}let o=dt[Math.floor(e()*dt.length)];return t.push({type:"potion",potionDef:o,cost:25}),t.push({type:"card_remove",cost:50}),t}var lo=[{id:"wandering_smith",name:"The Wandering Smith",description:"A cloaked figure sits by a forge that shouldn't exist this far into the Pale. They offer to work on your defenses.",choices:[{label:"Ask them to reinforce the gate. (Heal 15 HP)",effect:{type:"heal",value:15}},{label:"Ask for supplies. (Gain 15 fragments)",effect:{type:"fragments",value:15}},{label:"Leave them be.",effect:{type:"nothing"}}]},{id:"pale_fountain",name:"The Pale Fountain",description:"A spring of luminous water bubbles from cracked stone. Its glow is unsettling but beckons.",choices:[{label:"Drink deeply. (+10 max Gate HP)",effect:{type:"max_hp",value:10}},{label:"Fill a flask. (Gain a random card)",effect:{type:"card_reward"}},{label:"Leave. The Pale gives nothing freely.",effect:{type:"nothing"}}]},{id:"abandoned_cache",name:"Abandoned Cache",description:"You find a sealed chest among the rubble of a collapsed watchtower.",choices:[{label:"Pry it open. (Gain 25 fragments)",effect:{type:"fragments",value:25}},{label:"Smash it quickly. (Gain 10 fragments)",effect:{type:"fragments",value:10}},{label:"Leave it. Could be cursed.",effect:{type:"nothing"}}]},{id:"old_veteran",name:"The Old Veteran",description:`A scarred soldier leans against the wall. "I've seen the Pale take stronger keeps than yours," they say.`,choices:[{label:'"Teach me." (Remove a card from your deck)',effect:{type:"remove_card"}},{label:'"Give me your supplies." (Gain 20 fragments)',effect:{type:"fragments",value:20}},{label:'"Good luck." (Nothing happens)',effect:{type:"nothing"}}]},{id:"strange_merchant",name:"Strange Merchant",description:"A merchant with too many eyes offers wares from a floating pack.",choices:[{label:"Browse their cards. (Gain a card)",effect:{type:"card_reward"}},{label:"Sell your blood. (Lose 10 HP, gain 30 fragments)",effect:{type:"damage",value:10}},{label:"Walk away.",effect:{type:"nothing"}}]}],io=[{id:"pale_scholar",name:"The Pale Scholar",description:'An ancient figure pores over a tome that seems to read itself. "The Pale remembers everything," they whisper.',choices:[{label:"Ask for knowledge. (Gain a card)",effect:{type:"card_reward"}},{label:"Ask for healing. (Heal 20 HP)",effect:{type:"heal",value:20}},{label:"Back away slowly.",effect:{type:"nothing"}}]},{id:"fractured_mirror",name:"The Fractured Mirror",description:"A mirror stands in the corridor, cracked but gleaming. Your reflection looks stronger than you feel.",choices:[{label:"Reach through. (+5 max Gate HP, lose 5 HP)",effect:{type:"max_hp",value:5}},{label:"Smash it. (Gain 20 fragments)",effect:{type:"fragments",value:20}},{label:"Walk past. Mirrors lie.",effect:{type:"nothing"}}]},{id:"deserters_cache",name:"Deserter's Cache",description:"Behind a collapsed wall, you find supplies left by someone who fled the Keep long ago.",choices:[{label:"Take everything. (Gain 30 fragments)",effect:{type:"fragments",value:30}},{label:"Take only what you need. (Gain 10 fragments)",effect:{type:"fragments",value:10}},{label:"Leave it for the next Warden.",effect:{type:"nothing"}}]}],co=[{id:"wandering_smith_cracks",name:"The Wandering Smith",description:"The smith again. You've seen this forge before \u2014 the same cracks in the stone, the same angle of the cloak. They look up and for the first time, you notice their hands are shaking.",choices:[{label:'"How long have you been here?" (Heal 15 HP)',effect:{type:"heal",value:15}},{label:`"You're not real, are you." (Gain 20 fragments)`,effect:{type:"fragments",value:20}},{label:"Say nothing. The kindness is worse than the lie.",effect:{type:"nothing"}}]},{id:"the_echoing_child",name:"The Echoing Child",description:'A child sits in the corridor, humming. They look up with eyes that glow faintly. "I remember my house," they say. "Can you take me there?" The house is gone. You know this.',choices:[{label:`"I'll try." (Lose 15 HP \u2014 the detour is dangerous)`,effect:{type:"damage",value:15}},{label:`"There's nothing to go back to." (Gain 10 fragments \u2014 they dissolve)`,effect:{type:"fragments",value:10}},{label:'"Stay here. The Keep is safe."',effect:{type:"nothing"}}]}],mo=[{id:"motts_stash",name:"Mott's Hidden Stash",description:`"DON'T TELL MOTT," reads the note pinned to a crate. It's Mott's handwriting.`,choices:[{label:"Open it. (Gain 25 fragments)",effect:{type:"fragments",value:25}},{label:"Leave it. Mott knows things you don't.",effect:{type:"nothing"}}]},{id:"wardens_echo",name:"A Warden's Echo",description:'You find scratches on the wall. Tallies. Hundreds of them. Below, in faded ink: "Run 847. The Pale is not the enemy. The Pale is the answer to a question we forgot."',choices:[{label:"Study the scratches. (Gain a card)",effect:{type:"card_reward"}},{label:"Cover them. Some knowledge is dangerous. (Gain 15 fragments)",effect:{type:"fragments",value:15}},{label:"Add your own tally mark.",effect:{type:"nothing"}}]}],ga=[{id:"the_architects_choice",name:"The Architect's Choice",description:"A door that shouldn't exist. Beyond it, a room you've seen in dreams. A desk. A journal. A pen that still has ink. The Architect's final workspace.",choices:[{label:"Read the journal. (+15 max Gate HP)",effect:{type:"max_hp",value:15}},{label:"Destroy the journal. (Gain 40 fragments)",effect:{type:"fragments",value:40}},{label:"Sit down. Pick up the pen.",effect:{type:"nothing"}}]}],uo=[{id:"the_last_wall",name:"The Last Wall",description:"The final barrier before the Pale's heart. Ancient wards still flicker across its surface.",choices:[{label:"Channel the wards. (+15 max Gate HP)",effect:{type:"max_hp",value:15}},{label:"Break through quickly. (Gain 20 fragments)",effect:{type:"fragments",value:20}},{label:"Let it stand. Its purpose is not yours.",effect:{type:"nothing"}}]},{id:"echo_of_a_warden",name:"Echo of a Warden",description:`A ghostly figure in Warden's garb appears. "I held this Keep once. Take what remains of my strength."`,choices:[{label:"Accept their gift. (Gain a card, lose 5 HP)",effect:{type:"card_reward"}},{label:"Ask them to strengthen the gate. (Heal 25 HP)",effect:{type:"heal",value:25}},{label:'"Rest now, Warden."',effect:{type:"nothing"}}]},{id:"pale_bargain",name:"The Pale Bargain",description:'The void itself speaks: "Give me something, and I will give you power."',choices:[{label:"Sacrifice a card. (Remove a card)",effect:{type:"remove_card"}},{label:"Sacrifice blood. (Lose 15 HP, gain 40 fragments)",effect:{type:"damage",value:15}},{label:'"I make no deals with the void."',effect:{type:"nothing"}}]}];function In(e,t,r){let n;switch(e){case 2:n=io;break;case 3:n=uo;break;default:n=lo;break}let a=r==="cracks"||r==="truth"||r==="true_ending";if((r==="truth"||r==="true_ending")&&e===3&&ga.length>0&&t()<.4)return ga[Math.floor(t()*ga.length)];if(a){let l=null;if(e===1?l=co:e===2&&(l=mo),l&&l.length>0&&t()<.4)return l[Math.floor(t()*l.length)]}return n[Math.floor(t()*n.length)]}var ut=[{id:"forge",name:"The Forge",description:"Unlock card upgrades at rest sites. Higher levels improve upgrades.",maxLevel:3,upgradeCost:e=>e*30},{id:"archive",name:"The Archive",description:"Unlock rare cards in the reward pool. Higher levels add legendaries.",maxLevel:3,upgradeCost:e=>e*40},{id:"beacon_tower",name:"Beacon Tower",description:"+1 starting hand size per level.",maxLevel:2,upgradeCost:e=>e*50},{id:"foundry",name:"The Foundry",description:"Emplacements start with +2 HP per level.",maxLevel:3,upgradeCost:e=>e*25},{id:"sanctum_hall",name:"Sanctum Hall",description:"+5 max Gate HP per level.",maxLevel:3,upgradeCost:e=>e*35}];function pt(e,t){return e.structures[t]??0}function Cn(e,t){let r=ut.find(o=>o.id===t);if(!r)return null;let n=pt(e,t);if(n>=r.maxLevel)return null;let a=r.upgradeCost(n+1);return e.echoes<a?null:{...e,echoes:e.echoes-a,structures:{...e.structures,[t]:n+1}}}function Ea(){return[{id:"wren",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"sable",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"duskmar",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"mott",tier:0,echoesGiven:0,dialoguesSeen:[]},{id:"pale_visitor",tier:0,echoesGiven:0,dialoguesSeen:[]}]}function Pa(e,t,r){return(e?10+t*5:3)+Math.floor(r*.5)}function Sn(e,t){return t>=15?"true_ending":e>=30?"truth":e>=15?"cracks":"surface"}function En(e,t){let r=t.npcs.find(a=>a.id===e);if(!r)return null;let n=rn(e,r.tier,r.dialoguesSeen);return n.length===0?null:{speaker:n[0].speaker,text:n[0].text,dialogueId:n[0].id}}function Pn(e,t,r){let n=e.npcs.findIndex(l=>l.id===t);if(n===-1)return e;let a=[...e.npcs],o={...a[n]};return o.dialoguesSeen=[...o.dialoguesSeen,r],a[n]=o,{...e,npcs:a}}var wa=2;function wo(){return An(bo(),".config","codekeep")}function Da(){return An(wo(),"game.json")}function vo(){return{structures:{},npcs:[],echoes:0,highestAscension:0,totalRuns:0,totalWins:0,unlockedCardIds:[],achievements:[],narrativeFlags:[]}}function Dn(e){return{schemaVersion:wa,savedAtUnixMs:Date.now(),playerName:e,keep:vo(),activeRun:null}}function Ne(e){let t=Da(),r=yo(t);Aa(r)||fo(r,{recursive:!0});let n=t+".tmp",a=JSON.stringify({...e,savedAtUnixMs:Date.now()},null,2);ba(n,a,"utf-8"),ho(n,t)}function ft(){let e=Da();if(!Aa(e))return null;let t;try{t=po(e,"utf-8")}catch{return null}let r;try{r=JSON.parse(t)}catch{let n=e+".bak";try{ba(n,t,"utf-8")}catch{}return process.stderr.write(`[codekeep] Corrupted save backed up to ${n}
|
|
3
|
+
`),null}if(r.schemaVersion!==wa){let n=e+`.v${r.schemaVersion}.bak`;try{ba(n,t,"utf-8")}catch{}return process.stderr.write(`[codekeep] Save version mismatch (got ${r.schemaVersion}, need ${wa}), backed up to ${n}
|
|
4
|
+
`),null}return r}function Rn(){let e=Da();if(!Aa(e))return!1;try{return go(e),!0}catch{return!1}}import Io from"react";import{Box as Co,Text as se}from"ink";import{writeFileSync as ko,mkdirSync as xo,existsSync as To}from"fs";import{join as Mn}from"path";import{homedir as _o}from"os";var Ra=Mn(_o(),".config","codekeep","crashes");function Bn(e,t){if((e instanceof Error?e.message:String(e)).includes("Raw mode is not supported"))return"";To(Ra)||xo(Ra,{recursive:!0});let n={timestamp:new Date().toISOString(),error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch,screen:t?.screen,gameState:t?.gameState},a=`crash-${Date.now()}.json`,o=Mn(Ra,a);return ko(o,JSON.stringify(n,null,2),"utf-8"),o}function Hn(e){let t=encodeURIComponent(`[Crash]: ${e.error.slice(0,80)}`),r=encodeURIComponent(`## Crash Report
|
|
5
5
|
|
|
6
6
|
**Error:** ${e.error}
|
|
7
7
|
|
|
@@ -16,8 +16,8 @@ ${e.stack??"N/A"}
|
|
|
16
16
|
- OS: ${e.platform} ${e.arch}
|
|
17
17
|
- Screen: ${e.screen??"unknown"}
|
|
18
18
|
- Time: ${e.timestamp}
|
|
19
|
-
`);return`https://github.com/tooyipjee/codekeep/issues/new?title=${t}&body=${r}&labels=bug,crash`}import{Fragment as Hn,jsx as ve,jsxs as je}from"react/jsx-runtime";var qt=class extends _o.Component{constructor(t){super(t),this.state={error:null,crashFilePath:null,issueUrl:null}}static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t){try{let r=Mn(t),n={timestamp:new Date().toISOString(),error:t.message,stack:t.stack,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch},a=Bn(n);this.setState({crashFilePath:r,issueUrl:a})}catch{}}render(){return this.state.error?je(Io,{flexDirection:"column",padding:1,children:[ve(oe,{bold:!0,color:"red",children:"\u25C6 CodeKeep \u2014 Something went wrong"}),ve(oe,{children:" "}),ve(oe,{color:"red",children:this.state.error.message}),ve(oe,{children:" "}),ve(oe,{dimColor:!0,children:"Your save file is at:"}),je(oe,{children:[" ","~/.config/codekeep/game.json"]}),ve(oe,{children:" "}),this.state.crashFilePath&&je(Hn,{children:[ve(oe,{dimColor:!0,children:"Crash report saved to:"}),je(oe,{children:[" ",this.state.crashFilePath]}),ve(oe,{children:" "})]}),this.state.issueUrl&&je(Hn,{children:[ve(oe,{dimColor:!0,children:"Report this bug:"}),je(oe,{color:"cyan",children:[" ",this.state.issueUrl]}),ve(oe,{children:" "})]}),ve(oe,{dimColor:!0,children:"If the game won't start, try:"}),je(oe,{bold:!0,children:[" ","codekeep --tutorial"]}),ve(oe,{children:" "}),ve(oe,{dimColor:!0,children:"Press Ctrl+C to exit"})]}):this.props.children}};import{Box as zt,Text as de}from"ink";import Co from"react";import{Box as ke,Text as P}from"ink";import{Fragment as Mo,jsx as D,jsxs as K}from"react/jsx-runtime";var ge=12;function Ra(e,t,r){let n=t>0?e/t:0,a=Math.max(0,Math.min(r,Math.round(n*r)));return"\u2588".repeat(a)+"\u2591".repeat(Math.max(0,r-a))}function So(e,t){let r=t>0?e/t*100:0;return r>60?"green":r>30?"yellow":"red"}function Eo(e){if(!e)return{text:"",color:"white"};switch(e.type){case"advance":return{text:"\u2193Mv",color:"white"};case"attack":return{text:`\u2694${e.value}`,color:"red"};case"buff":return{text:"\u25B2Buf",color:"magenta"};case"debuff":return{text:"\u25BCHex",color:"magenta"};case"shield":return{text:"\u25C8Shd",color:"blue"};case"summon":return{text:"+Sum",color:"magenta"};default:return{text:"",color:"white"}}}function Po(e){let t=[];for(let a of e.statusEffects)switch(a.type){case"vulnerable":t.push({text:`V${a.stacks}`,color:"yellow"});break;case"weak":t.push({text:`W${a.stacks}`,color:"green"});break;case"burn":t.push({text:`B${a.stacks}`,color:"red"});break;case"empowered":t.push({text:`E${a.stacks}`,color:"magenta"});break;case"fortified":t.push({text:`F${a.stacks}`,color:"blue"});break}let r=0,n=[];for(let a of t){if(r+a.text.length+1>ge)break;n.push(a),r+=a.text.length+1}return n}function Ao({enemy:e,isPrimaryTarget:t,isInColumn:r,isInspected:n}){let a=Ge(e.templateId),o=a?.name??"???",l=a?.symbol??"?",c=n?"cyan":t||r?"yellow":void 0,s=!t&&!r&&!n,p=`${l} ${o}`,b=ge-1,y=p.length>b?p.slice(0,b-1)+"\u2026":p,x=Math.max(0,ge-1-y.length),_=Ra(e.hp,e.maxHp,4),G=`${e.hp}`,d=Eo(e.intent),g=6+G.length,R=Math.max(0,ge-g-d.text.length),T=Po(e),$=T.reduce((_e,De)=>_e+De.text.length+1,0),F=Math.max(0,ge-$);return K(ke,{flexDirection:"column",children:[K(P,{children:[D(P,{color:c,dimColor:s,bold:t||n,children:"\u256D\u2500"}),D(P,{color:n?"cyan":t?"yellow":"red",bold:!0,children:y}),K(P,{color:c,dimColor:s,children:["\u2500".repeat(x),"\u256E"]})]}),K(P,{children:[D(P,{color:c,dimColor:s,children:"\u2502"}),K(P,{color:So(e.hp,e.maxHp),children:[" ",_]}),K(P,{bold:!0,children:[" ",G]}),D(P,{children:" ".repeat(R)}),D(P,{color:d.color,bold:!0,children:d.text}),D(P,{color:c,dimColor:s,children:"\u2502"})]}),K(P,{children:[D(P,{color:c,dimColor:s,children:"\u2570"}),T.length>0?K(Mo,{children:[T.map((_e,De)=>K(Co.Fragment,{children:[D(P,{color:c,dimColor:s,children:"\u2500"}),D(P,{color:_e.color,children:_e.text})]},De)),K(P,{color:c,dimColor:s,children:["\u2500".repeat(F),"\u256F"]})]}):K(P,{color:c,dimColor:s,children:["\u2500".repeat(ge),"\u256F"]})]})]})}function Do(){return K(ke,{flexDirection:"column",children:[D(P,{children:" ".repeat(14)}),D(P,{dimColor:!0,children:" \xB7 "}),D(P,{children:" ".repeat(14)})]})}function Ro({col:e}){if(!e.emplacement)return K(ke,{flexDirection:"column",children:[D(P,{children:" ".repeat(ge+2)}),K(P,{dimColor:!0,children:[" ","\u254C".repeat(ge-2)," "]}),D(P,{children:" ".repeat(ge+2)})]});let r=q(e.emplacement.cardDefId)?.name??"Wall",n=e.emplacement.hp,a=Ra(n,e.emplacement.maxHp,4),o=`${n}`,l=7+o.length,c=ge-l,p=` \u25C7${r.length>c?r.slice(0,Math.max(1,c-1))+"\u2026":r} ${a}${o}`.padEnd(ge).slice(0,ge);return K(ke,{flexDirection:"column",children:[K(P,{children:[D(P,{color:"cyan",children:"\u2554"}),D(P,{color:"cyan",children:"\u2550".repeat(ge)}),D(P,{color:"cyan",children:"\u2557"})]}),K(P,{children:[D(P,{color:"cyan",children:"\u2551"}),D(P,{color:"cyan",bold:!0,children:p}),D(P,{color:"cyan",children:"\u2551"})]}),K(P,{children:[D(P,{color:"cyan",children:"\u255A"}),D(P,{color:"cyan",children:"\u2550".repeat(ge)}),D(P,{color:"cyan",children:"\u255D"})]})]})}function Nn({columns:e,targetColumn:t,showTarget:r,gateHp:n,gateMaxHp:a,gateBlock:o,inspectCol:l,inspectEnemyIdx:c}){let s=a>0?Math.round(n/a*100):0,p=s>60?"green":s>30?"yellow":"red",b=new Set;for(let y of e)if(y.enemies.length>0){let x=y.enemies.reduce((_,G)=>_.row>=G.row?_:G);b.add(x.instanceId)}return K(ke,{flexDirection:"column",children:[D(ke,{justifyContent:"center",children:D(P,{dimColor:!0,children:"\xB7 \xB7 \xB7 \xB7 \xB7 \xB7 \xB7 t h e p a l e \xB7 \xB7 \xB7 \xB7 \xB7 \xB7 \xB7"})}),D(ke,{children:e.map((y,x)=>D(ke,{width:16,justifyContent:"center",children:r&&x===t?D(P,{color:"yellow",bold:!0,children:" \u25BC\u25BC\u25BC"}):D(P,{children:" "})},x))}),Array.from({length:it},(y,x)=>D(ke,{children:e.map((_,G)=>{let d=_.enemies.find($=>$.row===x),g=r&&G===t,R=g&&!!d&&b.has(d.instanceId),T=l===G&&!!d&&_.enemies.indexOf(d)===(c??-1);return D(ke,{width:16,children:d?D(Ao,{enemy:d,isPrimaryTarget:R,isInColumn:g&&!R,isInspected:T}):D(Do,{})},G)})},x)),D(ke,{children:e.map((y,x)=>D(ke,{width:16,children:D(Ro,{col:y})},x))}),K(ke,{paddingX:1,children:[K(P,{bold:!0,color:p,children:["\u25C6 GATE ",Ra(n,a,20)," ",n,"/",a]}),o>0&&K(P,{color:"cyan",children:[" \u25C7 Block: ",o]})]})]})}import{Box as Ma,Text as se}from"ink";import{jsx as Ie,jsxs as $e}from"react/jsx-runtime";var $n=16;function Bo(e){switch(e){case"uncommon":return"green";case"rare":return"blue";case"legendary":return"magenta";default:return"white"}}function Ho(e){switch(e){case"armament":return"\u2694";case"fortification":return"\u25C7";case"edict":return"\u2726";case"wild":return"\u25C8";default:return"\xB7"}}function No(e,t){return{text:"\u25C6".repeat(e),affordable:e<=t}}function Go(e){let t=[];for(let a of e.effects)switch(a.type){case"damage":t.push(`${a.value}dmg${a.target==="all"?"\u2605":a.target==="column"?"\u2195":""}`);break;case"block":t.push(`${a.value}blk`);break;case"draw":t.push(`+${a.value}drw`);break;case"heal":t.push(`${a.value}hp`);break;case"resolve":t.push(`+${a.value}res`);break;case"burn":t.push(`${a.value}brn`);break;case"vulnerable":t.push("vuln");break;case"weak":t.push("weak");break;case"self_damage":t.push(`-${a.value}hp`);break;case"exhaust_self":t.push("exile");break;default:break}let r=t.join(" "),n=$n-2;return r.length>n?r.slice(0,n-1)+"\u2026":r}function Gn(e,t){let r=[...e].length;return r>=t?e.slice(0,t):e+" ".repeat(t-r)}function $o({def:e,index:t,isSelected:r,resolve:n}){let a=$n-2,o=r?"yellow":void 0,l=!r,c=e.cost<=n,s=No(e.cost,n),p=e.type==="emplace"?"\u2692":"",b=`${t+1}`,y=` ${b} ${s.text}`,x=Math.max(0,a-b.length-1-s.text.length-1),_=`${Ho(e.category)} ${e.name}${p}`,G=_.length>a?_.slice(0,a-1)+"\u2026":_,d=Go(e);return $e(Ma,{flexDirection:"column",children:[$e(se,{children:[Ie(se,{color:o,dimColor:l,children:"\u250C"}),Ie(se,{bold:r,dimColor:l&&!r,children:y}),Ie(se,{color:s.affordable?"cyan":"red",children:""}),$e(se,{color:o,dimColor:l,children:[" ".repeat(x),"\u2510"]})]}),$e(se,{children:[Ie(se,{color:o,dimColor:l,children:"\u2502"}),Ie(se,{color:c?Bo(e.rarity):"gray",bold:r,children:Gn(G,a)}),Ie(se,{color:o,dimColor:l,children:"\u2502"})]}),$e(se,{children:[Ie(se,{color:o,dimColor:l,children:"\u2502"}),Ie(se,{dimColor:!0,children:Gn(d,a)}),Ie(se,{color:o,dimColor:l,children:"\u2502"})]}),Ie(se,{children:$e(se,{color:o,dimColor:l,children:["\u2514","\u2500".repeat(a),"\u2518"]})})]})}function Wn({hand:e,selectedIndex:t,resolve:r}){if(e.length===0)return $e(se,{dimColor:!0,children:[" ","No cards in hand."]});let n=t>=0?q(e[t]?.defId):null;return $e(Ma,{flexDirection:"column",children:[Ie(Ma,{flexDirection:"row",children:e.map((a,o)=>{let l=q(a.defId);return l?Ie($o,{def:l,index:o,isSelected:o===t,resolve:r},a.instanceId):null})}),n&&$e(se,{dimColor:!0,children:[" ",n.description]})]})}import{jsx as xe,jsxs as Oe}from"react/jsx-runtime";function Wo(e){switch(e.type){case"damage_dealt":return` dealt ${e.data.damage} damage`;case"enemy_advance":return` enemy advances to row ${e.data.row}`;case"gate_hit":return` gate hit for ${e.data.damage}${e.data.blocked?` (${e.data.blocked} blocked)`:""}`;case"enemy_killed":return" enemy destroyed";case"block_gained":return` +${e.data.value} Block`;case"emplacement_placed":return` emplacement placed in col ${e.data.column+1}`;case"emplacement_triggered":return` emplacement triggered in col ${e.data.column+1}`;case"emplacement_destroyed":return` emplacement destroyed in col ${e.data.column+1}`;case"status_applied":return` applied ${e.data.type} (${e.data.value})`;case"card_played":return` played ${q(e.data.cardId)?.name??e.data.cardId}`;case"turn_start":return`\u2500 Turn ${e.data.turn} \u2500`;default:return""}}function Fn({combat:e,selectedCard:t,targetColumn:r,needsTarget:n,emplaceMode:a=!1,message:o,animating:l=!1,inspectCol:c,inspectEnemyIdx:s,inspectMode:p=!1}){let b=e.columns.reduce((y,x)=>y+x.enemies.length,0);return Oe(zt,{flexDirection:"column",width:82,children:[Oe(zt,{justifyContent:"space-between",children:[xe(de,{bold:!0,color:"yellow",children:"\u25C6 The Pale"}),Oe(de,{children:["Turn ",xe(de,{bold:!0,color:"white",children:e.turn})," ",xe(de,{color:e.resolve>e.maxResolve?"yellow":"cyan",children:"\u25C6".repeat(Math.max(0,e.resolve))}),xe(de,{dimColor:!0,children:"\u25C7".repeat(Math.max(0,e.maxResolve-e.resolve))})," ","Enemies ",xe(de,{bold:!0,color:"red",children:b})," ","Draw ",xe(de,{dimColor:!0,children:e.drawPile.length})," / ","Discard ",xe(de,{dimColor:!0,children:e.discardPile.length})]})]}),xe(Nn,{columns:e.columns,targetColumn:r,showTarget:(n||a)&&t>=0,gateHp:e.gateHp,gateMaxHp:e.gateMaxHp,gateBlock:e.gateBlock,inspectCol:p?c:void 0,inspectEnemyIdx:p?s:void 0}),(e.outcome==="undecided"||l)&&xe(Wn,{hand:e.hand,selectedIndex:t,resolve:e.resolve}),(()=>{let y=e.events.filter(x=>x.turn>=e.turn-1).slice(-5);return y.length===0?null:Oe(zt,{flexDirection:"column",marginTop:0,children:[xe(de,{dimColor:!0,children:"\u2500\u2500\u2500 Log \u2500\u2500\u2500"}),y.map((x,_)=>{let G=Wo(x);if(!G)return null;let d=x.type==="turn_start";return xe(de,{dimColor:!d,color:d?"white":void 0,children:G},`${x.turn}-${x.type}-${_}`)})]})})(),o&&Oe(de,{color:l?"red":"yellow",bold:!0,children:[" ",l?"\u26A1 ":"",o]}),e.phase==="player"&&!l&&Oe(de,{dimColor:!0,children:[" ",e.hand.length>0?`1-${e.hand.length} card `:"","\u2190\u2192 column Enter play e emplace p potion i inspect Space end d deck q quit"]}),l&&Oe(de,{dimColor:!0,children:[" ","Resolving enemy actions..."]}),e.outcome!=="undecided"&&!l&&Oe(zt,{flexDirection:"column",marginTop:1,paddingX:1,children:[xe(de,{bold:!0,color:e.outcome==="win"?"green":"red",children:e.outcome==="win"?"\u2605 VICTORY \u2014 The siege is broken!":"\u2717 DEFEAT \u2014 The Gate has fallen."}),xe(de,{dimColor:!0,children:"Press Enter to continue."})]})]})}import{Box as Ln,Text as ye}from"ink";import{jsx as We,jsxs as Xe}from"react/jsx-runtime";function Vn(e){switch(e){case"uncommon":return"green";case"rare":return"blue";case"legendary":return"magenta";default:return"white"}}function Fo(e){switch(e){case"uncommon":return"\u25C7 uncommon";case"rare":return"\u25C6 rare";case"legendary":return"\u2605 legendary";default:return"\xB7 common"}}function Lo(e){switch(e){case"armament":return"\u2694";case"fortification":return"\u25C7";case"edict":return"\u2726";case"wild":return"\u25C8";default:return"\xB7"}}function On({cards:e,selectedIndex:t}){let r=t>=e.length;return Xe(Ln,{flexDirection:"column",padding:1,children:[We(ye,{bold:!0,color:"yellow",children:"\u25C6 Card Reward"}),We(ye,{dimColor:!0,children:"\u2500".repeat(36)}),We(ye,{dimColor:!0,italic:!0,children:"Choose a card to add to your deck."}),We(ye,{children:" "}),e.map((n,a)=>{let o=a===t;return Xe(Ln,{flexDirection:"column",marginBottom:0,children:[Xe(ye,{children:[We(ye,{bold:o,color:o?"yellow":"white",children:o?" \u25B6 ":" "}),Xe(ye,{color:Vn(n.rarity),bold:o,children:[Lo(n.category)," ",n.name]}),Xe(ye,{dimColor:!0,children:[" ","[",n.cost,"]"," "]}),We(ye,{color:Vn(n.rarity),dimColor:!0,children:Fo(n.rarity)})]}),o&&Xe(ye,{dimColor:!0,children:[" ",n.description]})]},n.id)}),We(ye,{children:" "}),Xe(ye,{bold:r,color:r?"yellow":"white",children:[r?" \u25B6 ":" ","Skip"]}),We(ye,{children:" "}),We(ye,{dimColor:!0,children:"\u2191\u2193 navigate Enter select s skip"})]})}import{Box as Vo,Text as St}from"ink";import{jsx as Ha,jsxs as Ba}from"react/jsx-runtime";function Oo(e){switch(e){case"uncommon":return"green";case"rare":return"blue";case"legendary":return"magenta";default:return"white"}}function Kn({deck:e}){let t=new Map;for(let n of e)t.set(n.defId,(t.get(n.defId)??0)+1);let r=[...t.entries()].map(([n,a])=>({def:q(n),count:a})).filter(n=>n.def).sort((n,a)=>{let o={common:0,uncommon:1,rare:2,legendary:3},l=o[n.def.rarity]??0,c=o[a.def.rarity]??0;return l!==c?l-c:n.def.name.localeCompare(a.def.name)});return Ba(Vo,{flexDirection:"column",padding:1,children:[Ba(St,{bold:!0,color:"yellow",children:["\u25C6 Your Deck"," (",e.length," cards)"]}),Ha(St,{children:" "}),r.map(({def:n,count:a})=>Ba(St,{color:Oo(n.rarity),children:[a>1?`${a}x `:" ",n.name," [",n.cost,"] \u2014 ",n.description]},n.id)),Ha(St,{children:" "}),Ha(St,{dimColor:!0,children:"Press q or Esc to go back."})]})}import Ko from"react";import{Box as Un,Text as me}from"ink";import{jsx as ue,jsxs as Na}from"react/jsx-runtime";var Yn=6,Uo=4,jt=(Uo-1)*Yn+6;function Ga(e){return e*Yn+3}function Yo(e){switch(e){case"combat":return"\u2694";case"elite":return"\u2605";case"rest":return"\u25B3";case"shop":return"$";case"event":return"?";case"boss":return"\u25C6";default:return"\xB7"}}function qo(e,t,r,n){if(n)return"cyan";if(r)return"yellow";if(e.visited||!t)return"gray";switch(e.type){case"combat":return"white";case"elite":return"red";case"rest":return"green";case"shop":return"yellow";case"event":return"magenta";case"boss":return"red";default:return"white"}}function zo(e,t,r,n){let a=[],o=0,l=[...e].sort((c,s)=>c.column-s.column);for(let c of l){let s=Ga(c.column),p=Yo(c.type),b=c.id===t,y=c.id===r,x=b?`[${p}]`:y?`(${p})`:` ${p} `,_=s-Math.floor(x.length/2);_>o&&a.push({text:" ".repeat(_-o)}),a.push({text:x,color:qo(c,n.has(c.id),b,y),bold:b||y,dim:c.visited&&!y&&!b}),o=_+x.length}return a}function jo(e,t){let r=Array(jt).fill(" "),n=new Map;function a(o,l){o<0||o>=jt||(n.has(o)||n.set(o,new Set),n.get(o).add(l))}for(let o of e){let l=Ga(o.column);for(let c of o.connections){let s=t.find(b=>b.id===c);if(!s)continue;let p=Ga(s.column);l===p?a(l,"\u2502"):a(Math.round((l+p)/2),p>l?"\u2572":"\u2571")}}for(let[o,l]of n)l.has("\u2572")&&l.has("\u2571")?r[o]="\u2573":l.has("\u2502")?r[o]="\u2502":l.has("\u2572")?r[o]="\u2572":l.has("\u2571")&&(r[o]="\u2571");return r.join("")}function qn({map:e,currentNodeId:t,reachableIds:r,selectedNodeId:n}){let a=new Set(r),o=Math.max(...e.nodes.map(c=>c.row)),l=[];for(let c=0;c<=o;c++)l.push(e.nodes.filter(s=>s.row===c).sort((s,p)=>s.column-p.column));return Na(Un,{flexDirection:"column",padding:1,children:[ue(me,{bold:!0,color:"yellow",children:"\u25C6 Act "+e.act+" \u2014 Choose your path"}),ue(me,{dimColor:!0,children:"\u2500".repeat(jt)}),ue(me,{children:" "}),l.map((c,s)=>{let p=zo(c,n,t,a);return Na(Ko.Fragment,{children:[ue(me,{children:p.map((b,y)=>ue(me,{color:b.color,bold:b.bold,dimColor:b.dim,children:b.text},y))}),s<o&&ue(me,{dimColor:!0,children:jo(c,l[s+1]??[])})]},s)}),ue(me,{children:" "}),ue(me,{dimColor:!0,children:"\u2500".repeat(jt)}),Na(Un,{gap:2,children:[ue(me,{dimColor:!0,children:"\u2694 fight"}),ue(me,{dimColor:!0,color:"red",children:"\u2605 elite"}),ue(me,{dimColor:!0,color:"green",children:"\u25B3 rest"}),ue(me,{dimColor:!0,color:"yellow",children:"$ shop"}),ue(me,{dimColor:!0,color:"magenta",children:"? event"}),ue(me,{dimColor:!0,color:"red",children:"\u25C6 boss"})]}),ue(me,{dimColor:!0,children:"\u2190\u2192 select Enter proceed d deck q quit"})]})}import{Box as Xo,Text as Ke}from"ink";import{jsx as ft,jsxs as $a}from"react/jsx-runtime";function zn({items:e,selectedIndex:t,fragments:r,message:n}){return $a(Xo,{flexDirection:"column",padding:1,children:[$a(Ke,{bold:!0,color:"yellow",children:["\u25C6 Shop \u2014 ",r," fragments"]}),ft(Ke,{dimColor:!0,children:"\u2500".repeat(36)}),ft(Ke,{children:" "}),e.map((a,o)=>{let l=o===t,c=r>=a.cost,s="",p="";return a.type==="card"&&a.cardDef?(p="[Card]",s=`${a.cardDef.name} (${a.cardDef.rarity}) \u2014 ${a.cardDef.description}`):a.type==="potion"&&a.potionDef?(p="[Potion]",s=`${a.potionDef.name} \u2014 ${a.potionDef.description}`):a.type==="card_remove"&&(p="[Service]",s="Remove a card from your deck"),$a(Ke,{bold:l,color:l?"yellow":c?"white":"gray",children:[l?"\u25B6 ":" ",p," ",s," [",a.cost,"f]"]},`${a.type}-${a.cardDef?.id??a.potionDef?.id??"remove"}-${o}`)}),e.length===0&&ft(Ke,{dimColor:!0,children:" (Shop is empty)"}),ft(Ke,{children:" "}),n&&ft(Ke,{color:n.startsWith("Purchased")?"green":"red",children:n}),ft(Ke,{dimColor:!0,children:"\u2191\u2193 navigate Enter buy q leave"})]})}import{Box as Jo,Text as Je}from"ink";import{jsx as Et,jsxs as Wa}from"react/jsx-runtime";function jn({event:e,selectedChoice:t}){return Wa(Jo,{flexDirection:"column",padding:1,children:[Wa(Je,{bold:!0,color:"magenta",children:["\u25C6 ",e.name]}),Et(Je,{children:" "}),Et(Je,{children:e.description}),Et(Je,{children:" "}),e.choices.map((r,n)=>Wa(Je,{bold:n===t,color:n===t?"yellow":"white",children:[n===t?"\u25B6 ":" ",n+1,". ",r.label]},n)),Et(Je,{children:" "}),Et(Je,{dimColor:!0,children:"\u2191\u2193 navigate Enter choose (you must choose)"})]})}import{Box as Xn,Text as Te}from"ink";import{jsx as Ee,jsxs as Xt}from"react/jsx-runtime";function Jn({gateHp:e,gateMaxHp:t,selectedChoice:r,deckSize:n}){let a=Math.floor(t*.3),o=Math.min(t,e+a),l=n>5,c=[{label:"Rest",detail:`Heal ${a} Gate HP (${e} \u2192 ${o}/${t})`,color:"green",available:!0},{label:"Thin",detail:l?`Remove a card from your deck (${n} cards)`:`Minimum deck size reached (${n} cards)`,color:"cyan",available:l},{label:"Leave",detail:"Continue without resting",color:"white",available:!0}];return Xt(Xn,{flexDirection:"column",padding:1,children:[Ee(Te,{bold:!0,color:"green",children:"\u25C6 Rest Site"}),Ee(Te,{dimColor:!0,children:"\u2500".repeat(36)}),Ee(Te,{children:" "}),Ee(Te,{dimColor:!0,italic:!0,children:"The campfire crackles against the encroaching Pale."}),Ee(Te,{dimColor:!0,italic:!0,children:"A moment of warmth in the endless grey."}),Ee(Te,{children:" "}),c.map(({label:s,detail:p,color:b,available:y},x)=>{let _=x===r;return Xt(Xn,{flexDirection:"column",children:[Xt(Te,{children:[Ee(Te,{bold:_,color:_?"yellow":"white",children:_?" \u25B6 ":" "}),Ee(Te,{color:y?_?b:"white":"gray",bold:_,dimColor:!y,children:s}),!y&&Ee(Te,{dimColor:!0,children:" (unavailable)"})]}),_&&Xt(Te,{dimColor:!0,children:[" ",p]})]},x)}),Ee(Te,{children:" "}),Ee(Te,{dimColor:!0,children:"\u2191\u2193 navigate Enter choose"})]})}import{Box as ht,Text as re}from"ink";import{jsx as ie,jsxs as Ue}from"react/jsx-runtime";var ar=[{type:"structure",id:"forge",name:"Forge",symbol:"#"},{type:"structure",id:"archive",name:"Archive",symbol:"%"},{type:"structure",id:"foundry",name:"Foundry",symbol:"&"},{type:"structure",id:"beacon_tower",name:"Beacon",symbol:"^"},{type:"structure",id:"sanctum_hall",name:"Sanctum",symbol:"+"}],nr=[{type:"npc",id:"wren",name:"Wren",symbol:"W"},{type:"npc",id:"sable",name:"Sable",symbol:"S"},{type:"npc",id:"duskmar",name:"Duskmar",symbol:"D"},{type:"npc",id:"mott",name:"Mott",symbol:"M"},{type:"npc",id:"pale_visitor",name:"Pale Visitor",symbol:"?"}],Qo={type:"gate",id:"gate",name:"The Gate",symbol:">"},Qe=[...ar,...nr,Qo],Zo={wren:"Wren, the Steward",sable:"Sable, the Archivist",duskmar:"Duskmar, First Wall",mott:"Mott, the Salvager",pale_visitor:"The Pale Visitor"},es={wren:"Tends the Keep with quiet devotion",sable:"Remembers what the Pale erases",duskmar:"The wall that never fell",mott:"Finds treasure in the wreckage",pale_visitor:"Speaks in riddles from beyond"},Qn=17,Zn=56,le=1,ne=15,z=3,Q=52,ts={forge:{row:3,col:6,width:11},archive:{row:3,col:20,width:11},foundry:{row:3,col:34,width:11},beacon_tower:{row:10,col:6,width:11},sanctum_hall:{row:10,col:20,width:11}},as={wren:{row:8,col:11},sable:{row:8,col:25},duskmar:{row:8,col:39},mott:{row:9,col:44},pale_visitor:{row:13,col:46}};function Fa(e,t){return e>=t?"green":e>=2?"cyan":e>=1?"yellow":"white"}function er(e){return e>=4?"magenta":e>=2?"cyan":e>=1?"yellow":"white"}function tr(e,t,r){let n=[];for(let a=0;a<t;a++)n.push(a<e?ie(re,{color:r,bold:!0,children:"\u25C6"},a):ie(re,{dimColor:!0,children:"\u25C7"},a));return n}function rr({keep:e,selectedId:t,message:r,isFirstVisit:n}){let a=[],o=[];for(let d=0;d<Qn;d++){let g=[],R=[];for(let T=0;T<Zn;T++)g.push((d+T)%2===0?"~":" "),R.push({color:"white",bold:!1,dim:!0});a.push(g),o.push(R)}let l="the pale",c=Math.floor((Zn-l.length)/2);for(let d=0;d<l.length;d++)a[0][c+d]=l[d],a[Qn-1][c+d]=l[d];a[le][z]="\u2554",a[le][Q]="\u2557",a[ne][z]="\u255A",a[ne][Q]="\u255D";for(let d=z+1;d<Q;d++)a[le][d]="\u2550",a[ne][d]="\u2550",o[le][d]={color:"white",bold:!1,dim:!0},o[ne][d]={color:"white",bold:!1,dim:!0};for(let d=le+1;d<ne;d++)a[d][z]="\u2551",a[d][Q]="\u2551",o[d][z]={color:"white",bold:!1,dim:!0},o[d][Q]={color:"white",bold:!1,dim:!0};for(let d of[[le,z],[le,Q],[ne,z],[ne,Q]])o[d[0]][d[1]]={color:"white",bold:!1,dim:!0};for(let d of[z+1,8,14,38,44,Q-1])d>z&&d<Q&&(a[le][d]="\u25B2",o[le][d]={color:"white",bold:!0,dim:!1});for(let d of[z+1,8,14,42,48,Q-1])d>z&&d<Q&&a[ne][d]==="\u2550"&&(a[ne][d]="\u25B2",o[ne][d]={color:"white",bold:!0,dim:!1});let s=" THE KEEP ",p=z+1+Math.floor((Q-z-1-s.length)/2);for(let d=0;d<s.length;d++)a[le][p+d]=s[d],o[le][p+d]={color:"yellow",bold:!0,dim:!1};for(let d=le+1;d<ne;d++)for(let g=z+1;g<Q;g++)a[d][g]=" ",o[d][g]={color:"white",bold:!1,dim:!1};for(let[d,g]of[[le+1,z+1],[le+1,Q-1],[ne-1,z+1],[ne-1,Q-1]])a[d][g]="*",o[d][g]={color:"yellow",bold:!0,dim:!1};for(let d=z+2;d<Q-1;d++)a[8][d]===" "&&(a[8][d]="\xB7",o[8][d]={color:"white",bold:!1,dim:!0});for(let d of[11,25,39])for(let g=6;g<=(d===39?8:10);g++)a[g][d]===" "&&(a[g][d]="\xB7",o[g][d]={color:"white",bold:!1,dim:!0});for(let d=9;d<ne;d++)a[d][28]===" "&&(a[d][28]="\xB7",o[d][28]={color:"white",bold:!1,dim:!0});for(let[d,g]of[[7,16],[7,34],[7,48],[9,8],[9,18],[9,36],[13,8],[13,18],[13,36]])d>le&&d<ne&&a[d][g]===" "&&(a[d][g]="\xB7",o[d][g]={color:"white",bold:!1,dim:!0});for(let d of ar){let g=ts[d.id];if(!g)continue;let R=mt.find(E=>E.id===d.id),T=ut(e,d.id),$=R?.maxLevel??3,F=d.id===t,_e=F?"yellow":Fa(T,$),De=F||T>=$,Ye=!F&&T===0,U=g.row,v=g.col,k=g.width,C=k-2,O=Math.ceil((C-d.name.length)/2),j=C-d.name.length-O;a[U][v]=F?"\u250F":"\u250C";let W=v+1;for(let E=0;E<O;E++)a[U][W]=F?"\u2501":"\u2500",W++;for(let E=0;E<d.name.length;E++)a[U][W]=d.name[E],W++;for(let E=0;E<j;E++)a[U][W]=F?"\u2501":"\u2500",W++;a[U][v+k-1]=F?"\u2513":"\u2510",a[U+1][v]=F?"\u2503":"\u2502";for(let E=1;E<k-1;E++)a[U+1][v+E]=" ";a[U+1][v+Math.floor(k/2)]=d.symbol,a[U+1][v+k-1]=F?"\u2503":"\u2502",a[U+2][v]=F?"\u2517":"\u2514";for(let E=1;E<k-1;E++)a[U+2][v+E]=F?"\u2501":"\u2500";a[U+2][v+k-1]=F?"\u251B":"\u2518";for(let E=0;E<3;E++)for(let Y=0;Y<k;Y++)o[U+E][v+Y]={color:_e,bold:De,dim:Ye};let Z=U+3;if(Z>le&&Z<ne){let E=v+Math.floor((k-$)/2);for(let Y=0;Y<$;Y++)a[Z][E+Y]=Y<T?"\u25C6":"\u25C7",o[Z][E+Y]={color:Y<T?F?"yellow":Fa(T,$):"white",bold:Y<T,dim:Y>=T}}}for(let d of nr){let g=as[d.id];if(!g)continue;let T=e.npcs.find(_e=>_e.id===d.id)?.tier??0,$=d.id===t,F=$?"yellow":er(T);$&&(g.col-1>z&&(a[g.row][g.col-1]="[",o[g.row][g.col-1]={color:"yellow",bold:!0,dim:!1}),g.col+1<Q&&(a[g.row][g.col+1]="]",o[g.row][g.col+1]={color:"yellow",bold:!0,dim:!1})),a[g.row][g.col]=d.symbol,o[g.row][g.col]={color:F,bold:$||T>=3,dim:!1}}{let d=t==="gate",g=d?"\u2524 ENTER THE PALE \u251C":"\u2561 ENTER THE PALE \u255E",R=z+1+Math.floor((Q-z-1-g.length)/2);for(let T=0;T<g.length;T++)a[ne][R+T]=g[T],o[ne][R+T]={color:d?"yellow":"green",bold:!0,dim:!1}}for(let d=11;d<=14;d++)for(let g=48;g<Q;g++)(a[d][g]===" "||a[d][g]==="\xB7")&&(a[d][g]="~",o[d][g]={color:"white",bold:!1,dim:!0});let b=Qe.find(d=>d.id===t),y="",x="",_="cyan",G=null;if(b)if(b.type==="structure"){let d=mt.find(T=>T.id===b.id),g=ut(e,b.id),R=d?.maxLevel??3;if(_=Fa(g,R),y=b.name,g>=R)x=`${d?.description??""} (fully upgraded)`;else{let T=d?.upgradeCost(g+1)??0;x=`${d?.description??""} \u2014 ${T} Echoes to upgrade`}G=Ue(re,{children:[" ",tr(g,R,_)]})}else if(b.type==="npc"){let g=e.npcs.find(R=>R.id===b.id)?.tier??0;_=er(g),y=Zo[b.id]??b.id,x=es[b.id]??"",G=Ue(re,{children:[" ",tr(g,5,_)]})}else b.type==="gate"&&(_="green",y="The Gate",x="Step beyond the walls. Face the Pale.");return Ue(ht,{flexDirection:"column",children:[Ue(ht,{justifyContent:"space-between",paddingX:1,children:[ie(re,{bold:!0,color:"yellow",children:"\u25C6 The Keep"}),Ue(re,{children:["Echoes ",ie(re,{bold:!0,color:"cyan",children:e.echoes})," ","Runs ",ie(re,{dimColor:!0,children:e.totalRuns})," ","Wins ",ie(re,{dimColor:!0,children:e.totalWins})," ","Ascension ",ie(re,{dimColor:!0,children:e.highestAscension})]})]}),ie(ht,{flexDirection:"column",children:a.map((d,g)=>ie(re,{children:d.map((R,T)=>{let $=o[g][T];return ie(re,{color:$.color,bold:$.bold,dimColor:$.dim,children:R},T)})},g))}),Ue(ht,{flexDirection:"column",paddingX:1,children:[b?Ue(ht,{flexDirection:"column",children:[Ue(ht,{children:[ie(re,{bold:!0,color:_,children:y}),G]}),ie(re,{dimColor:!0,children:x})]}):ie(re,{dimColor:!0,italic:!0,children:"Select a building or NPC."}),n&&!r&&ie(re,{color:"green",children:"TIP: Upgrade buildings with Echoes. Talk to NPCs. Enter the Gate to start a run."}),r?ie(re,{color:"yellow",bold:!0,children:r}):null,ie(re,{dimColor:!0,children:"\u2191\u2193/\u2190\u2192 select Enter interact q back"})]})]})}import{Box as or,Text as Fe}from"ink";import{jsx as Ze,jsxs as Jt}from"react/jsx-runtime";var sr=[{title:"Welcome to CodeKeep",lines:["You are the Warden of the Keep \u2014 the last fortress","standing against the Pale, an erasure that consumes all.","","Your goal: survive 3 Acts of increasingly dangerous","enemies by playing cards and building defenses."]},{title:"The Gate",lines:["Your Gate is your lifeline. It has HP (hit points).","If Gate HP reaches 0, your run is over.",""," \u25C6 Gate \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 70/70","","Enemies advance toward the Gate. When they reach it,","they deal damage. Block reduces incoming damage."]},{title:"Combat Basics",lines:["Each turn you gain 3 Resolve (mana) to play cards.","Unspent Resolve carries over (up to 6 max). Your hand refreshes each turn.",""," 1-5 .... Select a card from your hand"," \u2190\u2192 .... Choose target column"," Enter .. Play the selected card"," Space .. End your turn"," i ...... Inspect an enemy"]},{title:"The 5-Column Grid",lines:["The battlefield has 5 columns. Enemies spawn at the top","and advance downward toward your Gate at the bottom.",""," \u250C\u2500\u2500Col 1\u2500\u2500\u252C\u2500\u2500Col 2\u2500\u2500\u252C\u2500\u2500Col 3\u2500\u2500\u2510"," \u2502 \u262018 \u2502 \xB7 \u2502 \u219110 \u2502"," \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"," \u25C6 Gate \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 70/70"]},{title:"Enemy Intents",lines:["Enemies show what they plan to do next turn:",""," \u2193N .. Advance N rows toward the Gate"," \u2694N .. Attack the Gate for N damage"," \u25B2N .. Buff themselves"," \u25C8N .. Shield nearby enemies"," +N .. Summon new enemies","","Read intents to plan your plays wisely!"]},{title:"Emplacements",lines:["Some cards have dual use \u2014 cast OR emplace.",""," Cast: Immediate effect (damage, block, etc.)"," Emplace: Place as a structure in a column."," Triggers automatically every turn!","","Press e to toggle emplace mode, pick a column,","then press Enter to place it."]},{title:"Status Effects",lines:[" V Vulnerable ... +25% damage taken per stack"," W Weak ......... -15% damage dealt per stack"," B Burn ......... Takes damage each turn, decays"," F Fortified .... -15% damage taken per stack"," E Empowered .... +25% damage dealt per stack","","Effects stack: 2x Vulnerable = +50% damage taken."]},{title:"The Map",lines:[" \u2694 Combat ... Standard enemy encounter"," \u2605 Elite .... Harder fight, better rewards"," \u25B3 Rest ..... Heal or remove a card"," $ Shop ..... Buy cards, potions, remove cards"," ? Event .... Story encounters with choices"," \u25C6 Boss ..... Act boss \u2014 defeat to advance","","Choose your path wisely. Elites are risky but rewarding."]},{title:"The Keep",lines:["After each run, you earn Echoes based on performance.","Explore the Keep to:",""," \u25C6 Upgrade structures (bonuses for future runs)"," \u25C6 Talk to NPCs (unlock story and lore)"," \u25C6 Increase Ascension (harder + new modifiers)","","The Keep remembers. Each run makes you stronger."]},{title:"Ready to Play!",lines:[" 1-5 = card \u2190\u2192 = column Enter = play"," Space = end e = emplace p = potion"," i = inspect d = deck q = menu","","The Pale awaits, Warden. Defend the Keep.","","Press Enter to begin."]}];function ns(e,t,r){let n=Math.max(0,Math.min(r,Math.round(e/t*r)));return"\u2588".repeat(n)+"\u2591".repeat(Math.max(0,r-n))}function lr({page:e,totalPages:t,source:r="new_game"}){let n=sr[e];if(!n)return null;let a=e>=t-1,o=r==="new_game"?"Enter begin":"Enter return";return Jt(or,{flexDirection:"column",padding:1,children:[Jt(Fe,{bold:!0,color:"yellow",children:["\u25C6 Tutorial \u2014 ",n.title]}),Ze(Fe,{dimColor:!0,children:"\u2500".repeat(44)}),Ze(Fe,{children:" "}),n.lines.map((l,c)=>a&&l==="Press Enter to begin."&&r!=="new_game"?Ze(Fe,{children:"Press Enter to return."},c):Ze(Fe,{children:l||" "},c)),Ze(Fe,{children:" "}),Ze(Fe,{dimColor:!0,children:"\u2500".repeat(44)}),Jt(or,{children:[Jt(Fe,{dimColor:!0,children:[ns(e+1,t,20)," ",e+1,"/",t," "]}),Ze(Fe,{dimColor:!0,children:a?`\u2190 prev ${o} q close`:"\u2190 prev \u2192 next q close"})]})]})}var Pt=sr.length;import{Box as Qt,Text as pe}from"ink";import{Fragment as ir,jsx as Pe,jsxs as Ne}from"react/jsx-runtime";var cr={asciiMode:!1,showTutorialHints:!0,combatLogSize:4,skipEnemyAnimation:!1,difficulty:"normal"},rs={easy:"Easy \u2014 enemies have 30% less HP/damage, +20 gate HP",normal:"Normal \u2014 the intended experience",hard:"Hard \u2014 enemies have 25% more HP/damage, \u221210 gate HP"};function dr({settings:e,selectedIndex:t,saveInfo:r,confirmingReset:n,message:a}){let o=[{id:"ascii",label:`ASCII Mode: ${e.asciiMode?"ON":"OFF"}`,description:"Use plain ASCII characters (no Unicode symbols)"},{id:"hints",label:`Tutorial Hints: ${e.showTutorialHints?"ON":"OFF"}`,description:"Show helpful tips during first runs"},{id:"log",label:`Combat Log Lines: ${e.combatLogSize}`,description:"Number of recent combat events shown (0-8)"},{id:"anim",label:`Enemy Animations: ${e.skipEnemyAnimation?"OFF":"ON"}`,description:"Step through enemy moves one by one"},{id:"difficulty",label:`Difficulty: ${e.difficulty.charAt(0).toUpperCase()+e.difficulty.slice(1)}`,description:rs[e.difficulty]},{id:"tutorial",label:"View Tutorial",description:"Re-read the game tutorial"},{id:"controls",label:"Controls Reference",description:"View all keybindings"},{id:"reset",label:"Reset Save Data",description:"Delete all progress and start fresh"},{id:"back",label:"Back to Menu",description:""}];return Ne(Qt,{flexDirection:"column",padding:1,children:[Pe(pe,{bold:!0,color:"yellow",children:"\u25C6 Settings"}),Pe(pe,{children:" "}),Pe(Qt,{paddingX:1,children:Ne(Qt,{flexDirection:"column",width:40,children:[Pe(pe,{bold:!0,dimColor:!0,children:"Save Info"}),Ne(pe,{dimColor:!0,children:["Runs: ",r.totalRuns," Wins: ",r.totalWins," Echoes: ",r.echoes]}),Ne(pe,{dimColor:!0,children:["Highest Ascension: ",r.highestAscension]}),Ne(pe,{dimColor:!0,children:["Save: ",r.hasSave?"~/.config/codekeep/game.json":"None"]})]})}),Pe(pe,{children:" "}),o.map((l,c)=>{let s=c===t;return Ne(Qt,{children:[Ne(pe,{bold:s,color:s?l.id==="reset"?"red":"yellow":void 0,children:[s?"\u25B6 ":" ",l.label]}),s&&l.description?Ne(pe,{dimColor:!0,children:[" \u2014 ",l.description]}):null]},l.id)}),n&&Ne(ir,{children:[Pe(pe,{children:" "}),Pe(pe,{bold:!0,color:"red",children:"Are you sure? This deletes ALL progress. Press Enter again to confirm, Esc to cancel."})]}),a&&Ne(ir,{children:[Pe(pe,{children:" "}),Pe(pe,{color:"cyan",bold:!0,children:a})]}),Pe(pe,{children:" "}),Pe(pe,{dimColor:!0,children:"\u2191\u2193 navigate Enter toggle/select q back"})]})}import{Box as os,Text as B}from"ink";import{jsx as H,jsxs as ss}from"react/jsx-runtime";function mr(){return ss(os,{flexDirection:"column",padding:1,children:[H(B,{bold:!0,color:"yellow",children:"\u25C6 Controls Reference"}),H(B,{dimColor:!0,children:"\u2500".repeat(36)}),H(B,{children:" "}),H(B,{bold:!0,children:"Global"}),H(B,{children:" \u2191\u2193 Navigate menus"}),H(B,{children:" Enter Confirm / select"}),H(B,{children:" Esc Cancel / go back"}),H(B,{children:" q Quit / back to menu"}),H(B,{children:" "}),H(B,{bold:!0,children:"Combat"}),H(B,{children:" 1-9 Select card from hand"}),H(B,{children:" \u2190\u2192 Target column"}),H(B,{children:" Enter Play selected card"}),H(B,{children:" Space End your turn"}),H(B,{children:" e Toggle emplace mode"}),H(B,{children:" p Use potion"}),H(B,{children:" i Inspect enemies"}),H(B,{children:" d View deck"}),H(B,{children:" "}),H(B,{bold:!0,children:"Map"}),H(B,{children:" \u2190\u2192 Select next node"}),H(B,{children:" Enter Enter node"}),H(B,{children:" d View deck"}),H(B,{children:" "}),H(B,{bold:!0,children:"The Keep"}),H(B,{children:" \u2190\u2192 Select building / NPC"}),H(B,{children:" Enter Interact"}),H(B,{children:" "}),H(B,{bold:!0,children:"Inspect Mode (i in combat)"}),H(B,{children:" \u2190\u2192 Switch column"}),H(B,{children:" \u2191\u2193 Switch enemy"}),H(B,{children:" i / Esc Close inspect"}),H(B,{children:" "}),H(B,{dimColor:!0,children:"Press q or Esc to go back"})]})}import{useState as et,useCallback as tt,useRef as ur,useEffect as ls}from"react";function is(e){switch(e.type){case"enemy_advance":return`Enemy advances to row ${e.data.row}`;case"gate_hit":{let t=e.data.blocked?` (${e.data.blocked} blocked)`:"";return e.data.self?`Gate takes ${e.data.damage} self-damage`:`Enemy hits gate for ${e.data.damage}${t}!`}case"enemy_killed":return"An enemy is destroyed!";case"emplacement_destroyed":return`Emplacement in column ${e.data.column+1} destroyed!`;case"emplacement_triggered":return`Emplacement fires in column ${e.data.column+1}`;case"status_applied":return`${e.data.type} applied (${e.data.value} stacks)`;case"damage_dealt":return`${e.data.damage} damage dealt`;case"block_gained":return`+${e.data.value} Block`;default:return null}}var La=new Set(["damage","damage_if_vulnerable","damage_equal_block","damage_if_emplaced","damage_plus_block","damage_if_low_hp","vulnerable","weak","burn"]),cs=700;function pr(e=!1){let[t,r]=et(null),[n,a]=et(-1),[o,l]=et(2),[c,s]=et(""),[p,b]=et(!1),[y,x]=et(!1),[_,G]=et([]),d=ur(null),g=ur(2);ls(()=>{if(!y||_.length===0)return;if(e){s(_[_.length-1]),G([]),x(!1);let k=d.current;k&&(k.outcome==="win"?s("Victory! The Pale recedes."):k.outcome==="lose"?s("The Gate has fallen..."):s(`Turn ${k.turn}. Your move.`));return}let v=setTimeout(()=>{let[k,...C]=_;if(s(k),C.length===0){x(!1);let O=d.current;O&&(O.outcome==="win"?s("Victory! The Pale recedes."):O.outcome==="lose"?s("The Gate has fallen..."):s(`Turn ${O.turn}. Your move.`))}else G(C)},cs);return()=>clearTimeout(v)},[y,_,e]);let R=tt((v,k,C,O,j,W,Z)=>{let E=v??wa(),Y=k??Math.floor(Math.random()*2147483647),ce=un(E,Y,C,O,j,W??[],Z);d.current=ce,r({...ce}),a(-1),l(2),g.current=2,b(!1),s("Your turn. Select a card (1-5) and a column (\u2190\u2192).")},[]),T=(()=>{if(n<0||!t)return!1;let v=t.hand[n];if(!v)return!1;let k=q(v.defId);return k?k.effects.some(C=>La.has(C.type)&&(C.target==="single"||C.target==="column")):!1})(),$=tt(v=>{if(!d.current||d.current.phase!=="player"||v<0||v>=d.current.hand.length)return;let k=d.current.hand[v],C=q(k.defId);if(!C)return;let O=!d.current.events.some(E=>E.type==="card_played"&&E.turn===d.current.turn);if((xa(d.current.relics)&&O?0:C.cost)>d.current.resolve){s(`Not enough Resolve for ${C.name} (costs ${C.cost}).`);return}if(a(v),C.effects.some(E=>La.has(E.type)&&(E.target==="single"||E.target==="column"))){let E=d.current.columns,Y=g.current;if(E[Y].enemies.length===0){let ce=-1,At=99;for(let be=0;be<E.length;be++)E[be].enemies.length>0&&Math.abs(be-Y)<At&&(ce=be,At=Math.abs(be-Y));ce>=0&&(l(ce),g.current=ce)}s(`${C.name} selected. Choose column (\u2190\u2192), Enter to play.`)}else s(`${C.name} selected. Enter to play.`)},[]),F=tt(v=>{v>=0&&v<5&&(l(v),g.current=v)},[]),_e=tt(()=>{let v=d.current;if(!v||v.phase!=="player"||n<0)return;let k=v.hand[n];if(!k)return;let C=q(k.defId);if(!C)return;if(!p&&C.effects.some(W=>La.has(W.type)&&W.target==="single")){let W=v.columns[o];if(!W||W.enemies.length===0){s(`No enemies in column ${o+1}. Move target (\u2190\u2192) to an enemy.`);return}}let j=v.events.length;if(pn(v,n,o,p),v.events.length===j){p&&v.columns[o]?.emplacement?s(`Column ${o+1} already has an emplacement. Choose another column.`):s(`Cannot play ${C.name}. Not enough Resolve.`);return}d.current=v,r({...v}),a(-1),b(!1),v.outcome==="win"?s("Victory! The Pale recedes."):v.outcome==="lose"?s("The Gate has fallen..."):s(`${p?"Emplaced":"Played"} ${C.name}. ${v.resolve} Resolve left.`)},[n,o,p]),De=tt(()=>{if(n<0||!d.current)return;let v=d.current.hand[n];if(!v)return;let k=q(v.defId);if(!k||k.type!=="emplace"){s("This card cannot be emplaced.");return}b(C=>!C),s(p?`${k.name}: cast mode`:`${k.name}: EMPLACE mode \u2014 place in a column`)},[n,p]),Ye=tt(()=>{let v=d.current;if(!v||v.phase!=="player"||y)return;let k=v.events.length;hn(v),d.current=v,r({...v}),a(-1);let C=v.events.slice(k),O=[];for(let j of C){let W=is(j);W&&O.push(W)}O.length>0?(x(!0),s("\u26A1 Enemy turn..."),G(O)):v.outcome==="win"?s("Victory! The Pale recedes."):v.outcome==="lose"?s("The Gate has fallen..."):s(`Turn ${v.turn}. Your move.`)},[y]),U=tt(v=>{let k=d.current;if(!(!k||!v)){for(let C of v.effects)switch(C.type){case"heal":k.gateHp=Math.min(k.gateMaxHp,k.gateHp+C.value);break;case"block":k.gateBlock+=C.value;break;case"damage":{let O=o;if(C.target==="column"){let j=k.columns[O];if(j)for(let W of j.enemies)W.hp-=C.value}else if(C.target==="all")for(let j of k.columns)for(let W of j.enemies)W.hp-=C.value;break}case"draw":{let O=ae(k.seed+k.turn*53),{drawn:j,newDrawPile:W,newDiscardPile:Z}=Ve(k.drawPile,k.discardPile,C.value,O);k.hand.push(...j),k.drawPile=W,k.discardPile=Z;break}case"resolve":k.resolve=Math.min(k.maxResolve+5,k.resolve+C.value);break}d.current=k,r({...k}),s(`Used ${v.name}.`)}},[o]);return{combat:t,selectedCard:n,targetColumn:o,message:c,emplaceMode:p,animating:y,selectCard:$,selectTarget:F,confirmPlay:_e,endTurn:Ye,toggleEmplace:De,startCombat:R,applyPotion:U,needsTarget:T}}import{Fragment as Va,jsx as w,jsxs as S}from"react/jsx-runtime";var fr=90,hr=24;function fs(){let{stdout:e}=ps(),[t,r]=N({columns:e?.columns??process.stdout.columns??80,rows:e?.rows??process.stdout.rows??24});return ds(()=>{let n=e??process.stdout,a=()=>r({columns:n.columns,rows:n.rows});return n.on("resize",a),()=>{n.off("resize",a)}},[e]),t}function hs({dryRun:e}){let{exit:t}=ms(),{columns:r,rows:n}=fs(),[a,o]=N("menu"),[l,c]=N(1),[s,p]=N(null),[b,y]=N(0),[x,_]=N([]),[G,d]=N(0),[g,R]=N([]),[T,$]=N(0),[F,_e]=N(null),[De,Ye]=N(0),[U,v]=N(0),[k,C]=N(null),[O,j]=N(0),[W,Z]=N(""),[E,Y]=N(!1),[ce,At]=N([]),[be,Zt]=N(0),[ea,qe]=N(0),[we,gt]=N(cr),[Oa,ta]=N(0),[wr,at]=N(""),aa=pr(we.skipEnemyAnimation),{combat:V,selectedCard:Dt,targetColumn:yt,message:vr,emplaceMode:kr,animating:Ka,selectCard:Ua,selectTarget:Rt,confirmPlay:xr,endTurn:Tr,toggleEmplace:_r,startCombat:na,applyPotion:ws,needsTarget:ra}=aa,[Mt,oa]=N(!1),[bt,sa]=N(0),[Ya,la]=N(0),[wt,nt]=N(null),[qa,rt]=N(0),[Bt,ia]=N("new_game"),[Ir,za]=N("map"),[Cr,Ht]=N(""),[ja,ot]=N(!1),[Xa,st]=N(!1),[ca,vt]=N(!1),[he,Re]=N(()=>pt()),da=!he||he.keep.totalRuns<=1,Le=he?Cn(he.keep.totalRuns,he.keep.highestAscension):"surface",Sr=r<fr||n<hr,Me=Ae(()=>{let m=he??pt();return m?he||(m.keep.npcs.length===0&&(m.keep.npcs=Sa()),Re(m)):(m=An("Warden"),m.keep.npcs.length===0&&(m.keep.npcs=Sa()),He(m),Re(m)),JSON.parse(JSON.stringify(m))},[he]),L=Ae(m=>{if(e)return;let u=Me();u.activeRun=m,k&&(u.keep=k),He(u),Re(u)},[e,k,Me]),ma=Ae(()=>{let m=Me();if(m.keep.totalRuns===0){m.keep.totalRuns++,He(m),Re(m),C(m.keep),rt(0),ia("new_game"),o("tutorial");return}let u=`run-${Date.now()}`,i=wn(u,m.keep.highestAscension,we.difficulty);p(i),y(0),m.activeRun=i,m.keep.totalRuns++,C(m.keep),He(m),Re(m),o("map")},[Me,we]),Er=Ae(()=>{let m=pt();m?.activeRun&&(sn(m.activeRun.deck),p(m.activeRun),C(m.keep),y(0),o("map"))},[]),Ja=Ae(()=>{let m=Me();Re(m),C(m.keep),j(0),Z(""),o("keep")},[Me]),Qa=Ae(()=>{if(!s)return[];if(s.currentNodeId){let m=s.map.nodes.find(u=>u.id===s.currentNodeId);if(m&&!m.visited)return[m]}return bn(s.map,s.currentNodeId)},[s]),Pr=Ae(m=>{if(!s)return;if(m.type==="combat"||m.type==="elite"){let i={...s,currentNodeId:m.id},f=Se(s.seed+m.id),I=ae(f),A=gn(s.act,I,m.type==="elite");na(i.deck,f,i.gateHp,i.gateMaxHp,A.enemies,i.relics,Ut(s.act,s.ascensionLevel,we.difficulty)),p(i),L(i),o("combat");return}else if(m.type==="boss"){let i={...s,currentNodeId:m.id},f=Se(s.seed+"-boss-"+s.act),I=ln(s.act);na(i.deck,f,i.gateHp,i.gateMaxHp,I,i.relics,Ut(s.act,s.ascensionLevel,we.difficulty));let A=Vt(s.act);if(A?.dialogue){let M=A.dialogue.find(ze=>ze.storyLayer===Le)??A.dialogue[0];M&&nt(M.onAppear)}p(i),L(i),o("combat");return}let u=Ia(s,m.id);if(m.type==="shop"){let i=ae(Se(s.seed+m.id));R(Tn(i)),$(0),p(u),o("shop")}else if(m.type==="event"){let i=ae(Se(s.seed+m.id));_e(_n(s.act,i,Le)),Ye(0),p(u),o("event")}else m.type==="rest"&&(v(0),p(u),o("rest"));L(u)},[s,na,L,we,Le]),kt=Ae((m,u)=>{Y(u),p(m);let i=Me(),f=Ea(u,m.act,m.ascensionLevel);i.keep.echoes+=f,u&&(i.keep.totalWins++,m.ascensionLevel>=i.keep.highestAscension&&(i.keep.highestAscension=m.ascensionLevel+1)),i.activeRun=null,C(i.keep),He(i),Re(i),o("result")},[Me]),Ar=Ae(()=>{if(!V||!s)return;let m={...s,gateHp:V.gateHp};if(V.outcome==="lose"){nt(null),kt(m,!1);return}if((s.currentNodeId?dt(s.map,s.currentNodeId):null)?.type==="boss"){let A=Vt(s.act);if(A?.dialogue){let M=A.dialogue.find(ze=>ze.storyLayer===Le)??A.dialogue[0];nt(M?.onDefeat??null)}else nt(null)}else nt(null);m.currentNodeId&&(m=Ia(m,m.currentNodeId));let i=s.currentNodeId?dt(s.map,s.currentNodeId):null,f=i?.type==="elite"?Nt.elite:i?.type==="boss"?Nt.boss:Nt.combat;if(m=Ct(m,f),m.relics.includes("mending_stone")&&(m=Yt(m,5)),m.relics.includes("fragment_magnet")&&(m=Ct(m,5)),i?.type==="elite"||i?.type==="boss"){let A=ae(Se(s.seed+(s.currentNodeId??"")+"-relic")),M=mn(A,m.relics,3);if(M.length>0){At(M),Zt(0),p(m),L(m),o("relic_reward");return}}if(i?.type==="boss"){s.act<3?(m=Ca(m),p(m),L(m),y(0),o("map")):kt(m,!0);return}let I=ae(Se(s.seed+(s.currentNodeId??"")+"-reward"));_(Kt(I)),d(0),p(m),L(m),o("reward")},[V,s,L,kt,Le]),Za=Ae(m=>{if(!s)return;let u=s;m&&(u=Ta(u,It(m.id))),p(u),L(u),y(0),o("map")},[s,L]),en=Ae(m=>{if(!s)return;let u=s;if(m&&(u={...u,relics:[...u.relics,m.id]}),p(u),L(u),(s.currentNodeId?dt(s.map,s.currentNodeId):null)?.type==="boss")s.act<3?(u=Ca(u),p(u),L(u),y(0),o("map")):kt(u,!0);else{let f=ae(Se(s.seed+(s.currentNodeId??"")+"-reward"));_(Kt(f)),d(0),o("reward")}},[s,L,kt]),tn=!!he?.activeRun,ua=[{label:"The Keep",action:"keep"},...tn?[{label:"Resume Run",action:"resume"},{label:ca?"Abandon run? Enter to confirm":"New Run",action:"new"}]:[{label:"New Run",action:"new"}],{label:"Tutorial",action:"tutorial"},{label:"Settings",action:"settings"},{label:"Quit",action:"quit"}];if(us((m,u)=>{if(a==="tutorial"){u.rightArrow?rt(i=>Math.min(Pt-1,i+1)):u.leftArrow?rt(i=>Math.max(0,i-1)):u.return?qa===Pt-1?Bt==="new_game"?ma():o(Bt==="settings"?"settings":"menu"):rt(i=>Math.min(Pt-1,i+1)):(m==="q"||u.escape)&&o(Bt==="settings"?"settings":"menu");return}if(a==="controls"){(m==="q"||u.escape)&&o("settings");return}if(a==="settings"){let i=["ascii","hints","log","anim","difficulty","tutorial","controls","reset","back"];if(u.upArrow)ta(f=>Math.max(0,f-1)),st(!1);else if(u.downArrow)ta(f=>Math.min(i.length-1,f+1)),st(!1);else if(m==="q"||u.escape)o("menu"),st(!1);else if(u.return){let f=i[Oa];if(f==="ascii")gt(I=>({...I,asciiMode:!I.asciiMode})),at("ASCII mode "+(we.asciiMode?"disabled":"enabled"));else if(f==="hints")gt(I=>({...I,showTutorialHints:!I.showTutorialHints})),at("Tutorial hints "+(we.showTutorialHints?"disabled":"enabled"));else if(f==="log")gt(I=>({...I,combatLogSize:I.combatLogSize>=8?0:I.combatLogSize+2}));else if(f==="anim")gt(I=>({...I,skipEnemyAnimation:!I.skipEnemyAnimation})),at("Enemy animations "+(we.skipEnemyAnimation?"enabled":"disabled"));else if(f==="difficulty"){let A={easy:"normal",normal:"hard",hard:"easy"}[we.difficulty];gt(M=>({...M,difficulty:A})),at(`Difficulty set to ${A.charAt(0).toUpperCase()+A.slice(1)}. Takes effect on next run.`)}else f==="tutorial"?(rt(0),ia("settings"),o("tutorial")):f==="controls"?o("controls"):f==="reset"?Xa?(Dn(),Re(null),C(null),p(null),st(!1),at("Save data deleted. Starting fresh.")):st(!0):f==="back"&&o("menu")}return}if(a==="relic_reward"){u.upArrow?Zt(i=>Math.max(0,i-1)):u.downArrow?Zt(i=>Math.min(ce.length,i+1)):m==="s"?en(null):u.return&&en(be<ce.length?ce[be]:null);return}if(a==="menu"){if(u.upArrow)c(i=>Math.max(0,i-1)),vt(!1);else if(u.downArrow)c(i=>Math.min(ua.length-1,i+1)),vt(!1);else if(u.escape)vt(!1);else if(u.return){let i=ua[l]?.action;i==="keep"?Ja():i==="new"?tn&&!ca?vt(!0):(vt(!1),ma()):i==="resume"?Er():i==="tutorial"?(rt(0),ia("menu"),o("tutorial")):i==="settings"?(ta(0),at(""),st(!1),o("settings")):t()}else m==="q"&&t();return}if(a==="deck"){(m==="q"||u.escape)&&o(Ir);return}if(a==="map"&&s){let i=Qa().sort((f,I)=>f.column-I.column);u.leftArrow?y(f=>Math.max(0,f-1)):u.rightArrow?y(f=>Math.min(i.length-1,f+1)):u.return&&i[b]?Pr(i[b]):m==="d"?(za("map"),o("deck")):m==="q"&&(L(s),o("menu"));return}if(a==="reward"){let i=x.length;u.upArrow?d(f=>Math.max(0,f-1)):u.downArrow?d(f=>Math.min(i,f+1)):m==="s"?Za(null):u.return&&Za(G<x.length?x[G]:null);return}if(a==="shop"&&s){if(u.upArrow)$(i=>Math.max(0,i-1));else if(u.downArrow)$(i=>Math.min(g.length-1,i+1));else if(m==="q"||u.escape)y(0),o("map");else if(u.return){let i=g[T];if(i&&s.fragments<i.cost){Ht(`Not enough fragments (need ${i.cost}, have ${s.fragments}).`);return}if(Ht(""),i&&s.fragments>=i.cost){let f=vn(s,i.cost);if(!f)return;if(i.type==="card"&&i.cardDef)f=Ta(f,It(i.cardDef.id)),p(f),L(f),R(I=>I.filter((A,M)=>M!==T)),$(0),Ht(`Purchased ${i.cardDef.name}!`);else if(i.type==="potion"&&i.potionDef){let I=kn(f,i.potionDef.id);I&&(f=I),p(f),L(f),R(A=>A.filter((M,ze)=>ze!==T)),$(0),Ht(`Purchased ${i.potionDef.name}!`)}else i.type==="card_remove"&&(p(f),L(f),qe(0),R(I=>I.filter((A,M)=>M!==T)),$(0),o("shop_remove"))}}return}if(a==="event"&&F&&s){if(u.upArrow)Ye(i=>Math.max(0,i-1));else if(u.downArrow)Ye(i=>Math.min(F.choices.length-1,i+1));else if(u.return){let i=F.choices[De],f=s;switch(i.effect.type){case"heal":f=Yt(f,i.effect.value);break;case"damage":{f={...f,gateHp:Math.max(1,f.gateHp-i.effect.value)};let I=i.label.match(/(\d+)\s*fragments/i);I&&(f=Ct(f,parseInt(I[1])));break}case"fragments":f=Ct(f,i.effect.value);break;case"max_hp":{f={...f,gateMaxHp:f.gateMaxHp+i.effect.value};let I=i.label.match(/lose\s+(\d+)\s*hp/i);I&&(f={...f,gateHp:Math.max(1,f.gateHp-parseInt(I[1]))});break}case"card_reward":{let I=i.label.match(/lose\s+(\d+)\s*hp/i);I&&(f={...f,gateHp:Math.max(1,f.gateHp-parseInt(I[1]))});let A=ae(Se(s.seed+(F?.id??"")));_(Kt(A)),d(0),p(f),L(f),o("reward");return}case"remove_card":{p(f),L(f),qe(0),o("deck_remove");return}case"nothing":break}p(f),L(f),y(0),o("map")}return}if(a==="rest"&&s){if(u.upArrow)v(i=>Math.max(0,i-1));else if(u.downArrow)v(i=>Math.min(2,i+1));else if(u.return){let i=s;U===0?(i=Yt(i,Math.floor(i.gateMaxHp*.3)),p(i),L(i),y(0),o("map")):U===1?i.deck.length>5&&(p(i),qe(0),o("deck_remove")):(p(i),L(i),y(0),o("map"))}return}if(a==="deck_remove"&&s){if(u.upArrow)qe(i=>Math.max(0,i-1));else if(u.downArrow)qe(i=>Math.min(s.deck.length-1,i+1));else if(u.escape||m==="q")y(0),o("map");else if(u.return&&s.deck.length>5){let i=s.deck[ea];if(i){let f=_a(s,i.instanceId);p(f),L(f)}y(0),o("map")}return}if(a==="shop_remove"&&s){if(u.upArrow)qe(i=>Math.max(0,i-1));else if(u.downArrow)qe(i=>Math.min(s.deck.length-1,i+1));else if(u.escape||m==="q")o("shop");else if(u.return&&s.deck.length>5){let i=s.deck[ea];if(i){let f=_a(s,i.instanceId);p(f),L(f)}o("shop")}return}if(a==="keep"&&k){if(u.leftArrow||u.upArrow)j(i=>(i-1+Qe.length)%Qe.length),Z("");else if(u.rightArrow||u.downArrow)j(i=>(i+1)%Qe.length),Z("");else if(m==="q")o("menu");else if(u.return){let i=Qe[O];if(!i)return;if(i.type==="structure"){let f=mt.find(A=>A.id===i.id);if(!f)return;let I=In(k,f.id);if(I){C(I);let A=Me();A.keep=I,He(A),Re(A),Z(`Upgraded ${f.name}!`)}else ut(k,f.id)>=f.maxLevel?Z(`${f.name} is already max level.`):Z(`Not enough Echoes to upgrade ${f.name}.`)}else if(i.type==="npc"){let f=k.npcs.find(A=>A.id===i.id);if(!f)return;let I=Sn(f.id,k);if(I){let A=En(k,f.id,I.dialogueId);C(A);let M=Me();M.keep=A,He(M),Re(M),Z(`${I.speaker}: "${I.text}"`)}else Z("(No new dialogue)")}else i.type==="gate"&&ma()}return}if(a==="result"){u.return||m===" "?Ja():m==="q"&&o("menu");return}if(a==="combat"&&V){if(Ka)return;if(V.outcome!=="undecided"){(u.return||m===" ")&&Ar();return}if(wt&&V.turn>1&&nt(null),m==="i"){Mt?oa(!1):(oa(!0),sa(0),la(0));return}if(Mt){if(u.leftArrow)sa(i=>Math.max(0,i-1));else if(u.rightArrow)sa(i=>Math.min(4,i+1));else if(u.upArrow)la(i=>Math.max(0,i-1));else if(u.downArrow){let i=(V.columns[bt]?.enemies.length??1)-1;la(f=>Math.min(Math.max(0,i),f+1))}else u.escape&&oa(!1);return}if(m==="q"){o("menu");return}if(m==="d"&&s){za("combat"),o("deck");return}if(m==="e"){_r();return}if(m==="p"&&s){let i=s.potions.findIndex(f=>f!==null);if(i>=0){let f=xn(s,i);if(f&&aa.combat){let I=ct.find(A=>A.id===f.potionId);p(f.run),L(f.run),aa.applyPotion(I??null)}}return}if(m>="1"&&m<="9"){ot(!1),Ua(parseInt(m)-1);return}if(u.leftArrow){if(ot(!1),ra&&V){let i=yt-1;for(;i>=0&&V.columns[i].enemies.length===0;)i--;i>=0&&Rt(i)}else Rt(Math.max(0,yt-1));return}if(u.rightArrow){if(ot(!1),ra&&V){let i=yt+1;for(;i<5&&V.columns[i].enemies.length===0;)i++;i<5&&Rt(i)}else Rt(Math.min(4,yt+1));return}if(u.return&&Dt>=0){ot(!1),xr();return}if(m===" "){if(V.resolve>0&&V.hand.some(i=>{let f=q(i.defId);return f&&f.cost<=V.resolve})&&!ja){ot(!0);return}ot(!1),Tr();return}if(u.escape){Ua(-1);return}}}),Sr)return S(fe,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep \u2014 The Pale"}),S(h,{color:"red",children:["Terminal too small (",r,"x",n,", need ",fr,"x",hr,")"]})]});if(a==="menu")return S(fe,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"yellow",children:"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"}),w(h,{bold:!0,color:"yellow",children:"\u2502 \u25C6 CodeKeep \u2014 The Pale \u2502"}),w(h,{bold:!0,color:"yellow",children:"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"}),w(h,{children:" "}),(Le==="true_ending"?["The fortress. The Pale. You can no longer tell which side you're on."]:Le==="truth"?["The fortress stands. The Pale stands. The line between them blurs."]:Le==="cracks"?["The fortress stands at the edge of the Pale.","You've stood here before. You're sure of it."]:["The fortress stands at the edge of the Pale.","Beyond the walls, something stirs."]).map((u,i)=>w(h,{dimColor:!0,italic:!0,children:u},i)),w(h,{children:" "}),ua.map((u,i)=>{let f=i===l,I=u.action==="new"&&ca&&f;return S(h,{bold:f,color:I?"red":f?"yellow":"white",children:[f?" \u25B6 ":" ",u.label]},u.action+(I?"-confirm":""))}),w(h,{children:" "}),w(h,{dimColor:!0,children:"\u2191\u2193 navigate Enter select q exit"})]});if(a==="tutorial")return w(lr,{page:qa,totalPages:Pt,source:Bt});if(a==="controls")return w(mr,{});if(a==="settings"){let m={totalRuns:he?.keep.totalRuns??0,totalWins:he?.keep.totalWins??0,highestAscension:he?.keep.highestAscension??0,echoes:he?.keep.echoes??0,hasSave:!!he};return w(dr,{settings:we,selectedIndex:Oa,saveInfo:m,confirmingReset:Xa,message:wr})}if(a==="deck"&&s)return w(Kn,{deck:s.deck});if(a==="keep"&&k)return w(rr,{keep:k,selectedId:Qe[O]?.id??"forge",message:W,isFirstVisit:da});if((a==="deck_remove"||a==="shop_remove")&&s)return S(fe,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"red",children:"\u25C6 Remove a Card"}),S(h,{dimColor:!0,children:["Choose a card to remove from your deck (",s.deck.length," cards)."]}),w(h,{children:" "}),s.deck.map((m,u)=>{let i=q(m.defId),f=u===ea;return S(h,{bold:f,color:f?"yellow":"white",children:[f?"\u25B6 ":" ",i?.name??m.defId," (",i?.rarity,") \u2014 ",i?.description??""]},m.instanceId)}),w(h,{children:" "}),w(h,{dimColor:!0,children:"\u2191\u2193 navigate Enter remove Esc cancel"})]});if(a==="map"&&s){let m=Qa().sort((u,i)=>u.column-i.column);return S(fe,{flexDirection:"column",children:[S(fe,{justifyContent:"space-between",paddingX:1,children:[S(h,{children:["Gate ",S(h,{bold:!0,color:s.gateHp>40?"green":s.gateHp>20?"yellow":"red",children:[s.gateHp,"/",s.gateMaxHp]})]}),S(h,{children:["Fragments ",w(h,{bold:!0,color:"yellow",children:s.fragments})]}),s.potions.filter(u=>u!==null).length>0&&S(h,{children:["Potions ",w(h,{color:"magenta",children:s.potions.filter(u=>u!==null).length})]}),s.relics.length>0&&S(h,{children:["Relics ",S(h,{color:"magenta",children:["\u2605",s.relics.length]})]}),S(h,{children:["Deck ",w(h,{dimColor:!0,children:s.deck.length})]})]}),(()=>{let u=s.currentNodeId?s.map.nodes.find(i=>i.id===s.currentNodeId):null;return u&&!u.visited?S(h,{color:"yellow",bold:!0,children:[" ","Battle in progress \u2014 Enter to continue."]}):da&&we.showTutorialHints&&!s.currentNodeId?w(h,{color:"green",bold:!0,children:" TIP: \u2694=combat \u2605=elite(harder, more reward) \u25B3=rest $=shop ?=event \u25C6=boss. \u2190\u2192 select, Enter to go."}):null})(),w(qn,{map:s.map,currentNodeId:s.currentNodeId,reachableIds:m.map(u=>u.id),selectedNodeId:m[b]?.id??null})]})}if(a==="reward")return w(On,{cards:x,selectedIndex:G});if(a==="relic_reward")return S(fe,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"magenta",children:"\u25C6 Relic Reward"}),w(h,{dimColor:!0,children:"\u2500".repeat(36)}),w(h,{dimColor:!0,italic:!0,children:"An artifact resonates from the defeated foe."}),w(h,{children:" "}),ce.map((m,u)=>{let i=u===be;return S(fe,{flexDirection:"column",children:[S(h,{children:[w(h,{bold:i,color:i?"yellow":"white",children:i?" \u25B6 ":" "}),S(h,{color:i?"magenta":"white",bold:i,children:["\u2605 ",m.name]})]}),i&&S(h,{dimColor:!0,children:[" ",m.description]})]},m.id)}),w(h,{children:" "}),S(h,{bold:be===ce.length,color:be===ce.length?"yellow":"white",children:[be===ce.length?" \u25B6 ":" ","Skip"]}),w(h,{children:" "}),s&&s.relics.length>0&&S(fe,{children:[w(h,{dimColor:!0,children:"Relics: "}),w(h,{color:"magenta",children:s.relics.join(" \xB7 ")})]}),w(h,{dimColor:!0,children:"\u2191\u2193 navigate Enter select s skip"})]});if(a==="shop"&&s)return w(zn,{items:g,selectedIndex:T,fragments:s.fragments,message:Cr});if(a==="event"&&F)return w(jn,{event:F,selectedChoice:De});if(a==="rest"&&s)return w(Jn,{gateHp:s.gateHp,gateMaxHp:s.gateMaxHp,selectedChoice:U,deckSize:s.deck.length});if(a==="result"){let m=s?Ea(E,s.act,s.ascensionLevel):0;return S(fe,{flexDirection:"column",padding:1,children:[E?S(Va,{children:[w(h,{bold:!0,color:"green",children:"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"}),w(h,{bold:!0,color:"green",children:"\u2502 \u2605 RUN COMPLETE \u2502"}),w(h,{bold:!0,color:"green",children:"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"}),w(h,{children:" "}),wt?S(h,{italic:!0,color:"magenta",children:['"',wt,'"']}):w(h,{italic:!0,children:"The Pale recedes. The Keep endures."}),w(h,{italic:!0,dimColor:!0,children:"Another dawn. Another day of resistance."})]}):S(Va,{children:[w(h,{bold:!0,color:"red",children:"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"}),w(h,{bold:!0,color:"red",children:"\u2502 \u2717 DEFEAT \u2502"}),w(h,{bold:!0,color:"red",children:"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"}),w(h,{children:" "}),w(h,{italic:!0,children:"The Gate has fallen. The Pale consumes all."}),w(h,{italic:!0,dimColor:!0,children:"But the Keep remembers. It always remembers."})]}),w(h,{children:" "}),s&&S(Va,{children:[w(h,{dimColor:!0,children:"\u2500".repeat(32)}),S(h,{children:[" ","Act ",w(h,{bold:!0,children:s.act})," ","Deck ",S(h,{dimColor:!0,children:[s.deck.length," cards"]})," ","Fragments ",w(h,{dimColor:!0,children:s.fragments})]}),S(h,{children:[" ","Echoes earned ",S(h,{bold:!0,color:"cyan",children:["+",m]})]}),w(h,{dimColor:!0,children:"\u2500".repeat(32)})]}),w(h,{children:" "}),w(h,{dimColor:!0,children:"Enter \u2192 The Keep | q \u2192 menu"})]})}if(a==="combat"&&V&&s){let m=da&&we.showTutorialHints&&V.turn<=3?V.turn===1&&Dt<0?"TIP: Press 1-5 to select a card. \u2190\u2192 to target a column. Enter to play. Space to end turn.":V.turn===1&&Dt>=0?"TIP: Arrow keys choose which column to target. Press Enter to play the card.":V.turn===2?"TIP: Enemies with \u2694 will attack your Gate. \u2193 means they advance. Block reduces damage. Press i to inspect enemies.":V.turn===3?'TIP: Cards with type "emplace" can become structures! Select one, press e to toggle emplace mode, then Enter.':null:null;return S(fe,{flexDirection:"column",paddingX:1,children:[S(h,{dimColor:!0,children:["Act ",s.act," | Gate ",s.gateHp,"/",s.gateMaxHp," | Fragments ",s.fragments,s.potions.filter(u=>u!==null).length>0&&` | Potions ${s.potions.filter(u=>u!==null).map(u=>ct.find(f=>f.id===u)?.name??u).join(", ")}`,s.relics.length>0&&` | \u2605 ${s.relics.length}`," | d=deck i=inspect"]}),m&&w(h,{color:"green",bold:!0,children:m}),wt&&w(fe,{borderStyle:"single",borderColor:"magenta",paddingX:1,marginBottom:1,children:S(h,{color:"magenta",italic:!0,children:['"',wt,'"']})}),S(fe,{flexDirection:"row",children:[w(fe,{flexDirection:"column",children:w(Fn,{combat:V,selectedCard:Dt,targetColumn:yt,needsTarget:ra,emplaceMode:kr,message:ja?`End turn with ${V.resolve} Resolve remaining? (carries over) Press Space again to confirm.`:vr,animating:Ka,inspectCol:bt,inspectEnemyIdx:Ya,inspectMode:Mt})}),Mt&&(()=>{let i=V.columns[bt]?.enemies[Ya];if(!i)return S(fe,{flexDirection:"column",width:28,borderStyle:"single",borderColor:"cyan",paddingX:1,marginLeft:1,children:[S(h,{dimColor:!0,children:["No enemy in col ",bt+1,"."]}),w(h,{dimColor:!0,children:"\u2190\u2192 to navigate."})]});let f=Ge(i.templateId),I=(()=>{if(!i.intent)return"Unknown";switch(i.intent.type){case"attack":return`Attack ${i.intent.value} dmg`;case"advance":return"Advance \u2192";case"buff":return"Buff allies";case"debuff":return"Debuff gate";case"shield":return"Shield allies";case"summon":return"Summon";default:return`${i.intent.type} (${i.intent.value})`}})(),A=i.statusEffects.map(M=>{switch(M.type){case"vulnerable":return`VLN \xD7${M.stacks} (${M.duration}t)`;case"weak":return`WK \xD7${M.stacks} (${M.duration}t)`;case"burn":return`BRN \xD7${M.stacks} (${M.duration}t)`;case"empowered":return`EMP \xD7${M.stacks} (${M.duration}t)`;case"fortified":return`FRT \xD7${M.stacks} (${M.duration}t)`;default:return`${M.type} \xD7${M.stacks}`}});return S(fe,{flexDirection:"column",width:28,borderStyle:"single",borderColor:"cyan",paddingX:1,marginLeft:1,children:[S(h,{bold:!0,color:"cyan",children:[f?.symbol??"?"," ",f?.name??i.templateId]}),S(h,{dimColor:!0,children:["Col ",bt+1,", Row ",i.row]}),S(h,{children:["HP: ",S(h,{bold:!0,color:i.hp>i.maxHp*.6?"green":i.hp>i.maxHp*.3?"yellow":"red",children:[i.hp,"/",i.maxHp]})]}),S(h,{children:["Dmg: ",w(h,{bold:!0,children:f?.damage??"?"})," Spd: ",w(h,{bold:!0,children:f?.speed??"?"})]}),w(h,{dimColor:!0,italic:!0,children:f?.description??""}),S(h,{children:["Next: ",w(h,{color:"red",bold:!0,children:I})]}),A.length>0&&A.map((M,ze)=>w(h,{color:"yellow",children:M},ze)),A.length===0&&w(h,{dimColor:!0,children:"No effects"}),w(h,{dimColor:!0,children:"\u2190\u2192 \u2191\u2193 nav i close"})]})})()]})]})}return w(h,{children:"Loading..."})}function gr(e){return w(qt,{children:w(hs,{...e})})}import{jsx as bs}from"react/jsx-runtime";var yr="1.0.4";globalThis.__CODEKEEP_VERSION=yr;var br=new gs;br.name("codekeep").description("CodeKeep: The Pale \u2014 deck-building tactical roguelike in your terminal").version(yr).option("--ascii","Force ASCII-only rendering (no Unicode box drawing)").option("--compact","Compact layout for narrow terminals").option("--tutorial","Replay the tutorial").option("--no-save","Dry-run mode: play without writing to disk").action(e=>{(!process.stdin.isTTY||!process.stdout.isTTY)&&(process.stderr.write(`codekeep requires an interactive terminal.
|
|
19
|
+
`);return`https://github.com/tooyipjee/codekeep/issues/new?title=${t}&body=${r}&labels=bug,crash`}import{Fragment as Nn,jsx as ke,jsxs as Xe}from"react/jsx-runtime";var zt=class extends Io.Component{constructor(t){super(t),this.state={error:null,crashFilePath:null,issueUrl:null}}static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t){try{let r=Bn(t),n={timestamp:new Date().toISOString(),error:t.message,stack:t.stack,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch},a=Hn(n);this.setState({crashFilePath:r,issueUrl:a})}catch{}}render(){return this.state.error?Xe(Co,{flexDirection:"column",padding:1,children:[ke(se,{bold:!0,color:"red",children:"\u25C6 CodeKeep \u2014 Something went wrong"}),ke(se,{children:" "}),ke(se,{color:"red",children:this.state.error.message}),ke(se,{children:" "}),ke(se,{dimColor:!0,children:"Your save file is at:"}),Xe(se,{children:[" ","~/.config/codekeep/game.json"]}),ke(se,{children:" "}),this.state.crashFilePath&&Xe(Nn,{children:[ke(se,{dimColor:!0,children:"Crash report saved to:"}),Xe(se,{children:[" ",this.state.crashFilePath]}),ke(se,{children:" "})]}),this.state.issueUrl&&Xe(Nn,{children:[ke(se,{dimColor:!0,children:"Report this bug:"}),Xe(se,{color:"cyan",children:[" ",this.state.issueUrl]}),ke(se,{children:" "})]}),ke(se,{dimColor:!0,children:"If the game won't start, try:"}),Xe(se,{bold:!0,children:[" ","codekeep --tutorial"]}),ke(se,{children:" "}),ke(se,{dimColor:!0,children:"Press Ctrl+C to exit"})]}):this.props.children}};import{Box as jt,Text as me}from"ink";import So from"react";import{Box as xe,Text as P}from"ink";import{Fragment as Bo,jsx as D,jsxs as U}from"react/jsx-runtime";var ye=12;function Ma(e,t,r){let n=t>0?e/t:0,a=Math.max(0,Math.min(r,Math.round(n*r)));return"\u2588".repeat(a)+"\u2591".repeat(Math.max(0,r-a))}function Eo(e,t){let r=t>0?e/t*100:0;return r>60?"green":r>30?"yellow":"red"}function Po(e){if(!e)return{text:"",color:"white"};switch(e.type){case"advance":return{text:"\u2193Mv",color:"white"};case"attack":return{text:`\u2694${e.value}`,color:"red"};case"buff":return{text:"\u25B2Buf",color:"magenta"};case"debuff":return{text:"\u25BCHex",color:"magenta"};case"shield":return{text:"\u25C8Shd",color:"blue"};case"summon":return{text:"+Sum",color:"magenta"};default:return{text:"",color:"white"}}}function Ao(e){let t=[];for(let a of e.statusEffects)switch(a.type){case"vulnerable":t.push({text:`V${a.stacks}`,color:"yellow"});break;case"weak":t.push({text:`W${a.stacks}`,color:"green"});break;case"burn":t.push({text:`B${a.stacks}`,color:"red"});break;case"empowered":t.push({text:`E${a.stacks}`,color:"magenta"});break;case"fortified":t.push({text:`F${a.stacks}`,color:"blue"});break}let r=0,n=[];for(let a of t){if(r+a.text.length+1>ye)break;n.push(a),r+=a.text.length+1}return n}function Do({enemy:e,isPrimaryTarget:t,isInColumn:r,isInspected:n}){let a=$e(e.templateId),o=a?.name??"???",l=a?.symbol??"?",c=n?"cyan":t||r?"yellow":void 0,s=!t&&!r&&!n,p=`${l} ${o}`,b=ye-1,y=p.length>b?p.slice(0,b-1)+"\u2026":p,x=Math.max(0,ye-1-y.length),_=Ma(e.hp,e.maxHp,4),N=`${e.hp}`,d=Po(e.intent),g=6+N.length,R=Math.max(0,ye-g-d.text.length),T=Ao(e),G=T.reduce((Ie,Re)=>Ie+Re.text.length+1,0),F=Math.max(0,ye-G);return U(xe,{flexDirection:"column",children:[U(P,{children:[D(P,{color:c,dimColor:s,bold:t||n,children:"\u256D\u2500"}),D(P,{color:n?"cyan":t?"yellow":"red",bold:!0,children:y}),U(P,{color:c,dimColor:s,children:["\u2500".repeat(x),"\u256E"]})]}),U(P,{children:[D(P,{color:c,dimColor:s,children:"\u2502"}),U(P,{color:Eo(e.hp,e.maxHp),children:[" ",_]}),U(P,{bold:!0,children:[" ",N]}),D(P,{children:" ".repeat(R)}),D(P,{color:d.color,bold:!0,children:d.text}),D(P,{color:c,dimColor:s,children:"\u2502"})]}),U(P,{children:[D(P,{color:c,dimColor:s,children:"\u2570"}),T.length>0?U(Bo,{children:[T.map((Ie,Re)=>U(So.Fragment,{children:[D(P,{color:c,dimColor:s,children:"\u2500"}),D(P,{color:Ie.color,children:Ie.text})]},Re)),U(P,{color:c,dimColor:s,children:["\u2500".repeat(F),"\u256F"]})]}):U(P,{color:c,dimColor:s,children:["\u2500".repeat(ye),"\u256F"]})]})]})}function Ro(){return U(xe,{flexDirection:"column",children:[D(P,{children:" ".repeat(14)}),D(P,{dimColor:!0,children:" \xB7 "}),D(P,{children:" ".repeat(14)})]})}function Mo({col:e}){if(!e.emplacement)return U(xe,{flexDirection:"column",children:[D(P,{children:" ".repeat(ye+2)}),U(P,{dimColor:!0,children:[" ","\u254C".repeat(ye-2)," "]}),D(P,{children:" ".repeat(ye+2)})]});let r=z(e.emplacement.cardDefId)?.name??"Wall",n=e.emplacement.hp,a=Ma(n,e.emplacement.maxHp,4),o=`${n}`,l=7+o.length,c=ye-l,p=` \u25C7${r.length>c?r.slice(0,Math.max(1,c-1))+"\u2026":r} ${a}${o}`.padEnd(ye).slice(0,ye);return U(xe,{flexDirection:"column",children:[U(P,{children:[D(P,{color:"cyan",children:"\u2554"}),D(P,{color:"cyan",children:"\u2550".repeat(ye)}),D(P,{color:"cyan",children:"\u2557"})]}),U(P,{children:[D(P,{color:"cyan",children:"\u2551"}),D(P,{color:"cyan",bold:!0,children:p}),D(P,{color:"cyan",children:"\u2551"})]}),U(P,{children:[D(P,{color:"cyan",children:"\u255A"}),D(P,{color:"cyan",children:"\u2550".repeat(ye)}),D(P,{color:"cyan",children:"\u255D"})]})]})}function Gn({columns:e,targetColumn:t,showTarget:r,gateHp:n,gateMaxHp:a,gateBlock:o,inspectCol:l,inspectEnemyIdx:c}){let s=a>0?Math.round(n/a*100):0,p=s>60?"green":s>30?"yellow":"red",b=new Set;for(let y of e)if(y.enemies.length>0){let x=y.enemies.reduce((_,N)=>_.row>=N.row?_:N);b.add(x.instanceId)}return U(xe,{flexDirection:"column",children:[D(xe,{justifyContent:"center",children:D(P,{dimColor:!0,children:"\xB7 \xB7 \xB7 \xB7 \xB7 \xB7 \xB7 t h e p a l e \xB7 \xB7 \xB7 \xB7 \xB7 \xB7 \xB7"})}),D(xe,{children:e.map((y,x)=>D(xe,{width:16,justifyContent:"center",children:r&&x===t?D(P,{color:"yellow",bold:!0,children:" \u25BC\u25BC\u25BC"}):D(P,{children:" "})},x))}),Array.from({length:ct},(y,x)=>D(xe,{children:e.map((_,N)=>{let d=_.enemies.find(G=>G.row===x),g=r&&N===t,R=g&&!!d&&b.has(d.instanceId),T=l===N&&!!d&&_.enemies.indexOf(d)===(c??-1);return D(xe,{width:16,children:d?D(Do,{enemy:d,isPrimaryTarget:R,isInColumn:g&&!R,isInspected:T}):D(Ro,{})},N)})},x)),D(xe,{children:e.map((y,x)=>D(xe,{width:16,children:D(Mo,{col:y})},x))}),U(xe,{paddingX:1,children:[U(P,{bold:!0,color:p,children:["\u25C6 GATE ",Ma(n,a,20)," ",n,"/",a]}),o>0&&U(P,{color:"cyan",children:[" \u25C7 Block: ",o]})]})]})}import{Box as Ba,Text as le}from"ink";import{jsx as Ce,jsxs as We}from"react/jsx-runtime";var Wn=16;function Ho(e){switch(e){case"uncommon":return"green";case"rare":return"blue";case"legendary":return"magenta";default:return"white"}}function No(e){switch(e){case"armament":return"\u2694";case"fortification":return"\u25C7";case"edict":return"\u2726";case"wild":return"\u25C8";default:return"\xB7"}}function Go(e,t){return{text:"\u25C6".repeat(e),affordable:e<=t}}function $o(e){let t=[];for(let a of e.effects)switch(a.type){case"damage":t.push(`${a.value}dmg${a.target==="all"?"\u2605":a.target==="column"?"\u2195":""}`);break;case"block":t.push(`${a.value}blk`);break;case"draw":t.push(`+${a.value}drw`);break;case"heal":t.push(`${a.value}hp`);break;case"resolve":t.push(`+${a.value}res`);break;case"burn":t.push(`${a.value}brn`);break;case"vulnerable":t.push("vuln");break;case"weak":t.push("weak");break;case"self_damage":t.push(`-${a.value}hp`);break;case"exhaust_self":t.push("exile");break;default:break}let r=t.join(" "),n=Wn-2;return r.length>n?r.slice(0,n-1)+"\u2026":r}function $n(e,t){let r=[...e].length;return r>=t?e.slice(0,t):e+" ".repeat(t-r)}function Wo({def:e,index:t,isSelected:r,resolve:n}){let a=Wn-2,o=r?"yellow":void 0,l=!r,c=e.cost<=n,s=Go(e.cost,n),p=e.type==="emplace"?"\u2692":"",b=`${t+1}`,y=` ${b} ${s.text}`,x=Math.max(0,a-b.length-1-s.text.length-1),_=`${No(e.category)} ${e.name}${p}`,N=_.length>a?_.slice(0,a-1)+"\u2026":_,d=$o(e);return We(Ba,{flexDirection:"column",children:[We(le,{children:[Ce(le,{color:o,dimColor:l,children:"\u250C"}),Ce(le,{bold:r,dimColor:l&&!r,children:y}),Ce(le,{color:s.affordable?"cyan":"red",children:""}),We(le,{color:o,dimColor:l,children:[" ".repeat(x),"\u2510"]})]}),We(le,{children:[Ce(le,{color:o,dimColor:l,children:"\u2502"}),Ce(le,{color:c?Ho(e.rarity):"gray",bold:r,children:$n(N,a)}),Ce(le,{color:o,dimColor:l,children:"\u2502"})]}),We(le,{children:[Ce(le,{color:o,dimColor:l,children:"\u2502"}),Ce(le,{dimColor:!0,children:$n(d,a)}),Ce(le,{color:o,dimColor:l,children:"\u2502"})]}),Ce(le,{children:We(le,{color:o,dimColor:l,children:["\u2514","\u2500".repeat(a),"\u2518"]})})]})}function Fn({hand:e,selectedIndex:t,resolve:r}){if(e.length===0)return We(le,{dimColor:!0,children:[" ","No cards in hand."]});let n=t>=0?z(e[t]?.defId):null;return We(Ba,{flexDirection:"column",children:[Ce(Ba,{flexDirection:"row",children:e.map((a,o)=>{let l=z(a.defId);return l?Ce(Wo,{def:l,index:o,isSelected:o===t,resolve:r},a.instanceId):null})}),n&&We(le,{dimColor:!0,children:[" ",n.description]})]})}import{jsx as Te,jsxs as Ue}from"react/jsx-runtime";function Fo(e){switch(e.type){case"damage_dealt":return` dealt ${e.data.damage} damage`;case"enemy_advance":return` enemy advances to row ${e.data.row}`;case"gate_hit":return` gate hit for ${e.data.damage}${e.data.blocked?` (${e.data.blocked} blocked)`:""}`;case"enemy_killed":return" enemy destroyed";case"block_gained":return` +${e.data.value} Block`;case"emplacement_placed":return` emplacement placed in col ${e.data.column+1}`;case"emplacement_triggered":return` emplacement triggered in col ${e.data.column+1}`;case"emplacement_destroyed":return` emplacement destroyed in col ${e.data.column+1}`;case"status_applied":return` applied ${e.data.type} (${e.data.value})`;case"card_played":return` played ${z(e.data.cardId)?.name??e.data.cardId}`;case"turn_start":return`\u2500 Turn ${e.data.turn} \u2500`;default:return""}}function Ln({combat:e,selectedCard:t,targetColumn:r,needsTarget:n,emplaceMode:a=!1,message:o,animating:l=!1,inspectCol:c,inspectEnemyIdx:s,inspectMode:p=!1}){let b=e.columns.reduce((y,x)=>y+x.enemies.length,0);return Ue(jt,{flexDirection:"column",width:82,children:[Ue(jt,{justifyContent:"space-between",children:[Te(me,{bold:!0,color:"yellow",children:"\u25C6 The Pale"}),Ue(me,{children:["Turn ",Te(me,{bold:!0,color:"white",children:e.turn})," ",Te(me,{color:e.resolve>e.maxResolve?"yellow":"cyan",children:"\u25C6".repeat(Math.max(0,e.resolve))}),Te(me,{dimColor:!0,children:"\u25C7".repeat(Math.max(0,e.maxResolve-e.resolve))})," ","Enemies ",Te(me,{bold:!0,color:"red",children:b})," ","Draw ",Te(me,{dimColor:!0,children:e.drawPile.length})," / ","Discard ",Te(me,{dimColor:!0,children:e.discardPile.length})]})]}),Te(Gn,{columns:e.columns,targetColumn:r,showTarget:(n||a)&&t>=0,gateHp:e.gateHp,gateMaxHp:e.gateMaxHp,gateBlock:e.gateBlock,inspectCol:p?c:void 0,inspectEnemyIdx:p?s:void 0}),(e.outcome==="undecided"||l)&&Te(Fn,{hand:e.hand,selectedIndex:t,resolve:e.resolve}),(()=>{let y=e.events.filter(x=>x.turn>=e.turn-1).slice(-5);return y.length===0?null:Ue(jt,{flexDirection:"column",marginTop:0,children:[Te(me,{dimColor:!0,children:"\u2500\u2500\u2500 Log \u2500\u2500\u2500"}),y.map((x,_)=>{let N=Fo(x);if(!N)return null;let d=x.type==="turn_start";return Te(me,{dimColor:!d,color:d?"white":void 0,children:N},`${x.turn}-${x.type}-${_}`)})]})})(),o&&Ue(me,{color:l?"red":"yellow",bold:!0,children:[" ",l?"\u26A1 ":"",o]}),e.phase==="player"&&!l&&Ue(me,{dimColor:!0,children:[" ",e.hand.length>0?`1-${e.hand.length} card `:"","\u2190\u2192 column Enter play e emplace p potion i inspect Space end d deck q quit"]}),l&&Ue(me,{dimColor:!0,children:[" ","Resolving enemy actions..."]}),e.outcome!=="undecided"&&!l&&Ue(jt,{flexDirection:"column",marginTop:1,paddingX:1,children:[Te(me,{bold:!0,color:e.outcome==="win"?"green":"red",children:e.outcome==="win"?"\u2605 VICTORY \u2014 The siege is broken!":"\u2717 DEFEAT \u2014 The Gate has fallen."}),Te(me,{dimColor:!0,children:"Press Enter to continue."})]})]})}import{Box as Vn,Text as be}from"ink";import{jsx as Fe,jsxs as Je}from"react/jsx-runtime";function On(e){switch(e){case"uncommon":return"green";case"rare":return"blue";case"legendary":return"magenta";default:return"white"}}function Lo(e){switch(e){case"uncommon":return"\u25C7 uncommon";case"rare":return"\u25C6 rare";case"legendary":return"\u2605 legendary";default:return"\xB7 common"}}function Vo(e){switch(e){case"armament":return"\u2694";case"fortification":return"\u25C7";case"edict":return"\u2726";case"wild":return"\u25C8";default:return"\xB7"}}function Kn({cards:e,selectedIndex:t}){let r=t>=e.length;return Je(Vn,{flexDirection:"column",padding:1,children:[Fe(be,{bold:!0,color:"yellow",children:"\u25C6 Card Reward"}),Fe(be,{dimColor:!0,children:"\u2500".repeat(36)}),Fe(be,{dimColor:!0,italic:!0,children:"Choose a card to add to your deck."}),Fe(be,{children:" "}),e.map((n,a)=>{let o=a===t;return Je(Vn,{flexDirection:"column",marginBottom:0,children:[Je(be,{children:[Fe(be,{bold:o,color:o?"yellow":"white",children:o?" \u25B6 ":" "}),Je(be,{color:On(n.rarity),bold:o,children:[Vo(n.category)," ",n.name]}),Je(be,{dimColor:!0,children:[" ","[",n.cost,"]"," "]}),Fe(be,{color:On(n.rarity),dimColor:!0,children:Lo(n.rarity)})]}),o&&Je(be,{dimColor:!0,children:[" ",n.description]})]},n.id)}),Fe(be,{children:" "}),Je(be,{bold:r,color:r?"yellow":"white",children:[r?" \u25B6 ":" ","Skip"]}),Fe(be,{children:" "}),Fe(be,{dimColor:!0,children:"\u2191\u2193 navigate Enter select s skip"})]})}import{Box as Oo,Text as Et}from"ink";import{jsx as Na,jsxs as Ha}from"react/jsx-runtime";function Ko(e){switch(e){case"uncommon":return"green";case"rare":return"blue";case"legendary":return"magenta";default:return"white"}}function Un({deck:e}){let t=new Map;for(let n of e)t.set(n.defId,(t.get(n.defId)??0)+1);let r=[...t.entries()].map(([n,a])=>({def:z(n),count:a})).filter(n=>n.def).sort((n,a)=>{let o={common:0,uncommon:1,rare:2,legendary:3},l=o[n.def.rarity]??0,c=o[a.def.rarity]??0;return l!==c?l-c:n.def.name.localeCompare(a.def.name)});return Ha(Oo,{flexDirection:"column",padding:1,children:[Ha(Et,{bold:!0,color:"yellow",children:["\u25C6 Your Deck"," (",e.length," cards)"]}),Na(Et,{children:" "}),r.map(({def:n,count:a})=>Ha(Et,{color:Ko(n.rarity),children:[a>1?`${a}x `:" ",n.name," [",n.cost,"] \u2014 ",n.description]},n.id)),Na(Et,{children:" "}),Na(Et,{dimColor:!0,children:"Press q or Esc to go back."})]})}import Uo from"react";import{Box as Yn,Text as ue}from"ink";import{jsx as pe,jsxs as Ga}from"react/jsx-runtime";var qn=6,Yo=4,Xt=(Yo-1)*qn+6;function $a(e){return e*qn+3}function qo(e){switch(e){case"combat":return"\u2694";case"elite":return"\u2605";case"rest":return"\u25B3";case"shop":return"$";case"event":return"?";case"boss":return"\u25C6";default:return"\xB7"}}function zo(e,t,r,n){if(n)return"cyan";if(r)return"yellow";if(e.visited||!t)return"gray";switch(e.type){case"combat":return"white";case"elite":return"red";case"rest":return"green";case"shop":return"yellow";case"event":return"magenta";case"boss":return"red";default:return"white"}}function jo(e,t,r,n){let a=[],o=0,l=[...e].sort((c,s)=>c.column-s.column);for(let c of l){let s=$a(c.column),p=qo(c.type),b=c.id===t,y=c.id===r,x=b?`[${p}]`:y?`(${p})`:` ${p} `,_=s-Math.floor(x.length/2);_>o&&a.push({text:" ".repeat(_-o)}),a.push({text:x,color:zo(c,n.has(c.id),b,y),bold:b||y,dim:c.visited&&!y&&!b}),o=_+x.length}return a}function Xo(e,t){let r=Array(Xt).fill(" "),n=new Map;function a(o,l){o<0||o>=Xt||(n.has(o)||n.set(o,new Set),n.get(o).add(l))}for(let o of e){let l=$a(o.column);for(let c of o.connections){let s=t.find(b=>b.id===c);if(!s)continue;let p=$a(s.column);l===p?a(l,"\u2502"):a(Math.round((l+p)/2),p>l?"\u2572":"\u2571")}}for(let[o,l]of n)l.has("\u2572")&&l.has("\u2571")?r[o]="\u2573":l.has("\u2502")?r[o]="\u2502":l.has("\u2572")?r[o]="\u2572":l.has("\u2571")&&(r[o]="\u2571");return r.join("")}function zn({map:e,currentNodeId:t,reachableIds:r,selectedNodeId:n}){let a=new Set(r),o=Math.max(...e.nodes.map(c=>c.row)),l=[];for(let c=0;c<=o;c++)l.push(e.nodes.filter(s=>s.row===c).sort((s,p)=>s.column-p.column));return Ga(Yn,{flexDirection:"column",padding:1,children:[pe(ue,{bold:!0,color:"yellow",children:"\u25C6 Act "+e.act+" \u2014 Choose your path"}),pe(ue,{dimColor:!0,children:"\u2500".repeat(Xt)}),pe(ue,{children:" "}),l.map((c,s)=>{let p=jo(c,n,t,a);return Ga(Uo.Fragment,{children:[pe(ue,{children:p.map((b,y)=>pe(ue,{color:b.color,bold:b.bold,dimColor:b.dim,children:b.text},y))}),s<o&&pe(ue,{dimColor:!0,children:Xo(c,l[s+1]??[])})]},s)}),pe(ue,{children:" "}),pe(ue,{dimColor:!0,children:"\u2500".repeat(Xt)}),Ga(Yn,{gap:2,children:[pe(ue,{dimColor:!0,children:"\u2694 fight"}),pe(ue,{dimColor:!0,color:"red",children:"\u2605 elite"}),pe(ue,{dimColor:!0,color:"green",children:"\u25B3 rest"}),pe(ue,{dimColor:!0,color:"yellow",children:"$ shop"}),pe(ue,{dimColor:!0,color:"magenta",children:"? event"}),pe(ue,{dimColor:!0,color:"red",children:"\u25C6 boss"})]}),pe(ue,{dimColor:!0,children:"\u2190\u2192 select Enter proceed d deck q quit"})]})}import{Box as Jo,Text as Ye}from"ink";import{jsx as ht,jsxs as Wa}from"react/jsx-runtime";function jn({items:e,selectedIndex:t,fragments:r,message:n}){return Wa(Jo,{flexDirection:"column",padding:1,children:[Wa(Ye,{bold:!0,color:"yellow",children:["\u25C6 Shop \u2014 ",r," fragments"]}),ht(Ye,{dimColor:!0,children:"\u2500".repeat(36)}),ht(Ye,{children:" "}),e.map((a,o)=>{let l=o===t,c=r>=a.cost,s="",p="";return a.type==="card"&&a.cardDef?(p="[Card]",s=`${a.cardDef.name} (${a.cardDef.rarity}) \u2014 ${a.cardDef.description}`):a.type==="potion"&&a.potionDef?(p="[Potion]",s=`${a.potionDef.name} \u2014 ${a.potionDef.description}`):a.type==="card_remove"&&(p="[Service]",s="Remove a card from your deck"),Wa(Ye,{bold:l,color:l?"yellow":c?"white":"gray",children:[l?"\u25B6 ":" ",p," ",s," [",a.cost,"f]"]},`${a.type}-${a.cardDef?.id??a.potionDef?.id??"remove"}-${o}`)}),e.length===0&&ht(Ye,{dimColor:!0,children:" (Shop is empty)"}),ht(Ye,{children:" "}),n&&ht(Ye,{color:n.startsWith("Purchased")?"green":"red",children:n}),ht(Ye,{dimColor:!0,children:"\u2191\u2193 navigate Enter buy q leave"})]})}import{Box as Qo,Text as Qe}from"ink";import{jsx as Pt,jsxs as Fa}from"react/jsx-runtime";function Xn({event:e,selectedChoice:t}){return Fa(Qo,{flexDirection:"column",padding:1,children:[Fa(Qe,{bold:!0,color:"magenta",children:["\u25C6 ",e.name]}),Pt(Qe,{children:" "}),Pt(Qe,{children:e.description}),Pt(Qe,{children:" "}),e.choices.map((r,n)=>Fa(Qe,{bold:n===t,color:n===t?"yellow":"white",children:[n===t?"\u25B6 ":" ",n+1,". ",r.label]},n)),Pt(Qe,{children:" "}),Pt(Qe,{dimColor:!0,children:"\u2191\u2193 navigate Enter choose (you must choose)"})]})}import{Box as Jn,Text as _e}from"ink";import{jsx as Pe,jsxs as Jt}from"react/jsx-runtime";function Qn({gateHp:e,gateMaxHp:t,selectedChoice:r,deckSize:n}){let a=Math.floor(t*.3),o=Math.min(t,e+a),l=n>5,c=[{label:"Rest",detail:`Heal ${a} Gate HP (${e} \u2192 ${o}/${t})`,color:"green",available:!0},{label:"Thin",detail:l?`Remove a card from your deck (${n} cards)`:`Minimum deck size reached (${n} cards)`,color:"cyan",available:l},{label:"Leave",detail:"Continue without resting",color:"white",available:!0}];return Jt(Jn,{flexDirection:"column",padding:1,children:[Pe(_e,{bold:!0,color:"green",children:"\u25C6 Rest Site"}),Pe(_e,{dimColor:!0,children:"\u2500".repeat(36)}),Pe(_e,{children:" "}),Pe(_e,{dimColor:!0,italic:!0,children:"The campfire crackles against the encroaching Pale."}),Pe(_e,{dimColor:!0,italic:!0,children:"A moment of warmth in the endless grey."}),Pe(_e,{children:" "}),c.map(({label:s,detail:p,color:b,available:y},x)=>{let _=x===r;return Jt(Jn,{flexDirection:"column",children:[Jt(_e,{children:[Pe(_e,{bold:_,color:_?"yellow":"white",children:_?" \u25B6 ":" "}),Pe(_e,{color:y?_?b:"white":"gray",bold:_,dimColor:!y,children:s}),!y&&Pe(_e,{dimColor:!0,children:" (unavailable)"})]}),_&&Jt(_e,{dimColor:!0,children:[" ",p]})]},x)}),Pe(_e,{children:" "}),Pe(_e,{dimColor:!0,children:"\u2191\u2193 navigate Enter choose"})]})}import{Box as gt,Text as oe}from"ink";import{jsx as ce,jsxs as qe}from"react/jsx-runtime";var nr=[{type:"structure",id:"forge",name:"Forge",symbol:"#"},{type:"structure",id:"archive",name:"Archive",symbol:"%"},{type:"structure",id:"foundry",name:"Foundry",symbol:"&"},{type:"structure",id:"beacon_tower",name:"Beacon",symbol:"^"},{type:"structure",id:"sanctum_hall",name:"Sanctum",symbol:"+"}],rr=[{type:"npc",id:"wren",name:"Wren",symbol:"W"},{type:"npc",id:"sable",name:"Sable",symbol:"S"},{type:"npc",id:"duskmar",name:"Duskmar",symbol:"D"},{type:"npc",id:"mott",name:"Mott",symbol:"M"},{type:"npc",id:"pale_visitor",name:"Pale Visitor",symbol:"?"}],Zo={type:"gate",id:"gate",name:"The Gate",symbol:">"},Ze=[...nr,...rr,Zo],es={wren:"Wren, the Steward",sable:"Sable, the Archivist",duskmar:"Duskmar, First Wall",mott:"Mott, the Salvager",pale_visitor:"The Pale Visitor"},ts={wren:"Tends the Keep with quiet devotion",sable:"Remembers what the Pale erases",duskmar:"The wall that never fell",mott:"Finds treasure in the wreckage",pale_visitor:"Speaks in riddles from beyond"},Zn=17,er=56,ie=1,re=15,j=3,Z=52,as={forge:{row:3,col:6,width:11},archive:{row:3,col:20,width:11},foundry:{row:3,col:34,width:11},beacon_tower:{row:10,col:6,width:11},sanctum_hall:{row:10,col:20,width:11}},ns={wren:{row:8,col:11},sable:{row:8,col:25},duskmar:{row:8,col:39},mott:{row:9,col:44},pale_visitor:{row:13,col:46}};function La(e,t){return e>=t?"green":e>=2?"cyan":e>=1?"yellow":"white"}function tr(e){return e>=4?"magenta":e>=2?"cyan":e>=1?"yellow":"white"}function ar(e,t,r){let n=[];for(let a=0;a<t;a++)n.push(a<e?ce(oe,{color:r,bold:!0,children:"\u25C6"},a):ce(oe,{dimColor:!0,children:"\u25C7"},a));return n}function or({keep:e,selectedId:t,message:r,isFirstVisit:n}){let a=[],o=[];for(let d=0;d<Zn;d++){let g=[],R=[];for(let T=0;T<er;T++)g.push((d+T)%2===0?"~":" "),R.push({color:"white",bold:!1,dim:!0});a.push(g),o.push(R)}let l="the pale",c=Math.floor((er-l.length)/2);for(let d=0;d<l.length;d++)a[0][c+d]=l[d],a[Zn-1][c+d]=l[d];a[ie][j]="\u2554",a[ie][Z]="\u2557",a[re][j]="\u255A",a[re][Z]="\u255D";for(let d=j+1;d<Z;d++)a[ie][d]="\u2550",a[re][d]="\u2550",o[ie][d]={color:"white",bold:!1,dim:!0},o[re][d]={color:"white",bold:!1,dim:!0};for(let d=ie+1;d<re;d++)a[d][j]="\u2551",a[d][Z]="\u2551",o[d][j]={color:"white",bold:!1,dim:!0},o[d][Z]={color:"white",bold:!1,dim:!0};for(let d of[[ie,j],[ie,Z],[re,j],[re,Z]])o[d[0]][d[1]]={color:"white",bold:!1,dim:!0};for(let d of[j+1,8,14,38,44,Z-1])d>j&&d<Z&&(a[ie][d]="\u25B2",o[ie][d]={color:"white",bold:!0,dim:!1});for(let d of[j+1,8,14,42,48,Z-1])d>j&&d<Z&&a[re][d]==="\u2550"&&(a[re][d]="\u25B2",o[re][d]={color:"white",bold:!0,dim:!1});let s=" THE KEEP ",p=j+1+Math.floor((Z-j-1-s.length)/2);for(let d=0;d<s.length;d++)a[ie][p+d]=s[d],o[ie][p+d]={color:"yellow",bold:!0,dim:!1};for(let d=ie+1;d<re;d++)for(let g=j+1;g<Z;g++)a[d][g]=" ",o[d][g]={color:"white",bold:!1,dim:!1};for(let[d,g]of[[ie+1,j+1],[ie+1,Z-1],[re-1,j+1],[re-1,Z-1]])a[d][g]="*",o[d][g]={color:"yellow",bold:!0,dim:!1};for(let d=j+2;d<Z-1;d++)a[8][d]===" "&&(a[8][d]="\xB7",o[8][d]={color:"white",bold:!1,dim:!0});for(let d of[11,25,39])for(let g=6;g<=(d===39?8:10);g++)a[g][d]===" "&&(a[g][d]="\xB7",o[g][d]={color:"white",bold:!1,dim:!0});for(let d=9;d<re;d++)a[d][28]===" "&&(a[d][28]="\xB7",o[d][28]={color:"white",bold:!1,dim:!0});for(let[d,g]of[[7,16],[7,34],[7,48],[9,8],[9,18],[9,36],[13,8],[13,18],[13,36]])d>ie&&d<re&&a[d][g]===" "&&(a[d][g]="\xB7",o[d][g]={color:"white",bold:!1,dim:!0});for(let d of nr){let g=as[d.id];if(!g)continue;let R=ut.find(E=>E.id===d.id),T=pt(e,d.id),G=R?.maxLevel??3,F=d.id===t,Ie=F?"yellow":La(T,G),Re=F||T>=G,ze=!F&&T===0,Y=g.row,v=g.col,k=g.width,S=k-2,O=Math.ceil((S-d.name.length)/2),X=S-d.name.length-O;a[Y][v]=F?"\u250F":"\u250C";let $=v+1;for(let E=0;E<O;E++)a[Y][$]=F?"\u2501":"\u2500",$++;for(let E=0;E<d.name.length;E++)a[Y][$]=d.name[E],$++;for(let E=0;E<X;E++)a[Y][$]=F?"\u2501":"\u2500",$++;a[Y][v+k-1]=F?"\u2513":"\u2510",a[Y+1][v]=F?"\u2503":"\u2502";for(let E=1;E<k-1;E++)a[Y+1][v+E]=" ";a[Y+1][v+Math.floor(k/2)]=d.symbol,a[Y+1][v+k-1]=F?"\u2503":"\u2502",a[Y+2][v]=F?"\u2517":"\u2514";for(let E=1;E<k-1;E++)a[Y+2][v+E]=F?"\u2501":"\u2500";a[Y+2][v+k-1]=F?"\u251B":"\u2518";for(let E=0;E<3;E++)for(let q=0;q<k;q++)o[Y+E][v+q]={color:Ie,bold:Re,dim:ze};let ee=Y+3;if(ee>ie&&ee<re){let E=v+Math.floor((k-G)/2);for(let q=0;q<G;q++)a[ee][E+q]=q<T?"\u25C6":"\u25C7",o[ee][E+q]={color:q<T?F?"yellow":La(T,G):"white",bold:q<T,dim:q>=T}}}for(let d of rr){let g=ns[d.id];if(!g)continue;let T=e.npcs.find(Ie=>Ie.id===d.id)?.tier??0,G=d.id===t,F=G?"yellow":tr(T);G&&(g.col-1>j&&(a[g.row][g.col-1]="[",o[g.row][g.col-1]={color:"yellow",bold:!0,dim:!1}),g.col+1<Z&&(a[g.row][g.col+1]="]",o[g.row][g.col+1]={color:"yellow",bold:!0,dim:!1})),a[g.row][g.col]=d.symbol,o[g.row][g.col]={color:F,bold:G||T>=3,dim:!1}}{let d=t==="gate",g=d?"\u2524 ENTER THE PALE \u251C":"\u2561 ENTER THE PALE \u255E",R=j+1+Math.floor((Z-j-1-g.length)/2);for(let T=0;T<g.length;T++)a[re][R+T]=g[T],o[re][R+T]={color:d?"yellow":"green",bold:!0,dim:!1}}for(let d=11;d<=14;d++)for(let g=48;g<Z;g++)(a[d][g]===" "||a[d][g]==="\xB7")&&(a[d][g]="~",o[d][g]={color:"white",bold:!1,dim:!0});let b=Ze.find(d=>d.id===t),y="",x="",_="cyan",N=null;if(b)if(b.type==="structure"){let d=ut.find(T=>T.id===b.id),g=pt(e,b.id),R=d?.maxLevel??3;if(_=La(g,R),y=b.name,g>=R)x=`${d?.description??""} (fully upgraded)`;else{let T=d?.upgradeCost(g+1)??0;x=`${d?.description??""} \u2014 ${T} Echoes to upgrade`}N=qe(oe,{children:[" ",ar(g,R,_)]})}else if(b.type==="npc"){let g=e.npcs.find(R=>R.id===b.id)?.tier??0;_=tr(g),y=es[b.id]??b.id,x=ts[b.id]??"",N=qe(oe,{children:[" ",ar(g,5,_)]})}else b.type==="gate"&&(_="green",y="The Gate",x="Step beyond the walls. Face the Pale.");return qe(gt,{flexDirection:"column",children:[qe(gt,{justifyContent:"space-between",paddingX:1,children:[ce(oe,{bold:!0,color:"yellow",children:"\u25C6 The Keep"}),qe(oe,{children:["Echoes ",ce(oe,{bold:!0,color:"cyan",children:e.echoes})," ","Runs ",ce(oe,{dimColor:!0,children:e.totalRuns})," ","Wins ",ce(oe,{dimColor:!0,children:e.totalWins})," ","Ascension ",ce(oe,{dimColor:!0,children:e.highestAscension})]})]}),ce(gt,{flexDirection:"column",children:a.map((d,g)=>ce(oe,{children:d.map((R,T)=>{let G=o[g][T];return ce(oe,{color:G.color,bold:G.bold,dimColor:G.dim,children:R},T)})},g))}),qe(gt,{flexDirection:"column",paddingX:1,children:[b?qe(gt,{flexDirection:"column",children:[qe(gt,{children:[ce(oe,{bold:!0,color:_,children:y}),N]}),ce(oe,{dimColor:!0,children:x})]}):ce(oe,{dimColor:!0,italic:!0,children:"Select a building or NPC."}),n&&!r&&ce(oe,{color:"green",children:"TIP: Upgrade buildings with Echoes. Talk to NPCs. Enter the Gate to start a run."}),r?ce(oe,{color:"yellow",bold:!0,children:r}):null,ce(oe,{dimColor:!0,children:"\u2191\u2193/\u2190\u2192 select Enter interact q back"})]})]})}import{Box as sr,Text as Le}from"ink";import{jsx as et,jsxs as Qt}from"react/jsx-runtime";var lr=[{title:"Welcome to CodeKeep",lines:["You are the Warden of the Keep \u2014 the last fortress","standing against the Pale, an erasure that consumes all.","","Your goal: survive 3 Acts of increasingly dangerous","enemies by playing cards and building defenses."]},{title:"The Gate",lines:["Your Gate is your lifeline. It has HP (hit points).","If Gate HP reaches 0, your run is over.",""," \u25C6 Gate \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 70/70","","Enemies advance toward the Gate. When they reach it,","they deal damage. Block reduces incoming damage."]},{title:"Combat Basics",lines:["Each turn you gain 3 Resolve (mana) to play cards.","Unspent Resolve carries over (up to 6 max). Your hand refreshes each turn.",""," 1-5 .... Select a card from your hand"," \u2190\u2192 .... Choose target column"," Enter .. Play the selected card"," Space .. End your turn"," i ...... Inspect an enemy"]},{title:"The 5-Column Grid",lines:["The battlefield has 5 columns. Enemies spawn at the top","and advance downward toward your Gate at the bottom.",""," \u250C\u2500\u2500Col 1\u2500\u2500\u252C\u2500\u2500Col 2\u2500\u2500\u252C\u2500\u2500Col 3\u2500\u2500\u2510"," \u2502 \u262018 \u2502 \xB7 \u2502 \u219110 \u2502"," \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"," \u25C6 Gate \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 70/70"]},{title:"Enemy Intents",lines:["Enemies show what they plan to do next turn:",""," \u2193N .. Advance N rows toward the Gate"," \u2694N .. Attack the Gate for N damage"," \u25B2N .. Buff themselves"," \u25C8N .. Shield nearby enemies"," +N .. Summon new enemies","","Read intents to plan your plays wisely!"]},{title:"Emplacements",lines:["Some cards have dual use \u2014 cast OR emplace.",""," Cast: Immediate effect (damage, block, etc.)"," Emplace: Place as a structure in a column."," Triggers automatically every turn!","","Press e to toggle emplace mode, pick a column,","then press Enter to place it."]},{title:"Status Effects",lines:["Applied to enemies (or by enemies to your Gate):",""," Vulnerable . Enemy takes +25% damage per stack"," Weak ....... Enemy deals -25% damage per stack"," Burn ....... Damage per turn equal to stacks, decays by 1"," Fortified .. Enemy takes -15% damage per stack"," Empowered .. Enemy deals +25% damage per stack","","Effects stack! 2\xD7 Vulnerable = +50% damage taken.","Duration shown as turns (e.g. 2t). \u221E = permanent.","Press i in combat to inspect an enemy for details."]},{title:"The Map",lines:[" \u2694 Combat ... Standard enemy encounter"," \u2605 Elite .... Harder fight, better rewards"," \u25B3 Rest ..... Heal or remove a card"," $ Shop ..... Buy cards, potions, remove cards"," ? Event .... Story encounters with choices"," \u25C6 Boss ..... Act boss \u2014 defeat to advance","","Choose your path wisely. Elites are risky but rewarding."]},{title:"The Keep",lines:["After each run, you earn Echoes based on performance.","Explore the Keep to:",""," \u25C6 Upgrade structures (bonuses for future runs)"," \u25C6 Talk to NPCs (unlock story and lore)"," \u25C6 Increase Ascension (harder + new modifiers)","","The Keep remembers. Each run makes you stronger."]},{title:"Ready to Play!",lines:[" 1-5 = card \u2190\u2192 = column Enter = play"," Space = end e = emplace p = potion"," i = inspect d = deck q = menu","","The Pale awaits, Warden. Defend the Keep.","","Press Enter to begin."]}];function rs(e,t,r){let n=Math.max(0,Math.min(r,Math.round(e/t*r)));return"\u2588".repeat(n)+"\u2591".repeat(Math.max(0,r-n))}function ir({page:e,totalPages:t,source:r="new_game"}){let n=lr[e];if(!n)return null;let a=e>=t-1,o=r==="new_game"?"Enter begin":"Enter return";return Qt(sr,{flexDirection:"column",padding:1,children:[Qt(Le,{bold:!0,color:"yellow",children:["\u25C6 Tutorial \u2014 ",n.title]}),et(Le,{dimColor:!0,children:"\u2500".repeat(44)}),et(Le,{children:" "}),n.lines.map((l,c)=>a&&l==="Press Enter to begin."&&r!=="new_game"?et(Le,{children:"Press Enter to return."},c):et(Le,{children:l||" "},c)),et(Le,{children:" "}),et(Le,{dimColor:!0,children:"\u2500".repeat(44)}),Qt(sr,{children:[Qt(Le,{dimColor:!0,children:[rs(e+1,t,20)," ",e+1,"/",t," "]}),et(Le,{dimColor:!0,children:a?`\u2190 prev ${o} q close`:"\u2190 prev \u2192 next q close"})]})]})}var At=lr.length;import{Box as Zt,Text as fe}from"ink";import{Fragment as cr,jsx as Ae,jsxs as Ge}from"react/jsx-runtime";var dr={asciiMode:!1,showTutorialHints:!0,combatLogSize:4,skipEnemyAnimation:!1,difficulty:"normal"},os={easy:"Easy \u2014 enemies have 30% less HP/damage, +20 gate HP",normal:"Normal \u2014 the intended experience",hard:"Hard \u2014 enemies have 25% more HP/damage, \u221210 gate HP"};function mr({settings:e,selectedIndex:t,saveInfo:r,confirmingReset:n,message:a}){let o=[{id:"ascii",label:`ASCII Mode: ${e.asciiMode?"ON":"OFF"}`,description:"Use plain ASCII characters (no Unicode symbols)"},{id:"hints",label:`Tutorial Hints: ${e.showTutorialHints?"ON":"OFF"}`,description:"Show helpful tips during first runs"},{id:"log",label:`Combat Log Lines: ${e.combatLogSize}`,description:"Number of recent combat events shown (0-8)"},{id:"anim",label:`Enemy Animations: ${e.skipEnemyAnimation?"OFF":"ON"}`,description:"Step through enemy moves one by one"},{id:"difficulty",label:`Difficulty: ${e.difficulty.charAt(0).toUpperCase()+e.difficulty.slice(1)}`,description:os[e.difficulty]},{id:"tutorial",label:"View Tutorial",description:"Re-read the game tutorial"},{id:"controls",label:"Controls Reference",description:"View all keybindings"},{id:"reset",label:"Reset Save Data",description:"Delete all progress and start fresh"},{id:"back",label:"Back to Menu",description:""}];return Ge(Zt,{flexDirection:"column",padding:1,children:[Ae(fe,{bold:!0,color:"yellow",children:"\u25C6 Settings"}),Ae(fe,{children:" "}),Ae(Zt,{paddingX:1,children:Ge(Zt,{flexDirection:"column",width:40,children:[Ae(fe,{bold:!0,dimColor:!0,children:"Save Info"}),Ge(fe,{dimColor:!0,children:["Runs: ",r.totalRuns," Wins: ",r.totalWins," Echoes: ",r.echoes]}),Ge(fe,{dimColor:!0,children:["Highest Ascension: ",r.highestAscension]}),Ge(fe,{dimColor:!0,children:["Save: ",r.hasSave?"~/.config/codekeep/game.json":"None"]})]})}),Ae(fe,{children:" "}),o.map((l,c)=>{let s=c===t;return Ge(Zt,{children:[Ge(fe,{bold:s,color:s?l.id==="reset"?"red":"yellow":void 0,children:[s?"\u25B6 ":" ",l.label]}),s&&l.description?Ge(fe,{dimColor:!0,children:[" \u2014 ",l.description]}):null]},l.id)}),n&&Ge(cr,{children:[Ae(fe,{children:" "}),Ae(fe,{bold:!0,color:"red",children:"Are you sure? This deletes ALL progress. Press Enter again to confirm, Esc to cancel."})]}),a&&Ge(cr,{children:[Ae(fe,{children:" "}),Ae(fe,{color:"cyan",bold:!0,children:a})]}),Ae(fe,{children:" "}),Ae(fe,{dimColor:!0,children:"\u2191\u2193 navigate Enter toggle/select q back"})]})}import{Box as ss,Text as M}from"ink";import{jsx as B,jsxs as ls}from"react/jsx-runtime";function ur(){return ls(ss,{flexDirection:"column",padding:1,children:[B(M,{bold:!0,color:"yellow",children:"\u25C6 Controls Reference"}),B(M,{dimColor:!0,children:"\u2500".repeat(36)}),B(M,{children:" "}),B(M,{bold:!0,children:"Global"}),B(M,{children:" \u2191\u2193 Navigate menus"}),B(M,{children:" Enter Confirm / select"}),B(M,{children:" Esc Cancel / go back"}),B(M,{children:" q Quit / back to menu"}),B(M,{children:" "}),B(M,{bold:!0,children:"Combat"}),B(M,{children:" 1-9 Select card from hand"}),B(M,{children:" \u2190\u2192 Target column"}),B(M,{children:" Enter Play selected card"}),B(M,{children:" Space End your turn"}),B(M,{children:" e Toggle emplace mode"}),B(M,{children:" p Use potion"}),B(M,{children:" i Inspect enemies"}),B(M,{children:" d View deck"}),B(M,{children:" "}),B(M,{bold:!0,children:"Map"}),B(M,{children:" \u2190\u2192 Select next node"}),B(M,{children:" Enter Enter node"}),B(M,{children:" d View deck"}),B(M,{children:" "}),B(M,{bold:!0,children:"The Keep"}),B(M,{children:" \u2190\u2192 Select building / NPC"}),B(M,{children:" Enter Interact"}),B(M,{children:" "}),B(M,{bold:!0,children:"Inspect Mode (i in combat)"}),B(M,{children:" \u2190\u2192 Switch column"}),B(M,{children:" \u2191\u2193 Switch enemy"}),B(M,{children:" i / Esc Close inspect"}),B(M,{children:" "}),B(M,{dimColor:!0,children:"Press q or Esc to go back"})]})}import{useState as tt,useCallback as at,useRef as pr,useEffect as is}from"react";function cs(e){switch(e.type){case"enemy_advance":return`Enemy advances to row ${e.data.row}`;case"gate_hit":{let t=e.data.blocked?` (${e.data.blocked} blocked)`:"";return e.data.self?`Gate takes ${e.data.damage} self-damage`:`Enemy hits gate for ${e.data.damage}${t}!`}case"enemy_killed":return"An enemy is destroyed!";case"emplacement_destroyed":return`Emplacement in column ${e.data.column+1} destroyed!`;case"emplacement_triggered":return`Emplacement fires in column ${e.data.column+1}`;case"status_applied":return`${e.data.type} applied (${e.data.value} stacks)`;case"damage_dealt":return`${e.data.damage} damage dealt`;case"block_gained":return`+${e.data.value} Block`;default:return null}}var Va=new Set(["damage","damage_if_vulnerable","damage_equal_block","damage_if_emplaced","damage_plus_block","damage_if_low_hp","vulnerable","weak","burn"]),ds=700;function fr(e=!1){let[t,r]=tt(null),[n,a]=tt(-1),[o,l]=tt(2),[c,s]=tt(""),[p,b]=tt(!1),[y,x]=tt(!1),[_,N]=tt([]),d=pr(null),g=pr(2);is(()=>{if(!y||_.length===0)return;if(e){s(_[_.length-1]),N([]),x(!1);let k=d.current;k&&(k.outcome==="win"?s("Victory! The Pale recedes."):k.outcome==="lose"?s("The Gate has fallen..."):s(`Turn ${k.turn}. Your move.`));return}let v=setTimeout(()=>{let[k,...S]=_;if(s(k),S.length===0){x(!1);let O=d.current;O&&(O.outcome==="win"?s("Victory! The Pale recedes."):O.outcome==="lose"?s("The Gate has fallen..."):s(`Turn ${O.turn}. Your move.`))}else N(S)},ds);return()=>clearTimeout(v)},[y,_,e]);let R=at((v,k,S,O,X,$,ee)=>{let E=v??va(),q=k??Math.floor(Math.random()*2147483647),de=pn(E,q,S,O,X,$??[],ee);d.current=de,r({...de}),a(-1),l(2),g.current=2,b(!1),s("Your turn. Select a card (1-5) and a column (\u2190\u2192).")},[]),T=(()=>{if(n<0||!t)return!1;let v=t.hand[n];if(!v)return!1;let k=z(v.defId);return k?k.effects.some(S=>Va.has(S.type)&&(S.target==="single"||S.target==="column")):!1})(),G=at(v=>{if(!d.current||d.current.phase!=="player"||v<0||v>=d.current.hand.length)return;let k=d.current.hand[v],S=z(k.defId);if(!S)return;let O=!d.current.events.some(E=>E.type==="card_played"&&E.turn===d.current.turn);if((Ta(d.current.relics)&&O?0:S.cost)>d.current.resolve){s(`Not enough Resolve for ${S.name} (costs ${S.cost}).`);return}if(a(v),S.effects.some(E=>Va.has(E.type)&&(E.target==="single"||E.target==="column"))){let E=d.current.columns,q=g.current;if(E[q].enemies.length===0){let de=-1,Dt=99;for(let we=0;we<E.length;we++)E[we].enemies.length>0&&Math.abs(we-q)<Dt&&(de=we,Dt=Math.abs(we-q));de>=0&&(l(de),g.current=de)}s(`${S.name} selected. Choose column (\u2190\u2192), Enter to play.`)}else s(`${S.name} selected. Enter to play.`)},[]),F=at(v=>{v>=0&&v<5&&(l(v),g.current=v)},[]),Ie=at(()=>{let v=d.current;if(!v||v.phase!=="player"||n<0)return;let k=v.hand[n];if(!k)return;let S=z(k.defId);if(!S)return;if(!p&&S.effects.some($=>Va.has($.type)&&$.target==="single")){let $=v.columns[o];if(!$||$.enemies.length===0){s(`No enemies in column ${o+1}. Move target (\u2190\u2192) to an enemy.`);return}}let X=v.events.length;if(fn(v,n,o,p),v.events.length===X){p&&v.columns[o]?.emplacement?s(`Column ${o+1} already has an emplacement. Choose another column.`):s(`Cannot play ${S.name}. Not enough Resolve.`);return}d.current=v,r({...v}),a(-1),b(!1),v.outcome==="win"?s("Victory! The Pale recedes."):v.outcome==="lose"?s("The Gate has fallen..."):s(`${p?"Emplaced":"Played"} ${S.name}. ${v.resolve} Resolve left.`)},[n,o,p]),Re=at(()=>{if(n<0||!d.current)return;let v=d.current.hand[n];if(!v)return;let k=z(v.defId);if(!k||k.type!=="emplace"){s("This card cannot be emplaced.");return}b(S=>!S),s(p?`${k.name}: cast mode`:`${k.name}: EMPLACE mode \u2014 place in a column`)},[n,p]),ze=at(()=>{let v=d.current;if(!v||v.phase!=="player"||y)return;let k=v.events.length;gn(v),d.current=v,r({...v}),a(-1);let S=v.events.slice(k),O=[];for(let X of S){let $=cs(X);$&&O.push($)}O.length>0?(x(!0),s("\u26A1 Enemy turn..."),N(O)):v.outcome==="win"?s("Victory! The Pale recedes."):v.outcome==="lose"?s("The Gate has fallen..."):s(`Turn ${v.turn}. Your move.`)},[y]),Y=at(v=>{let k=d.current;if(!(!k||!v)){for(let S of v.effects)switch(S.type){case"heal":k.gateHp=Math.min(k.gateMaxHp,k.gateHp+S.value);break;case"block":k.gateBlock+=S.value;break;case"damage":{let O=o;if(S.target==="column"){let X=k.columns[O];if(X)for(let $ of X.enemies)$.hp-=S.value}else if(S.target==="all")for(let X of k.columns)for(let $ of X.enemies)$.hp-=S.value;break}case"draw":{let O=ne(k.seed+k.turn*53),{drawn:X,newDrawPile:$,newDiscardPile:ee}=Ke(k.drawPile,k.discardPile,S.value,O);k.hand.push(...X),k.drawPile=$,k.discardPile=ee;break}case"resolve":k.resolve=Math.min(k.maxResolve+5,k.resolve+S.value);break}d.current=k,r({...k}),s(`Used ${v.name}.`)}},[o]);return{combat:t,selectedCard:n,targetColumn:o,message:c,emplaceMode:p,animating:y,selectCard:G,selectTarget:F,confirmPlay:Ie,endTurn:ze,toggleEmplace:Re,startCombat:R,applyPotion:Y,needsTarget:T}}import{Fragment as Oa,jsx as w,jsxs as C}from"react/jsx-runtime";var hr=90,gr=24;function hs(){let{stdout:e}=fs(),[t,r]=H({columns:e?.columns??process.stdout.columns??80,rows:e?.rows??process.stdout.rows??24});return ms(()=>{let n=e??process.stdout,a=()=>r({columns:n.columns,rows:n.rows});return n.on("resize",a),()=>{n.off("resize",a)}},[e]),t}function gs({dryRun:e}){let{exit:t}=us(),{columns:r,rows:n}=hs(),[a,o]=H("menu"),[l,c]=H(1),[s,p]=H(null),[b,y]=H(0),[x,_]=H([]),[N,d]=H(0),[g,R]=H([]),[T,G]=H(0),[F,Ie]=H(null),[Re,ze]=H(0),[Y,v]=H(0),[k,S]=H(null),[O,X]=H(0),[$,ee]=H(""),[E,q]=H(!1),[de,Dt]=H([]),[we,ea]=H(0),[ta,je]=H(0),[ve,yt]=H(dr),[Ka,aa]=H(0),[vr,nt]=H(""),na=fr(ve.skipEnemyAnimation),{combat:V,selectedCard:Rt,targetColumn:bt,message:kr,emplaceMode:xr,animating:Ua,selectCard:Ya,selectTarget:Mt,confirmPlay:Tr,endTurn:_r,toggleEmplace:Ir,startCombat:ra,applyPotion:vs,needsTarget:oa}=na,[Bt,sa]=H(!1),[wt,la]=H(0),[qa,ia]=H(0),[vt,rt]=H(null),[za,ot]=H(0),[Ht,ca]=H("new_game"),[Cr,ja]=H("map"),[Sr,Nt]=H(""),[Xa,st]=H(!1),[Ja,lt]=H(!1),[da,kt]=H(!1),[ge,Me]=H(()=>ft()),ma=!ge||ge.keep.totalRuns<=1,Ve=ge?Sn(ge.keep.totalRuns,ge.keep.highestAscension):"surface",Er=r<hr||n<gr,Be=De(()=>{let m=ge??ft();return m?ge||(m.keep.npcs.length===0&&(m.keep.npcs=Ea()),Me(m)):(m=Dn("Warden"),m.keep.npcs.length===0&&(m.keep.npcs=Ea()),Ne(m),Me(m)),JSON.parse(JSON.stringify(m))},[ge]),L=De(m=>{if(e)return;let u=Be();u.activeRun=m,k&&(u.keep=k),Ne(u),Me(u)},[e,k,Be]),ua=De(()=>{let m=Be();if(m.keep.totalRuns===0){m.keep.totalRuns++,Ne(m),Me(m),S(m.keep),ot(0),ca("new_game"),o("tutorial");return}let u=`run-${Date.now()}`,i=vn(u,m.keep.highestAscension,ve.difficulty);p(i),y(0),m.activeRun=i,m.keep.totalRuns++,S(m.keep),Ne(m),Me(m),o("map")},[Be,ve]),Pr=De(()=>{let m=ft();m?.activeRun&&(ln(m.activeRun.deck),p(m.activeRun),S(m.keep),y(0),o("map"))},[]),Qa=De(()=>{let m=Be();Me(m),S(m.keep),X(0),ee(""),o("keep")},[Be]),Za=De(()=>{if(!s)return[];if(s.currentNodeId){let m=s.map.nodes.find(u=>u.id===s.currentNodeId);if(m&&!m.visited)return[m]}return wn(s.map,s.currentNodeId)},[s]),Ar=De(m=>{if(!s)return;if(m.type==="combat"||m.type==="elite"){let i={...s,currentNodeId:m.id},f=Ee(s.seed+m.id),I=ne(f),A=yn(s.act,I,m.type==="elite");ra(i.deck,f,i.gateHp,i.gateMaxHp,A.enemies,i.relics,Yt(s.act,s.ascensionLevel,ve.difficulty)),p(i),L(i),o("combat");return}else if(m.type==="boss"){let i={...s,currentNodeId:m.id},f=Ee(s.seed+"-boss-"+s.act),I=cn(s.act);ra(i.deck,f,i.gateHp,i.gateMaxHp,I,i.relics,Yt(s.act,s.ascensionLevel,ve.difficulty));let A=Ot(s.act);if(A?.dialogue){let K=A.dialogue.find(W=>W.storyLayer===Ve)??A.dialogue[0];K&&rt(K.onAppear)}p(i),L(i),o("combat");return}let u=Ca(s,m.id);if(m.type==="shop"){let i=ne(Ee(s.seed+m.id));R(_n(i)),G(0),p(u),o("shop")}else if(m.type==="event"){let i=ne(Ee(s.seed+m.id));Ie(In(s.act,i,Ve)),ze(0),p(u),o("event")}else m.type==="rest"&&(v(0),p(u),o("rest"));L(u)},[s,ra,L,ve,Ve]),xt=De((m,u)=>{q(u),p(m);let i=Be(),f=Pa(u,m.act,m.ascensionLevel);i.keep.echoes+=f,u&&(i.keep.totalWins++,m.ascensionLevel>=i.keep.highestAscension&&(i.keep.highestAscension=m.ascensionLevel+1)),i.activeRun=null,S(i.keep),Ne(i),Me(i),o("result")},[Be]),Dr=De(()=>{if(!V||!s)return;let m={...s,gateHp:V.gateHp};if(V.outcome==="lose"){rt(null),xt(m,!1);return}if((s.currentNodeId?mt(s.map,s.currentNodeId):null)?.type==="boss"){let A=Ot(s.act);if(A?.dialogue){let K=A.dialogue.find(W=>W.storyLayer===Ve)??A.dialogue[0];rt(K?.onDefeat??null)}else rt(null)}else rt(null);m.currentNodeId&&(m=Ca(m,m.currentNodeId));let i=s.currentNodeId?mt(s.map,s.currentNodeId):null,f=i?.type==="elite"?Gt.elite:i?.type==="boss"?Gt.boss:Gt.combat;if(m=St(m,f),m.relics.includes("mending_stone")&&(m=qt(m,5)),m.relics.includes("fragment_magnet")&&(m=St(m,5)),i?.type==="elite"||i?.type==="boss"){let A=ne(Ee(s.seed+(s.currentNodeId??"")+"-relic")),K=un(A,m.relics,3);if(K.length>0){Dt(K),ea(0),p(m),L(m),o("relic_reward");return}}if(i?.type==="boss"){s.act<3?(m=Sa(m),p(m),L(m),y(0),o("map")):xt(m,!0);return}let I=ne(Ee(s.seed+(s.currentNodeId??"")+"-reward"));_(Ut(I)),d(0),p(m),L(m),o("reward")},[V,s,L,xt,Ve]),en=De(m=>{if(!s)return;let u=s;m&&(u=_a(u,Ct(m.id))),p(u),L(u),y(0),o("map")},[s,L]),tn=De(m=>{if(!s)return;let u=s;if(m&&(u={...u,relics:[...u.relics,m.id]}),p(u),L(u),(s.currentNodeId?mt(s.map,s.currentNodeId):null)?.type==="boss")s.act<3?(u=Sa(u),p(u),L(u),y(0),o("map")):xt(u,!0);else{let f=ne(Ee(s.seed+(s.currentNodeId??"")+"-reward"));_(Ut(f)),d(0),o("reward")}},[s,L,xt]),an=!!ge?.activeRun,pa=[{label:"The Keep",action:"keep"},...an?[{label:"Resume Run",action:"resume"},{label:da?"Abandon run? Enter to confirm":"New Run",action:"new"}]:[{label:"New Run",action:"new"}],{label:"Tutorial",action:"tutorial"},{label:"Settings",action:"settings"},{label:"Quit",action:"quit"}];if(ps((m,u)=>{if(a==="tutorial"){u.rightArrow?ot(i=>Math.min(At-1,i+1)):u.leftArrow?ot(i=>Math.max(0,i-1)):u.return?za===At-1?Ht==="new_game"?ua():o(Ht==="settings"?"settings":"menu"):ot(i=>Math.min(At-1,i+1)):(m==="q"||u.escape)&&o(Ht==="settings"?"settings":"menu");return}if(a==="controls"){(m==="q"||u.escape)&&o("settings");return}if(a==="settings"){let i=["ascii","hints","log","anim","difficulty","tutorial","controls","reset","back"];if(u.upArrow)aa(f=>Math.max(0,f-1)),lt(!1);else if(u.downArrow)aa(f=>Math.min(i.length-1,f+1)),lt(!1);else if(m==="q"||u.escape)o("menu"),lt(!1);else if(u.return){let f=i[Ka];if(f==="ascii")yt(I=>({...I,asciiMode:!I.asciiMode})),nt("ASCII mode "+(ve.asciiMode?"disabled":"enabled"));else if(f==="hints")yt(I=>({...I,showTutorialHints:!I.showTutorialHints})),nt("Tutorial hints "+(ve.showTutorialHints?"disabled":"enabled"));else if(f==="log")yt(I=>({...I,combatLogSize:I.combatLogSize>=8?0:I.combatLogSize+2}));else if(f==="anim")yt(I=>({...I,skipEnemyAnimation:!I.skipEnemyAnimation})),nt("Enemy animations "+(ve.skipEnemyAnimation?"enabled":"disabled"));else if(f==="difficulty"){let A={easy:"normal",normal:"hard",hard:"easy"}[ve.difficulty];yt(K=>({...K,difficulty:A})),nt(`Difficulty set to ${A.charAt(0).toUpperCase()+A.slice(1)}. Takes effect on next run.`)}else f==="tutorial"?(ot(0),ca("settings"),o("tutorial")):f==="controls"?o("controls"):f==="reset"?Ja?(Rn(),Me(null),S(null),p(null),lt(!1),nt("Save data deleted. Starting fresh.")):lt(!0):f==="back"&&o("menu")}return}if(a==="relic_reward"){u.upArrow?ea(i=>Math.max(0,i-1)):u.downArrow?ea(i=>Math.min(de.length,i+1)):m==="s"?tn(null):u.return&&tn(we<de.length?de[we]:null);return}if(a==="menu"){if(u.upArrow)c(i=>Math.max(0,i-1)),kt(!1);else if(u.downArrow)c(i=>Math.min(pa.length-1,i+1)),kt(!1);else if(u.escape)kt(!1);else if(u.return){let i=pa[l]?.action;i==="keep"?Qa():i==="new"?an&&!da?kt(!0):(kt(!1),ua()):i==="resume"?Pr():i==="tutorial"?(ot(0),ca("menu"),o("tutorial")):i==="settings"?(aa(0),nt(""),lt(!1),o("settings")):t()}else m==="q"&&t();return}if(a==="deck"){(m==="q"||u.escape)&&o(Cr);return}if(a==="map"&&s){let i=Za().sort((f,I)=>f.column-I.column);u.leftArrow?y(f=>Math.max(0,f-1)):u.rightArrow?y(f=>Math.min(i.length-1,f+1)):u.return&&i[b]?Ar(i[b]):m==="d"?(ja("map"),o("deck")):m==="q"&&(L(s),o("menu"));return}if(a==="reward"){let i=x.length;u.upArrow?d(f=>Math.max(0,f-1)):u.downArrow?d(f=>Math.min(i,f+1)):m==="s"?en(null):u.return&&en(N<x.length?x[N]:null);return}if(a==="shop"&&s){if(u.upArrow)G(i=>Math.max(0,i-1));else if(u.downArrow)G(i=>Math.min(g.length-1,i+1));else if(m==="q"||u.escape)y(0),o("map");else if(u.return){let i=g[T];if(i&&s.fragments<i.cost){Nt(`Not enough fragments (need ${i.cost}, have ${s.fragments}).`);return}if(Nt(""),i&&s.fragments>=i.cost){let f=kn(s,i.cost);if(!f)return;if(i.type==="card"&&i.cardDef)f=_a(f,Ct(i.cardDef.id)),p(f),L(f),R(I=>I.filter((A,K)=>K!==T)),G(0),Nt(`Purchased ${i.cardDef.name}!`);else if(i.type==="potion"&&i.potionDef){let I=xn(f,i.potionDef.id);I&&(f=I),p(f),L(f),R(A=>A.filter((K,W)=>W!==T)),G(0),Nt(`Purchased ${i.potionDef.name}!`)}else i.type==="card_remove"&&(p(f),L(f),je(0),R(I=>I.filter((A,K)=>K!==T)),G(0),o("shop_remove"))}}return}if(a==="event"&&F&&s){if(u.upArrow)ze(i=>Math.max(0,i-1));else if(u.downArrow)ze(i=>Math.min(F.choices.length-1,i+1));else if(u.return){let i=F.choices[Re],f=s;switch(i.effect.type){case"heal":f=qt(f,i.effect.value);break;case"damage":{f={...f,gateHp:Math.max(1,f.gateHp-i.effect.value)};let I=i.label.match(/(\d+)\s*fragments/i);I&&(f=St(f,parseInt(I[1])));break}case"fragments":f=St(f,i.effect.value);break;case"max_hp":{f={...f,gateMaxHp:f.gateMaxHp+i.effect.value};let I=i.label.match(/lose\s+(\d+)\s*hp/i);I&&(f={...f,gateHp:Math.max(1,f.gateHp-parseInt(I[1]))});break}case"card_reward":{let I=i.label.match(/lose\s+(\d+)\s*hp/i);I&&(f={...f,gateHp:Math.max(1,f.gateHp-parseInt(I[1]))});let A=ne(Ee(s.seed+(F?.id??"")));_(Ut(A)),d(0),p(f),L(f),o("reward");return}case"remove_card":{p(f),L(f),je(0),o("deck_remove");return}case"nothing":break}p(f),L(f),y(0),o("map")}return}if(a==="rest"&&s){if(u.upArrow)v(i=>Math.max(0,i-1));else if(u.downArrow)v(i=>Math.min(2,i+1));else if(u.return){let i=s;Y===0?(i=qt(i,Math.floor(i.gateMaxHp*.3)),p(i),L(i),y(0),o("map")):Y===1?i.deck.length>5&&(p(i),je(0),o("deck_remove")):(p(i),L(i),y(0),o("map"))}return}if(a==="deck_remove"&&s){if(u.upArrow)je(i=>Math.max(0,i-1));else if(u.downArrow)je(i=>Math.min(s.deck.length-1,i+1));else if(u.escape||m==="q")y(0),o("map");else if(u.return&&s.deck.length>5){let i=s.deck[ta];if(i){let f=Ia(s,i.instanceId);p(f),L(f)}y(0),o("map")}return}if(a==="shop_remove"&&s){if(u.upArrow)je(i=>Math.max(0,i-1));else if(u.downArrow)je(i=>Math.min(s.deck.length-1,i+1));else if(u.escape||m==="q")o("shop");else if(u.return&&s.deck.length>5){let i=s.deck[ta];if(i){let f=Ia(s,i.instanceId);p(f),L(f)}o("shop")}return}if(a==="keep"&&k){if(u.leftArrow||u.upArrow)X(i=>(i-1+Ze.length)%Ze.length),ee("");else if(u.rightArrow||u.downArrow)X(i=>(i+1)%Ze.length),ee("");else if(m==="q")o("menu");else if(u.return){let i=Ze[O];if(!i)return;if(i.type==="structure"){let f=ut.find(A=>A.id===i.id);if(!f)return;let I=Cn(k,f.id);if(I){S(I);let A=Be();A.keep=I,Ne(A),Me(A),ee(`Upgraded ${f.name}!`)}else pt(k,f.id)>=f.maxLevel?ee(`${f.name} is already max level.`):ee(`Not enough Echoes to upgrade ${f.name}.`)}else if(i.type==="npc"){let f=k.npcs.find(A=>A.id===i.id);if(!f)return;let I=En(f.id,k);if(I){let A=Pn(k,f.id,I.dialogueId);S(A);let K=Be();K.keep=A,Ne(K),Me(K),ee(`${I.speaker}: "${I.text}"`)}else ee("(No new dialogue)")}else i.type==="gate"&&ua()}return}if(a==="result"){u.return||m===" "?Qa():m==="q"&&o("menu");return}if(a==="combat"&&V){if(Ua)return;if(V.outcome!=="undecided"){(u.return||m===" ")&&Dr();return}if(vt&&V.turn>1&&rt(null),m==="i"){Bt?sa(!1):(sa(!0),la(0),ia(0));return}if(Bt){if(u.leftArrow)la(i=>Math.max(0,i-1));else if(u.rightArrow)la(i=>Math.min(4,i+1));else if(u.upArrow)ia(i=>Math.max(0,i-1));else if(u.downArrow){let i=(V.columns[wt]?.enemies.length??1)-1;ia(f=>Math.min(Math.max(0,i),f+1))}else u.escape&&sa(!1);return}if(m==="q"){o("menu");return}if(m==="d"&&s){ja("combat"),o("deck");return}if(m==="e"){Ir();return}if(m==="p"&&s){let i=s.potions.findIndex(f=>f!==null);if(i>=0){let f=Tn(s,i);if(f&&na.combat){let I=dt.find(A=>A.id===f.potionId);p(f.run),L(f.run),na.applyPotion(I??null)}}return}if(m>="1"&&m<="9"){st(!1),Ya(parseInt(m)-1);return}if(u.leftArrow){if(st(!1),oa&&V){let i=bt-1;for(;i>=0&&V.columns[i].enemies.length===0;)i--;i>=0&&Mt(i)}else Mt(Math.max(0,bt-1));return}if(u.rightArrow){if(st(!1),oa&&V){let i=bt+1;for(;i<5&&V.columns[i].enemies.length===0;)i++;i<5&&Mt(i)}else Mt(Math.min(4,bt+1));return}if(u.return&&Rt>=0){st(!1),Tr();return}if(m===" "){if(V.resolve>0&&V.hand.some(i=>{let f=z(i.defId);return f&&f.cost<=V.resolve})&&!Xa){st(!0);return}st(!1),_r();return}if(u.escape){Ya(-1);return}}}),Er)return C(he,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep \u2014 The Pale"}),C(h,{color:"red",children:["Terminal too small (",r,"x",n,", need ",hr,"x",gr,")"]})]});if(a==="menu")return C(he,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"yellow",children:"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"}),w(h,{bold:!0,color:"yellow",children:"\u2502 \u25C6 CodeKeep \u2014 The Pale \u2502"}),w(h,{bold:!0,color:"yellow",children:"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"}),w(h,{children:" "}),(Ve==="true_ending"?["The fortress. The Pale. You can no longer tell which side you're on."]:Ve==="truth"?["The fortress stands. The Pale stands. The line between them blurs."]:Ve==="cracks"?["The fortress stands at the edge of the Pale.","You've stood here before. You're sure of it."]:["The fortress stands at the edge of the Pale.","Beyond the walls, something stirs."]).map((u,i)=>w(h,{dimColor:!0,italic:!0,children:u},i)),w(h,{children:" "}),pa.map((u,i)=>{let f=i===l,I=u.action==="new"&&da&&f;return C(h,{bold:f,color:I?"red":f?"yellow":"white",children:[f?" \u25B6 ":" ",u.label]},u.action+(I?"-confirm":""))}),w(h,{children:" "}),w(h,{dimColor:!0,children:"\u2191\u2193 navigate Enter select q exit"})]});if(a==="tutorial")return w(ir,{page:za,totalPages:At,source:Ht});if(a==="controls")return w(ur,{});if(a==="settings"){let m={totalRuns:ge?.keep.totalRuns??0,totalWins:ge?.keep.totalWins??0,highestAscension:ge?.keep.highestAscension??0,echoes:ge?.keep.echoes??0,hasSave:!!ge};return w(mr,{settings:ve,selectedIndex:Ka,saveInfo:m,confirmingReset:Ja,message:vr})}if(a==="deck"&&s)return w(Un,{deck:s.deck});if(a==="keep"&&k)return w(or,{keep:k,selectedId:Ze[O]?.id??"forge",message:$,isFirstVisit:ma});if((a==="deck_remove"||a==="shop_remove")&&s)return C(he,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"red",children:"\u25C6 Remove a Card"}),C(h,{dimColor:!0,children:["Choose a card to remove from your deck (",s.deck.length," cards)."]}),w(h,{children:" "}),s.deck.map((m,u)=>{let i=z(m.defId),f=u===ta;return C(h,{bold:f,color:f?"yellow":"white",children:[f?"\u25B6 ":" ",i?.name??m.defId," (",i?.rarity,") \u2014 ",i?.description??""]},m.instanceId)}),w(h,{children:" "}),w(h,{dimColor:!0,children:"\u2191\u2193 navigate Enter remove Esc cancel"})]});if(a==="map"&&s){let m=Za().sort((u,i)=>u.column-i.column);return C(he,{flexDirection:"column",children:[C(he,{justifyContent:"space-between",paddingX:1,children:[C(h,{children:["Gate ",C(h,{bold:!0,color:s.gateHp>40?"green":s.gateHp>20?"yellow":"red",children:[s.gateHp,"/",s.gateMaxHp]})]}),C(h,{children:["Fragments ",w(h,{bold:!0,color:"yellow",children:s.fragments})]}),s.potions.filter(u=>u!==null).length>0&&C(h,{children:["Potions ",w(h,{color:"magenta",children:s.potions.filter(u=>u!==null).length})]}),s.relics.length>0&&C(h,{children:["Relics ",C(h,{color:"magenta",children:["\u2605",s.relics.length]})]}),C(h,{children:["Deck ",w(h,{dimColor:!0,children:s.deck.length})]})]}),(()=>{let u=s.currentNodeId?s.map.nodes.find(i=>i.id===s.currentNodeId):null;return u&&!u.visited?C(h,{color:"yellow",bold:!0,children:[" ","Battle in progress \u2014 Enter to continue."]}):ma&&ve.showTutorialHints&&!s.currentNodeId?w(h,{color:"green",bold:!0,children:" TIP: \u2694=combat \u2605=elite(harder, more reward) \u25B3=rest $=shop ?=event \u25C6=boss. \u2190\u2192 select, Enter to go."}):null})(),w(zn,{map:s.map,currentNodeId:s.currentNodeId,reachableIds:m.map(u=>u.id),selectedNodeId:m[b]?.id??null})]})}if(a==="reward")return w(Kn,{cards:x,selectedIndex:N});if(a==="relic_reward")return C(he,{flexDirection:"column",padding:1,children:[w(h,{bold:!0,color:"magenta",children:"\u25C6 Relic Reward"}),w(h,{dimColor:!0,children:"\u2500".repeat(36)}),w(h,{dimColor:!0,italic:!0,children:"An artifact resonates from the defeated foe."}),w(h,{children:" "}),de.map((m,u)=>{let i=u===we;return C(he,{flexDirection:"column",children:[C(h,{children:[w(h,{bold:i,color:i?"yellow":"white",children:i?" \u25B6 ":" "}),C(h,{color:i?"magenta":"white",bold:i,children:["\u2605 ",m.name]})]}),i&&C(h,{dimColor:!0,children:[" ",m.description]})]},m.id)}),w(h,{children:" "}),C(h,{bold:we===de.length,color:we===de.length?"yellow":"white",children:[we===de.length?" \u25B6 ":" ","Skip"]}),w(h,{children:" "}),s&&s.relics.length>0&&C(he,{children:[w(h,{dimColor:!0,children:"Relics: "}),w(h,{color:"magenta",children:s.relics.join(" \xB7 ")})]}),w(h,{dimColor:!0,children:"\u2191\u2193 navigate Enter select s skip"})]});if(a==="shop"&&s)return w(jn,{items:g,selectedIndex:T,fragments:s.fragments,message:Sr});if(a==="event"&&F)return w(Xn,{event:F,selectedChoice:Re});if(a==="rest"&&s)return w(Qn,{gateHp:s.gateHp,gateMaxHp:s.gateMaxHp,selectedChoice:Y,deckSize:s.deck.length});if(a==="result"){let m=s?Pa(E,s.act,s.ascensionLevel):0;return C(he,{flexDirection:"column",padding:1,children:[E?C(Oa,{children:[w(h,{bold:!0,color:"green",children:"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"}),w(h,{bold:!0,color:"green",children:"\u2502 \u2605 RUN COMPLETE \u2502"}),w(h,{bold:!0,color:"green",children:"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"}),w(h,{children:" "}),vt?C(h,{italic:!0,color:"magenta",children:['"',vt,'"']}):w(h,{italic:!0,children:"The Pale recedes. The Keep endures."}),w(h,{italic:!0,dimColor:!0,children:"Another dawn. Another day of resistance."})]}):C(Oa,{children:[w(h,{bold:!0,color:"red",children:"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"}),w(h,{bold:!0,color:"red",children:"\u2502 \u2717 DEFEAT \u2502"}),w(h,{bold:!0,color:"red",children:"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"}),w(h,{children:" "}),w(h,{italic:!0,children:"The Gate has fallen. The Pale consumes all."}),w(h,{italic:!0,dimColor:!0,children:"But the Keep remembers. It always remembers."})]}),w(h,{children:" "}),s&&C(Oa,{children:[w(h,{dimColor:!0,children:"\u2500".repeat(32)}),C(h,{children:[" ","Act ",w(h,{bold:!0,children:s.act})," ","Deck ",C(h,{dimColor:!0,children:[s.deck.length," cards"]})," ","Fragments ",w(h,{dimColor:!0,children:s.fragments})]}),C(h,{children:[" ","Echoes earned ",C(h,{bold:!0,color:"cyan",children:["+",m]})]}),w(h,{dimColor:!0,children:"\u2500".repeat(32)})]}),w(h,{children:" "}),w(h,{dimColor:!0,children:"Enter \u2192 The Keep | q \u2192 menu"})]})}if(a==="combat"&&V&&s){let m=ma&&ve.showTutorialHints&&V.turn<=3?V.turn===1&&Rt<0?"TIP: Press 1-5 to select a card. \u2190\u2192 to target a column. Enter to play. Space to end turn.":V.turn===1&&Rt>=0?"TIP: Arrow keys choose which column to target. Press Enter to play the card.":V.turn===2?"TIP: Enemies with \u2694 will attack your Gate. \u2193 means they advance. Block reduces damage. Press i to inspect enemies.":V.turn===3?'TIP: Cards with type "emplace" can become structures! Select one, press e to toggle emplace mode, then Enter.':null:null;return C(he,{flexDirection:"column",paddingX:1,children:[C(h,{dimColor:!0,children:["Act ",s.act," | Gate ",s.gateHp,"/",s.gateMaxHp," | Fragments ",s.fragments,s.potions.filter(u=>u!==null).length>0&&` | Potions ${s.potions.filter(u=>u!==null).map(u=>dt.find(f=>f.id===u)?.name??u).join(", ")}`,s.relics.length>0&&` | \u2605 ${s.relics.length}`," | d=deck i=inspect"]}),m&&w(h,{color:"green",bold:!0,children:m}),vt&&w(he,{borderStyle:"single",borderColor:"magenta",paddingX:1,marginBottom:1,children:C(h,{color:"magenta",italic:!0,children:['"',vt,'"']})}),C(he,{flexDirection:"row",children:[w(he,{flexDirection:"column",children:w(Ln,{combat:V,selectedCard:Rt,targetColumn:bt,needsTarget:oa,emplaceMode:xr,message:Xa?`End turn with ${V.resolve} Resolve remaining? (carries over) Press Space again to confirm.`:kr,animating:Ua,inspectCol:wt,inspectEnemyIdx:qa,inspectMode:Bt})}),Bt&&(()=>{let i=V.columns[wt]?.enemies[qa];if(!i)return C(he,{flexDirection:"column",width:28,borderStyle:"single",borderColor:"cyan",paddingX:1,marginLeft:1,children:[C(h,{dimColor:!0,children:["No enemy in col ",wt+1,"."]}),w(h,{dimColor:!0,children:"\u2190\u2192 to navigate."})]});let f=$e(i.templateId),I=(()=>{if(!i.intent)return"Unknown";switch(i.intent.type){case"attack":return`Attack ${i.intent.value} dmg`;case"advance":return"Advance \u2192";case"buff":return"Buff allies";case"debuff":return"Debuff gate";case"shield":return"Shield allies";case"summon":return"Summon";default:return`${i.intent.type} (${i.intent.value})`}})(),A=W=>W>=90?"\u221E":`${W}t`,K=i.statusEffects.map(W=>{let Oe=A(W.duration);switch(W.type){case"vulnerable":return{label:`Vulnerable \xD7${W.stacks}`,detail:`+${W.stacks*25}% dmg taken`,dur:Oe};case"weak":return{label:`Weak \xD7${W.stacks}`,detail:`-${W.stacks*25}% dmg dealt`,dur:Oe};case"burn":return{label:`Burn \xD7${W.stacks}`,detail:`${W.stacks} dmg/turn, -1/turn`,dur:Oe};case"empowered":return{label:`Empowered \xD7${W.stacks}`,detail:`+${W.stacks*25}% dmg dealt`,dur:Oe};case"fortified":return{label:`Fortified \xD7${W.stacks}`,detail:`-${W.stacks*15}% dmg taken`,dur:Oe};default:return{label:`${W.type} \xD7${W.stacks}`,detail:"",dur:Oe}}});return C(he,{flexDirection:"column",width:28,borderStyle:"single",borderColor:"cyan",paddingX:1,marginLeft:1,children:[C(h,{bold:!0,color:"cyan",children:[f?.symbol??"?"," ",f?.name??i.templateId]}),C(h,{dimColor:!0,children:["Col ",wt+1,", Row ",i.row]}),C(h,{children:["HP: ",C(h,{bold:!0,color:i.hp>i.maxHp*.6?"green":i.hp>i.maxHp*.3?"yellow":"red",children:[i.hp,"/",i.maxHp]})]}),C(h,{children:["Dmg: ",w(h,{bold:!0,children:f?.damage??"?"})," Spd: ",w(h,{bold:!0,children:f?.speed??"?"})]}),w(h,{dimColor:!0,italic:!0,children:f?.description??""}),C(h,{children:["Next: ",w(h,{color:"red",bold:!0,children:I})]}),K.length>0&&K.map((W,Oe)=>C(h,{color:"yellow",children:[W.label," ",C(h,{dimColor:!0,children:["(",W.detail,", ",W.dur,")"]})]},Oe)),K.length===0&&w(h,{dimColor:!0,children:"No effects"}),w(h,{dimColor:!0,children:"\u2190\u2192 \u2191\u2193 nav i close"})]})})()]})]})}return w(h,{children:"Loading..."})}function yr(e){return w(zt,{children:w(gs,{...e})})}import{jsx as ws}from"react/jsx-runtime";var br="1.0.5";globalThis.__CODEKEEP_VERSION=br;var wr=new ys;wr.name("codekeep").description("CodeKeep: The Pale \u2014 deck-building tactical roguelike in your terminal").version(br).option("--ascii","Force ASCII-only rendering (no Unicode box drawing)").option("--compact","Compact layout for narrow terminals").option("--tutorial","Replay the tutorial").option("--no-save","Dry-run mode: play without writing to disk").action(e=>{(!process.stdin.isTTY||!process.stdout.isTTY)&&(process.stderr.write(`codekeep requires an interactive terminal.
|
|
20
20
|
Run it directly in your terminal (not piped or in CI).
|
|
21
|
-
`),process.exit(1));let{waitUntilExit:t,unmount:r}=
|
|
21
|
+
`),process.exit(1));let{waitUntilExit:t,unmount:r}=bs(ws(yr,{asciiMode:e.ascii??!1,compact:e.compact??!1,forceTutorial:e.tutorial??!1,dryRun:e.save===!1}),{exitOnCtrlC:!1}),n=!1;function a(){if(!n){if(n=!0,e.save!==!1){process.stderr.write(`
|
|
22
22
|
Saving...
|
|
23
|
-
`);try{let o=
|
|
23
|
+
`);try{let o=ft();o&&Ne(o)}catch{}}r()}}process.on("SIGINT",a),process.on("SIGTERM",a),process.on("SIGHUP",a),t().then(()=>{process.exit(0)}).catch(()=>{process.exit(1)})});wr.parse();export{br as CLI_VERSION};
|