codekeep 0.2.2 → 0.2.3
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 +9 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as Oa}from"commander";import{render as Ua}from"ink";import{useState as Re,useCallback as Yt,useEffect as Pa,useRef as Bs}from"react";import{Box as Lt,Text as Me,useApp as Da,useInput as La,useStdout as $a}from"ink";var B={gold:"\u25CF",wood:"\u2663",stone:"\u25A0"};var I=16,cr=8,ur=2400;var St={gold:80,wood:40,stone:20},no={gold:200,wood:120,stone:80},fe={wall:"#",trap:"%",treasury:"$",ward:"@",watchtower:"^",archerTower:"!",vault:"&"},tt={wall:"Stone Wall",trap:"Bear Trap",treasury:"Treasury",ward:"Ward",watchtower:"Watchtower",archerTower:"Archer Tower",vault:"Vault"},Ot="\xB7",ot={wall:{1:{gold:12,wood:0,stone:2},2:{gold:10,wood:0,stone:4},3:{gold:14,wood:0,stone:6}},trap:{1:{gold:8,wood:4,stone:6},2:{gold:8,wood:4,stone:6},3:{gold:10,wood:6,stone:8}},treasury:{1:{gold:6,wood:14,stone:2},2:{gold:6,wood:18,stone:4},3:{gold:8,wood:24,stone:6}},ward:{1:{gold:10,wood:10,stone:0},2:{gold:12,wood:12,stone:0},3:{gold:16,wood:16,stone:0}},watchtower:{1:{gold:6,wood:2,stone:12},2:{gold:6,wood:4,stone:14},3:{gold:8,wood:6,stone:18}},archerTower:{1:{gold:14,wood:6,stone:8},2:{gold:16,wood:8,stone:10},3:{gold:20,wood:12,stone:14}},vault:{1:{gold:20,wood:20,stone:15},2:{gold:35,wood:35,stone:25},3:{gold:55,wood:55,stone:40}}},ao={1:40,2:70,3:110},io={1:25,2:40,3:60},lo={1:20,2:35,3:50},pr={1:4,2:6,3:9},fr={1:16,2:12,3:8},mr={1:80,2:160,3:280},gr={1:.15,2:.28,3:.4},bt={1:1,2:2,3:3},hr={1:4,2:7,3:10},co={1:2,2:2,3:3},Tr={1:4,2:3,3:2};var Le={raider:{hp:30,damage:8,loot:3,speed:1},scout:{hp:14,damage:4,loot:2,speed:2},brute:{hp:55,damage:14,loot:5,speed:1}},xr=6e4,uo={gold:2,wood:4,stone:1},po={gold:1,wood:0,stone:3},fo={build_success:{gold:12,wood:0,stone:4},tests_pass:{gold:0,wood:8,stone:4},git_commit:{gold:5,wood:5,stone:10},session_reward:{gold:8,wood:3,stone:6},daily_login:{gold:5,wood:5,stone:10}},yr={build_success:"Miners returned",tests_pass:"Harvest complete",git_commit:"Trade caravan arrived",session_reward:"Tax collection",daily_login:"Morning tribute"},qt=9e5,wr=5,Ut=10,Rr=.5,zt=[{id:"first_structure",name:"Builder",desc:"Place your first structure",bonus:"+20 gold"},{id:"defense_win_5",name:"Warden",desc:"Win 5 defense raids",bonus:"+25 gold, +15 wood, +15 stone"},{id:"win_streak_3",name:"Rallying Cry",desc:"Achieve a 3-win streak",bonus:"+10 all resources"},{id:"win_streak_5",name:"Unstoppable",desc:"Achieve a 5-win streak",bonus:"+30 all resources"},{id:"all_types",name:"Master Builder",desc:"Place every structure type",bonus:"+15 all resources"},{id:"max_level",name:"Fortifier",desc:"Upgrade a structure to Level 3",bonus:"+20 gold, +10 stone"},{id:"archer_kills_10",name:"Marksman",desc:"Kill 10 raiders with archers",bonus:"+20 gold, +10 wood, +10 stone"},{id:"structures_20",name:"Architect",desc:"Place 20 structures total",bonus:"+10 all resources"},{id:"raids_10",name:"Veteran",desc:"Complete 10 raids",bonus:"+15 all resources"},{id:"hoarder",name:"Treasure Keeper",desc:"Hold 500+ total resources",bonus:"+25 gold"}],Zt={gold_nugget:{symbol:"~",color:"cyan",yield:{gold:4,wood:0,stone:0},weight:35},timber:{symbol:"~",color:"yellow",yield:{gold:0,wood:4,stone:0},weight:35},ore:{symbol:"~",color:"green",yield:{gold:0,wood:0,stone:4},weight:20},gem:{symbol:"~",color:"white",yield:{gold:2,wood:2,stone:2},weight:10}},$o=6,Sr=35e3,br=18e4,kr=.25,vr=2,ct=["wall","trap","treasury","ward","watchtower","archerTower","vault"],mo={1:60,2:90,3:130};var Er=1;var Ka={defense_win:7200*1e3,partial_breach:14400*1e3,full_breach:480*60*1e3},go={raider:{gold:8,wood:4,stone:2},scout:{gold:6,wood:6,stone:0},brute:{gold:15,wood:8,stone:8}},Ar={raider:120*1e3,scout:120*1e3,brute:300*1e3};var Ha=1440*60*1e3,Fa=2880*60*1e3,Va=672*60*60*1e3;var Ya=336*60*60*1e3,ja=2880*60*1e3;var qa=720*60*1e3;import{writeFileSync as Oo,readFileSync as wn,mkdirSync as Rn,existsSync as Fo,renameSync as Sn,unlinkSync as bn}from"fs";import{join as Zr,dirname as kn}from"path";import{homedir as vn}from"os";function Js(e){return e.x>=0&&e.x<I&&e.y>=0&&e.y<I}function Go(e,t){return e.structures.find(o=>o.pos.x===t.x&&o.pos.y===t.y)}function Xs(e,t,o){return Js(t)?Go(e,t)?{ok:!1,reason:"Cell is occupied"}:{ok:!0}:{ok:!1,reason:"Out of bounds"}}function Nr(e,t){return e.gold>=t.gold&&e.wood>=t.wood&&e.stone>=t.stone}function Br(e,t,o){let s=Xs(e.grid,t,o);if(!s.ok)return{ok:!1,reason:s.reason};let r=ot[o][1];if(!Nr(e.resources,r))return{ok:!1,reason:Gr(e.resources,r)};let a={id:`${o}-${t.x}-${t.y}-${Date.now()}`,kind:o,level:1,pos:{...t},placedAtUnixMs:Date.now()};return{ok:!0,keep:{...e,resources:Wr(e.resources,r),grid:{...e.grid,structures:[...e.grid.structures,a]},updatedAtUnixMs:Date.now()}}}function Or(e,t){let o=Go(e.grid,t);if(!o)return{ok:!1,reason:"No structure at this position"};if(o.level>=3)return{ok:!1,reason:"Already at max level"};let s=o.level+1,r=ot[o.kind][s];if(!Nr(e.resources,r))return{ok:!1,reason:Gr(e.resources,r)};let a={...o,level:s};return{ok:!0,keep:{...e,resources:Wr(e.resources,r),grid:{...e.grid,structures:e.grid.structures.map(T=>T.id===o.id?a:T)},updatedAtUnixMs:Date.now()}}}function Ur(e,t){let o=Go(e.grid,t);if(!o)return{ok:!1,reason:"No structure at this position"};let s=Qs(o);return{ok:!0,keep:{...e,resources:Oe(e.resources,s),grid:{...e.grid,structures:e.grid.structures.filter(r=>r.id!==o.id)},updatedAtUnixMs:Date.now()}}}function Qs(e){let t=0,o=0,s=0;for(let r=1;r<=e.level;r++){let a=ot[e.kind][r];t+=a.gold,o+=a.wood,s+=a.stone}return{gold:Math.floor(t*.5),wood:Math.floor(o*.5),stone:Math.floor(s*.5)}}function Gr(e,t){let o=[];return t.gold>e.gold&&o.push(`${t.gold-e.gold} more gold`),t.wood>e.wood&&o.push(`${t.wood-e.wood} more wood`),t.stone>e.stone&&o.push(`${t.stone-e.stone} more stone`),o.length>0?`Need ${o.join(", ")}`:"Not enough resources"}function Wr(e,t){return{gold:e.gold-t.gold,wood:e.wood-t.wood,stone:e.stone-t.stone}}function Oe(e,t){return{gold:e.gold+t.gold,wood:e.wood+t.wood,stone:e.stone+t.stone}}function Wo(e,t){let o=t.grants??fo[t.type]??{gold:0,wood:0,stone:0},s=Oe(e.resources,o);return{...e,resources:rt(s),updatedAtUnixMs:Date.now()}}function rt(e){return{gold:Math.min(e.gold,no.gold*10),wood:Math.min(e.wood,no.wood*10),stone:Math.min(e.stone,no.stone*10)}}function Ko(e,t){let o=Math.min(Math.floor(t/xr),60),s=e.structures.filter(a=>a.kind==="treasury").length,r=e.structures.filter(a=>a.kind==="watchtower").length;return{gold:o*(s*uo.gold+r*po.gold),wood:o*(s*uo.wood+r*po.wood),stone:o*(s*uo.stone+r*po.stone)}}function Kr(){let e=["build_success","tests_pass","git_commit","session_reward"],t=e[Math.floor(Math.random()*e.length)];return{type:t,timestamp:Date.now(),grants:fo[t]}}function en(e){return ao[e]}function tn(e){return io[e]}function on(e){return lo[e]}function rn(e){return mo[e]}function Cr(e){return pr[e]}function _r(e){return fr[e]}function No(e){return mr[e]}function sn(e){return gr[e]}function nn(e){return bt[e]}function an(e){return hr[e]}function ln(e){return co[e]}function dn(e){return Tr[e]}function Mr(e,t){return Math.max(Math.abs(e.x-t.x),Math.abs(e.y-t.y))}function _e(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function cn(e,t){let o=e.structures.filter(a=>a.kind==="ward"),s=e.structures.filter(a=>a.kind==="watchtower"),r=0;for(let a of o){let T=1;for(let f of s)Mr(a.pos,f.pos)<=1&&(T=Math.max(T,1+nn(f.level)));Mr(a.pos,t)<=T&&(r=Math.max(r,sn(a.level)))}return r}function Hr(e){let t=e|0;return()=>{t=t+1831565813|0;let o=Math.imul(t^t>>>15,1|t);return o=o+Math.imul(o^o>>>7,61|o)^o,((o^o>>>14)>>>0)/4294967296}}function Fr(e){let t=0;for(let o=0;o<e.length;o++){let s=e.charCodeAt(o);t=(t<<5)-t+s|0}return t}function un(e,t){switch(e){case"N":return{x:t,y:0};case"S":return{x:t,y:I-1};case"E":return{x:I-1,y:t};case"W":return{x:0,y:t}}}function Vr(e,t){return t.x<0||t.x>=I||t.y<0||t.y>=I?!1:e.grid[t.y][t.x]}function Yr(e){for(let t=0;t<I;t++)for(let o=0;o<I;o++)e.grid[t][o]=!0;for(let t of e.solids)t.destroyed||(e.grid[t.pos.y][t.pos.x]=!1)}function jr(e,t,o){if(e.x===t.x&&e.y===t.y)return null;let s=l=>`${l.x},${l.y}`,r=new Map,a=new Set,T=l=>Math.abs(l.x-t.x)+Math.abs(l.y-t.y),f=s(e);r.set(f,{pos:e,g:0,f:T(e),parent:null});let d=new Map;for(;r.size>0;){let l="",x=1/0;for(let[m,p]of r)p.f<x&&(x=p.f,l=m);let k=r.get(l);if(r.delete(l),k.pos.x===t.x&&k.pos.y===t.y){let m=s(t),p=d.get(m);for(;p&&p!==f;)m=p,p=d.get(m);let[b,c]=m.split(",").map(Number);return{x:b,y:c}}a.add(l);let R=[{x:k.pos.x-1,y:k.pos.y},{x:k.pos.x+1,y:k.pos.y},{x:k.pos.x,y:k.pos.y-1},{x:k.pos.x,y:k.pos.y+1}];for(let m of R){let p=s(m);if(a.has(p)||!Vr(o,m)&&!(m.x===t.x&&m.y===t.y))continue;let b=k.g+1,c=r.get(p);c&&b>=c.g||(d.set(p,l),r.set(p,{pos:m,g:b,f:b+T(m),parent:l}))}}return null}function pn(e,t){switch(e){case"wall":return en(t);case"archerTower":return tn(t);case"watchtower":return on(t);case"vault":return rn(t);default:return 0}}var fn=["archerTower","watchtower"];function Ir(e,t){return e.solids.find(o=>!o.destroyed&&_e(o.pos,t)===1)}function ho(e,t,o){o.hp-=Le[t.raiderType].damage;let s=o.hp<=0;if(s){o.destroyed=!0,Yr(e);let r=e.archers.find(a=>a.structureId===o.structureId);r&&(r.destroyed=!0)}o.kind==="wall"?e.events.push({t:e.tick,type:"wall_damaged",structureId:o.structureId,hpRemaining:Math.max(0,o.hp),destroyed:s}):e.events.push({t:e.tick,type:"structure_damaged",structureId:o.structureId,structureKind:o.kind,hpRemaining:Math.max(0,o.hp),destroyed:s})}function Pr(e,t,o){let r=[{x:o.pos.x-1,y:o.pos.y},{x:o.pos.x+1,y:o.pos.y},{x:o.pos.x,y:o.pos.y-1},{x:o.pos.x,y:o.pos.y+1}].filter(f=>Vr(e,f)).sort((f,d)=>{let l=_e(f,t.pos),x=_e(d,t.pos);return l-x})[0];if(!r||_e(t.pos,r)===0)return!1;let a=jr(t.pos,r,e);if(!a)return!1;let T={...t.pos};return t.pos={...a},e.events.push({t:e.tick,type:"raider_move",probeId:t.id,from:T,to:{...t.pos}}),!0}function mn(e,t){return e.solids.filter(s=>!s.destroyed&&fn.includes(s.kind)).sort((s,r)=>{let a=s.kind==="archerTower"?0:1,T=r.kind==="archerTower"?0:1;return a!==T?a-T:_e(s.pos,t.pos)-_e(r.pos,t.pos)})[0]??null}function gn(e,t){let o=e.solids.filter(s=>!s.destroyed&&_e(s.pos,t.pos)===1);return o.length===0?null:o.sort((s,r)=>s.hp-r.hp)[0]}function ut(e){let t=Hr(Fr(e.seed)),{keepGrid:o}=e,s=["wall","archerTower","watchtower","vault"],r=o.structures.filter(c=>s.includes(c.kind)).sort((c,$)=>c.id.localeCompare($.id)).map(c=>({structureId:c.id,kind:c.kind,pos:{...c.pos},hp:pn(c.kind,c.level),destroyed:!1})),a=o.structures.filter(c=>c.kind==="trap").sort((c,$)=>c.id.localeCompare($.id)).map(c=>({structureId:c.id,pos:{...c.pos},level:c.level,cooldownRemaining:0})),T=o.structures.filter(c=>c.kind==="archerTower").sort((c,$)=>c.id.localeCompare($.id)).map(c=>({structureId:c.id,pos:{...c.pos},level:c.level,cooldownRemaining:0,destroyed:!1})),f=o.structures.filter(c=>c.kind==="treasury").sort((c,$)=>c.id.localeCompare($.id)).map(c=>({structureId:c.id,pos:{...c.pos},level:c.level,storedResources:No(c.level)}));f.length===0&&(f=[{structureId:"__virtual_center_treasury",pos:{x:8,y:8},level:1,storedResources:No(1)}]);let d={tick:0,raiders:[],solids:r,traps:a,archers:T,treasuries:f,events:[],grid:Array.from({length:I},()=>Array(I).fill(!0)),totalLoot:{gold:0,wood:0,stone:0}};Yr(d);let l=[],x=["N","S","E","W"];for(let c=0;c<e.probeCount;c++){let $=e.spawnSpecs?.[c],z=$?.edge??x[Math.floor(t()*x.length)],U=$?.offset??Math.floor(t()*I),u=$?.raiderType??e.probeTypes?.[c]??"raider",K=$?.waveDelay??0;l.push({id:c,edge:z,offset:U,raiderType:u,spawnAtTick:K})}function k(c,$){let z=un(c.edge,c.offset),U=null,u=1/0;for(let K of f){let w=_e(K.pos,z);w<u&&(u=w,U=K)}d.raiders.push({id:c.id,pos:{...z},hp:Le[c.raiderType].hp,raiderType:c.raiderType,stunRemaining:0,targetId:U?.structureId??null,alive:!0,looted:{gold:0,wood:0,stone:0}}),d.events.push({t:$,type:"raider_spawn",probeId:c.id,edge:c.edge,pos:{...z},raiderType:c.raiderType,maxHp:Le[c.raiderType].hp})}for(let c of l)c.spawnAtTick===0&&k(c,0);for(;d.tick<ur;){d.tick++;for(let u of l)u.spawnAtTick===d.tick&&k(u,d.tick);for(let u of d.traps)u.cooldownRemaining>0&&u.cooldownRemaining--;for(let u of d.archers)u.cooldownRemaining>0&&u.cooldownRemaining--;let c=d.raiders.filter(u=>u.alive).sort((u,K)=>u.id-K.id),$=l.some(u=>u.spawnAtTick>d.tick);if(c.length===0&&!$)break;for(let u of d.archers){if(u.destroyed||u.cooldownRemaining>0)continue;let K=ln(u.level),w=an(u.level),H=c.filter(h=>h.alive&&_e(u.pos,h.pos)<=K),y=H.length===0?void 0:H.sort((h,E)=>{let C=h.stunRemaining>0?0:1,N=E.stunRemaining>0?0:1;if(C!==N)return C-N;let A=d.treasuries.find(Se=>Se.structureId===h.targetId),O=d.treasuries.find(Se=>Se.structureId===E.targetId),W=A?_e(h.pos,A.pos):1/0,ue=O?_e(E.pos,O.pos):1/0;return W-ue})[0];y&&(y.hp-=w,u.cooldownRemaining=dn(u.level),y.hp<=0?(y.alive=!1,d.events.push({t:d.tick,type:"arrow_hit",probeId:y.id,archerId:u.structureId,damage:w,hpRemaining:0}),d.events.push({t:d.tick,type:"raider_destroyed",probeId:y.id,pos:{...y.pos}})):d.events.push({t:d.tick,type:"arrow_hit",probeId:y.id,archerId:u.structureId,damage:w,hpRemaining:y.hp}))}for(let u of c){if(!u.alive)continue;if(u.stunRemaining>0){u.stunRemaining--;continue}let K=Le[u.raiderType].speed;for(let w=0;w<K&&u.alive;w++){if(u.raiderType==="brute"){let N=mn(d,u);if(N){if(_e(u.pos,N.pos)===1){ho(d,u,N);break}if(Pr(d,u,N)){let O=d.traps.find(W=>W.pos.x===u.pos.x&&W.pos.y===u.pos.y&&W.cooldownRemaining===0);if(O){u.stunRemaining=Cr(O.level),O.cooldownRemaining=_r(O.level),d.events.push({t:d.tick,type:"raider_stunned",probeId:u.id,pos:{...u.pos},trapId:O.structureId,stunTicks:u.stunRemaining});break}continue}let A=Ir(d,u.pos);if(A){ho(d,u,A);break}}}let H=d.treasuries.find(N=>N.structureId===u.targetId);if(!H||H.storedResources<=0){let N=d.treasuries.filter(A=>A.storedResources>0).sort((A,O)=>{let W=_e(A.pos,u.pos),ue=_e(O.pos,u.pos);return W-ue||A.structureId.localeCompare(O.structureId)})[0];if(!N){u.alive=!1,d.events.push({t:d.tick,type:"raider_destroyed",probeId:u.id,pos:{...u.pos}});break}u.targetId=N.structureId}let y=d.treasuries.find(N=>N.structureId===u.targetId);if(u.pos.x===y.pos.x&&u.pos.y===y.pos.y){let N=cn(o,y.pos),A=Math.max(1,Math.floor(Le[u.raiderType].loot*y.level*(1-N))),O=Math.min(A,y.storedResources);y.storedResources-=O;let W=Math.ceil(O*.4),ue=Math.ceil(O*.35),Se=Math.max(0,O-W-ue),nt={gold:W,wood:ue,stone:Se};u.looted.gold+=W,u.looted.wood+=ue,u.looted.stone+=Se,d.totalLoot.gold+=W,d.totalLoot.wood+=ue,d.totalLoot.stone+=Se,d.events.push({t:d.tick,type:"treasury_breach",structureId:y.structureId,lootTaken:nt});break}let h=jr(u.pos,y.pos,d);if(!h){if(u.raiderType==="scout"){let A=gn(d,u);if(A){ho(d,u,A);break}}let N=Ir(d,u.pos);if(N)ho(d,u,N);else{let A=d.solids.filter(O=>!O.destroyed).sort((O,W)=>_e(O.pos,u.pos)-_e(W.pos,u.pos))[0];A&&Pr(d,u,A)}break}let E={...u.pos};u.pos={...h},d.events.push({t:d.tick,type:"raider_move",probeId:u.id,from:E,to:{...u.pos}});let C=d.traps.find(N=>N.pos.x===u.pos.x&&N.pos.y===u.pos.y&&N.cooldownRemaining===0);if(C){let N=Cr(C.level);u.stunRemaining=N,C.cooldownRemaining=_r(C.level),d.events.push({t:d.tick,type:"raider_stunned",probeId:u.id,pos:{...u.pos},trapId:C.structureId,stunTicks:N});break}}}let z=d.raiders.every(u=>!u.alive||u.stunRemaining>100),U=l.some(u=>u.spawnAtTick>d.tick);if(z&&!U)break}let R=d.treasuries.reduce((c,$)=>c+$.storedResources,0),m=d.treasuries.reduce((c,$)=>c+No($.level),0),p,b=d.totalLoot.gold+d.totalLoot.wood+d.totalLoot.stone;return b===0?p="defense_win":R>0&&b<m*.5?p="partial_breach":p="full_breach",d.events.push({t:d.tick,type:"raid_end",outcome:p}),{tickRateHz:cr,maxTicks:d.tick,events:d.events}}function Bo(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function hn(e){let t=Object.entries(Zt),o=t.reduce((r,[,a])=>r+a.weight,0),s=e()*o;for(let[r,a]of t)if(s-=a.weight,s<=0)return r;return"gold_nugget"}function Tn(e,t,o){return!!(t.structures.some(s=>s.pos.x===e.x&&s.pos.y===e.y)||o.some(s=>s.pos.x===e.x&&s.pos.y===e.y))}function qr(e,t,o,s){if(e.length>=$o)return e;let r=t.structures.filter(l=>l.kind==="archerTower").length,a=Math.min(r,2),T=1+(s()<.4?1:0),f=Math.min(T+a,$o-e.length),d=[];for(let l=0;l<f;l++){let x=null;for(let k=0;k<30;k++){let R=Math.floor(s()*I),m=Math.floor(s()*I),p={x:R,y:m};if(!Tn(p,t,[...e,...d])){x=p;break}}if(!x)break;d.push({id:`frag-${o}-${l}`,type:hn(s),pos:x,spawnedAtMs:o})}return[...e,...d]}function Ho(e,t,o){let s=e.findIndex(l=>l.pos.x===t.x&&l.pos.y===t.y);if(s===-1)return null;let r=[e[s]],a=o.structures.filter(l=>l.kind==="watchtower"),T=0;for(let l of a)Bo(l.pos,t)<=bt[l.level]&&(T=Math.max(T,l.level));if(T>0)for(let l of e)r.includes(l)||Bo(t,l.pos)<=T&&r.push(l);let f=new Set(r.map(l=>l.id)),d={gold:0,wood:0,stone:0};for(let l of r){let x=Zt[l.type].yield,k=1;o.structures.some(m=>m.kind==="treasury"&&Bo(m.pos,l.pos)<=vr)&&(k+=kr),d={gold:d.gold+Math.ceil(x.gold*k),wood:d.wood+Math.ceil(x.wood*k),stone:d.stone+Math.ceil(x.stone*k)}}return{yield:d,updatedFragments:e.filter(l=>!f.has(l.id)),collected:r}}function zr(e,t){return e.filter(o=>t-o.spawnedAtMs<br)}var Dr=[{level:1,structureCount:4,maxUpgradeLevel:1,probeCount:3},{level:2,structureCount:8,maxUpgradeLevel:1,probeCount:4},{level:3,structureCount:12,maxUpgradeLevel:2,probeCount:5},{level:4,structureCount:16,maxUpgradeLevel:2,probeCount:6},{level:5,structureCount:22,maxUpgradeLevel:3,probeCount:8}];function To(e,t){let o=Dr[Math.max(0,Math.min(t-1,Dr.length-1))],s=Hr(Fr(e)),r=[],a=new Set,T=Math.max(1,Math.floor(o.structureCount/5));for(let R=0;R<T;R++){let m=Lr(s,a);if(!m)break;r.push(kt("treasury",m,vt(s,o.maxUpgradeLevel))),a.add(`${m.x},${m.y}`)}let f=Math.floor(o.structureCount*.35);for(let R=0;R<f;R++){let m=$r(s,a,r);if(!m)break;r.push(kt("wall",m,vt(s,o.maxUpgradeLevel))),a.add(`${m.x},${m.y}`)}let d=Math.floor(o.structureCount*.2);for(let R=0;R<d;R++){let m=xn(s,a);if(!m)break;r.push(kt("trap",m,vt(s,o.maxUpgradeLevel))),a.add(`${m.x},${m.y}`)}let l=Math.max(1,Math.floor(o.structureCount*.15));for(let R=0;R<l;R++){let m=r.filter(c=>c.kind==="treasury"),p=m.length>0?m[Math.floor(s()*m.length)]:void 0;if(!p)break;let b=[{x:p.pos.x-1,y:p.pos.y},{x:p.pos.x+1,y:p.pos.y},{x:p.pos.x,y:p.pos.y-1},{x:p.pos.x,y:p.pos.y+1}].filter(c=>c.x>=0&&c.x<I&&c.y>=0&&c.y<I&&!a.has(`${c.x},${c.y}`));if(b.length>0){let c=b[Math.floor(s()*b.length)];r.push(kt("ward",c,vt(s,o.maxUpgradeLevel))),a.add(`${c.x},${c.y}`)}}let x=Math.max(0,Math.floor(o.structureCount*.1));for(let R=0;R<x;R++){let m=$r(s,a,r);if(!m)break;r.push(kt("archerTower",m,vt(s,o.maxUpgradeLevel))),a.add(`${m.x},${m.y}`)}for(let R=0;R<Er;R++){let m=Lr(s,a);if(!m)break;r.push(kt("vault",m,vt(s,o.maxUpgradeLevel))),a.add(`${m.x},${m.y}`)}let k=o.structureCount-r.length;for(let R=0;R<k;R++){let m=yn(s,a);if(!m)break;r.push(kt("watchtower",m,vt(s,o.maxUpgradeLevel))),a.add(`${m.x},${m.y}`)}return{id:`npc-${e}-${t}`,name:`NPC Keep (Lv.${o.level})`,ownerPlayerId:"npc",grid:{width:16,height:16,structures:r},resources:{...St},createdAtUnixMs:Date.now(),updatedAtUnixMs:Date.now()}}function kt(e,t,o){return{id:`${e}-${t.x}-${t.y}`,kind:e,level:o,pos:{...t},placedAtUnixMs:Date.now()}}function vt(e,t){let o=Math.floor(e()*t)+1;return Math.min(t,o)}function Lr(e,t){for(let o=0;o<50;o++){let s=4+Math.floor(e()*(I-8)),r=4+Math.floor(e()*(I-8));if(!t.has(`${s},${r}`))return{x:s,y:r}}return null}function $r(e,t,o){for(let s=0;s<50;s++){let r=o[Math.floor(e()*o.length)],a=Math.floor(e()*3)-1,T=Math.floor(e()*3)-1,f=r.pos.x+a,d=r.pos.y+T;if(f>=0&&f<I&&d>=0&&d<I&&!t.has(`${f},${d}`))return{x:f,y:d}}return null}function xn(e,t){for(let o=0;o<50;o++){let s=Math.floor(e()*4),r,a;if(s===0?(r=Math.floor(e()*I),a=Math.floor(e()*3)):s===1?(r=Math.floor(e()*I),a=I-1-Math.floor(e()*3)):s===2?(r=Math.floor(e()*3),a=Math.floor(e()*I)):(r=I-1-Math.floor(e()*3),a=Math.floor(e()*I)),!t.has(`${r},${a}`))return{x:r,y:a}}return null}function yn(e,t){for(let o=0;o<50;o++){let s=Math.floor(e()*I),r=Math.floor(e()*I);if(!t.has(`${s},${r}`))return{x:s,y:r}}return null}var Uo=1;function En(){return Zr(vn(),".config","codekeep")}function Vo(){return Zr(En(),"game.json")}function Jr(e){let t=`player-${Date.now()}`;return{schemaVersion:Uo,savedAtUnixMs:Date.now(),player:{id:t,displayName:e,settings:{asciiMode:!1}},keep:{id:`keep-${t}-${Date.now()}`,name:`${e}'s Keep`,ownerPlayerId:t,grid:{width:16,height:16,structures:[]},resources:{...St},createdAtUnixMs:Date.now(),updatedAtUnixMs:Date.now()},raidHistory:[],tutorialCompleted:!1,lastPlayedAtUnixMs:Date.now(),progression:{totalBuildsToday:0,totalCommitsToday:0,lastDailyResetDay:Math.floor(Date.now()/864e5),totalRaidsWon:0,totalRaidsLost:0,totalStructuresPlaced:0,currentWinStreak:0,bestWinStreak:0,achievements:[],totalRaidersKilledByArcher:0}}}function Et(e){let t=Vo(),o=kn(t);Fo(o)||Rn(o,{recursive:!0});let s=t+".tmp",r=JSON.stringify({...e,savedAtUnixMs:Date.now()},null,2);Oo(s,r,"utf-8"),Sn(s,t)}function Gt(){let e=Vo();if(!Fo(e))return null;let t;try{t=wn(e,"utf-8")}catch{return null}let o;try{o=JSON.parse(t)}catch{let s=e+".bak";try{Oo(s,t,"utf-8")}catch{}return process.stderr.write(`[codekeep] Corrupted save backed up to ${s}
|
|
2
|
+
import{Command as Ua}from"commander";import{render as Ga}from"ink";import{useState as be,useCallback as Yt,useEffect as Da,useRef as Bs}from"react";import{Box as Lt,Text as Ie,useApp as La,useInput as $a,useStdout as Na}from"ink";var O={gold:"\u25CF",wood:"\u2663",stone:"\u25A0"};var P=16,cr=8,ur=2400;var St={gold:80,wood:40,stone:20},io={gold:200,wood:120,stone:80},ge={wall:"#",trap:"%",treasury:"$",ward:"@",watchtower:"^",archerTower:"!",vault:"&"},tt={wall:"Stone Wall",trap:"Bear Trap",treasury:"Treasury",ward:"Ward",watchtower:"Watchtower",archerTower:"Archer Tower",vault:"Vault"},Ot="\xB7",ot={wall:{1:{gold:12,wood:0,stone:2},2:{gold:10,wood:0,stone:4},3:{gold:14,wood:0,stone:6}},trap:{1:{gold:8,wood:4,stone:6},2:{gold:8,wood:4,stone:6},3:{gold:10,wood:6,stone:8}},treasury:{1:{gold:6,wood:14,stone:2},2:{gold:6,wood:18,stone:4},3:{gold:8,wood:24,stone:6}},ward:{1:{gold:10,wood:10,stone:0},2:{gold:12,wood:12,stone:0},3:{gold:16,wood:16,stone:0}},watchtower:{1:{gold:6,wood:2,stone:12},2:{gold:6,wood:4,stone:14},3:{gold:8,wood:6,stone:18}},archerTower:{1:{gold:14,wood:6,stone:8},2:{gold:16,wood:8,stone:10},3:{gold:20,wood:12,stone:14}},vault:{1:{gold:20,wood:20,stone:15},2:{gold:35,wood:35,stone:25},3:{gold:55,wood:55,stone:40}}},lo={1:40,2:70,3:110},co={1:25,2:40,3:60},uo={1:20,2:35,3:50},pr={1:4,2:6,3:9},fr={1:16,2:12,3:8},mr={1:80,2:160,3:280},gr={1:.15,2:.28,3:.4},bt={1:1,2:2,3:3},hr={1:4,2:7,3:10},po={1:2,2:2,3:3},Tr={1:4,2:3,3:2};var $e={raider:{hp:30,damage:8,loot:3,speed:1},scout:{hp:14,damage:4,loot:2,speed:2},brute:{hp:55,damage:14,loot:5,speed:1}},xr=6e4,fo={gold:2,wood:4,stone:1},mo={gold:1,wood:0,stone:3},go={build_success:{gold:12,wood:0,stone:4},tests_pass:{gold:0,wood:8,stone:4},git_commit:{gold:5,wood:5,stone:10},session_reward:{gold:8,wood:3,stone:6},daily_login:{gold:5,wood:5,stone:10}},yr={build_success:"Miners returned",tests_pass:"Harvest complete",git_commit:"Trade caravan arrived",session_reward:"Tax collection",daily_login:"Morning tribute"},qt=9e5,wr=5,Ut=10,Rr=.5,zt=[{id:"first_structure",name:"Builder",desc:"Place your first structure",bonus:"+20 gold"},{id:"defense_win_5",name:"Warden",desc:"Win 5 defense raids",bonus:"+25 gold, +15 wood, +15 stone"},{id:"win_streak_3",name:"Rallying Cry",desc:"Achieve a 3-win streak",bonus:"+10 all resources"},{id:"win_streak_5",name:"Unstoppable",desc:"Achieve a 5-win streak",bonus:"+30 all resources"},{id:"all_types",name:"Master Builder",desc:"Place every structure type",bonus:"+15 all resources"},{id:"max_level",name:"Fortifier",desc:"Upgrade a structure to Level 3",bonus:"+20 gold, +10 stone"},{id:"archer_kills_10",name:"Marksman",desc:"Kill 10 raiders with archers",bonus:"+20 gold, +10 wood, +10 stone"},{id:"structures_20",name:"Architect",desc:"Place 20 structures total",bonus:"+10 all resources"},{id:"raids_10",name:"Veteran",desc:"Complete 10 raids",bonus:"+15 all resources"},{id:"hoarder",name:"Treasure Keeper",desc:"Hold 500+ total resources",bonus:"+25 gold"}],Zt={gold_nugget:{symbol:"~",color:"cyan",yield:{gold:4,wood:0,stone:0},weight:35},timber:{symbol:"~",color:"yellow",yield:{gold:0,wood:4,stone:0},weight:35},ore:{symbol:"~",color:"green",yield:{gold:0,wood:0,stone:4},weight:20},gem:{symbol:"~",color:"white",yield:{gold:2,wood:2,stone:2},weight:10}},$o=6,Sr=35e3,br=18e4,kr=.25,vr=2,ut=["wall","trap","treasury","ward","watchtower","archerTower","vault"],ho={1:60,2:90,3:130};var Er=1;var Ha={defense_win:7200*1e3,partial_breach:14400*1e3,full_breach:480*60*1e3},To={raider:{gold:8,wood:4,stone:2},scout:{gold:6,wood:6,stone:0},brute:{gold:15,wood:8,stone:8}},Ar={raider:120*1e3,scout:120*1e3,brute:300*1e3};var Fa=1440*60*1e3,Va=2880*60*1e3,Ya=672*60*60*1e3;var ja=336*60*60*1e3,qa=2880*60*1e3;var za=720*60*1e3;import{writeFileSync as Oo,readFileSync as wn,mkdirSync as Rn,existsSync as Fo,renameSync as Sn,unlinkSync as bn}from"fs";import{join as Zr,dirname as kn}from"path";import{homedir as vn}from"os";function Js(e){return e.x>=0&&e.x<P&&e.y>=0&&e.y<P}function Go(e,t){return e.structures.find(o=>o.pos.x===t.x&&o.pos.y===t.y)}function Xs(e,t,o){return Js(t)?Go(e,t)?{ok:!1,reason:"Cell is occupied"}:{ok:!0}:{ok:!1,reason:"Out of bounds"}}function Nr(e,t){return e.gold>=t.gold&&e.wood>=t.wood&&e.stone>=t.stone}function Br(e,t,o){let s=Xs(e.grid,t,o);if(!s.ok)return{ok:!1,reason:s.reason};let r=ot[o][1];if(!Nr(e.resources,r))return{ok:!1,reason:Gr(e.resources,r)};let a={id:`${o}-${t.x}-${t.y}-${Date.now()}`,kind:o,level:1,pos:{...t},placedAtUnixMs:Date.now()};return{ok:!0,keep:{...e,resources:Wr(e.resources,r),grid:{...e.grid,structures:[...e.grid.structures,a]},updatedAtUnixMs:Date.now()}}}function Or(e,t){let o=Go(e.grid,t);if(!o)return{ok:!1,reason:"No structure at this position"};if(o.level>=3)return{ok:!1,reason:"Already at max level"};let s=o.level+1,r=ot[o.kind][s];if(!Nr(e.resources,r))return{ok:!1,reason:Gr(e.resources,r)};let a={...o,level:s};return{ok:!0,keep:{...e,resources:Wr(e.resources,r),grid:{...e.grid,structures:e.grid.structures.map(T=>T.id===o.id?a:T)},updatedAtUnixMs:Date.now()}}}function Ur(e,t){let o=Go(e.grid,t);if(!o)return{ok:!1,reason:"No structure at this position"};let s=Qs(o);return{ok:!0,keep:{...e,resources:Oe(e.resources,s),grid:{...e.grid,structures:e.grid.structures.filter(r=>r.id!==o.id)},updatedAtUnixMs:Date.now()}}}function Qs(e){let t=0,o=0,s=0;for(let r=1;r<=e.level;r++){let a=ot[e.kind][r];t+=a.gold,o+=a.wood,s+=a.stone}return{gold:Math.floor(t*.5),wood:Math.floor(o*.5),stone:Math.floor(s*.5)}}function Gr(e,t){let o=[];return t.gold>e.gold&&o.push(`${t.gold-e.gold} more gold`),t.wood>e.wood&&o.push(`${t.wood-e.wood} more wood`),t.stone>e.stone&&o.push(`${t.stone-e.stone} more stone`),o.length>0?`Need ${o.join(", ")}`:"Not enough resources"}function Wr(e,t){return{gold:e.gold-t.gold,wood:e.wood-t.wood,stone:e.stone-t.stone}}function Oe(e,t){return{gold:e.gold+t.gold,wood:e.wood+t.wood,stone:e.stone+t.stone}}function Wo(e,t){let o=t.grants??go[t.type]??{gold:0,wood:0,stone:0},s=Oe(e.resources,o);return{...e,resources:rt(s),updatedAtUnixMs:Date.now()}}function rt(e){return{gold:Math.min(e.gold,io.gold*10),wood:Math.min(e.wood,io.wood*10),stone:Math.min(e.stone,io.stone*10)}}function Ko(e,t){let o=Math.min(Math.floor(t/xr),60),s=e.structures.filter(a=>a.kind==="treasury").length,r=e.structures.filter(a=>a.kind==="watchtower").length;return{gold:o*(s*fo.gold+r*mo.gold),wood:o*(s*fo.wood+r*mo.wood),stone:o*(s*fo.stone+r*mo.stone)}}function Kr(){let e=["build_success","tests_pass","git_commit","session_reward"],t=e[Math.floor(Math.random()*e.length)];return{type:t,timestamp:Date.now(),grants:go[t]}}function en(e){return lo[e]}function tn(e){return co[e]}function on(e){return uo[e]}function rn(e){return ho[e]}function Cr(e){return pr[e]}function _r(e){return fr[e]}function No(e){return mr[e]}function sn(e){return gr[e]}function nn(e){return bt[e]}function an(e){return hr[e]}function ln(e){return po[e]}function dn(e){return Tr[e]}function Mr(e,t){return Math.max(Math.abs(e.x-t.x),Math.abs(e.y-t.y))}function Me(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function cn(e,t){let o=e.structures.filter(a=>a.kind==="ward"),s=e.structures.filter(a=>a.kind==="watchtower"),r=0;for(let a of o){let T=1;for(let p of s)Mr(a.pos,p.pos)<=1&&(T=Math.max(T,1+nn(p.level)));Mr(a.pos,t)<=T&&(r=Math.max(r,sn(a.level)))}return r}function Hr(e){let t=e|0;return()=>{t=t+1831565813|0;let o=Math.imul(t^t>>>15,1|t);return o=o+Math.imul(o^o>>>7,61|o)^o,((o^o>>>14)>>>0)/4294967296}}function Fr(e){let t=0;for(let o=0;o<e.length;o++){let s=e.charCodeAt(o);t=(t<<5)-t+s|0}return t}function un(e,t){switch(e){case"N":return{x:t,y:0};case"S":return{x:t,y:P-1};case"E":return{x:P-1,y:t};case"W":return{x:0,y:t}}}function Vr(e,t){return t.x<0||t.x>=P||t.y<0||t.y>=P?!1:e.grid[t.y][t.x]}function Yr(e){for(let t=0;t<P;t++)for(let o=0;o<P;o++)e.grid[t][o]=!0;for(let t of e.solids)t.destroyed||(e.grid[t.pos.y][t.pos.x]=!1)}function jr(e,t,o){if(e.x===t.x&&e.y===t.y)return null;let s=l=>`${l.x},${l.y}`,r=new Map,a=new Set,T=l=>Math.abs(l.x-t.x)+Math.abs(l.y-t.y),p=s(e);r.set(p,{pos:e,g:0,f:T(e),parent:null});let d=new Map;for(;r.size>0;){let l="",x=1/0;for(let[f,m]of r)m.f<x&&(x=m.f,l=f);let b=r.get(l);if(r.delete(l),b.pos.x===t.x&&b.pos.y===t.y){let f=s(t),m=d.get(f);for(;m&&m!==p;)f=m,m=d.get(f);let[C,u]=f.split(",").map(Number);return{x:C,y:u}}a.add(l);let R=[{x:b.pos.x-1,y:b.pos.y},{x:b.pos.x+1,y:b.pos.y},{x:b.pos.x,y:b.pos.y-1},{x:b.pos.x,y:b.pos.y+1}];for(let f of R){let m=s(f);if(a.has(m)||!Vr(o,f)&&!(f.x===t.x&&f.y===t.y))continue;let C=b.g+1,u=r.get(m);u&&C>=u.g||(d.set(m,l),r.set(m,{pos:f,g:C,f:C+T(f),parent:l}))}}return null}function pn(e,t){switch(e){case"wall":return en(t);case"archerTower":return tn(t);case"watchtower":return on(t);case"vault":return rn(t);default:return 0}}var fn=["archerTower","watchtower"];function Ir(e,t){return e.solids.find(o=>!o.destroyed&&Me(o.pos,t)===1)}function xo(e,t,o){o.hp-=$e[t.raiderType].damage;let s=o.hp<=0;if(s){o.destroyed=!0,Yr(e);let r=e.archers.find(a=>a.structureId===o.structureId);r&&(r.destroyed=!0)}o.kind==="wall"?e.events.push({t:e.tick,type:"wall_damaged",structureId:o.structureId,hpRemaining:Math.max(0,o.hp),destroyed:s}):e.events.push({t:e.tick,type:"structure_damaged",structureId:o.structureId,structureKind:o.kind,hpRemaining:Math.max(0,o.hp),destroyed:s})}function Pr(e,t,o){let r=[{x:o.pos.x-1,y:o.pos.y},{x:o.pos.x+1,y:o.pos.y},{x:o.pos.x,y:o.pos.y-1},{x:o.pos.x,y:o.pos.y+1}].filter(p=>Vr(e,p)).sort((p,d)=>{let l=Me(p,t.pos),x=Me(d,t.pos);return l-x})[0];if(!r||Me(t.pos,r)===0)return!1;let a=jr(t.pos,r,e);if(!a)return!1;let T={...t.pos};return t.pos={...a},e.events.push({t:e.tick,type:"raider_move",probeId:t.id,from:T,to:{...t.pos}}),!0}function mn(e,t){return e.solids.filter(s=>!s.destroyed&&fn.includes(s.kind)).sort((s,r)=>{let a=s.kind==="archerTower"?0:1,T=r.kind==="archerTower"?0:1;return a!==T?a-T:Me(s.pos,t.pos)-Me(r.pos,t.pos)})[0]??null}function gn(e,t){let o=e.solids.filter(s=>!s.destroyed&&Me(s.pos,t.pos)===1);return o.length===0?null:o.sort((s,r)=>s.hp-r.hp)[0]}function pt(e){let t=Hr(Fr(e.seed)),{keepGrid:o}=e,s=["wall","archerTower","watchtower","vault"],r=o.structures.filter(u=>s.includes(u.kind)).sort((u,v)=>u.id.localeCompare(v.id)).map(u=>({structureId:u.id,kind:u.kind,pos:{...u.pos},hp:pn(u.kind,u.level),destroyed:!1})),a=o.structures.filter(u=>u.kind==="trap").sort((u,v)=>u.id.localeCompare(v.id)).map(u=>({structureId:u.id,pos:{...u.pos},level:u.level,cooldownRemaining:0})),T=o.structures.filter(u=>u.kind==="archerTower").sort((u,v)=>u.id.localeCompare(v.id)).map(u=>({structureId:u.id,pos:{...u.pos},level:u.level,cooldownRemaining:0,destroyed:!1})),p=o.structures.filter(u=>u.kind==="treasury").sort((u,v)=>u.id.localeCompare(v.id)).map(u=>({structureId:u.id,pos:{...u.pos},level:u.level,storedResources:No(u.level)}));p.length===0&&(p=[{structureId:"__virtual_center_treasury",pos:{x:8,y:8},level:1,storedResources:No(1)}]);let d={tick:0,raiders:[],solids:r,traps:a,archers:T,treasuries:p,events:[],grid:Array.from({length:P},()=>Array(P).fill(!0)),totalLoot:{gold:0,wood:0,stone:0}};Yr(d);let l=[],x=["N","S","E","W"];for(let u=0;u<e.probeCount;u++){let v=e.spawnSpecs?.[u],U=v?.edge??x[Math.floor(t()*x.length)],B=v?.offset??Math.floor(t()*P),c=v?.raiderType??e.probeTypes?.[u]??"raider",H=v?.waveDelay??0;l.push({id:u,edge:U,offset:B,raiderType:c,spawnAtTick:H})}function b(u,v){let U=un(u.edge,u.offset),B=null,c=1/0;for(let H of p){let w=Me(H.pos,U);w<c&&(c=w,B=H)}d.raiders.push({id:u.id,pos:{...U},hp:$e[u.raiderType].hp,raiderType:u.raiderType,stunRemaining:0,targetId:B?.structureId??null,alive:!0,looted:{gold:0,wood:0,stone:0}}),d.events.push({t:v,type:"raider_spawn",probeId:u.id,edge:u.edge,pos:{...U},raiderType:u.raiderType,maxHp:$e[u.raiderType].hp})}for(let u of l)u.spawnAtTick===0&&b(u,0);for(;d.tick<ur;){d.tick++;for(let c of l)c.spawnAtTick===d.tick&&b(c,d.tick);for(let c of d.traps)c.cooldownRemaining>0&&c.cooldownRemaining--;for(let c of d.archers)c.cooldownRemaining>0&&c.cooldownRemaining--;let u=d.raiders.filter(c=>c.alive).sort((c,H)=>c.id-H.id),v=l.some(c=>c.spawnAtTick>d.tick);if(u.length===0&&!v)break;for(let c of d.archers){if(c.destroyed||c.cooldownRemaining>0)continue;let H=ln(c.level),w=an(c.level),F=u.filter(h=>h.alive&&Me(c.pos,h.pos)<=H),y=F.length===0?void 0:F.sort((h,E)=>{let _=h.stunRemaining>0?0:1,N=E.stunRemaining>0?0:1;if(_!==N)return _-N;let A=d.treasuries.find(ke=>ke.structureId===h.targetId),G=d.treasuries.find(ke=>ke.structureId===E.targetId),K=A?Me(h.pos,A.pos):1/0,pe=G?Me(E.pos,G.pos):1/0;return K-pe})[0];y&&(y.hp-=w,c.cooldownRemaining=dn(c.level),y.hp<=0?(y.alive=!1,d.events.push({t:d.tick,type:"arrow_hit",probeId:y.id,archerId:c.structureId,damage:w,hpRemaining:0}),d.events.push({t:d.tick,type:"raider_destroyed",probeId:y.id,pos:{...y.pos}})):d.events.push({t:d.tick,type:"arrow_hit",probeId:y.id,archerId:c.structureId,damage:w,hpRemaining:y.hp}))}for(let c of u){if(!c.alive)continue;if(c.stunRemaining>0){c.stunRemaining--;continue}let H=$e[c.raiderType].speed;for(let w=0;w<H&&c.alive;w++){if(c.raiderType==="brute"){let N=mn(d,c);if(N){if(Me(c.pos,N.pos)===1){xo(d,c,N);break}if(Pr(d,c,N)){let G=d.traps.find(K=>K.pos.x===c.pos.x&&K.pos.y===c.pos.y&&K.cooldownRemaining===0);if(G){c.stunRemaining=Cr(G.level),G.cooldownRemaining=_r(G.level),d.events.push({t:d.tick,type:"raider_stunned",probeId:c.id,pos:{...c.pos},trapId:G.structureId,stunTicks:c.stunRemaining});break}continue}let A=Ir(d,c.pos);if(A){xo(d,c,A);break}}}let F=d.treasuries.find(N=>N.structureId===c.targetId);if(!F||F.storedResources<=0){let N=d.treasuries.filter(A=>A.storedResources>0).sort((A,G)=>{let K=Me(A.pos,c.pos),pe=Me(G.pos,c.pos);return K-pe||A.structureId.localeCompare(G.structureId)})[0];if(!N){c.alive=!1,d.events.push({t:d.tick,type:"raider_destroyed",probeId:c.id,pos:{...c.pos}});break}c.targetId=N.structureId}let y=d.treasuries.find(N=>N.structureId===c.targetId);if(c.pos.x===y.pos.x&&c.pos.y===y.pos.y){let N=cn(o,y.pos),A=Math.max(1,Math.floor($e[c.raiderType].loot*y.level*(1-N))),G=Math.min(A,y.storedResources);y.storedResources-=G;let K=Math.ceil(G*.4),pe=Math.ceil(G*.35),ke=Math.max(0,G-K-pe),at={gold:K,wood:pe,stone:ke};c.looted.gold+=K,c.looted.wood+=pe,c.looted.stone+=ke,d.totalLoot.gold+=K,d.totalLoot.wood+=pe,d.totalLoot.stone+=ke,d.events.push({t:d.tick,type:"treasury_breach",structureId:y.structureId,lootTaken:at});break}let h=jr(c.pos,y.pos,d);if(!h){if(c.raiderType==="scout"){let A=gn(d,c);if(A){xo(d,c,A);break}}let N=Ir(d,c.pos);if(N)xo(d,c,N);else{let A=d.solids.filter(G=>!G.destroyed).sort((G,K)=>Me(G.pos,c.pos)-Me(K.pos,c.pos))[0];A&&Pr(d,c,A)}break}let E={...c.pos};c.pos={...h},d.events.push({t:d.tick,type:"raider_move",probeId:c.id,from:E,to:{...c.pos}});let _=d.traps.find(N=>N.pos.x===c.pos.x&&N.pos.y===c.pos.y&&N.cooldownRemaining===0);if(_){let N=Cr(_.level);c.stunRemaining=N,_.cooldownRemaining=_r(_.level),d.events.push({t:d.tick,type:"raider_stunned",probeId:c.id,pos:{...c.pos},trapId:_.structureId,stunTicks:N});break}}}let U=d.raiders.every(c=>!c.alive||c.stunRemaining>100),B=l.some(c=>c.spawnAtTick>d.tick);if(U&&!B)break}let R=d.treasuries.reduce((u,v)=>u+v.storedResources,0),f=d.treasuries.reduce((u,v)=>u+No(v.level),0),m,C=d.totalLoot.gold+d.totalLoot.wood+d.totalLoot.stone;return C===0?m="defense_win":R>0&&C<f*.5?m="partial_breach":m="full_breach",d.events.push({t:d.tick,type:"raid_end",outcome:m}),{tickRateHz:cr,maxTicks:d.tick,events:d.events}}function Bo(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function hn(e){let t=Object.entries(Zt),o=t.reduce((r,[,a])=>r+a.weight,0),s=e()*o;for(let[r,a]of t)if(s-=a.weight,s<=0)return r;return"gold_nugget"}function Tn(e,t,o){return!!(t.structures.some(s=>s.pos.x===e.x&&s.pos.y===e.y)||o.some(s=>s.pos.x===e.x&&s.pos.y===e.y))}function qr(e,t,o,s){if(e.length>=$o)return e;let r=t.structures.filter(l=>l.kind==="archerTower").length,a=Math.min(r,2),T=1+(s()<.4?1:0),p=Math.min(T+a,$o-e.length),d=[];for(let l=0;l<p;l++){let x=null;for(let b=0;b<30;b++){let R=Math.floor(s()*P),f=Math.floor(s()*P),m={x:R,y:f};if(!Tn(m,t,[...e,...d])){x=m;break}}if(!x)break;d.push({id:`frag-${o}-${l}`,type:hn(s),pos:x,spawnedAtMs:o})}return[...e,...d]}function Ho(e,t,o){let s=e.findIndex(l=>l.pos.x===t.x&&l.pos.y===t.y);if(s===-1)return null;let r=[e[s]],a=o.structures.filter(l=>l.kind==="watchtower"),T=0;for(let l of a)Bo(l.pos,t)<=bt[l.level]&&(T=Math.max(T,l.level));if(T>0)for(let l of e)r.includes(l)||Bo(t,l.pos)<=T&&r.push(l);let p=new Set(r.map(l=>l.id)),d={gold:0,wood:0,stone:0};for(let l of r){let x=Zt[l.type].yield,b=1;o.structures.some(f=>f.kind==="treasury"&&Bo(f.pos,l.pos)<=vr)&&(b+=kr),d={gold:d.gold+Math.ceil(x.gold*b),wood:d.wood+Math.ceil(x.wood*b),stone:d.stone+Math.ceil(x.stone*b)}}return{yield:d,updatedFragments:e.filter(l=>!p.has(l.id)),collected:r}}function zr(e,t){return e.filter(o=>t-o.spawnedAtMs<br)}var Dr=[{level:1,structureCount:4,maxUpgradeLevel:1,probeCount:3},{level:2,structureCount:8,maxUpgradeLevel:1,probeCount:4},{level:3,structureCount:12,maxUpgradeLevel:2,probeCount:5},{level:4,structureCount:16,maxUpgradeLevel:2,probeCount:6},{level:5,structureCount:22,maxUpgradeLevel:3,probeCount:8}];function yo(e,t){let o=Dr[Math.max(0,Math.min(t-1,Dr.length-1))],s=Hr(Fr(e)),r=[],a=new Set,T=Math.max(1,Math.floor(o.structureCount/5));for(let R=0;R<T;R++){let f=Lr(s,a);if(!f)break;r.push(kt("treasury",f,vt(s,o.maxUpgradeLevel))),a.add(`${f.x},${f.y}`)}let p=Math.floor(o.structureCount*.35);for(let R=0;R<p;R++){let f=$r(s,a,r);if(!f)break;r.push(kt("wall",f,vt(s,o.maxUpgradeLevel))),a.add(`${f.x},${f.y}`)}let d=Math.floor(o.structureCount*.2);for(let R=0;R<d;R++){let f=xn(s,a);if(!f)break;r.push(kt("trap",f,vt(s,o.maxUpgradeLevel))),a.add(`${f.x},${f.y}`)}let l=Math.max(1,Math.floor(o.structureCount*.15));for(let R=0;R<l;R++){let f=r.filter(u=>u.kind==="treasury"),m=f.length>0?f[Math.floor(s()*f.length)]:void 0;if(!m)break;let C=[{x:m.pos.x-1,y:m.pos.y},{x:m.pos.x+1,y:m.pos.y},{x:m.pos.x,y:m.pos.y-1},{x:m.pos.x,y:m.pos.y+1}].filter(u=>u.x>=0&&u.x<P&&u.y>=0&&u.y<P&&!a.has(`${u.x},${u.y}`));if(C.length>0){let u=C[Math.floor(s()*C.length)];r.push(kt("ward",u,vt(s,o.maxUpgradeLevel))),a.add(`${u.x},${u.y}`)}}let x=Math.max(0,Math.floor(o.structureCount*.1));for(let R=0;R<x;R++){let f=$r(s,a,r);if(!f)break;r.push(kt("archerTower",f,vt(s,o.maxUpgradeLevel))),a.add(`${f.x},${f.y}`)}for(let R=0;R<Er;R++){let f=Lr(s,a);if(!f)break;r.push(kt("vault",f,vt(s,o.maxUpgradeLevel))),a.add(`${f.x},${f.y}`)}let b=o.structureCount-r.length;for(let R=0;R<b;R++){let f=yn(s,a);if(!f)break;r.push(kt("watchtower",f,vt(s,o.maxUpgradeLevel))),a.add(`${f.x},${f.y}`)}return{id:`npc-${e}-${t}`,name:`NPC Keep (Lv.${o.level})`,ownerPlayerId:"npc",grid:{width:16,height:16,structures:r},resources:{...St},createdAtUnixMs:Date.now(),updatedAtUnixMs:Date.now()}}function kt(e,t,o){return{id:`${e}-${t.x}-${t.y}`,kind:e,level:o,pos:{...t},placedAtUnixMs:Date.now()}}function vt(e,t){let o=Math.floor(e()*t)+1;return Math.min(t,o)}function Lr(e,t){for(let o=0;o<50;o++){let s=4+Math.floor(e()*(P-8)),r=4+Math.floor(e()*(P-8));if(!t.has(`${s},${r}`))return{x:s,y:r}}return null}function $r(e,t,o){for(let s=0;s<50;s++){let r=o[Math.floor(e()*o.length)],a=Math.floor(e()*3)-1,T=Math.floor(e()*3)-1,p=r.pos.x+a,d=r.pos.y+T;if(p>=0&&p<P&&d>=0&&d<P&&!t.has(`${p},${d}`))return{x:p,y:d}}return null}function xn(e,t){for(let o=0;o<50;o++){let s=Math.floor(e()*4),r,a;if(s===0?(r=Math.floor(e()*P),a=Math.floor(e()*3)):s===1?(r=Math.floor(e()*P),a=P-1-Math.floor(e()*3)):s===2?(r=Math.floor(e()*3),a=Math.floor(e()*P)):(r=P-1-Math.floor(e()*3),a=Math.floor(e()*P)),!t.has(`${r},${a}`))return{x:r,y:a}}return null}function yn(e,t){for(let o=0;o<50;o++){let s=Math.floor(e()*P),r=Math.floor(e()*P);if(!t.has(`${s},${r}`))return{x:s,y:r}}return null}var Uo=1;function En(){return Zr(vn(),".config","codekeep")}function Vo(){return Zr(En(),"game.json")}function Jr(e){let t=`player-${Date.now()}`;return{schemaVersion:Uo,savedAtUnixMs:Date.now(),player:{id:t,displayName:e,settings:{asciiMode:!1}},keep:{id:`keep-${t}-${Date.now()}`,name:`${e}'s Keep`,ownerPlayerId:t,grid:{width:16,height:16,structures:[]},resources:{...St},createdAtUnixMs:Date.now(),updatedAtUnixMs:Date.now()},raidHistory:[],tutorialCompleted:!1,lastPlayedAtUnixMs:Date.now(),progression:{totalBuildsToday:0,totalCommitsToday:0,lastDailyResetDay:Math.floor(Date.now()/864e5),totalRaidsWon:0,totalRaidsLost:0,totalStructuresPlaced:0,currentWinStreak:0,bestWinStreak:0,achievements:[],totalRaidersKilledByArcher:0}}}function Et(e){let t=Vo(),o=kn(t);Fo(o)||Rn(o,{recursive:!0});let s=t+".tmp",r=JSON.stringify({...e,savedAtUnixMs:Date.now()},null,2);Oo(s,r,"utf-8"),Sn(s,t)}function Gt(){let e=Vo();if(!Fo(e))return null;let t;try{t=wn(e,"utf-8")}catch{return null}let o;try{o=JSON.parse(t)}catch{let s=e+".bak";try{Oo(s,t,"utf-8")}catch{}return process.stderr.write(`[codekeep] Corrupted save backed up to ${s}
|
|
3
3
|
`),null}if(o.schemaVersion!==Uo){let s=e+`.v${o.schemaVersion}.bak`;try{Oo(s,t,"utf-8")}catch{}return process.stderr.write(`[codekeep] Save version mismatch (got ${o.schemaVersion}, need ${Uo}), backed up to ${s}
|
|
4
|
-
`),null}return o}function Xr(){let e=Vo();if(!Fo(e))return!1;try{return bn(e),!0}catch{return!1}}import{Box as Qr,Text as qe}from"ink";import{jsx as ze,jsxs as es}from"react/jsx-runtime";var An={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"},Cn={wall:"whiteBright",trap:"magentaBright",treasury:"yellowBright",ward:"cyanBright",watchtower:"greenBright",archerTower:"redBright"},_n=Object.fromEntries(Object.entries(Zt).map(([e,t])=>[e,t.color]));function Mn(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function Yo(e,t){return Math.max(Math.abs(e.x-t.x),Math.abs(e.y-t.y))}function In(e,t){let o=new Set;if(e.kind==="archerTower"){let s=co[e.level];for(let r=0;r<I;r++)for(let a=0;a<I;a++)Mn(e.pos,{x:a,y:r})<=s&&!(a===e.pos.x&&r===e.pos.y)&&o.add(`${a},${r}`);return{tiles:o,color:"red"}}if(e.kind==="watchtower"){let s=bt[e.level];for(let r=0;r<I;r++)for(let a=0;a<I;a++)Yo(e.pos,{x:a,y:r})<=s&&!(a===e.pos.x&&r===e.pos.y)&&o.add(`${a},${r}`);return{tiles:o,color:"green"}}if(e.kind==="ward"){let s=1,r=t.filter(a=>a.kind==="watchtower");for(let a of r)Yo(e.pos,a.pos)<=1&&(s=Math.max(s,1+bt[a.level]));s=Math.min(s,5);for(let a=0;a<I;a++)for(let T=0;T<I;T++)Yo(e.pos,{x:T,y:a})<=s&&!(T===e.pos.x&&a===e.pos.y)&&o.add(`${T},${a}`);return{tiles:o,color:"cyan"}}return null}function ts({grid:e,cursor:t,asciiMode:o,compact:s,fragments:r=[]}){let a=o?"-":"\u2500",T=o?"|":"\u2502",f=o?"+":"\u250C",d=o?"+":"\u2510",l=o?"+":"\u2514",x=o?"+":"\u2518",k=s?1:2,R=(s?" ":" ")+Array.from({length:I},(u,K)=>s?K.toString(16).toUpperCase():K.toString(16).toUpperCase()+" ").join(""),m=(s?" ":" ")+f+a.repeat(I*k)+d,p=(s?" ":" ")+l+a.repeat(I*k)+x,b=new Map;for(let u of e.structures)b.set(`${u.pos.x},${u.pos.y}`,u);let c=new Map;for(let u of r)c.set(`${u.pos.x},${u.pos.y}`,u);let $=b.get(`${t.x},${t.y}`),z=$?In($,e.structures):null,U=[];for(let u=0;u<I;u++){let K=s?u.toString(16).toUpperCase():" "+u.toString(16).toUpperCase(),w=[];for(let H=0;H<I;H++){let y=t.x===H&&t.y===u,h=b.get(`${H},${u}`),E=`${H},${u}`,C=z?.tiles.has(E)??!1,N,A,O=!1,W=c.get(E);h?(N=fe[h.kind],A=An[h.kind],h.level>=2&&(O=!0),h.level===3&&(A=Cn[h.kind])):W?(N="~",A=_n[W.type]??"cyan",O=!0):(N=Ot,A=void 0);let ue=s?"":h&&h.level>1?String(h.level):" ";y?w.push(ze(qe,{backgroundColor:"white",color:"black",bold:!0,children:N+ue},H)):C&&!h&&!W?w.push(ze(qe,{color:z.color,children:"\u2591"+(s?"":" ")},H)):C&&h?w.push(ze(qe,{color:A,bold:O,backgroundColor:z.color==="red"?"red":void 0,children:N+ue},H)):A?w.push(ze(qe,{color:A,bold:O,children:N+ue},H)):w.push(ze(qe,{dimColor:!0,children:N+(s?"":" ")},H))}U.push(es(Qr,{children:[ze(qe,{dimColor:!0,children:K}),ze(qe,{dimColor:!0,children:T}),w,ze(qe,{dimColor:!0,children:T})]},u))}return es(Qr,{flexDirection:"column",children:[ze(qe,{dimColor:!0,children:R}),ze(qe,{dimColor:!0,children:m}),U,ze(qe,{dimColor:!0,children:p})]})}import{Box as jo,Text as ce}from"ink";import{Fragment as Ln,jsx as ve,jsxs as Ve}from"react/jsx-runtime";var Pn={wall:"WL",trap:"TR",treasury:"TY",ward:"WD",watchtower:"WT",archerTower:"AT"};function Dn(e){let t=[];return e.gold>0&&t.push(`${B.gold}${e.gold}`),e.wood>0&&t.push(`${B.wood}${e.wood}`),e.stone>0&&t.push(`${B.stone}${e.stone}`),t.join(" ")}function os({resources:e,selectedStructure:t,message:o,compact:s,structureAtCursor:r,fragmentCount:a=0,dryRun:T}){if(s){let d=Pn[t];return ve(jo,{flexDirection:"column",marginBottom:0,children:Ve(ce,{children:[ve(ce,{bold:!0,color:"yellow",children:"\u25C6"}),ve(ce,{dimColor:!0,children:" \u2502 "}),Ve(ce,{color:"yellow",children:[B.gold,e.gold]}),ve(ce,{children:" "}),Ve(ce,{color:"green",children:[B.wood,e.wood]}),ve(ce,{children:" "}),Ve(ce,{color:"white",children:[B.stone,e.stone]}),ve(ce,{dimColor:!0,children:" \u2502 "}),ve(ce,{bold:!0,children:d}),o?ve(ce,{dimColor:!0,children:" \u2502 "}):null,o?ve(ce,{color:o.startsWith("!")?"red":"yellow",children:o}):null]})})}let f=r?`${tt[r.kind]} Lv.${r.level}`+(r.level<3?` \u2192 Lv.${r.level+1}: ${Dn(ot[r.kind][r.level+1])}`:" (MAX)"):"";return Ve(jo,{flexDirection:"column",marginBottom:1,children:[Ve(jo,{flexDirection:"row",gap:1,children:[ve(ce,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep"}),T&&ve(ce,{color:"magenta",bold:!0,children:" [DRY RUN]"}),ve(ce,{dimColor:!0,children:"\u2502"}),Ve(ce,{color:"yellow",children:[B.gold," ",e.gold]}),Ve(ce,{color:"green",children:[B.wood," ",e.wood]}),Ve(ce,{color:"white",children:[B.stone," ",e.stone]}),ve(ce,{dimColor:!0,children:"\u2502"}),Ve(ce,{children:["Sel: ",ve(ce,{bold:!0,children:tt[t]})]}),a>0&&Ve(Ln,{children:[ve(ce,{dimColor:!0,children:"\u2502"}),Ve(ce,{color:"cyan",bold:!0,children:["~ ",a," on ground"]})]})]}),ve(ce,{color:o.startsWith("!")?"red":"yellow",children:o||" "}),ve(ce,{dimColor:!0,children:f||" "})]})}import{Box as $n,Text as _}from"ink";import{jsx as F,jsxs as pe}from"react/jsx-runtime";function Wt(e){let t=[];return e.gold>0&&t.push(`${B.gold}${e.gold}`),e.wood>0&&t.push(`${B.wood}${e.wood}`),e.stone>0&&t.push(`${B.stone}${e.stone}`),t.join(" ")}function rs(){let e=Le,t=ot;return pe($n,{flexDirection:"column",padding:1,children:[F(_,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep \u2014 Help"}),F(_,{children:" "}),F(_,{bold:!0,children:"Navigation"}),F(_,{children:" h/j/k/l or WASD or Arrows Move cursor"}),F(_,{children:" g + hex coords + Enter Jump to coordinate (e.g. g 5,3)"}),F(_,{children:" Tab Jump to next structure"}),F(_,{children:" Esc Back to menu"}),F(_,{children:" "}),F(_,{bold:!0,children:"Building"}),pe(_,{children:[" ","[ / ] Cycle structure type"]}),F(_,{children:" 1-6 Select structure by number"}),F(_,{children:" Enter or e Place structure at cursor"}),F(_,{children:" u Upgrade structure (Lv.1\u21922\u21923)"}),F(_,{children:" x Demolish structure (50% refund)"}),F(_,{children:" "}),F(_,{bold:!0,children:"Foraging"}),F(_,{children:" c Collect forage at cursor (~ on the grid)"}),pe(_,{children:[" "," Archer towers improve spawn rate; treasuries improve yield"]}),pe(_,{children:[" "," Watchtowers auto-gather nearby forage"]}),F(_,{children:" "}),F(_,{bold:!0,children:"Combat"}),F(_,{children:" r Quick defend (instant result)"}),F(_,{children:" v View last quick-defend replay"}),F(_,{children:" Defend Keep (menu) Watch raiders assault your grid"}),F(_,{children:" Attack NPC (menu) Raid an NPC keep for loot"}),F(_,{children:" "}),F(_,{bold:!0,children:"Other"}),F(_,{children:" f Kingdom boon (+resources)"}),F(_,{children:" ? Toggle this help (works on any screen)"}),F(_,{children:" q Save and quit"}),F(_,{children:" "}),F(_,{bold:!0,children:"Structures (cost Lv.1 \u2192 Lv.2 \u2192 Lv.3)"}),pe(_,{children:[" ",pe(_,{color:"white",children:["1 ",fe.wall]})," Stone Wall Blocks raiders, has HP ",Wt(t.wall[1])]}),pe(_,{children:[" ",pe(_,{color:"magenta",children:["2 ",fe.trap]})," Bear Trap Stuns raiders on contact ",Wt(t.trap[1])]}),pe(_,{children:[" ",pe(_,{color:"yellow",children:["3 ",fe.treasury]})," Treasury Stores loot, generates income ",Wt(t.treasury[1])]}),pe(_,{children:[" ",pe(_,{color:"cyan",children:["4 ",fe.ward]})," Ward Reduces loot stolen nearby ",Wt(t.ward[1])]}),pe(_,{children:[" ",pe(_,{color:"green",children:["5 ",fe.watchtower]})," Watchtower Extends ward range, auto-gathers ",Wt(t.watchtower[1])]}),pe(_,{children:[" ",pe(_,{color:"redBright",children:["6 ",fe.archerTower]})," Archer Tower Fires arrows at raiders in range ",Wt(t.archerTower[1])]}),F(_,{children:" "}),F(_,{bold:!0,children:"Resources"}),pe(_,{children:[" ",F(_,{color:"yellow",children:B.gold})," Gold Earned from events, foraging, and raids"]}),pe(_,{children:[" ",F(_,{color:"green",children:B.wood})," Wood Earned from treasuries and events"]}),pe(_,{children:[" ",F(_,{color:"white",children:B.stone})," Stone Earned from watchtowers and events"]}),F(_,{children:" Treasuries and watchtowers generate passive income over time."}),F(_,{children:" "}),F(_,{bold:!0,children:"Raider Types"}),pe(_,{children:[" Raider HP ",e.raider.hp,", dmg ",e.raider.damage,", speed ",e.raider.speed," \u2014 standard foot soldier"]}),pe(_,{children:[" Scout HP ",e.scout.hp,", dmg ",e.scout.damage,", speed ",e.scout.speed," \u2014 fast, moves twice per tick"]}),pe(_,{children:[" Brute HP ",e.brute.hp,", dmg ",e.brute.damage,", speed ",e.brute.speed," \u2014 heavy hitter, hard to kill"]}),F(_,{children:" "}),F(_,{bold:!0,children:"Raid Difficulty"}),F(_,{children:" Difficulty scales with total raids completed (Lv.1\u20135)."}),F(_,{children:" Higher levels bring more raiders, scouts, and brutes."}),pe(_,{children:[" Outcomes: ",F(_,{color:"green",children:"Defense Win"})," \xB7 ",F(_,{color:"yellow",children:"Partial Breach"})," \xB7 ",F(_,{color:"red",children:"Full Breach"})]}),F(_,{children:" "}),F(_,{dimColor:!0,children:"Press any key to close"})]})}import{useState as Nn}from"react";import{Box as ss,Text as Ze,useInput as Bn}from"ink";import{jsx as Jt,jsxs as xt}from"react/jsx-runtime";function On(e){let t=[{key:"keep",label:"Build Keep",desc:"Place and upgrade structures",disabled:!1},{key:"defend",label:"Defend Keep",desc:"Watch NPCs attack YOUR defenses",disabled:!1},{key:"attack",label:"Attack NPC",desc:"Raid an NPC keep for resources",disabled:!1}];return e?t.push({key:"pvp",label:"\u2694 PvP Arena",desc:"Fight real players for trophies",disabled:!1}):t.push({key:"pvp-locked",label:"\u2694 PvP Arena",desc:"Coming soon \u2014 use --online to connect",disabled:!0}),t.push({key:"friendRaid",label:"Raid Rival Keep",desc:"Plunder a rival lord's fortress",disabled:!1},{key:"raidLog",label:"Raid Log",desc:"View recent raid history",disabled:!1},{key:"settings",label:"Settings",desc:"Game options and reset",disabled:!1},{key:"quit",label:"Rest for the Night",desc:"Save and exit",disabled:!1}),t}function ns({gameSave:e,onlineMode:t,onKeep:o,onAttack:s,onDefend:r,onFriendRaid:a,onPvp:T,onRaidLog:f,onSettings:d,onQuit:l}){let x=On(!!t),[k,R]=Nn(0);Bn((U,u)=>{if(U==="k"||U==="w"||u.upArrow)R(K=>{let w=K-1;for(;w>=0&&x[w].disabled;)w--;return w>=0?w:K});else if(U==="j"||U==="s"||u.downArrow)R(K=>{let w=K+1;for(;w<x.length&&x[w].disabled;)w++;return w<x.length?w:K});else if(u.return){let K=x[k];if(K.disabled)return;K.key==="keep"?o():K.key==="defend"?r():K.key==="attack"?s():K.key==="pvp"?T?.():K.key==="friendRaid"?a():K.key==="raidLog"?f():K.key==="settings"?d():K.key==="quit"&&l()}else U==="q"&&l()});let m=e.keep.grid.structures.length,p=e.progression,b=p.totalRaidsWon+p.totalRaidsLost,c=Math.max(1,Math.floor((Date.now()-e.keep.createdAtUnixMs)/864e5)),$=e.keep.grid.structures.filter(U=>U.kind==="treasury").length,z=e.keep.grid.structures.filter(U=>U.kind==="archerTower").length;return xt(ss,{flexDirection:"column",padding:1,children:[Jt(Ze,{bold:!0,color:"yellow",children:`
|
|
4
|
+
`),null}return o}function Xr(){let e=Vo();if(!Fo(e))return!1;try{return bn(e),!0}catch{return!1}}import{Box as Qr,Text as qe}from"ink";import{jsx as ze,jsxs as es}from"react/jsx-runtime";var An={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"},Cn={wall:"whiteBright",trap:"magentaBright",treasury:"yellowBright",ward:"cyanBright",watchtower:"greenBright",archerTower:"redBright"},_n=Object.fromEntries(Object.entries(Zt).map(([e,t])=>[e,t.color]));function Mn(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function Yo(e,t){return Math.max(Math.abs(e.x-t.x),Math.abs(e.y-t.y))}function In(e,t){let o=new Set;if(e.kind==="archerTower"){let s=po[e.level];for(let r=0;r<P;r++)for(let a=0;a<P;a++)Mn(e.pos,{x:a,y:r})<=s&&!(a===e.pos.x&&r===e.pos.y)&&o.add(`${a},${r}`);return{tiles:o,color:"red"}}if(e.kind==="watchtower"){let s=bt[e.level];for(let r=0;r<P;r++)for(let a=0;a<P;a++)Yo(e.pos,{x:a,y:r})<=s&&!(a===e.pos.x&&r===e.pos.y)&&o.add(`${a},${r}`);return{tiles:o,color:"green"}}if(e.kind==="ward"){let s=1,r=t.filter(a=>a.kind==="watchtower");for(let a of r)Yo(e.pos,a.pos)<=1&&(s=Math.max(s,1+bt[a.level]));s=Math.min(s,5);for(let a=0;a<P;a++)for(let T=0;T<P;T++)Yo(e.pos,{x:T,y:a})<=s&&!(T===e.pos.x&&a===e.pos.y)&&o.add(`${T},${a}`);return{tiles:o,color:"cyan"}}return null}function ts({grid:e,cursor:t,asciiMode:o,compact:s,fragments:r=[]}){let a=o?"-":"\u2500",T=o?"|":"\u2502",p=o?"+":"\u250C",d=o?"+":"\u2510",l=o?"+":"\u2514",x=o?"+":"\u2518",b=s?1:2,R=(s?" ":" ")+Array.from({length:P},(c,H)=>s?H.toString(16).toUpperCase():H.toString(16).toUpperCase()+" ").join(""),f=(s?" ":" ")+p+a.repeat(P*b)+d,m=(s?" ":" ")+l+a.repeat(P*b)+x,C=new Map;for(let c of e.structures)C.set(`${c.pos.x},${c.pos.y}`,c);let u=new Map;for(let c of r)u.set(`${c.pos.x},${c.pos.y}`,c);let v=C.get(`${t.x},${t.y}`),U=v?In(v,e.structures):null,B=[];for(let c=0;c<P;c++){let H=s?c.toString(16).toUpperCase():" "+c.toString(16).toUpperCase(),w=[];for(let F=0;F<P;F++){let y=t.x===F&&t.y===c,h=C.get(`${F},${c}`),E=`${F},${c}`,_=U?.tiles.has(E)??!1,N,A,G=!1,K=u.get(E);h?(N=ge[h.kind],A=An[h.kind],h.level>=2&&(G=!0),h.level===3&&(A=Cn[h.kind])):K?(N="~",A=_n[K.type]??"cyan",G=!0):(N=Ot,A=void 0);let pe=s?"":h&&h.level>1?String(h.level):" ";y?w.push(ze(qe,{backgroundColor:"white",color:"black",bold:!0,children:N+pe},F)):_&&!h&&!K?w.push(ze(qe,{color:U.color,children:"\u2591"+(s?"":" ")},F)):_&&h?w.push(ze(qe,{color:A,bold:G,backgroundColor:U.color==="red"?"red":void 0,children:N+pe},F)):A?w.push(ze(qe,{color:A,bold:G,children:N+pe},F)):w.push(ze(qe,{dimColor:!0,children:N+(s?"":" ")},F))}B.push(es(Qr,{children:[ze(qe,{dimColor:!0,children:H}),ze(qe,{dimColor:!0,children:T}),w,ze(qe,{dimColor:!0,children:T})]},c))}return es(Qr,{flexDirection:"column",children:[ze(qe,{dimColor:!0,children:R}),ze(qe,{dimColor:!0,children:f}),B,ze(qe,{dimColor:!0,children:m})]})}import{Box as jo,Text as ue}from"ink";import{Fragment as Ln,jsx as Ee,jsxs as Ve}from"react/jsx-runtime";var Pn={wall:"WL",trap:"TR",treasury:"TY",ward:"WD",watchtower:"WT",archerTower:"AT"};function Dn(e){let t=[];return e.gold>0&&t.push(`${O.gold}${e.gold}`),e.wood>0&&t.push(`${O.wood}${e.wood}`),e.stone>0&&t.push(`${O.stone}${e.stone}`),t.join(" ")}function os({resources:e,selectedStructure:t,message:o,compact:s,structureAtCursor:r,fragmentCount:a=0,dryRun:T}){if(s){let d=Pn[t];return Ee(jo,{flexDirection:"column",marginBottom:0,children:Ve(ue,{children:[Ee(ue,{bold:!0,color:"yellow",children:"\u25C6"}),Ee(ue,{dimColor:!0,children:" \u2502 "}),Ve(ue,{color:"yellow",children:[O.gold,e.gold]}),Ee(ue,{children:" "}),Ve(ue,{color:"green",children:[O.wood,e.wood]}),Ee(ue,{children:" "}),Ve(ue,{color:"white",children:[O.stone,e.stone]}),Ee(ue,{dimColor:!0,children:" \u2502 "}),Ee(ue,{bold:!0,children:d}),o?Ee(ue,{dimColor:!0,children:" \u2502 "}):null,o?Ee(ue,{color:o.startsWith("!")?"red":"yellow",children:o}):null]})})}let p=r?`${tt[r.kind]} Lv.${r.level}`+(r.level<3?` \u2192 Lv.${r.level+1}: ${Dn(ot[r.kind][r.level+1])}`:" (MAX)"):"";return Ve(jo,{flexDirection:"column",marginBottom:1,children:[Ve(jo,{flexDirection:"row",gap:1,children:[Ee(ue,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep"}),T&&Ee(ue,{color:"magenta",bold:!0,children:" [DRY RUN]"}),Ee(ue,{dimColor:!0,children:"\u2502"}),Ve(ue,{color:"yellow",children:[O.gold," ",e.gold]}),Ve(ue,{color:"green",children:[O.wood," ",e.wood]}),Ve(ue,{color:"white",children:[O.stone," ",e.stone]}),Ee(ue,{dimColor:!0,children:"\u2502"}),Ve(ue,{children:["Sel: ",Ee(ue,{bold:!0,children:tt[t]})]}),a>0&&Ve(Ln,{children:[Ee(ue,{dimColor:!0,children:"\u2502"}),Ve(ue,{color:"cyan",bold:!0,children:["~ ",a," on ground"]})]})]}),Ee(ue,{color:o.startsWith("!")?"red":"yellow",children:o||" "}),Ee(ue,{dimColor:!0,children:p||" "})]})}import{Box as $n,Text as M}from"ink";import{jsx as V,jsxs as fe}from"react/jsx-runtime";function Wt(e){let t=[];return e.gold>0&&t.push(`${O.gold}${e.gold}`),e.wood>0&&t.push(`${O.wood}${e.wood}`),e.stone>0&&t.push(`${O.stone}${e.stone}`),t.join(" ")}function rs(){let e=$e,t=ot;return fe($n,{flexDirection:"column",padding:1,children:[V(M,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep \u2014 Help"}),V(M,{children:" "}),V(M,{bold:!0,children:"Navigation"}),V(M,{children:" h/j/k/l or WASD or Arrows Move cursor"}),V(M,{children:" g + hex coords + Enter Jump to coordinate (e.g. g 5,3)"}),V(M,{children:" Tab Jump to next structure"}),V(M,{children:" Esc Back to menu"}),V(M,{children:" "}),V(M,{bold:!0,children:"Building"}),fe(M,{children:[" ","[ / ] Cycle structure type"]}),V(M,{children:" 1-6 Select structure by number"}),V(M,{children:" Enter or e Place structure at cursor"}),V(M,{children:" u Upgrade structure (Lv.1\u21922\u21923)"}),V(M,{children:" x Demolish structure (50% refund)"}),V(M,{children:" "}),V(M,{bold:!0,children:"Foraging"}),V(M,{children:" c Collect forage at cursor (~ on the grid)"}),fe(M,{children:[" "," Archer towers improve spawn rate; treasuries improve yield"]}),fe(M,{children:[" "," Watchtowers auto-gather nearby forage"]}),V(M,{children:" "}),V(M,{bold:!0,children:"Combat"}),V(M,{children:" r Quick defend (instant result)"}),V(M,{children:" v View last quick-defend replay"}),V(M,{children:" Defend Keep (menu) Watch raiders assault your grid"}),V(M,{children:" Attack NPC (menu) Raid an NPC keep for loot"}),V(M,{children:" "}),V(M,{bold:!0,children:"Other"}),V(M,{children:" f Kingdom boon (+resources)"}),V(M,{children:" ? Toggle this help (works on any screen)"}),V(M,{children:" q Save and quit"}),V(M,{children:" "}),V(M,{bold:!0,children:"Structures (cost Lv.1 \u2192 Lv.2 \u2192 Lv.3)"}),fe(M,{children:[" ",fe(M,{color:"white",children:["1 ",ge.wall]})," Stone Wall Blocks raiders, has HP ",Wt(t.wall[1])]}),fe(M,{children:[" ",fe(M,{color:"magenta",children:["2 ",ge.trap]})," Bear Trap Stuns raiders on contact ",Wt(t.trap[1])]}),fe(M,{children:[" ",fe(M,{color:"yellow",children:["3 ",ge.treasury]})," Treasury Stores loot, generates income ",Wt(t.treasury[1])]}),fe(M,{children:[" ",fe(M,{color:"cyan",children:["4 ",ge.ward]})," Ward Reduces loot stolen nearby ",Wt(t.ward[1])]}),fe(M,{children:[" ",fe(M,{color:"green",children:["5 ",ge.watchtower]})," Watchtower Extends ward range, auto-gathers ",Wt(t.watchtower[1])]}),fe(M,{children:[" ",fe(M,{color:"redBright",children:["6 ",ge.archerTower]})," Archer Tower Fires arrows at raiders in range ",Wt(t.archerTower[1])]}),V(M,{children:" "}),V(M,{bold:!0,children:"Resources"}),fe(M,{children:[" ",V(M,{color:"yellow",children:O.gold})," Gold Earned from events, foraging, and raids"]}),fe(M,{children:[" ",V(M,{color:"green",children:O.wood})," Wood Earned from treasuries and events"]}),fe(M,{children:[" ",V(M,{color:"white",children:O.stone})," Stone Earned from watchtowers and events"]}),V(M,{children:" Treasuries and watchtowers generate passive income over time."}),V(M,{children:" "}),V(M,{bold:!0,children:"Raider Types"}),fe(M,{children:[" Raider HP ",e.raider.hp,", dmg ",e.raider.damage,", speed ",e.raider.speed," \u2014 standard foot soldier"]}),fe(M,{children:[" Scout HP ",e.scout.hp,", dmg ",e.scout.damage,", speed ",e.scout.speed," \u2014 fast, moves twice per tick"]}),fe(M,{children:[" Brute HP ",e.brute.hp,", dmg ",e.brute.damage,", speed ",e.brute.speed," \u2014 heavy hitter, hard to kill"]}),V(M,{children:" "}),V(M,{bold:!0,children:"Raid Difficulty"}),V(M,{children:" Difficulty scales with total raids completed (Lv.1\u20135)."}),V(M,{children:" Higher levels bring more raiders, scouts, and brutes."}),fe(M,{children:[" Outcomes: ",V(M,{color:"green",children:"Defense Win"})," \xB7 ",V(M,{color:"yellow",children:"Partial Breach"})," \xB7 ",V(M,{color:"red",children:"Full Breach"})]}),V(M,{children:" "}),V(M,{dimColor:!0,children:"Press any key to close"})]})}import{useState as Nn}from"react";import{Box as ss,Text as Ze,useInput as Bn}from"ink";import{jsx as Jt,jsxs as xt}from"react/jsx-runtime";function On(e){let t=[{key:"keep",label:"Build Keep",desc:"Place and upgrade structures",disabled:!1},{key:"defend",label:"Defend Keep",desc:"Watch NPCs attack YOUR defenses",disabled:!1},{key:"attack",label:"Attack NPC",desc:"Raid an NPC keep for resources",disabled:!1}];return e?t.push({key:"pvp",label:"\u2694 PvP Arena",desc:"Fight real players for trophies",disabled:!1}):t.push({key:"pvp-locked",label:"\u2694 PvP Arena",desc:"Coming soon \u2014 use --online to connect",disabled:!0}),t.push({key:"friendRaid",label:"Raid Rival Keep",desc:"Plunder a rival lord's fortress",disabled:!1},{key:"raidLog",label:"Raid Log",desc:"View recent raid history",disabled:!1},{key:"settings",label:"Settings",desc:"Game options and reset",disabled:!1},{key:"quit",label:"Rest for the Night",desc:"Save and exit",disabled:!1}),t}function ns({gameSave:e,onlineMode:t,onKeep:o,onAttack:s,onDefend:r,onFriendRaid:a,onPvp:T,onRaidLog:p,onSettings:d,onQuit:l}){let x=On(!!t),[b,R]=Nn(0);Bn((B,c)=>{if(B==="k"||B==="w"||c.upArrow)R(H=>{let w=H-1;for(;w>=0&&x[w].disabled;)w--;return w>=0?w:H});else if(B==="j"||B==="s"||c.downArrow)R(H=>{let w=H+1;for(;w<x.length&&x[w].disabled;)w++;return w<x.length?w:H});else if(c.return){let H=x[b];if(H.disabled)return;H.key==="keep"?o():H.key==="defend"?r():H.key==="attack"?s():H.key==="pvp"?T?.():H.key==="friendRaid"?a():H.key==="raidLog"?p():H.key==="settings"?d():H.key==="quit"&&l()}else B==="q"&&l()});let f=e.keep.grid.structures.length,m=e.progression,C=m.totalRaidsWon+m.totalRaidsLost,u=Math.max(1,Math.floor((Date.now()-e.keep.createdAtUnixMs)/864e5)),v=e.keep.grid.structures.filter(B=>B.kind==="treasury").length,U=e.keep.grid.structures.filter(B=>B.kind==="archerTower").length;return xt(ss,{flexDirection:"column",padding:1,children:[Jt(Ze,{bold:!0,color:"yellow",children:`
|
|
5
5
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
6
6
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
7
7
|
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
8
8
|
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D
|
|
9
9
|
\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551
|
|
10
|
-
\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D`}),Jt(Ze,{children:" "}),xt(Ze,{dimColor:!0,children:[" ",e.player.displayName,"'s Keep \u2014 ",c,"d old"]}),xt(Ze,{dimColor:!0,children:[" ",m," structures (",$,"$ ",z,"!) \xB7 Raids ",p.totalRaidsWon,"W / ",p.totalRaidsLost,"L \xB7 Streak ",p.currentWinStreak," (best ",p.bestWinStreak,")"]}),xt(Ze,{dimColor:!0,children:[" Difficulty: Lv.",b<=2?1:b<=5?2:b<=9?3:b<=14?4:5]}),xt(Ze,{dimColor:!0,children:[" Achievements: ",e.progression.achievements?.length||0,"/",10]}),Jt(Ze,{children:" "}),x.map((U,u)=>xt(ss,{children:[xt(Ze,{color:U.disabled?void 0:u===k?"yellow":void 0,bold:!U.disabled&&u===k,dimColor:U.disabled,children:[u===k&&!U.disabled?" \u25B8 ":" ",U.label]}),xt(Ze,{dimColor:!0,children:[" ",U.desc]})]},U.key)),Jt(Ze,{children:" "}),Jt(Ze,{dimColor:!0,children:" \u2191\u2193 navigate Enter select q quit"})]})}import{useState as pt,useEffect as as,useCallback as Un,useRef as Gn}from"react";import{Box as Je,Text as J,useInput as Wn}from"ink";import{jsx as se,jsxs as ye}from"react/jsx-runtime";var qo={raider:"R",scout:"S",brute:"B"};function zo(e,t){let o=e/t;return o>.6?"green":o>.3?"yellow":"red"}function Kn(e,t){let o=e/t;return o>.6?"white":o>.3?"yellow":"red"}function Zo(e,t){let o=[];return e.gold>0&&o.push(`${t}${e.gold}${B.gold}`),e.wood>0&&o.push(`${t}${e.wood}${B.wood}`),e.stone>0&&o.push(`${t}${e.stone}${B.stone}`),o.join(" ")}function Hn(e,t,o=6){let s=Math.round(e/t*o);return"\u2588".repeat(s)+"\u2591".repeat(o-s)}var Fn={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"};function Jo({replay:e,keepGrid:t,raidType:o,summary:s,initialSpeed:r,onSpeedChange:a,onDone:T}){let[f,d]=pt(0),[l,x]=pt(r??1),k=Un(v=>{x(v),a?.(v)},[a]),[R,m]=pt(!1),[p,b]=pt(new Map),[c,$]=pt(()=>{let v=new Map,q={wall:ao,archerTower:io,watchtower:lo,vault:mo};for(let Y of t.structures){let Ie=q[Y.kind];if(Ie){let L=Ie[Y.level];v.set(Y.id,{structureId:Y.id,kind:Y.kind,pos:{...Y.pos},hp:L,maxHp:L,destroyed:!1})}}return v}),[z,U]=pt([]),[u,K]=pt(new Set),[w,H]=pt([]),[y,h]=pt(null),E=Gn(-1);Wn((v,q)=>{if(v==="q"||q.escape){T();return}if(v==="p"||v===" "){m(Y=>!Y);return}v==="1"&&k(1),v==="2"&&k(2),v==="4"&&k(4),v==="8"&&k(8),(v==="n"||q.return)&&d(e.maxTicks)}),as(()=>{if(R||y)return;let v=setInterval(()=>{d(q=>{let Y=q+1;return Y>e.maxTicks?(clearInterval(v),q):Y})},1e3/e.tickRateHz/l);return()=>clearInterval(v)},[R,l,y,e]),as(()=>{if(f<=E.current)return;let v=e.events.filter(L=>L.t>E.current&&L.t<=f),q=[],Y=[],Ie=new Map;b(L=>{let Z=new Map(L);for(let G of v)switch(G.type){case"raider_spawn":{let X=G.raiderType??"raider",Ce=G.maxHp??Le[X].hp;Z.set(G.probeId,{id:G.probeId,pos:{...G.pos},alive:!0,stunned:!1,hp:Ce,maxHp:Ce,raiderType:X});break}case"raider_move":{let X=Z.get(G.probeId);X&&(X.pos={...G.to},X.stunned=!1);break}case"raider_stunned":{let X=Z.get(G.probeId);X&&(X.stunned=!0);break}case"arrow_hit":{let X=Z.get(G.probeId);X&&(X.hp=Math.max(0,G.hpRemaining));break}case"raider_destroyed":{let X=Z.get(G.probeId);X&&(X.alive=!1);break}default:break}return Ie=Z,Z}),$(L=>{let Z=new Map(L);for(let G of v)if(G.type==="wall_damaged"||G.type==="structure_damaged"){let X=Z.get(G.structureId);X&&(X.hp=Math.max(0,G.hpRemaining),X.destroyed=G.destroyed)}return Z});for(let L of v)switch(L.type){case"raider_spawn":{let Z=L.raiderType??"raider",G=qo[Z]??"R",X=L.edge==="N"?"\u2193":L.edge==="S"?"\u2191":L.edge==="W"?"\u2192":"\u2190";q.push(`${G} Raider ${L.probeId+1} enters from ${L.edge} ${X}`),Y.push({pos:{...L.pos},char:X,color:"yellowBright",bold:!0,expiresAtTick:f+3,priority:0});break}case"raider_stunned":{q.push(`\u26A1 Raider ${L.probeId+1} STUNNED ${L.stunTicks}t`),Y.push({pos:{...L.pos},char:"\u2727",color:"cyanBright",bold:!0,expiresAtTick:f+3,priority:2});break}case"wall_damaged":{if(L.destroyed){q.push("\u{1F4A5} Wall DESTROYED!");let Z=t.structures.find(G=>G.id===L.structureId);Z&&Y.push({pos:{...Z.pos},char:"\u2717",color:"redBright",bold:!0,expiresAtTick:f+4,priority:2})}else q.push(`\u2694 Wall hit (${L.hpRemaining} HP)`);break}case"structure_damaged":{let Z=tt[L.structureKind]??L.structureKind;if(L.destroyed){q.push(`\u{1F4A5} ${Z} DESTROYED!`);let G=t.structures.find(X=>X.id===L.structureId);G&&Y.push({pos:{...G.pos},char:"\u2717",color:"redBright",bold:!0,expiresAtTick:f+5,priority:3})}else q.push(`\u2694 ${Z} hit (${L.hpRemaining} HP)`);break}case"arrow_hit":{let Z=Ie.get(L.probeId),G=Z?{...Z.pos}:null;G&&Y.push({pos:G,char:"\u2020",color:"redBright",bold:!0,expiresAtTick:f+2,priority:1}),L.hpRemaining<=0?(q.push(`\u{1F3F9} Archer slew raider ${L.probeId+1}!`),G&&Y.push({pos:G,char:"\u2716",color:"redBright",bold:!0,expiresAtTick:f+4,priority:3})):q.push(`\u{1F3F9} Arrow hit raider ${L.probeId+1} (-${L.damage} \u2192 ${L.hpRemaining} HP)`);break}case"treasury_breach":{let Z=Zo(L.lootTaken,""),G=o==="attack"?"Looted":"Lost";q.push(`\u{1F4B0} TREASURY BREACHED! ${G} ${Z}`),K(Ce=>new Set([...Ce,L.structureId]));let X=t.structures.find(Ce=>Ce.id===L.structureId);X&&Y.push({pos:{...X.pos},char:"\u26A1",color:"yellowBright",bold:!0,expiresAtTick:f+5,priority:3});break}case"raider_destroyed":{v.some(G=>G.type==="arrow_hit"&&G.probeId===L.probeId&&G.hpRemaining<=0&&G.t===L.t)||q.push(`\u{1F480} Raider ${L.probeId+1} eliminated`);break}case"raid_end":{let Z=L.outcome==="defense_win"?o==="defend"?"DEFENSE VICTORY \u2014 All raiders defeated!":"ATTACK FAILED \u2014 The keep held strong":L.outcome==="partial_breach"?o==="defend"?"PARTIAL BREACH \u2014 Raiders stole some supplies":"PARTIAL SUCCESS \u2014 Some loot seized":o==="defend"?"FULL BREACH \u2014 Major losses!":"FULL SUCCESS \u2014 Major plunder!";h(Z),q.push(Z);break}default:break}U(L=>[...L.filter(Z=>Z.expiresAtTick>f),...Y]),E.current=f,q.length>0&&H(L=>[...L.slice(-10),...q])},[f,o,e,t]);let C=new Map;for(let v of t.structures)C.set(`${v.pos.x},${v.pos.y}`,v);if(!t.structures.some(v=>v.kind==="treasury")){let v={id:"__virtual_center_treasury",kind:"treasury",pos:{x:8,y:8},level:1};C.set("8,8",v)}let A=new Map;for(let[,v]of p)v.alive&&A.set(`${v.pos.x},${v.pos.y}`,v);let O=new Map;for(let[,v]of c)O.set(`${v.pos.x},${v.pos.y}`,v);let W=new Map;for(let v of z){let q=`${v.pos.x},${v.pos.y}`,Y=W.get(q);(!Y||v.priority>Y.priority||v.priority===Y.priority&&v.expiresAtTick>Y.expiresAtTick)&&W.set(q,v)}let ue=" "+Array.from({length:I},(v,q)=>q.toString(16).toUpperCase()+" ").join(""),Se=[se(J,{dimColor:!0,children:ue},"hdr")];for(let v=0;v<I;v++){let q=[se(J,{dimColor:!0,children:v.toString(16).toUpperCase()+" "},"lbl")];for(let Y=0;Y<I;Y++){let Ie=`${Y},${v}`,L=A.get(Ie),Z=C.get(Ie),G=O.get(Ie),X=W.get(Ie);if(X&&!L)q.push(se(J,{color:X.color,bold:X.bold,children:X.char+" "},Y));else if(L){let Ce=L.stunned?"\u25CA":qo[L.raiderType]??"\u25CF",Tt=L.stunned?"cyan":zo(L.hp,L.maxHp);q.push(se(J,{color:Tt,bold:!0,children:Ce+" "},Y))}else if(Z)if(G&&!G.destroyed){let Ce=Kn(G.hp,G.maxHp),Tt=fe[G.kind]??"#";q.push(se(J,{color:Ce,bold:G.hp>G.maxHp*.6,children:Tt+" "},Y))}else if(G&&G.destroyed)q.push(se(J,{color:"red",dimColor:!0,children:"x "},Y));else if(u.has(Z.id))q.push(se(J,{color:"red",bold:!0,children:"$ "},Y));else{let Ce=fe[Z.kind];q.push(se(J,{color:Fn[Z.kind]||"white",children:Ce+" "},Y))}else q.push(se(J,{dimColor:!0,children:Ot+" "},Y))}Se.push(se(Je,{children:q},v))}let nt=Array.from(p.values()).filter(v=>v.alive),$t=Array.from(p.values()).filter(v=>!v.alive);return ye(Je,{flexDirection:"column",padding:1,children:[ye(Je,{flexDirection:"row",gap:2,children:[se(J,{bold:!0,color:o==="defend"?"red":"green",children:o==="defend"?"\u2694 DEFENDING YOUR KEEP":"\u2694 ATTACKING NPC KEEP"}),ye(J,{children:["Tick: ",f,"/",e.maxTicks]}),ye(J,{children:["Speed: ",l,"x"]}),se(J,{dimColor:!0,children:R?"[PAUSED]":""})]}),se(J,{children:" "}),ye(Je,{flexDirection:"row",children:[se(Je,{flexDirection:"column",children:Se}),ye(Je,{flexDirection:"column",marginLeft:2,width:38,children:[nt.length>0&&ye(Je,{flexDirection:"column",marginBottom:1,children:[se(J,{bold:!0,children:"Raiders"}),nt.map(v=>ye(Je,{children:[ye(J,{color:zo(v.hp,v.maxHp),bold:!0,children:[qo[v.raiderType],v.id+1]}),se(J,{children:" "}),se(J,{color:zo(v.hp,v.maxHp),children:Hn(v.hp,v.maxHp)}),ye(J,{dimColor:!0,children:[" ",v.hp,"/",v.maxHp]}),v.stunned&&se(J,{color:"cyan",children:" STUN"})]},v.id)),$t.length>0&&ye(J,{dimColor:!0,children:[" ",$t.length," slain"]})]}),se(J,{bold:!0,children:"Battle Log"}),(()=>{let v=w.slice(-10);return v.map((q,Y)=>se(J,{dimColor:Y<Math.max(0,v.length-3),wrap:"truncate",children:q},Y))})()]})]}),se(J,{children:" "}),y?ye(Je,{flexDirection:"column",children:[se(J,{bold:!0,color:y.includes("VICTORY")||y.includes("SUCCESS")?"green":"red",children:y}),s&&ye(Je,{flexDirection:"column",marginTop:1,children:[se(J,{bold:!0,children:"\u2550\u2550\u2550 Raid Summary \u2550\u2550\u2550"}),ye(J,{children:["Difficulty: Lv.",s.difficulty," Raiders: ",s.raidersKilled,"/",s.raidersTotal," slain"]}),ye(J,{children:["Walls destroyed: ",s.wallsDestroyed," Archer towers: ",s.archersActive]}),s.lootGained.gold+s.lootGained.wood+s.lootGained.stone>0&&ye(J,{color:"green",children:["Resources gained: ",Zo(s.lootGained,"+")]}),s.lootLost.gold+s.lootLost.wood+s.lootLost.stone>0&&ye(J,{color:"red",children:["Resources lost: ",Zo(s.lootLost,"-")]})]}),se(J,{dimColor:!0,children:"Press q to return"})]}):ye(Je,{flexDirection:"column",children:[se(J,{dimColor:!0,children:"p pause 1/2/4/8 speed n/\u21B5 skip q back"}),ye(J,{dimColor:!0,children:[se(J,{color:"green",bold:!0,children:"R"}),"=Raider ",se(J,{color:"yellow",bold:!0,children:"S"}),"=Scout ",se(J,{color:"red",bold:!0,children:"B"}),"=Brute ",se(J,{color:"cyan",children:"\u25CA"}),"=Stunned ",se(J,{color:"red",children:"\u2716"}),"=Kill ",se(J,{color:"redBright",children:"\u2020"}),"=Arrow ",se(J,{color:"yellowBright",children:"\u2193\u2191\u2192\u2190"}),"=Spawn"]})]})]})}import{useState as Kt,useCallback as is}from"react";import{Box as xo,Text as n,useInput as Vn}from"ink";import{Fragment as Ue,jsx as i,jsxs as P}from"react/jsx-runtime";var Xt=["welcome","resources","move","place_wall","place_treasury","place_archer","place_trap","upgrade_explain","first_raid","raid_result","foraging","tips","done"],ls={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"};function ds({gameSave:e,onComplete:t}){let[o,s]=Kt("welcome"),[r,a]=Kt({x:7,y:7}),[T,f]=Kt([]),[d,l]=Kt([]),[x,k]=Kt(""),[R,m]=Kt(0),p=R>=4,b=Xt.indexOf(o),c=Xt.length-1,$=is(()=>{let w=Xt.indexOf(o);w<Xt.length-1&&s(Xt[w+1])},[o]),z=o==="place_wall"?"wall":o==="place_treasury"?"treasury":o==="place_archer"?"archerTower":o==="place_trap"?"trap":null,U=is(()=>{let w={width:16,height:16,structures:[...T]},H=ut({probeCount:2,keepGrid:w,seed:"tutorial-raid",probeTypes:["raider","scout"]}),y=[],h=0,E=0,C=!1;for(let A of H.events)A.type==="raider_spawn"?y.push(` Raider ${A.probeId+1} appears from the ${A.edge} edge`):A.type==="wall_damaged"&&A.destroyed?(E++,y.push(" A stone wall was destroyed!")):A.type==="raider_stunned"?y.push(` Raider ${A.probeId+1} stepped on a bear trap \u2014 stunned!`):A.type==="arrow_hit"&&A.hpRemaining<=0?(h++,y.push(` Archer tower eliminated raider ${A.probeId+1}!`)):A.type==="raider_destroyed"&&!y[y.length-1]?.includes(`raider ${A.probeId+1}`)?(h++,y.push(` Raider ${A.probeId+1} was destroyed`)):A.type==="treasury_breach"?(C=!0,y.push(" Raiders breached your treasury!")):A.type==="raid_end"&&(A.outcome==="defense_win"?y.push(" \u2713 DEFENSE WIN \u2014 all raiders defeated!"):y.push(" \u2717 Raiders got through \u2014 time to strengthen defenses"));if(y.length>10){let A=[...y.slice(0,3),` ... ${y.length-6} more events ...`,...y.slice(-3)];l(A)}else l(y);let N=H.events.find(A=>A.type==="raid_end");k(N?.type==="raid_end"&&N.outcome==="defense_win"?"win":"loss"),$()},[T,$]);Vn((w,H)=>{if(w==="s"&&o!=="move"){t();return}if(["welcome","resources","move","upgrade_explain","first_raid","raid_result","foraging","tips","done"].includes(o)){if(o==="move"){if(w==="h"||w==="a"||H.leftArrow){a(h=>({...h,x:Math.max(0,h.x-1)})),m(h=>h+1);return}if(w==="l"||w==="d"||H.rightArrow){a(h=>({...h,x:Math.min(I-1,h.x+1)})),m(h=>h+1);return}if(w==="k"||w==="w"||H.upArrow){a(h=>({...h,y:Math.max(0,h.y-1)})),m(h=>h+1);return}if(w==="j"||H.downArrow){a(h=>({...h,y:Math.min(I-1,h.y+1)})),m(h=>h+1);return}if((H.return||w===" ")&&p){$();return}return}if(o==="first_raid"){U();return}if(o==="done"){t();return}(H.return||w===" ")&&$();return}if(z){if(w==="h"||w==="a"||H.leftArrow)a(h=>({...h,x:Math.max(0,h.x-1)}));else if(w==="l"||w==="d"||H.rightArrow)a(h=>({...h,x:Math.min(I-1,h.x+1)}));else if(w==="k"||w==="w"||H.upArrow)a(h=>({...h,y:Math.max(0,h.y-1)}));else if(w==="j"||H.downArrow)a(h=>({...h,y:Math.min(I-1,h.y+1)}));else if(H.return||w==="e"){if(T.some(C=>C.pos.x===r.x&&C.pos.y===r.y))return;let E={id:`tut-${z}-${r.x}-${r.y}`,kind:z,level:1,pos:{...r},placedAtUnixMs:Date.now()};f(C=>[...C,E]),$()}}});let u=()=>{let h=new Map;for(let C of T)h.set(`${C.pos.x},${C.pos.y}`,C);let E=[];for(let C=3;C<13;C++){let N=[];for(let A=3;A<13;A++){let O=r.x===A&&r.y===C,W=h.get(`${A},${C}`),ue=Ot,Se;W&&(ue=fe[W.kind],Se=ls[W.kind]),O?N.push(i(n,{backgroundColor:"white",color:"black",bold:!0,children:ue+" "},A)):Se?N.push(i(n,{color:Se,bold:!0,children:ue+" "},A)):N.push(i(n,{dimColor:!0,children:ue+" "},A))}E.push(i(xo,{children:N},C))}return i(xo,{flexDirection:"column",children:E})},K=`[${b}/${c}]`;return P(xo,{flexDirection:"column",padding:1,children:[P(xo,{children:[i(n,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep \u2014 Tutorial "}),i(n,{dimColor:!0,children:K})]}),i(n,{children:" "}),o==="welcome"&&P(Ue,{children:[P(n,{bold:!0,color:"cyan",children:["Welcome to CodeKeep, ",e.player.displayName,"!"]}),i(n,{children:" "}),P(n,{children:["CodeKeep is a ",i(n,{bold:!0,children:"tower defense"})," game played in your terminal."]}),i(n,{children:" "}),P(n,{children:[" ","Build a ",i(n,{bold:!0,children:"fortress"})," on a 16\xD716 grid"]}),P(n,{children:[" ","Place ",i(n,{bold:!0,children:"walls"}),", ",i(n,{bold:!0,children:"traps"}),", and ",i(n,{bold:!0,children:"archer towers"})," to defend"]}),P(n,{children:[" ","Protect your ",i(n,{bold:!0,color:"yellow",children:"treasuries"})," from raiding parties"]}),P(n,{children:[" ","Earn resources over time and from coding activity"]}),i(n,{children:" "}),i(n,{children:"Let's learn how to play!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue \xB7 s to skip tutorial"})]}),o==="resources"&&P(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Resources"}),i(n,{children:" "}),i(n,{children:"You have three resources to build and upgrade structures:"}),i(n,{children:" "}),P(n,{children:[" ",P(n,{color:"yellow",bold:!0,children:[B.gold," Gold"]})," \u2014 Your main currency. Earned from events and raids."]}),P(n,{children:[" ",P(n,{color:"green",bold:!0,children:[B.wood," Wood"]})," \u2014 Building material. Earned from treasuries and events."]}),P(n,{children:[" ",P(n,{color:"white",bold:!0,children:[B.stone," Stone"]})," \u2014 Heavy material. Used for walls and towers."]}),i(n,{children:" "}),i(n,{children:"Resources grow passively and from coding activity (git commits!)."}),P(n,{children:["You can also press ",i(n,{bold:!0,children:"f"})," for a kingdom boon anytime."]}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="move"&&P(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Movement"}),i(n,{children:" "}),i(n,{children:"Move the cursor with:"}),P(n,{children:[" ",i(n,{bold:!0,children:"h j k l"})," (vim) \xB7 ",i(n,{bold:!0,children:"W A S D"})," \xB7 ",i(n,{bold:!0,children:"Arrow keys"})]}),i(n,{children:" "}),P(n,{children:["Try moving around \u2014 ",p?i(n,{color:"green",bold:!0,children:"Great! Press Enter to continue."}):i(n,{dimColor:!0,children:"move at least 4 times to proceed"})]}),i(n,{children:" "}),u()]}),o==="place_wall"&&P(Ue,{children:[P(n,{bold:!0,color:"cyan",children:["Build: Stone Wall ",i(n,{color:"white",children:"#"})]}),i(n,{children:" "}),P(n,{children:["Walls ",i(n,{bold:!0,children:"block raiders"})," and force them to find another path."]}),i(n,{children:"They have HP and will eventually break if attacked."}),i(n,{children:" "}),P(n,{children:["Move to an empty cell and press ",i(n,{bold:!0,children:"Enter"})," to place one."]}),i(n,{children:" "}),u(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move with h/j/k/l \xB7 place with Enter"})]}),o==="place_treasury"&&P(Ue,{children:[P(n,{bold:!0,color:"cyan",children:["Build: Treasury ",i(n,{color:"yellow",children:"$"})]}),i(n,{children:" "}),P(n,{children:["Treasuries are what ",i(n,{bold:!0,color:"red",children:"raiders target"}),"."]}),P(n,{children:["They also generate ",i(n,{bold:!0,children:"passive income"})," over time."]}),i(n,{children:"Place them in protected spots behind your walls!"}),i(n,{children:" "}),i(n,{children:"Place a treasury somewhere on the grid."}),i(n,{children:" "}),u(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move and press Enter to place"})]}),o==="place_archer"&&P(Ue,{children:[P(n,{bold:!0,color:"cyan",children:["Build: Archer Tower ",i(n,{color:"redBright",children:"!"})]}),i(n,{children:" "}),P(n,{children:["Archer towers ",i(n,{bold:!0,children:"shoot arrows"})," at raiders within range."]}),i(n,{children:"They fire automatically and can kill raiders before they reach your treasury."}),i(n,{children:"Place them where raiders will pass by!"}),i(n,{children:" "}),i(n,{children:"Place an archer tower on the grid."}),i(n,{children:" "}),u(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move and press Enter to place"})]}),o==="place_trap"&&P(Ue,{children:[P(n,{bold:!0,color:"cyan",children:["Build: Bear Trap ",i(n,{color:"magenta",children:"%"})]}),i(n,{children:" "}),P(n,{children:["Traps ",i(n,{bold:!0,children:"stun raiders"})," that walk over them."]}),i(n,{children:"A stunned raider can't move for several ticks \u2014 perfect for"}),i(n,{children:"your archer towers to finish them off!"}),i(n,{children:" "}),i(n,{children:"Place a bear trap on a path raiders might take."}),i(n,{children:" "}),u(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move and press Enter to place"})]}),o==="upgrade_explain"&&P(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Upgrading & More Structures"}),i(n,{children:" "}),i(n,{children:"In the full game you can:"}),i(n,{children:" "}),P(n,{children:[" ",i(n,{bold:!0,children:"u"})," Upgrade a structure (Lv.1 \u2192 2 \u2192 3) for better stats"]}),P(n,{children:[" ",i(n,{bold:!0,children:"x"})," Demolish a structure (get 50% refund)"]}),P(n,{children:[" ",i(n,{bold:!0,children:"1-6"})," Quick-select any structure type"]}),i(n,{children:" "}),i(n,{children:"Two structures you haven't placed yet:"}),i(n,{children:" "}),P(n,{children:[" ",i(n,{color:"cyan",bold:!0,children:fe.ward})," ",tt.ward," \u2014 Place next to a treasury to ",i(n,{bold:!0,children:"reduce loot"})]}),i(n,{children:" raiders can steal. Wards protect a 1-tile radius around them."}),i(n,{children:" "}),P(n,{children:[" ",i(n,{color:"green",bold:!0,children:fe.watchtower})," ",tt.watchtower," \u2014 ",i(n,{bold:!0,children:"Extends ward range"})," when adjacent."]}),i(n,{children:" Also auto-gathers forage nearby and earns passive stone."}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Combo: Ward next to Treasury + Watchtower next to Ward = max protection!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="first_raid"&&P(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Your First Raid!"}),i(n,{children:" "}),i(n,{children:"Let's test your defenses. Two raiders will attack your layout:"}),i(n,{children:" "}),u(),i(n,{children:" "}),P(n,{children:[" You placed: ",T.map(w=>P(n,{children:[i(n,{color:ls[w.kind],bold:!0,children:fe[w.kind]})," "]},w.id))]}),i(n,{children:" "}),i(n,{bold:!0,color:"yellow",children:"Press any key to simulate the raid!"})]}),o==="raid_result"&&P(Ue,{children:[i(n,{bold:!0,color:x==="win"?"green":"yellow",children:x==="win"?"Victory! Your defenses held!":"The raiders got through \u2014 but that's okay!"}),i(n,{children:" "}),i(n,{bold:!0,children:"Raid replay:"}),d.map((w,H)=>i(n,{color:w.includes("WIN")?"green":w.includes("eliminated")||w.includes("destroyed")?"cyan":w.includes("breached")||w.includes("through")?"red":w.includes("stunned")?"magenta":void 0,children:w},H)),i(n,{children:" "}),x!=="win"&&i(n,{dimColor:!0,children:"Don't worry \u2014 you'll have plenty of resources to build better defenses!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="foraging"&&P(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Foraging"}),i(n,{children:" "}),P(n,{children:["Glowing ",i(n,{color:"cyan",bold:!0,children:"~"})," fragments appear on your grid over time."]}),P(n,{children:["Move your cursor over one and press ",i(n,{bold:!0,children:"c"})," to collect resources!"]}),i(n,{children:" "}),P(n,{children:[" ",i(n,{color:"cyan",children:"~"})," Gold Nugget ",i(n,{color:"yellow",children:"~"})," Timber ",i(n,{color:"green",children:"~"})," Ore ",i(n,{color:"white",children:"~"})," Gem"]}),i(n,{children:" "}),i(n,{children:"Structure synergies:"}),P(n,{children:[" ",i(n,{color:"redBright",children:"!"})," Archer Towers increase spawn rate"]}),P(n,{children:[" ",i(n,{color:"yellow",children:"$"})," Treasuries boost yield when nearby"]}),P(n,{children:[" ",i(n,{color:"green",children:"^"})," Watchtowers auto-collect nearby fragments"]}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="tips"&&P(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Pro Tips"}),i(n,{children:" "}),P(n,{children:[" ",i(n,{bold:!0,children:"?"})," Full help screen (anytime)"]}),P(n,{children:[" ",i(n,{bold:!0,children:"1-6"})," Quick-select structures"]}),P(n,{children:[" ",i(n,{bold:!0,children:"Tab"})," Jump between your structures"]}),P(n,{children:[" ",i(n,{bold:!0,children:"r"})," Quick defend (instant raid result)"]}),P(n,{children:[" ",i(n,{bold:!0,children:"v"})," Watch replay of last defense"]}),P(n,{children:[" ",i(n,{bold:!0,children:"f"})," Kingdom boon (free resources)"]}),P(n,{children:[" ",i(n,{bold:!0,children:"Esc"})," Back to main menu"]}),i(n,{children:" "}),i(n,{bold:!0,children:"Strategy:"}),P(n,{children:[" \u2022 Place walls to create ",i(n,{bold:!0,children:"chokepoints"})]}),P(n,{children:[" \u2022 Put traps in the chokepoint so raiders get ",i(n,{bold:!0,children:"stunned"})]}),P(n,{children:[" \u2022 Put archer towers nearby to ",i(n,{bold:!0,children:"finish stunned raiders"})]}),P(n,{children:[" \u2022 Hide treasuries ",i(n,{bold:!0,children:"deep inside"})," your walls"]}),P(n,{children:[" \u2022 Place wards next to treasuries to ",i(n,{bold:!0,children:"reduce loot stolen"})]}),P(n,{children:[" \u2022 ",i(n,{bold:!0,children:"Hover over"})," towers/wards to see their ",i(n,{bold:!0,color:"red",children:"range overlay"})]}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="done"&&P(Ue,{children:[i(n,{bold:!0,color:"yellow",children:"You're ready to defend your keep!"}),i(n,{children:" "}),i(n,{children:"Your structures from the tutorial won't carry over \u2014 you start"}),i(n,{children:"with a clean grid and starting resources."}),i(n,{children:" "}),i(n,{children:"Go forth, build wisely, and may your walls hold strong!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Press Enter to start playing"})]})]})}import{Box as cs,Text as Xo}from"ink";import{jsx as qn,jsxs as yo}from"react/jsx-runtime";var Yn={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"};function jn(e){let t=[];return e.gold>0&&t.push(`${B.gold}${e.gold}`),e.wood>0&&t.push(`${B.wood}${e.wood}`),e.stone>0&&t.push(`${B.stone}${e.stone}`),t.join(" ")}function us({selected:e}){return yo(cs,{flexDirection:"column",children:[qn(Xo,{bold:!0,children:"Structures [ ]"}),ct.map((t,o)=>{let s=t===e,r=ot[t][1];return yo(cs,{children:[yo(Xo,{color:s?Yn[t]:void 0,bold:s,dimColor:!s,children:[s?"\u25B8":" "," ",o+1," ",fe[t]," ",tt[t]]}),s&&yo(Xo,{dimColor:!0,children:[" ",jn(r)]})]},t)})]})}import{useState as zn}from"react";import{Box as wo,Text as ft,useInput as Zn}from"ink";import{jsx as At,jsxs as Ht}from"react/jsx-runtime";var Qo=[{name:"Lord Ironhelm",seed:"friend-ironhelm-42",difficulty:1,tagline:"A minor lord with modest fortifications"},{name:"Lady Ashwood",seed:"friend-ashwood-99",difficulty:2,tagline:"Her archers never miss \u2014 or so she claims"},{name:"Baron Stonewatch",seed:"friend-stonewatch-256",difficulty:3,tagline:"Walls thick as legends and twice as old"}];function ps({onSelectFriend:e,onBack:t}){let[o,s]=zn(0);return Zn((r,a)=>{if(r==="k"||r==="w"||a.upArrow)s(T=>Math.max(0,T-1));else if(r==="j"||r==="s"||a.downArrow)s(T=>Math.min(Qo.length-1,T+1));else if(a.return){let T=Qo[o],f=To(T.seed,T.difficulty);f.name=`${T.name}'s Keep`,f.ownerPlayerId=T.name.toLowerCase(),e(f)}else(r==="q"||a.escape)&&t()}),Ht(wo,{flexDirection:"column",padding:1,children:[At(wo,{children:At(ft,{bold:!0,color:"cyan",children:"\u2694 Rival Keeps"})}),At(ft,{children:" "}),At(ft,{dimColor:!0,children:" Choose a rival lord's keep to raid for plunder."}),At(ft,{children:" "}),Qo.map((r,a)=>Ht(wo,{flexDirection:"column",children:[Ht(wo,{children:[Ht(ft,{color:a===o?"yellow":void 0,bold:a===o,children:[a===o?" \u25B8 ":" ",r.name]}),Ht(ft,{dimColor:!0,children:[" Lv.",r.difficulty]})]}),a===o&&Ht(ft,{dimColor:!0,children:[' "',r.tagline,'"']})]},r.name)),At(ft,{children:" "}),At(ft,{dimColor:!0,children:" \u2191\u2193 navigate Enter raid Esc back"})]})}import{useState as fs}from"react";import{Box as Qt,Text as we,useInput as Jn}from"ink";import{Fragment as ta,jsx as $e,jsxs as Xe}from"react/jsx-runtime";function Xn(e){let t=[];return e.gold>0&&t.push(`+${e.gold}${B.gold}`),e.wood>0&&t.push(`+${e.wood}${B.wood}`),e.stone>0&&t.push(`+${e.stone}${B.stone}`),t.join(" ")}function Qn(e){let t=[];return e.gold>0&&t.push(`-${e.gold}${B.gold}`),e.wood>0&&t.push(`-${e.wood}${B.wood}`),e.stone>0&&t.push(`-${e.stone}${B.stone}`),t.join(" ")}function ms({gameSave:e,onBack:t,onWatchReplay:o}){let[s,r]=fs("raids"),[a,T]=fs(0),f=[...e.raidHistory].reverse().slice(0,15);Jn((l,x)=>{if(l==="q"||x.escape){t();return}(x.tab||l==="t")&&r(k=>k==="raids"?"achievements":"raids"),s==="raids"&&f.length>0&&((l==="j"||x.downArrow)&&T(k=>Math.min(k+1,f.length-1)),(l==="k"||x.upArrow)&&T(k=>Math.max(k-1,0)),(x.return||l==="v")&&f[a]&&o&&o(f[a]))});let d=e.progression;return Xe(Qt,{flexDirection:"column",padding:1,children:[Xe(Qt,{gap:2,children:[$e(we,{bold:!0,color:s==="raids"?"yellow":void 0,underline:s==="raids",children:"Raid Log"}),$e(we,{bold:!0,color:s==="achievements"?"yellow":void 0,underline:s==="achievements",children:"Achievements"}),$e(we,{dimColor:!0,children:"(Tab to switch)"})]}),$e(we,{children:" "}),s==="raids"&&Xe(Qt,{flexDirection:"column",children:[Xe(we,{dimColor:!0,children:[" Total: ",d.totalRaidsWon,"W / ",d.totalRaidsLost,"L \xB7 Streak: ",d.currentWinStreak," (best ",d.bestWinStreak,")"]}),$e(we,{children:" "}),f.length===0?$e(we,{dimColor:!0,children:" No raids yet. Try Defend Keep or Attack NPC!"}):f.map((l,x)=>{let k=ea(l.resolvedAtUnixMs),R=l.attackerId!==e.player.id,m=l.outcome==="defense_win"?R?"\u2713":"\u2717":R?"\u2717":"\u2713",p=(R?l.outcome==="defense_win":l.outcome!=="defense_win")?"green":"red",b=R?"DEF":"ATK",c=l.lootLost.gold+l.lootLost.wood+l.lootLost.stone>0?` ${Qn(l.lootLost)}`:"",$=l.lootGained.gold+l.lootGained.wood+l.lootGained.stone>0?` ${Xn(l.lootGained)}`:"",z=x===a?">":" ";return Xe(we,{dimColor:x>4&&x!==a,bold:x===a,children:[z," ",$e(we,{color:p,children:m})," ",b," ",l.outcome.replace("_"," "),c,$," ",Xe(we,{dimColor:!0,children:["(",k,")"]})]},l.id)}),f.length>0&&Xe(ta,{children:[$e(we,{children:" "}),$e(we,{dimColor:!0,children:" j/k navigate \xB7 Enter/v watch replay"})]})]}),s==="achievements"&&Xe(Qt,{flexDirection:"column",children:[Xe(we,{dimColor:!0,children:[" ",d.achievements?.length||0," / ",zt.length," earned"]}),$e(we,{children:" "}),zt.map(l=>{let x=d.achievements?.includes(l.id);return $e(Qt,{children:Xe(we,{color:x?"green":void 0,dimColor:!x,children:[" ",x?"\u2605":"\u2606"," ",$e(we,{bold:x,children:l.name})," \u2014 ",l.desc,l.bonus&&x?Xe(we,{color:"yellow",children:[" (",l.bonus,")"]}):null]})},l.id)})]}),$e(we,{children:" "}),$e(we,{dimColor:!0,children:" Tab switch \xB7 Esc/q back"})]})}function ea(e){let t=Date.now()-e;return t<6e4?"just now":t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}import{useState as So}from"react";import{Box as bo,Text as be,useInput as aa}from"ink";import{writeFileSync as oa,mkdirSync as ra,existsSync as sa}from"fs";import{join as gs}from"path";import{homedir as na}from"os";var er=gs(na(),".config","codekeep","crashes");function hs(e,t){if((e instanceof Error?e.message:String(e)).includes("Raw mode is not supported"))return"";sa(er)||ra(er,{recursive:!0});let s={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},r=`crash-${Date.now()}.json`,a=gs(er,r);return oa(a,JSON.stringify(s,null,2),"utf-8"),a}function Ro(e){let t=encodeURIComponent(`[Crash]: ${e.error.slice(0,80)}`),o=encodeURIComponent(`## Crash Report
|
|
10
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D`}),Jt(Ze,{children:" "}),xt(Ze,{dimColor:!0,children:[" ",e.player.displayName,"'s Keep \u2014 ",u,"d old"]}),xt(Ze,{dimColor:!0,children:[" ",f," structures (",v,"$ ",U,"!) \xB7 Raids ",m.totalRaidsWon,"W / ",m.totalRaidsLost,"L \xB7 Streak ",m.currentWinStreak," (best ",m.bestWinStreak,")"]}),xt(Ze,{dimColor:!0,children:[" Difficulty: Lv.",C<=2?1:C<=5?2:C<=9?3:C<=14?4:5]}),xt(Ze,{dimColor:!0,children:[" Achievements: ",e.progression.achievements?.length||0,"/",10]}),Jt(Ze,{children:" "}),x.map((B,c)=>xt(ss,{children:[xt(Ze,{color:B.disabled?void 0:c===b?"yellow":void 0,bold:!B.disabled&&c===b,dimColor:B.disabled,children:[c===b&&!B.disabled?" \u25B8 ":" ",B.label]}),xt(Ze,{dimColor:!0,children:[" ",B.desc]})]},B.key)),Jt(Ze,{children:" "}),Jt(Ze,{dimColor:!0,children:" \u2191\u2193 navigate Enter select q quit"})]})}import{useState as ft,useEffect as as,useCallback as Un,useRef as Gn}from"react";import{Box as Je,Text as J,useInput as Wn}from"ink";import{jsx as se,jsxs as Re}from"react/jsx-runtime";var qo={raider:"R",scout:"S",brute:"B"};function zo(e,t){let o=e/t;return o>.6?"green":o>.3?"yellow":"red"}function Kn(e,t){let o=e/t;return o>.6?"white":o>.3?"yellow":"red"}function Zo(e,t){let o=[];return e.gold>0&&o.push(`${t}${e.gold}${O.gold}`),e.wood>0&&o.push(`${t}${e.wood}${O.wood}`),e.stone>0&&o.push(`${t}${e.stone}${O.stone}`),o.join(" ")}function Hn(e,t,o=6){let s=Math.round(e/t*o);return"\u2588".repeat(s)+"\u2591".repeat(o-s)}var Fn={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"};function Jo({replay:e,keepGrid:t,raidType:o,summary:s,initialSpeed:r,onSpeedChange:a,onDone:T}){let[p,d]=ft(0),[l,x]=ft(r??1),b=Un(k=>{x(k),a?.(k)},[a]),[R,f]=ft(!1),[m,C]=ft(new Map),[u,v]=ft(()=>{let k=new Map,z={wall:lo,archerTower:co,watchtower:uo,vault:ho};for(let j of t.structures){let Pe=z[j.kind];if(Pe){let $=Pe[j.level];k.set(j.id,{structureId:j.id,kind:j.kind,pos:{...j.pos},hp:$,maxHp:$,destroyed:!1})}}return k}),[U,B]=ft([]),[c,H]=ft(new Set),[w,F]=ft([]),[y,h]=ft(null),E=Gn(-1);Wn((k,z)=>{if(k==="q"||z.escape){T();return}if(k==="p"||k===" "){f(j=>!j);return}k==="1"&&b(1),k==="2"&&b(2),k==="4"&&b(4),k==="8"&&b(8),(k==="n"||z.return)&&d(e.maxTicks)}),as(()=>{if(R||y)return;let k=setInterval(()=>{d(z=>{let j=z+1;return j>e.maxTicks?(clearInterval(k),z):j})},1e3/e.tickRateHz/l);return()=>clearInterval(k)},[R,l,y,e]),as(()=>{if(p<=E.current)return;let k=e.events.filter($=>$.t>E.current&&$.t<=p),z=[],j=[],Pe=new Map;C($=>{let Z=new Map($);for(let W of k)switch(W.type){case"raider_spawn":{let X=W.raiderType??"raider",_e=W.maxHp??$e[X].hp;Z.set(W.probeId,{id:W.probeId,pos:{...W.pos},alive:!0,stunned:!1,hp:_e,maxHp:_e,raiderType:X});break}case"raider_move":{let X=Z.get(W.probeId);X&&(X.pos={...W.to},X.stunned=!1);break}case"raider_stunned":{let X=Z.get(W.probeId);X&&(X.stunned=!0);break}case"arrow_hit":{let X=Z.get(W.probeId);X&&(X.hp=Math.max(0,W.hpRemaining));break}case"raider_destroyed":{let X=Z.get(W.probeId);X&&(X.alive=!1);break}default:break}return Pe=Z,Z}),v($=>{let Z=new Map($);for(let W of k)if(W.type==="wall_damaged"||W.type==="structure_damaged"){let X=Z.get(W.structureId);X&&(X.hp=Math.max(0,W.hpRemaining),X.destroyed=W.destroyed)}return Z});for(let $ of k)switch($.type){case"raider_spawn":{let Z=$.raiderType??"raider",W=qo[Z]??"R",X=$.edge==="N"?"\u2193":$.edge==="S"?"\u2191":$.edge==="W"?"\u2192":"\u2190";z.push(`${W} Raider ${$.probeId+1} enters from ${$.edge} ${X}`),j.push({pos:{...$.pos},char:X,color:"yellowBright",bold:!0,expiresAtTick:p+3,priority:0});break}case"raider_stunned":{z.push(`\u26A1 Raider ${$.probeId+1} STUNNED ${$.stunTicks}t`),j.push({pos:{...$.pos},char:"\u2727",color:"cyanBright",bold:!0,expiresAtTick:p+3,priority:2});break}case"wall_damaged":{if($.destroyed){z.push("\u{1F4A5} Wall DESTROYED!");let Z=t.structures.find(W=>W.id===$.structureId);Z&&j.push({pos:{...Z.pos},char:"\u2717",color:"redBright",bold:!0,expiresAtTick:p+4,priority:2})}else z.push(`\u2694 Wall hit (${$.hpRemaining} HP)`);break}case"structure_damaged":{let Z=tt[$.structureKind]??$.structureKind;if($.destroyed){z.push(`\u{1F4A5} ${Z} DESTROYED!`);let W=t.structures.find(X=>X.id===$.structureId);W&&j.push({pos:{...W.pos},char:"\u2717",color:"redBright",bold:!0,expiresAtTick:p+5,priority:3})}else z.push(`\u2694 ${Z} hit (${$.hpRemaining} HP)`);break}case"arrow_hit":{let Z=Pe.get($.probeId),W=Z?{...Z.pos}:null;W&&j.push({pos:W,char:"\u2020",color:"redBright",bold:!0,expiresAtTick:p+2,priority:1}),$.hpRemaining<=0?(z.push(`\u{1F3F9} Archer slew raider ${$.probeId+1}!`),W&&j.push({pos:W,char:"\u2716",color:"redBright",bold:!0,expiresAtTick:p+4,priority:3})):z.push(`\u{1F3F9} Arrow hit raider ${$.probeId+1} (-${$.damage} \u2192 ${$.hpRemaining} HP)`);break}case"treasury_breach":{let Z=Zo($.lootTaken,""),W=o==="attack"?"Looted":"Lost";z.push(`\u{1F4B0} TREASURY BREACHED! ${W} ${Z}`),H(_e=>new Set([..._e,$.structureId]));let X=t.structures.find(_e=>_e.id===$.structureId);X&&j.push({pos:{...X.pos},char:"\u26A1",color:"yellowBright",bold:!0,expiresAtTick:p+5,priority:3});break}case"raider_destroyed":{k.some(W=>W.type==="arrow_hit"&&W.probeId===$.probeId&&W.hpRemaining<=0&&W.t===$.t)||z.push(`\u{1F480} Raider ${$.probeId+1} eliminated`);break}case"raid_end":{let Z=$.outcome==="defense_win"?o==="defend"?"DEFENSE VICTORY \u2014 All raiders defeated!":"ATTACK FAILED \u2014 The keep held strong":$.outcome==="partial_breach"?o==="defend"?"PARTIAL BREACH \u2014 Raiders stole some supplies":"PARTIAL SUCCESS \u2014 Some loot seized":o==="defend"?"FULL BREACH \u2014 Major losses!":"FULL SUCCESS \u2014 Major plunder!";h(Z),z.push(Z);break}default:break}B($=>[...$.filter(Z=>Z.expiresAtTick>p),...j]),E.current=p,z.length>0&&F($=>[...$.slice(-10),...z])},[p,o,e,t]);let _=new Map;for(let k of t.structures)_.set(`${k.pos.x},${k.pos.y}`,k);if(!t.structures.some(k=>k.kind==="treasury")){let k={id:"__virtual_center_treasury",kind:"treasury",pos:{x:8,y:8},level:1};_.set("8,8",k)}let A=new Map;for(let[,k]of m)k.alive&&A.set(`${k.pos.x},${k.pos.y}`,k);let G=new Map;for(let[,k]of u)G.set(`${k.pos.x},${k.pos.y}`,k);let K=new Map;for(let k of U){let z=`${k.pos.x},${k.pos.y}`,j=K.get(z);(!j||k.priority>j.priority||k.priority===j.priority&&k.expiresAtTick>j.expiresAtTick)&&K.set(z,k)}let pe=" "+Array.from({length:P},(k,z)=>z.toString(16).toUpperCase()+" ").join(""),ke=[se(J,{dimColor:!0,children:pe},"hdr")];for(let k=0;k<P;k++){let z=[se(J,{dimColor:!0,children:k.toString(16).toUpperCase()+" "},"lbl")];for(let j=0;j<P;j++){let Pe=`${j},${k}`,$=A.get(Pe),Z=_.get(Pe),W=G.get(Pe),X=K.get(Pe);if(X&&!$)z.push(se(J,{color:X.color,bold:X.bold,children:X.char+" "},j));else if($){let _e=$.stunned?"\u25CA":qo[$.raiderType]??"\u25CF",Tt=$.stunned?"cyan":zo($.hp,$.maxHp);z.push(se(J,{color:Tt,bold:!0,children:_e+" "},j))}else if(Z)if(W&&!W.destroyed){let _e=Kn(W.hp,W.maxHp),Tt=ge[W.kind]??"#";z.push(se(J,{color:_e,bold:W.hp>W.maxHp*.6,children:Tt+" "},j))}else if(W&&W.destroyed)z.push(se(J,{color:"red",dimColor:!0,children:"x "},j));else if(c.has(Z.id))z.push(se(J,{color:"red",bold:!0,children:"$ "},j));else{let _e=ge[Z.kind];z.push(se(J,{color:Fn[Z.kind]||"white",children:_e+" "},j))}else z.push(se(J,{dimColor:!0,children:Ot+" "},j))}ke.push(se(Je,{children:z},k))}let at=Array.from(m.values()).filter(k=>k.alive),$t=Array.from(m.values()).filter(k=>!k.alive);return Re(Je,{flexDirection:"column",padding:1,children:[Re(Je,{flexDirection:"row",gap:2,children:[se(J,{bold:!0,color:o==="defend"?"red":"green",children:o==="defend"?"\u2694 DEFENDING YOUR KEEP":"\u2694 ATTACKING NPC KEEP"}),Re(J,{children:["Tick: ",p,"/",e.maxTicks]}),Re(J,{children:["Speed: ",l,"x"]}),se(J,{dimColor:!0,children:R?"[PAUSED]":""})]}),se(J,{children:" "}),Re(Je,{flexDirection:"row",children:[se(Je,{flexDirection:"column",children:ke}),Re(Je,{flexDirection:"column",marginLeft:2,width:38,children:[at.length>0&&Re(Je,{flexDirection:"column",marginBottom:1,children:[se(J,{bold:!0,children:"Raiders"}),at.map(k=>Re(Je,{children:[Re(J,{color:zo(k.hp,k.maxHp),bold:!0,children:[qo[k.raiderType],k.id+1]}),se(J,{children:" "}),se(J,{color:zo(k.hp,k.maxHp),children:Hn(k.hp,k.maxHp)}),Re(J,{dimColor:!0,children:[" ",k.hp,"/",k.maxHp]}),k.stunned&&se(J,{color:"cyan",children:" STUN"})]},k.id)),$t.length>0&&Re(J,{dimColor:!0,children:[" ",$t.length," slain"]})]}),se(J,{bold:!0,children:"Battle Log"}),(()=>{let k=w.slice(-10);return k.map((z,j)=>se(J,{dimColor:j<Math.max(0,k.length-3),wrap:"truncate",children:z},j))})()]})]}),se(J,{children:" "}),y?Re(Je,{flexDirection:"column",children:[se(J,{bold:!0,color:y.includes("VICTORY")||y.includes("SUCCESS")?"green":"red",children:y}),s&&Re(Je,{flexDirection:"column",marginTop:1,children:[se(J,{bold:!0,children:"\u2550\u2550\u2550 Raid Summary \u2550\u2550\u2550"}),Re(J,{children:["Difficulty: Lv.",s.difficulty," Raiders: ",s.raidersKilled,"/",s.raidersTotal," slain"]}),Re(J,{children:["Walls destroyed: ",s.wallsDestroyed," Archer towers: ",s.archersActive]}),s.lootGained.gold+s.lootGained.wood+s.lootGained.stone>0&&Re(J,{color:"green",children:["Resources gained: ",Zo(s.lootGained,"+")]}),s.lootLost.gold+s.lootLost.wood+s.lootLost.stone>0&&Re(J,{color:"red",children:["Resources lost: ",Zo(s.lootLost,"-")]})]}),se(J,{dimColor:!0,children:"Press q to return"})]}):Re(Je,{flexDirection:"column",children:[se(J,{dimColor:!0,children:"p pause 1/2/4/8 speed n/\u21B5 skip q back"}),Re(J,{dimColor:!0,children:[se(J,{color:"green",bold:!0,children:"R"}),"=Raider ",se(J,{color:"yellow",bold:!0,children:"S"}),"=Scout ",se(J,{color:"red",bold:!0,children:"B"}),"=Brute ",se(J,{color:"cyan",children:"\u25CA"}),"=Stunned ",se(J,{color:"red",children:"\u2716"}),"=Kill ",se(J,{color:"redBright",children:"\u2020"}),"=Arrow ",se(J,{color:"yellowBright",children:"\u2193\u2191\u2192\u2190"}),"=Spawn"]})]})]})}import{useState as Kt,useCallback as is}from"react";import{Box as wo,Text as n,useInput as Vn}from"ink";import{Fragment as Ue,jsx as i,jsxs as D}from"react/jsx-runtime";var Xt=["welcome","resources","move","place_wall","place_treasury","place_archer","place_trap","upgrade_explain","first_raid","raid_result","foraging","tips","done"],ls={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"};function ds({gameSave:e,onComplete:t}){let[o,s]=Kt("welcome"),[r,a]=Kt({x:7,y:7}),[T,p]=Kt([]),[d,l]=Kt([]),[x,b]=Kt(""),[R,f]=Kt(0),m=R>=4,C=Xt.indexOf(o),u=Xt.length-1,v=is(()=>{let w=Xt.indexOf(o);w<Xt.length-1&&s(Xt[w+1])},[o]),U=o==="place_wall"?"wall":o==="place_treasury"?"treasury":o==="place_archer"?"archerTower":o==="place_trap"?"trap":null,B=is(()=>{let w={width:16,height:16,structures:[...T]},F=pt({probeCount:2,keepGrid:w,seed:"tutorial-raid",probeTypes:["raider","scout"]}),y=[],h=0,E=0,_=!1;for(let A of F.events)A.type==="raider_spawn"?y.push(` Raider ${A.probeId+1} appears from the ${A.edge} edge`):A.type==="wall_damaged"&&A.destroyed?(E++,y.push(" A stone wall was destroyed!")):A.type==="raider_stunned"?y.push(` Raider ${A.probeId+1} stepped on a bear trap \u2014 stunned!`):A.type==="arrow_hit"&&A.hpRemaining<=0?(h++,y.push(` Archer tower eliminated raider ${A.probeId+1}!`)):A.type==="raider_destroyed"&&!y[y.length-1]?.includes(`raider ${A.probeId+1}`)?(h++,y.push(` Raider ${A.probeId+1} was destroyed`)):A.type==="treasury_breach"?(_=!0,y.push(" Raiders breached your treasury!")):A.type==="raid_end"&&(A.outcome==="defense_win"?y.push(" \u2713 DEFENSE WIN \u2014 all raiders defeated!"):y.push(" \u2717 Raiders got through \u2014 time to strengthen defenses"));if(y.length>10){let A=[...y.slice(0,3),` ... ${y.length-6} more events ...`,...y.slice(-3)];l(A)}else l(y);let N=F.events.find(A=>A.type==="raid_end");b(N?.type==="raid_end"&&N.outcome==="defense_win"?"win":"loss"),v()},[T,v]);Vn((w,F)=>{if(w==="s"&&o!=="move"){t();return}if(["welcome","resources","move","upgrade_explain","first_raid","raid_result","foraging","tips","done"].includes(o)){if(o==="move"){if(w==="h"||w==="a"||F.leftArrow){a(h=>({...h,x:Math.max(0,h.x-1)})),f(h=>h+1);return}if(w==="l"||w==="d"||F.rightArrow){a(h=>({...h,x:Math.min(P-1,h.x+1)})),f(h=>h+1);return}if(w==="k"||w==="w"||F.upArrow){a(h=>({...h,y:Math.max(0,h.y-1)})),f(h=>h+1);return}if(w==="j"||F.downArrow){a(h=>({...h,y:Math.min(P-1,h.y+1)})),f(h=>h+1);return}if((F.return||w===" ")&&m){v();return}return}if(o==="first_raid"){B();return}if(o==="done"){t();return}(F.return||w===" ")&&v();return}if(U){if(w==="h"||w==="a"||F.leftArrow)a(h=>({...h,x:Math.max(0,h.x-1)}));else if(w==="l"||w==="d"||F.rightArrow)a(h=>({...h,x:Math.min(P-1,h.x+1)}));else if(w==="k"||w==="w"||F.upArrow)a(h=>({...h,y:Math.max(0,h.y-1)}));else if(w==="j"||F.downArrow)a(h=>({...h,y:Math.min(P-1,h.y+1)}));else if(F.return||w==="e"){if(T.some(_=>_.pos.x===r.x&&_.pos.y===r.y))return;let E={id:`tut-${U}-${r.x}-${r.y}`,kind:U,level:1,pos:{...r},placedAtUnixMs:Date.now()};p(_=>[..._,E]),v()}}});let c=()=>{let h=new Map;for(let _ of T)h.set(`${_.pos.x},${_.pos.y}`,_);let E=[];for(let _=3;_<13;_++){let N=[];for(let A=3;A<13;A++){let G=r.x===A&&r.y===_,K=h.get(`${A},${_}`),pe=Ot,ke;K&&(pe=ge[K.kind],ke=ls[K.kind]),G?N.push(i(n,{backgroundColor:"white",color:"black",bold:!0,children:pe+" "},A)):ke?N.push(i(n,{color:ke,bold:!0,children:pe+" "},A)):N.push(i(n,{dimColor:!0,children:pe+" "},A))}E.push(i(wo,{children:N},_))}return i(wo,{flexDirection:"column",children:E})},H=`[${C}/${u}]`;return D(wo,{flexDirection:"column",padding:1,children:[D(wo,{children:[i(n,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep \u2014 Tutorial "}),i(n,{dimColor:!0,children:H})]}),i(n,{children:" "}),o==="welcome"&&D(Ue,{children:[D(n,{bold:!0,color:"cyan",children:["Welcome to CodeKeep, ",e.player.displayName,"!"]}),i(n,{children:" "}),D(n,{children:["CodeKeep is a ",i(n,{bold:!0,children:"tower defense"})," game played in your terminal."]}),i(n,{children:" "}),D(n,{children:[" ","Build a ",i(n,{bold:!0,children:"fortress"})," on a 16\xD716 grid"]}),D(n,{children:[" ","Place ",i(n,{bold:!0,children:"walls"}),", ",i(n,{bold:!0,children:"traps"}),", and ",i(n,{bold:!0,children:"archer towers"})," to defend"]}),D(n,{children:[" ","Protect your ",i(n,{bold:!0,color:"yellow",children:"treasuries"})," from raiding parties"]}),D(n,{children:[" ","Earn resources over time and from coding activity"]}),i(n,{children:" "}),i(n,{children:"Let's learn how to play!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue \xB7 s to skip tutorial"})]}),o==="resources"&&D(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Resources"}),i(n,{children:" "}),i(n,{children:"You have three resources to build and upgrade structures:"}),i(n,{children:" "}),D(n,{children:[" ",D(n,{color:"yellow",bold:!0,children:[O.gold," Gold"]})," \u2014 Your main currency. Earned from events and raids."]}),D(n,{children:[" ",D(n,{color:"green",bold:!0,children:[O.wood," Wood"]})," \u2014 Building material. Earned from treasuries and events."]}),D(n,{children:[" ",D(n,{color:"white",bold:!0,children:[O.stone," Stone"]})," \u2014 Heavy material. Used for walls and towers."]}),i(n,{children:" "}),i(n,{children:"Resources grow passively and from coding activity (git commits!)."}),D(n,{children:["You can also press ",i(n,{bold:!0,children:"f"})," for a kingdom boon anytime."]}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="move"&&D(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Movement"}),i(n,{children:" "}),i(n,{children:"Move the cursor with:"}),D(n,{children:[" ",i(n,{bold:!0,children:"h j k l"})," (vim) \xB7 ",i(n,{bold:!0,children:"W A S D"})," \xB7 ",i(n,{bold:!0,children:"Arrow keys"})]}),i(n,{children:" "}),D(n,{children:["Try moving around \u2014 ",m?i(n,{color:"green",bold:!0,children:"Great! Press Enter to continue."}):i(n,{dimColor:!0,children:"move at least 4 times to proceed"})]}),i(n,{children:" "}),c()]}),o==="place_wall"&&D(Ue,{children:[D(n,{bold:!0,color:"cyan",children:["Build: Stone Wall ",i(n,{color:"white",children:"#"})]}),i(n,{children:" "}),D(n,{children:["Walls ",i(n,{bold:!0,children:"block raiders"})," and force them to find another path."]}),i(n,{children:"They have HP and will eventually break if attacked."}),i(n,{children:" "}),D(n,{children:["Move to an empty cell and press ",i(n,{bold:!0,children:"Enter"})," to place one."]}),i(n,{children:" "}),c(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move with h/j/k/l \xB7 place with Enter"})]}),o==="place_treasury"&&D(Ue,{children:[D(n,{bold:!0,color:"cyan",children:["Build: Treasury ",i(n,{color:"yellow",children:"$"})]}),i(n,{children:" "}),D(n,{children:["Treasuries are what ",i(n,{bold:!0,color:"red",children:"raiders target"}),"."]}),D(n,{children:["They also generate ",i(n,{bold:!0,children:"passive income"})," over time."]}),i(n,{children:"Place them in protected spots behind your walls!"}),i(n,{children:" "}),i(n,{children:"Place a treasury somewhere on the grid."}),i(n,{children:" "}),c(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move and press Enter to place"})]}),o==="place_archer"&&D(Ue,{children:[D(n,{bold:!0,color:"cyan",children:["Build: Archer Tower ",i(n,{color:"redBright",children:"!"})]}),i(n,{children:" "}),D(n,{children:["Archer towers ",i(n,{bold:!0,children:"shoot arrows"})," at raiders within range."]}),i(n,{children:"They fire automatically and can kill raiders before they reach your treasury."}),i(n,{children:"Place them where raiders will pass by!"}),i(n,{children:" "}),i(n,{children:"Place an archer tower on the grid."}),i(n,{children:" "}),c(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move and press Enter to place"})]}),o==="place_trap"&&D(Ue,{children:[D(n,{bold:!0,color:"cyan",children:["Build: Bear Trap ",i(n,{color:"magenta",children:"%"})]}),i(n,{children:" "}),D(n,{children:["Traps ",i(n,{bold:!0,children:"stun raiders"})," that walk over them."]}),i(n,{children:"A stunned raider can't move for several ticks \u2014 perfect for"}),i(n,{children:"your archer towers to finish them off!"}),i(n,{children:" "}),i(n,{children:"Place a bear trap on a path raiders might take."}),i(n,{children:" "}),c(),i(n,{children:" "}),i(n,{dimColor:!0,children:"Move and press Enter to place"})]}),o==="upgrade_explain"&&D(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Upgrading & More Structures"}),i(n,{children:" "}),i(n,{children:"In the full game you can:"}),i(n,{children:" "}),D(n,{children:[" ",i(n,{bold:!0,children:"u"})," Upgrade a structure (Lv.1 \u2192 2 \u2192 3) for better stats"]}),D(n,{children:[" ",i(n,{bold:!0,children:"x"})," Demolish a structure (get 50% refund)"]}),D(n,{children:[" ",i(n,{bold:!0,children:"1-6"})," Quick-select any structure type"]}),i(n,{children:" "}),i(n,{children:"Two structures you haven't placed yet:"}),i(n,{children:" "}),D(n,{children:[" ",i(n,{color:"cyan",bold:!0,children:ge.ward})," ",tt.ward," \u2014 Place next to a treasury to ",i(n,{bold:!0,children:"reduce loot"})]}),i(n,{children:" raiders can steal. Wards protect a 1-tile radius around them."}),i(n,{children:" "}),D(n,{children:[" ",i(n,{color:"green",bold:!0,children:ge.watchtower})," ",tt.watchtower," \u2014 ",i(n,{bold:!0,children:"Extends ward range"})," when adjacent."]}),i(n,{children:" Also auto-gathers forage nearby and earns passive stone."}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Combo: Ward next to Treasury + Watchtower next to Ward = max protection!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="first_raid"&&D(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Your First Raid!"}),i(n,{children:" "}),i(n,{children:"Let's test your defenses. Two raiders will attack your layout:"}),i(n,{children:" "}),c(),i(n,{children:" "}),D(n,{children:[" You placed: ",T.map(w=>D(n,{children:[i(n,{color:ls[w.kind],bold:!0,children:ge[w.kind]})," "]},w.id))]}),i(n,{children:" "}),i(n,{bold:!0,color:"yellow",children:"Press any key to simulate the raid!"})]}),o==="raid_result"&&D(Ue,{children:[i(n,{bold:!0,color:x==="win"?"green":"yellow",children:x==="win"?"Victory! Your defenses held!":"The raiders got through \u2014 but that's okay!"}),i(n,{children:" "}),i(n,{bold:!0,children:"Raid replay:"}),d.map((w,F)=>i(n,{color:w.includes("WIN")?"green":w.includes("eliminated")||w.includes("destroyed")?"cyan":w.includes("breached")||w.includes("through")?"red":w.includes("stunned")?"magenta":void 0,children:w},F)),i(n,{children:" "}),x!=="win"&&i(n,{dimColor:!0,children:"Don't worry \u2014 you'll have plenty of resources to build better defenses!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="foraging"&&D(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Foraging"}),i(n,{children:" "}),D(n,{children:["Glowing ",i(n,{color:"cyan",bold:!0,children:"~"})," fragments appear on your grid over time."]}),D(n,{children:["Move your cursor over one and press ",i(n,{bold:!0,children:"c"})," to collect resources!"]}),i(n,{children:" "}),D(n,{children:[" ",i(n,{color:"cyan",children:"~"})," Gold Nugget ",i(n,{color:"yellow",children:"~"})," Timber ",i(n,{color:"green",children:"~"})," Ore ",i(n,{color:"white",children:"~"})," Gem"]}),i(n,{children:" "}),i(n,{children:"Structure synergies:"}),D(n,{children:[" ",i(n,{color:"redBright",children:"!"})," Archer Towers increase spawn rate"]}),D(n,{children:[" ",i(n,{color:"yellow",children:"$"})," Treasuries boost yield when nearby"]}),D(n,{children:[" ",i(n,{color:"green",children:"^"})," Watchtowers auto-collect nearby fragments"]}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="tips"&&D(Ue,{children:[i(n,{bold:!0,color:"cyan",children:"Pro Tips"}),i(n,{children:" "}),D(n,{children:[" ",i(n,{bold:!0,children:"?"})," Full help screen (anytime)"]}),D(n,{children:[" ",i(n,{bold:!0,children:"1-6"})," Quick-select structures"]}),D(n,{children:[" ",i(n,{bold:!0,children:"Tab"})," Jump between your structures"]}),D(n,{children:[" ",i(n,{bold:!0,children:"r"})," Quick defend (instant raid result)"]}),D(n,{children:[" ",i(n,{bold:!0,children:"v"})," Watch replay of last defense"]}),D(n,{children:[" ",i(n,{bold:!0,children:"f"})," Kingdom boon (free resources)"]}),D(n,{children:[" ",i(n,{bold:!0,children:"Esc"})," Back to main menu"]}),i(n,{children:" "}),i(n,{bold:!0,children:"Strategy:"}),D(n,{children:[" \u2022 Place walls to create ",i(n,{bold:!0,children:"chokepoints"})]}),D(n,{children:[" \u2022 Put traps in the chokepoint so raiders get ",i(n,{bold:!0,children:"stunned"})]}),D(n,{children:[" \u2022 Put archer towers nearby to ",i(n,{bold:!0,children:"finish stunned raiders"})]}),D(n,{children:[" \u2022 Hide treasuries ",i(n,{bold:!0,children:"deep inside"})," your walls"]}),D(n,{children:[" \u2022 Place wards next to treasuries to ",i(n,{bold:!0,children:"reduce loot stolen"})]}),D(n,{children:[" \u2022 ",i(n,{bold:!0,children:"Hover over"})," towers/wards to see their ",i(n,{bold:!0,color:"red",children:"range overlay"})]}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Enter to continue"})]}),o==="done"&&D(Ue,{children:[i(n,{bold:!0,color:"yellow",children:"You're ready to defend your keep!"}),i(n,{children:" "}),i(n,{children:"Your structures from the tutorial won't carry over \u2014 you start"}),i(n,{children:"with a clean grid and starting resources."}),i(n,{children:" "}),i(n,{children:"Go forth, build wisely, and may your walls hold strong!"}),i(n,{children:" "}),i(n,{dimColor:!0,children:"Press Enter to start playing"})]})]})}import{Box as cs,Text as Xo}from"ink";import{jsx as qn,jsxs as Ro}from"react/jsx-runtime";var Yn={wall:"white",trap:"magenta",treasury:"yellow",ward:"cyan",watchtower:"green",archerTower:"redBright"};function jn(e){let t=[];return e.gold>0&&t.push(`${O.gold}${e.gold}`),e.wood>0&&t.push(`${O.wood}${e.wood}`),e.stone>0&&t.push(`${O.stone}${e.stone}`),t.join(" ")}function us({selected:e}){return Ro(cs,{flexDirection:"column",children:[qn(Xo,{bold:!0,children:"Structures [ ]"}),ut.map((t,o)=>{let s=t===e,r=ot[t][1];return Ro(cs,{children:[Ro(Xo,{color:s?Yn[t]:void 0,bold:s,dimColor:!s,children:[s?"\u25B8":" "," ",o+1," ",ge[t]," ",tt[t]]}),s&&Ro(Xo,{dimColor:!0,children:[" ",jn(r)]})]},t)})]})}import{useState as zn}from"react";import{Box as So,Text as mt,useInput as Zn}from"ink";import{jsx as At,jsxs as Ht}from"react/jsx-runtime";var Qo=[{name:"Lord Ironhelm",seed:"friend-ironhelm-42",difficulty:1,tagline:"A minor lord with modest fortifications"},{name:"Lady Ashwood",seed:"friend-ashwood-99",difficulty:2,tagline:"Her archers never miss \u2014 or so she claims"},{name:"Baron Stonewatch",seed:"friend-stonewatch-256",difficulty:3,tagline:"Walls thick as legends and twice as old"}];function ps({onSelectFriend:e,onBack:t}){let[o,s]=zn(0);return Zn((r,a)=>{if(r==="k"||r==="w"||a.upArrow)s(T=>Math.max(0,T-1));else if(r==="j"||r==="s"||a.downArrow)s(T=>Math.min(Qo.length-1,T+1));else if(a.return){let T=Qo[o],p=yo(T.seed,T.difficulty);p.name=`${T.name}'s Keep`,p.ownerPlayerId=T.name.toLowerCase(),e(p)}else(r==="q"||a.escape)&&t()}),Ht(So,{flexDirection:"column",padding:1,children:[At(So,{children:At(mt,{bold:!0,color:"cyan",children:"\u2694 Rival Keeps"})}),At(mt,{children:" "}),At(mt,{dimColor:!0,children:" Choose a rival lord's keep to raid for plunder."}),At(mt,{children:" "}),Qo.map((r,a)=>Ht(So,{flexDirection:"column",children:[Ht(So,{children:[Ht(mt,{color:a===o?"yellow":void 0,bold:a===o,children:[a===o?" \u25B8 ":" ",r.name]}),Ht(mt,{dimColor:!0,children:[" Lv.",r.difficulty]})]}),a===o&&Ht(mt,{dimColor:!0,children:[' "',r.tagline,'"']})]},r.name)),At(mt,{children:" "}),At(mt,{dimColor:!0,children:" \u2191\u2193 navigate Enter raid Esc back"})]})}import{useState as fs}from"react";import{Box as Qt,Text as Se,useInput as Jn}from"ink";import{Fragment as ta,jsx as Ne,jsxs as Xe}from"react/jsx-runtime";function Xn(e){let t=[];return e.gold>0&&t.push(`+${e.gold}${O.gold}`),e.wood>0&&t.push(`+${e.wood}${O.wood}`),e.stone>0&&t.push(`+${e.stone}${O.stone}`),t.join(" ")}function Qn(e){let t=[];return e.gold>0&&t.push(`-${e.gold}${O.gold}`),e.wood>0&&t.push(`-${e.wood}${O.wood}`),e.stone>0&&t.push(`-${e.stone}${O.stone}`),t.join(" ")}function ms({gameSave:e,onBack:t,onWatchReplay:o}){let[s,r]=fs("raids"),[a,T]=fs(0),p=[...e.raidHistory].reverse().slice(0,15);Jn((l,x)=>{if(l==="q"||x.escape){t();return}(x.tab||l==="t")&&r(b=>b==="raids"?"achievements":"raids"),s==="raids"&&p.length>0&&((l==="j"||x.downArrow)&&T(b=>Math.min(b+1,p.length-1)),(l==="k"||x.upArrow)&&T(b=>Math.max(b-1,0)),(x.return||l==="v")&&p[a]&&o&&o(p[a]))});let d=e.progression;return Xe(Qt,{flexDirection:"column",padding:1,children:[Xe(Qt,{gap:2,children:[Ne(Se,{bold:!0,color:s==="raids"?"yellow":void 0,underline:s==="raids",children:"Raid Log"}),Ne(Se,{bold:!0,color:s==="achievements"?"yellow":void 0,underline:s==="achievements",children:"Achievements"}),Ne(Se,{dimColor:!0,children:"(Tab to switch)"})]}),Ne(Se,{children:" "}),s==="raids"&&Xe(Qt,{flexDirection:"column",children:[Xe(Se,{dimColor:!0,children:[" Total: ",d.totalRaidsWon,"W / ",d.totalRaidsLost,"L \xB7 Streak: ",d.currentWinStreak," (best ",d.bestWinStreak,")"]}),Ne(Se,{children:" "}),p.length===0?Ne(Se,{dimColor:!0,children:" No raids yet. Try Defend Keep or Attack NPC!"}):p.map((l,x)=>{let b=ea(l.resolvedAtUnixMs),R=l.attackerId!==e.player.id,f=l.outcome==="defense_win"?R?"\u2713":"\u2717":R?"\u2717":"\u2713",m=(R?l.outcome==="defense_win":l.outcome!=="defense_win")?"green":"red",C=R?"DEF":"ATK",u=l.lootLost.gold+l.lootLost.wood+l.lootLost.stone>0?` ${Qn(l.lootLost)}`:"",v=l.lootGained.gold+l.lootGained.wood+l.lootGained.stone>0?` ${Xn(l.lootGained)}`:"",U=x===a?">":" ";return Xe(Se,{dimColor:x>4&&x!==a,bold:x===a,children:[U," ",Ne(Se,{color:m,children:f})," ",C," ",l.outcome.replace("_"," "),u,v," ",Xe(Se,{dimColor:!0,children:["(",b,")"]})]},l.id)}),p.length>0&&Xe(ta,{children:[Ne(Se,{children:" "}),Ne(Se,{dimColor:!0,children:" j/k navigate \xB7 Enter/v watch replay"})]})]}),s==="achievements"&&Xe(Qt,{flexDirection:"column",children:[Xe(Se,{dimColor:!0,children:[" ",d.achievements?.length||0," / ",zt.length," earned"]}),Ne(Se,{children:" "}),zt.map(l=>{let x=d.achievements?.includes(l.id);return Ne(Qt,{children:Xe(Se,{color:x?"green":void 0,dimColor:!x,children:[" ",x?"\u2605":"\u2606"," ",Ne(Se,{bold:x,children:l.name})," \u2014 ",l.desc,l.bonus&&x?Xe(Se,{color:"yellow",children:[" (",l.bonus,")"]}):null]})},l.id)})]}),Ne(Se,{children:" "}),Ne(Se,{dimColor:!0,children:" Tab switch \xB7 Esc/q back"})]})}function ea(e){let t=Date.now()-e;return t<6e4?"just now":t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}import{useState as eo}from"react";import{Box as to,Text as le,useInput as aa}from"ink";import{exec as ia}from"child_process";import{writeFileSync as oa,mkdirSync as ra,existsSync as sa}from"fs";import{join as gs}from"path";import{homedir as na}from"os";var er=gs(na(),".config","codekeep","crashes");function hs(e,t){if((e instanceof Error?e.message:String(e)).includes("Raw mode is not supported"))return"";sa(er)||ra(er,{recursive:!0});let s={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},r=`crash-${Date.now()}.json`,a=gs(er,r);return oa(a,JSON.stringify(s,null,2),"utf-8"),a}function bo(e){let t=encodeURIComponent(`[Crash]: ${e.error.slice(0,80)}`),o=encodeURIComponent(`## Crash Report
|
|
11
11
|
|
|
12
12
|
**Error:** ${e.error}
|
|
13
13
|
|
|
@@ -22,20 +22,16 @@ ${e.stack??"N/A"}
|
|
|
22
22
|
- OS: ${e.platform} ${e.arch}
|
|
23
23
|
- Screen: ${e.screen??"unknown"}
|
|
24
24
|
- Time: ${e.timestamp}
|
|
25
|
-
`);return`https://github.com/tooyipjee/codekeep/issues/new?title=${t}&body=${o}&labels=bug,crash`}import{Fragment as ia,jsx as Ne,jsxs as mt}from"react/jsx-runtime";function Ts({onBack:e,onResetGame:t,onReplayTutorial:o,asciiMode:s,onToggleAscii:r}){let[a,T]=So(0),[f,d]=So(!1),[l,x]=So(!1),[k,R]=So(""),m=[{key:"ascii",label:`ASCII Mode: ${s?"ON":"OFF"}`,desc:"Use plain ASCII borders (for basic terminals)"},{key:"tutorial",label:"Replay Tutorial",desc:"Learn how to play again"},{key:"report",label:"Report Bug",desc:"Open a GitHub issue for bugs"},{key:"reset",label:"Reset Game",desc:"Delete save and start over"},{key:"back",label:"Back",desc:"Return to menu"}];return aa((p,b)=>{if(l){if(b.escape){x(!1),R("");return}if(b.return&&k.length>0){let c={timestamp:new Date().toISOString(),error:k,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch,screen:"settings"},$=Ro(c);process.stderr.write(`
|
|
26
|
-
Open this URL to report the bug:
|
|
27
|
-
${$}
|
|
28
|
-
|
|
29
|
-
`),x(!1),R("");return}if(b.backspace||b.delete){R(c=>c.slice(0,-1));return}p&&p.length===1&&R(c=>c+p);return}if(f){if(p==="y"||p==="Y"){t();return}d(!1);return}if(p==="q"||b.escape){e();return}if((p==="j"||p==="s"||b.downArrow)&&T(c=>Math.min(c+1,m.length-1)),(p==="k"||p==="w"||b.upArrow)&&T(c=>Math.max(c-1,0)),b.return){let c=m[a];c.key==="ascii"?r():c.key==="tutorial"?o():c.key==="report"?x(!0):c.key==="reset"?d(!0):c.key==="back"&&e()}}),mt(bo,{flexDirection:"column",padding:1,children:[Ne(be,{bold:!0,color:"yellow",children:"\u25C6 Settings"}),Ne(be,{children:" "}),l?mt(bo,{flexDirection:"column",children:[Ne(be,{bold:!0,color:"cyan",children:"Report a Bug"}),Ne(be,{children:"Describe the issue briefly:"}),mt(be,{color:"cyan",children:[k,Ne(be,{dimColor:!0,children:"_"})]}),Ne(be,{children:" "}),Ne(be,{dimColor:!0,children:"Enter to generate GitHub issue URL \xB7 Esc cancel"})]}):f?mt(bo,{flexDirection:"column",children:[Ne(be,{bold:!0,color:"red",children:"Are you sure you want to reset?"}),Ne(be,{children:"This will permanently delete your save file."}),Ne(be,{children:"All structures, resources, and achievements will be lost."}),Ne(be,{children:" "}),mt(be,{bold:!0,children:["Press ",Ne(be,{color:"red",children:"Y"})," to confirm, any other key to cancel"]})]}):mt(ia,{children:[m.map((p,b)=>mt(bo,{children:[mt(be,{color:b===a?"yellow":void 0,bold:b===a,children:[b===a?" \u25B8 ":" ",p.label]}),mt(be,{dimColor:!0,children:[" ",p.desc]})]},p.key)),Ne(be,{children:" "}),Ne(be,{dimColor:!0,children:" \u2191\u2193 navigate \xB7 Enter select \xB7 Esc back"})]})]})}import la from"react";import{Box as da,Text as Ee}from"ink";import{Fragment as xs,jsx as Ge,jsxs as Ct}from"react/jsx-runtime";var ko=class extends la.Component{constructor(t){super(t),this.state={error:null,crashFilePath:null,issueUrl:null}}static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t){try{let o=hs(t),s={timestamp:new Date().toISOString(),error:t.message,stack:t.stack,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch},r=Ro(s);this.setState({crashFilePath:o,issueUrl:r})}catch{}}render(){return this.state.error?Ct(da,{flexDirection:"column",padding:1,children:[Ge(Ee,{bold:!0,color:"red",children:"\u25C6 CodeKeep \u2014 Something went wrong"}),Ge(Ee,{children:" "}),Ge(Ee,{color:"red",children:this.state.error.message}),Ge(Ee,{children:" "}),Ge(Ee,{dimColor:!0,children:"Your save file is at:"}),Ct(Ee,{children:[" ","~/.config/codekeep/game.json"]}),Ge(Ee,{children:" "}),this.state.crashFilePath&&Ct(xs,{children:[Ge(Ee,{dimColor:!0,children:"Crash report saved to:"}),Ct(Ee,{children:[" ",this.state.crashFilePath]}),Ge(Ee,{children:" "})]}),this.state.issueUrl&&Ct(xs,{children:[Ge(Ee,{dimColor:!0,children:"Report this bug:"}),Ct(Ee,{color:"cyan",children:[" ",this.state.issueUrl]}),Ge(Ee,{children:" "})]}),Ge(Ee,{dimColor:!0,children:"If the game won't start, try:"}),Ct(Ee,{bold:!0,children:[" ","codekeep --tutorial"]}),Ge(Ee,{children:" "}),Ge(Ee,{dimColor:!0,children:"Press Ctrl+C to exit"})]}):this.props.children}};import{useState as ca}from"react";import{Box as vo,Text as Qe,useInput as ua}from"ink";import{jsx as yt,jsxs as _t}from"react/jsx-runtime";var pa={copper:"white",iron:"gray",silver:"whiteBright",gold:"yellow",diamond:"cyan"},fa={copper:"\u25CB",iron:"\u25CF",silver:"\u25C6",gold:"\u2605",diamond:"\u25C8"};function ys({pvpProfile:e,targets:t,isSearching:o,onSearch:s,onAttack:r,onWarCamp:a,onLeaderboard:T,onBack:f}){let[d,l]=ca(0),x=[{key:"search",label:"Find Opponent"},{key:"warcamp",label:"War Camp"},{key:"leaderboard",label:"Leaderboard"},...t.map((p,b)=>({key:`target-${b}`,label:`Attack ${p.displayName} (${p.trophies} trophies)`})),{key:"back",label:"Back"}];ua((p,b)=>{if(b.upArrow||p==="k")l(c=>Math.max(0,c-1));else if(b.downArrow||p==="j")l(c=>Math.min(x.length-1,c+1));else if(b.return){let c=x[d];if(c.key==="search")s();else if(c.key==="warcamp")a();else if(c.key==="leaderboard")T();else if(c.key==="back")f();else if(c.key.startsWith("target-")){let $=parseInt(c.key.split("-")[1]),z=t[$];z&&r(z)}}else(b.escape||p==="q")&&f()});let k=e?.league??"copper",R=pa[k]??"white",m=fa[k]??"\u25CB";return _t(vo,{flexDirection:"column",padding:1,children:[yt(Qe,{bold:!0,color:"red",children:"\u2694 PVP ARENA"}),yt(Qe,{children:" "}),e&&_t(vo,{flexDirection:"column",children:[_t(vo,{flexDirection:"row",gap:2,children:[_t(Qe,{color:R,bold:!0,children:[m," ",k.toUpperCase()," League"]}),_t(Qe,{children:["Trophies: ",yt(Qe,{bold:!0,color:"yellow",children:e.trophies})]})]}),e.shieldExpiresAt&&e.shieldExpiresAt>Date.now()&&_t(Qe,{color:"cyan",children:["Shield active (",Math.ceil((e.shieldExpiresAt-Date.now())/36e5),"h remaining)"]}),yt(Qe,{children:" "})]}),o&&yt(Qe,{color:"yellow",children:"Searching for opponents..."}),x.map((p,b)=>yt(vo,{children:_t(Qe,{color:b===d?"yellow":void 0,bold:b===d,children:[b===d?" \u25B8 ":" ",p.label]})},p.key)),yt(Qe,{children:" "}),yt(Qe,{dimColor:!0,children:"\u2191\u2193 select Enter choose Esc back"})]})}import{useState as Eo,useEffect as ma}from"react";import{Box as eo,Text as We,useInput as ga}from"ink";import{jsx as gt,jsxs as ht}from"react/jsx-runtime";var tr=[{type:"raider",name:"Raider",symbol:"R"},{type:"scout",name:"Scout",symbol:"S"},{type:"brute",name:"Brute",symbol:"B"}];function ws(e){let t=Math.ceil(e/1e3),o=Math.floor(t/60),s=t%60;return`${o}:${String(s).padStart(2,"0")}`}function Rs({warCamp:e,resources:t,onTrain:o,onBack:s}){let[r,a]=Eo(0),[T,f]=Eo(!1),[d,l]=Eo(0),[x,k]=Eo(Date.now());return ma(()=>{let R=setInterval(()=>k(Date.now()),1e3);return()=>clearInterval(R)},[]),ga((R,m)=>{if(T){m.upArrow||R==="k"?l(p=>Math.max(0,p-1)):m.downArrow||R==="j"?l(p=>Math.min(tr.length-1,p+1)):m.return?(o(r,tr[d].type),f(!1)):m.escape&&f(!1);return}m.upArrow||R==="k"?a(p=>Math.max(0,p-1)):m.downArrow||R==="j"?a(p=>Math.min(e.maxSlots-1,p+1)):m.return?e.slots[r]?.raiderType||f(!0):(m.escape||R==="q")&&s()}),ht(eo,{flexDirection:"column",padding:1,children:[gt(We,{bold:!0,color:"red",children:"\u2694 WAR CAMP"}),gt(We,{dimColor:!0,children:"Train raiders for PvP attacks"}),gt(We,{children:" "}),ht(eo,{flexDirection:"row",gap:2,children:[ht(We,{children:["Gold: ",t.gold,B.gold]}),ht(We,{children:["Wood: ",t.wood,B.wood]}),ht(We,{children:["Stone: ",t.stone,B.stone]})]}),gt(We,{children:" "}),ht(We,{bold:!0,children:["Slots (",e.slots.filter(R=>R.raiderType).length,"/",e.maxSlots,")"]}),Array.from({length:e.maxSlots},(R,m)=>{let p=e.slots[m],b=r===m,c=p?.readyAtMs?p.readyAtMs<=x:!1,$=p?.readyAtMs?p.readyAtMs>x:!1,z=$&&p?.readyAtMs?p.readyAtMs-x:0,U;return p?.raiderType?$?U=`${p.raiderType} training... ${ws(z)}`:c?U=`${p.raiderType} READY!`:U=`${p.raiderType}`:U="[ Empty ]",gt(eo,{children:ht(We,{color:b?"yellow":void 0,bold:b,children:[b?" \u25B8 ":" ","Slot ",m+1,": ",U]})},m)}),T&&ht(eo,{flexDirection:"column",marginTop:1,children:[gt(We,{bold:!0,color:"cyan",children:"Select raider type:"}),tr.map((R,m)=>{let p=go[R.type],b=Ar[R.type],c=Le[R.type],$=t.gold>=p.gold&&t.wood>=p.wood&&t.stone>=p.stone;return gt(eo,{children:ht(We,{color:d===m?"yellow":$?void 0:"red",bold:d===m,children:[d===m?" \u25B8 ":" ",R.name," \u2014 HP:",c.hp," DMG:",c.damage," SPD:",c.speed," \u2014 ",p.gold,"g ",p.wood,"w ",p.stone,"s \u2014 ",ws(b)]})},R.type)})]}),gt(We,{children:" "}),gt(We,{dimColor:!0,children:"\u2191\u2193 select Enter train Esc back"})]})}import{Box as Ao,Text as Be,useInput as ha}from"ink";import{jsx as Ke,jsxs as to}from"react/jsx-runtime";var Ta={copper:"white",iron:"gray",silver:"whiteBright",gold:"yellow",diamond:"cyan"};function Ss({entries:e,currentPlayerId:t,isLoading:o,onBack:s}){return ha((r,a)=>{(a.escape||r==="q")&&s()}),to(Ao,{flexDirection:"column",padding:1,children:[Ke(Be,{bold:!0,color:"yellow",children:"\u{1F3C6} LEADERBOARD"}),Ke(Be,{children:" "}),o&&Ke(Be,{color:"yellow",children:"Loading..."}),!o&&e.length===0&&Ke(Be,{dimColor:!0,children:"No players on the leaderboard yet."}),!o&&e.length>0&&to(Ao,{flexDirection:"column",children:[to(Ao,{children:[Ke(Be,{bold:!0,children:" # "}),Ke(Be,{bold:!0,children:"Name "}),Ke(Be,{bold:!0,children:"League "}),Ke(Be,{bold:!0,children:"Trophies"})]}),e.map(r=>{let a=r.id===t,T=a?"green":void 0,f=Ta[r.league]??"white";return to(Ao,{children:[to(Be,{color:T,bold:a,children:[String(r.rank).padStart(3," ")," "]}),Ke(Be,{color:T,bold:a,children:(r.displayName||"Unknown").padEnd(20," ")}),Ke(Be,{color:f,children:r.league.padEnd(10," ")}),Ke(Be,{color:"yellow",bold:!0,children:r.trophies})]},r.id)})]}),Ke(Be,{children:" "}),Ke(Be,{dimColor:!0,children:"Esc/q back"})]})}import{useState as or}from"react";import{Box as Co,Text as Ae,useInput as xa}from"ink";import{Fragment as ya,jsx as De,jsxs as Mt}from"react/jsx-runtime";function bs({onLogin:e,onRegister:t,onPlayOffline:o,error:s,isLoading:r}){let[a,T]=or("menu"),[f,d]=or(""),[l,x]=or(0),k=[{key:"register",label:"Register (new player)"},{key:"login",label:"Login (with API key)"},{key:"offline",label:"Play Offline"}];return xa((R,m)=>{if(!r){if(a==="menu"){if(m.upArrow||R==="k")x(p=>Math.max(0,p-1));else if(m.downArrow||R==="j")x(p=>Math.min(k.length-1,p+1));else if(m.return){let p=k[l];p.key==="register"?T("register"):p.key==="login"?T("login"):p.key==="offline"&&o()}return}if(m.escape){T("menu"),d("");return}if(m.return&&f.length>0){a==="login"?e(f):a==="register"&&t(f),d("");return}if(m.backspace||m.delete){d(p=>p.slice(0,-1));return}R&&R.length===1&&d(p=>p+R)}}),Mt(Co,{flexDirection:"column",padding:1,children:[De(Ae,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep Online"}),De(Ae,{children:" "}),s&&De(Ae,{color:"red",children:s}),r&&De(Ae,{color:"yellow",children:"Connecting..."}),a==="menu"&&!r&&Mt(ya,{children:[k.map((R,m)=>De(Co,{children:Mt(Ae,{color:m===l?"yellow":void 0,bold:m===l,children:[m===l?" \u25B8 ":" ",R.label]})},R.key)),De(Ae,{children:" "}),De(Ae,{dimColor:!0,children:"\u2191\u2193 navigate Enter select"})]}),a==="login"&&!r&&Mt(Co,{flexDirection:"column",children:[De(Ae,{children:"Enter your API key:"}),Mt(Ae,{color:"cyan",children:[f,De(Ae,{dimColor:!0,children:"_"})]}),De(Ae,{children:" "}),De(Ae,{dimColor:!0,children:"Enter to submit Esc back"})]}),a==="register"&&!r&&Mt(Co,{flexDirection:"column",children:[De(Ae,{children:"Choose a display name:"}),Mt(Ae,{color:"cyan",children:[f,De(Ae,{dimColor:!0,children:"_"})]}),De(Ae,{children:" "}),De(Ae,{dimColor:!0,children:"Enter to submit Esc back"})]})]})}import{useState as It}from"react";import{Box as Ye,Text as j,useInput as wa}from"ink";import{jsx as ee,jsxs as ke}from"react/jsx-runtime";var ks={N:"North",S:"South",E:"East",W:"West"},Ft={raider:"R",scout:"S",brute:"B"},_o={raider:"red",scout:"cyan",brute:"magenta"},vs=8;function Es(e,t){switch(e){case"N":return{x:t,y:0};case"S":return{x:t,y:I-1};case"E":return{x:I-1,y:t};case"W":return{x:0,y:t}}}function As({targetName:e,targetTrophies:t,targetGrid:o,warCamp:s,resources:r,onLaunch:a,onBack:T}){let[f,d]=It("scout"),[l,x]=It([]),[k,R]=It(0),[m,p]=It([]),[b,c]=It("N"),[$,z]=It(Math.floor(I/2)),[U,u]=It(0),K=["raider","scout","brute"];wa((y,h)=>{if(h.escape||y==="q"){if(f==="scout"){T();return}if(f==="army"){d("scout");return}if(f==="deploy"){d("army");return}if(f==="confirm"){d("deploy");return}return}if(f==="scout"){h.return&&d("army");return}if(f==="army"){h.upArrow||y==="k"?R(E=>Math.max(0,E-1)):h.downArrow||y==="j"?R(E=>Math.min(K.length-1,E+1)):h.return?l.length<vs&&x(E=>[...E,K[k]]):h.backspace||h.delete?x(E=>E.slice(0,-1)):(y==="n"||h.tab)&&l.length>0&&(p(l.map(()=>({edge:"N",offset:Math.floor(I/2)}))),u(0),d("deploy"));return}if(f==="deploy"){h.leftArrow||y==="a"?z(b==="E"||b==="W"?E=>Math.max(0,E-1):E=>Math.max(0,E-1)):h.rightArrow||y==="d"?z(E=>Math.min(I-1,E+1)):h.upArrow||y==="w"?z(E=>Math.max(0,E-1)):h.downArrow||y==="s"?z(E=>Math.min(I-1,E+1)):y==="n"?c("N"):y==="e"?c("E"):y==="z"?c("S"):y==="x"?c("W"):h.return?(p(E=>{let C=[...E];return C[U]={edge:b,offset:$},C}),U<l.length-1?u(E=>E+1):d("confirm")):h.tab&&u(E=>(E+1)%l.length);return}if(f==="confirm"){if(h.return||y==="y"){let E=l.map((C,N)=>({raiderType:C,edge:m[N]?.edge??"N",offset:m[N]?.offset??8,waveDelay:0}));a(E)}return}});let H=(()=>{let y=Array.from({length:I},()=>Array(I).fill("\xB7"));for(let C of o.structures)C.kind==="trap"||C.kind==="ward"?y[C.pos.y][C.pos.x]="?":y[C.pos.y][C.pos.x]=fe[C.kind];if(f==="deploy"||f==="confirm"){for(let C=0;C<m.length;C++){let N=m[C];if(!N)continue;let A=Es(N.edge,N.offset);y[A.y][A.x]=Ft[l[C]]}if(f==="deploy"){let C=Es(b,$);y[C.y][C.x]="\u25BC"}}let h=" "+Array.from({length:I},(C,N)=>N.toString(16).toUpperCase()).join(" "),E=y.map((C,N)=>{let A=N.toString(16).toUpperCase(),O=C.join(" ");return`${A} ${O}`});return[h,...E]})();return ke(Ye,{flexDirection:"column",padding:1,children:[ee(j,{bold:!0,color:"red",children:"\u2694 RAID PLANNER"}),ke(Ye,{gap:2,children:[ke(j,{children:["Target: ",ee(j,{bold:!0,color:"yellow",children:e})]}),ke(j,{children:["Trophies: ",ee(j,{bold:!0,children:t})]})]}),ee(j,{children:" "}),ee(Ye,{flexDirection:"column",children:H.map((y,h)=>h===0?ee(j,{dimColor:!0,children:y},h):ee(j,{children:y},h))}),ee(j,{children:" "}),ee(Ye,{gap:2,children:ee(j,{dimColor:!0,children:"# wall $ treasury ^ tower ! archer & vault ? hidden"})}),ee(j,{children:" "}),f==="scout"&&ke(Ye,{flexDirection:"column",children:[ee(j,{bold:!0,color:"cyan",children:"SCOUT PHASE"}),ee(j,{children:"Study the enemy keep. Traps and wards are hidden (?)."}),ke(j,{children:["Structures: ",o.structures.length," placed"]}),ee(j,{children:" "}),ee(j,{dimColor:!0,children:"Enter \u2192 pick your army Esc \u2192 back"})]}),f==="army"&&ke(Ye,{flexDirection:"column",children:[ke(j,{bold:!0,color:"cyan",children:["ARMY SELECTION (",l.length,"/",vs,")"]}),ee(j,{children:" "}),K.map((y,h)=>{let E=Le[y],C=go[y];return ee(Ye,{children:ke(j,{color:k===h?"yellow":void 0,bold:k===h,children:[k===h?" \u25B8 ":" ",ee(j,{color:_o[y],children:Ft[y]})," ",y.charAt(0).toUpperCase()+y.slice(1)," ","HP:",E.hp," DMG:",E.damage," SPD:",E.speed," ","(",C.gold,"g ",C.wood,"w ",C.stone,"s)"]})},y)}),ee(j,{children:" "}),ke(Ye,{gap:1,children:[ee(j,{children:"Army: "}),l.length===0?ee(j,{dimColor:!0,children:"(empty)"}):l.map((y,h)=>ee(j,{color:_o[y],bold:!0,children:Ft[y]},h))]}),ee(j,{children:" "}),ee(j,{dimColor:!0,children:"Enter add Backspace remove Tab/N \u2192 deploy Esc back"})]}),f==="deploy"&&ke(Ye,{flexDirection:"column",children:[ke(j,{bold:!0,color:"cyan",children:["DEPLOY \u2014 Placing ",Ft[l[U]]," (",l[U],") [",U+1,"/",l.length,"]"]}),ke(j,{children:["Edge: ",ee(j,{bold:!0,color:"yellow",children:ks[b]})," Position: ",ee(j,{bold:!0,children:$.toString(16).toUpperCase()})]}),ee(j,{children:" "}),ke(Ye,{gap:1,children:[ee(j,{children:"Spawns: "}),l.map((y,h)=>{let E=m[h],C=h<U||h===U&&!1;return ke(j,{color:h===U?"yellow":C?_o[y]:"gray",children:[Ft[y],E?`@${E.edge}${E.offset.toString(16)}`:"",h<l.length-1?" ":""]},h)})]}),ee(j,{children:" "}),ee(j,{dimColor:!0,children:"\u2190\u2192 / \u2191\u2193 position N/E/Z/X edge Enter place Tab next Esc back"})]}),f==="confirm"&&ke(Ye,{flexDirection:"column",children:[ee(j,{bold:!0,color:"green",children:"READY TO ATTACK"}),ee(j,{children:" "}),ee(Ye,{flexDirection:"column",children:l.map((y,h)=>{let E=m[h];return ke(j,{children:[ee(j,{color:_o[y],bold:!0,children:Ft[y]})," ",y," \u2192 ",E?`${ks[E.edge]} edge, pos ${E.offset.toString(16).toUpperCase()}`:"unplaced"]},h)})}),ee(j,{children:" "}),ke(j,{bold:!0,children:["Press ",ee(j,{color:"green",children:"Enter"})," to launch raid or ",ee(j,{color:"red",children:"Esc"})," to adjust"]})]})]})}import{useState as st,useEffect as so,useCallback as ge,useRef as Dt}from"react";import{useEffect as ba,useRef as _s,useCallback as ka}from"react";import{existsSync as Ms,statSync as Is,openSync as va,readSync as Ea,closeSync as Aa,watch as Ca}from"fs";import{existsSync as ud,mkdirSync as pd,readFileSync as fd,writeFileSync as md,unlinkSync as gd}from"fs";import{join as Cs}from"path";import{homedir as Ra}from"os";var Sa=Cs(Ra(),".config","codekeep"),Pt=Cs(Sa,"events.jsonl");var _a=2e3;function Ps(e){let t=_s(0),o=_s(e);o.current=e;let s=ka(()=>{if(!Ms(Pt))return;let r;try{r=Is(Pt).size}catch{return}if(r<t.current&&(t.current=0),r<=t.current)return;let a,T;try{let d=Buffer.alloc(r-t.current);T=va(Pt,"r"),Ea(T,d,0,d.length,t.current),a=d.toString("utf-8")}catch{return}finally{T!==void 0&&Aa(T)}t.current=r;let f=a.split(`
|
|
30
|
-
`).filter(Boolean);for(let d of f)try{let l=JSON.parse(d);o.current(l)}catch{}},[]);ba(()=>{if(Ms(Pt))try{t.current=Is(Pt).size}catch{t.current=0}let r=null;try{r=Ca(Pt,()=>s())}catch{}let a=setInterval(s,_a);return()=>{clearInterval(a),r?.close()}},[s])}function oo(e){return e<=2?1:e<=5?2:e<=9?3:e<=14?4:5}function ro(e,t,o){let s=[];for(let r=0;r<e;r++)t>=3&&o()<.2?s.push("brute"):t>=2&&o()<.3?s.push("scout"):s.push("raider");return s}function Vt(e){let t=e|0;return()=>{t=t+1831565813|0;let o=Math.imul(t^t>>>15,1|t);return o=o+Math.imul(o^o>>>7,61|o)^o,((o^o>>>14)>>>0)/4294967296}}function Ds(e){let t=e.progression;return{...e,progression:{...t,totalRaidsWon:t.totalRaidsWon??0,totalRaidsLost:t.totalRaidsLost??0,totalStructuresPlaced:t.totalStructuresPlaced??0,currentWinStreak:t.currentWinStreak??0,bestWinStreak:t.bestWinStreak??0,achievements:t.achievements??[],totalRaidersKilledByArcher:t.totalRaidersKilledByArcher??t.totalProbesKilledByScanner??0}}}var Ma={first_structure:{gold:20,wood:0,stone:0},defense_win_5:{gold:25,wood:15,stone:15},win_streak_3:{gold:10,wood:10,stone:10},win_streak_5:{gold:30,wood:30,stone:30},all_types:{gold:15,wood:15,stone:15},max_level:{gold:20,wood:0,stone:10},archer_kills_10:{gold:20,wood:10,stone:10},structures_20:{gold:10,wood:10,stone:10},raids_10:{gold:15,wood:15,stone:15},hoarder:{gold:25,wood:0,stone:0}};function rr(e){return Ma[e]??null}function sr(e){let t=e.progression,o=new Set(t.achievements),s=[],r=(T,f)=>{!o.has(T)&&f&&s.push(T)};r("first_structure",t.totalStructuresPlaced>=1),r("defense_win_5",t.totalRaidsWon>=5),r("win_streak_3",t.bestWinStreak>=3),r("win_streak_5",t.bestWinStreak>=5),r("structures_20",t.totalStructuresPlaced>=20),r("raids_10",t.totalRaidsWon+t.totalRaidsLost>=10),r("archer_kills_10",t.totalRaidersKilledByArcher>=10),r("hoarder",e.keep.resources.gold+e.keep.resources.wood+e.keep.resources.stone>=500);let a=new Set(e.keep.grid.structures.map(T=>T.kind));return r("all_types",a.size>=ct.length),r("max_level",e.keep.grid.structures.some(T=>T.level===3)),s}function Ls(e,t){if(t<=Ut)return e;let o=Math.pow(Rr,Math.floor((t-Ut)/Ut)+1);return{gold:Math.max(1,Math.floor(e.gold*o)),wood:Math.max(1,Math.floor(e.wood*o)),stone:Math.max(1,Math.floor(e.stone*o))}}var $s=5e3,nr=6e4;function Ia(e,t){if(e.keep.grid.structures.length<3)return{save:e,results:[]};let o=Math.min(Math.floor(t/qt),wr);if(o===0)return{save:e,results:[]};let s=[],r=e;for(let a=0;a<o;a++){let T=r.progression.totalRaidsWon+r.progression.totalRaidsLost,f=oo(T),d=`bg-${r.lastPlayedAtUnixMs}-${a}`,l=3+f,x=Vt(r.lastPlayedAtUnixMs+a),k=ro(l,f,x),R=ut({probeCount:l,keepGrid:r.keep.grid,seed:d,probeTypes:k}),m=R.events[R.events.length-1];if(m?.type!=="raid_end")continue;let p=m.outcome==="defense_win",b=R.events.filter(w=>w.type==="treasury_breach").reduce((w,H)=>({gold:w.gold+H.lootTaken.gold,wood:w.wood+H.lootTaken.wood,stone:w.stone+H.lootTaken.stone}),{gold:0,wood:0,stone:0}),c=R.events.filter(w=>w.type==="arrow_hit"&&w.hpRemaining<=0).length,$=p?{gold:10+f*3,wood:5+f*2,stone:5+f*2}:{gold:0,wood:0,stone:0},z={gold:Math.max(0,r.keep.resources.gold-b.gold),wood:Math.max(0,r.keep.resources.wood-b.wood),stone:Math.max(0,r.keep.resources.stone-b.stone)},U=rt(Oe(z,$)),u=p?r.progression.currentWinStreak+1:0,K={id:d,seed:d,rulesVersion:1,attackerId:"npc-bg",defenderKeepId:r.keep.id,startedAtUnixMs:r.lastPlayedAtUnixMs+a*qt,resolvedAtUnixMs:r.lastPlayedAtUnixMs+(a+1)*qt,outcome:m.outcome,lootLost:b,lootGained:$,replay:R};r={...r,keep:{...r.keep,resources:U},raidHistory:[...r.raidHistory.slice(-19),K],progression:{...r.progression,totalRaidsWon:r.progression.totalRaidsWon+(p?1:0),totalRaidsLost:r.progression.totalRaidsLost+(p?0:1),currentWinStreak:u,bestWinStreak:Math.max(r.progression.bestWinStreak,u),totalRaidersKilledByArcher:r.progression.totalRaidersKilledByArcher+c}},s.push({won:p,lootLost:b,defenseBonus:$,difficulty:f})}return{save:r,results:s}}function Ns(e,t){let[o,s]=st(null),[r,a]=st({x:8,y:8}),[T,f]=st(0),[d,l]=st(""),[x,k]=st(null),[R,m]=st(null),[p,b]=st(null),[c,$]=st(null),[z,U]=st(null),[u,K]=st([]),w=Dt(null),H=Dt(0),y=Dt(0),h=Dt(null),E=Dt(null),C=Dt(null),N=Dt(null),A=ct[T];Ps(g=>{s(S=>{if(!S)return S;let M=Wo(S.keep,g),V={...S,keep:M,lastPlayedAtUnixMs:Date.now()};return t||Et(V),V})}),so(()=>{let g=Gt();g||(g=Jr(process.env.USER||process.env.USERNAME||"Keeper")),g=Ds(g),g.lastPlayedAtUnixMs||(g={...g,lastPlayedAtUnixMs:g.savedAtUnixMs||Date.now()});let S=Date.now()-g.lastPlayedAtUnixMs,M={gold:0,wood:0,stone:0},V=[];if(S>nr&&g.keep.grid.structures.length>0&&(M=Ko(g.keep.grid,S),(M.gold>0||M.wood>0||M.stone>0)&&(g={...g,keep:{...g.keep,resources:rt(Oe(g.keep.resources,M))}})),S>qt){let re=Ia(g,S);g=re.save,V=re.results}let Q=sr(g);if(Q.length>0){g={...g,progression:{...g.progression,achievements:[...g.progression.achievements,...Q]}};for(let re of Q){let oe=rr(re);oe&&(g={...g,keep:{...g.keep,resources:Oe(g.keep.resources,oe)}})}}(M.gold>0||M.wood>0||M.stone>0||V.length>0||Q.length>0)&&U({resources:M,raids:V,newAchievements:Q}),g={...g,lastPlayedAtUnixMs:Date.now()},e&&(g={...g,tutorialCompleted:!1}),s(g),t||Et(g)},[]),so(()=>(h.current=setInterval(()=>{s(g=>{if(!g||g.keep.grid.structures.length===0)return g;let S=Ko(g.keep.grid,nr);if(S.gold===0&&S.wood===0&&S.stone===0)return g;let M={...g,keep:{...g.keep,resources:rt(Oe(g.keep.resources,S))},lastPlayedAtUnixMs:Date.now()};return t||Et(M),M})},nr),()=>{h.current&&clearInterval(h.current)}),[t]),so(()=>(E.current=setInterval(()=>{K(g=>{if(!o)return g;let S=Vt(Date.now()),M=zr(g,Date.now());return qr(M,o.keep.grid,Date.now(),S)})},Sr),()=>{E.current&&clearInterval(E.current)}),[o]),so(()=>{let g=N.current;if(!g||!o)return;N.current=null;let S=Ho(u,g,o.keep.grid);if(!S)return;K(S.updatedFragments);let M=S.yield,V={...o,keep:{...o.keep,resources:rt(Oe(o.keep.resources,M))}};O(V);let Q=[];M.gold>0&&Q.push(`+${M.gold}${B.gold}`),M.wood>0&&Q.push(`+${M.wood}${B.wood}`),M.stone>0&&Q.push(`+${M.stone}${B.stone}`);let he=S.collected[0]?.type.replace("_"," ")??"fragment",re=S.collected.length>1?` (${S.collected.length}x)`:"";W(`${Q.join(" ")} ${he}${re}`)},[r]),so(()=>()=>{w.current&&clearTimeout(w.current)},[]);let O=ge(g=>{let S=sr(g),M={...g,lastPlayedAtUnixMs:Date.now()};if(S.length>0){M={...M,progression:{...M.progression,achievements:[...M.progression.achievements,...S]}};for(let V of S){let Q=rr(V);Q&&(M={...M,keep:{...M.keep,resources:Oe(M.keep.resources,Q)}})}}if(s(M),t||Et(M),S.length>0){let V=S.map(Q=>zt.find(he=>he.id===Q)?.name||Q);W(`\u{1F3C6} ${V.join(", ")}!`)}},[t]),W=ge(g=>{w.current&&clearTimeout(w.current),l(g),w.current=setTimeout(()=>{l(""),w.current=null},3e3)},[]),ue=ge((g,S)=>{a(M=>{let V={x:Math.max(0,Math.min(I-1,M.x+g)),y:Math.max(0,Math.min(I-1,M.y+S))};return N.current=V,V})},[]),Se=ge(g=>{f(S=>{let M=S+g;return M<0?ct.length-1:M>=ct.length?0:M})},[]),nt=ge(g=>{g>=0&&g<ct.length&&f(g)},[]),$t=ge(()=>{if(!o)return;let g=Br(o.keep,r,A);if(g.ok&&g.keep){let S={...o,keep:g.keep,progression:{...o.progression,totalStructuresPlaced:o.progression.totalStructuresPlaced+1}};O(S),W(`Placed ${A}`)}else W(`!${g.reason}`)},[o,r,A,O,W]),jt=ge(()=>{if(!o)return;let g=Or(o.keep,r);g.ok&&g.keep?(O({...o,keep:g.keep}),W("Upgraded!")):W(`!${g.reason}`)},[o,r,O,W]),v=ge(()=>{if(!o)return;let g=Ur(o.keep,r);g.ok&&g.keep?(O({...o,keep:g.keep}),W("Demolished (50% refund)")):W(`!${g.reason}`)},[o,r,O,W]),q=ge(()=>{if(!o)return;let g=o.progression.totalRaidsWon+o.progression.totalRaidsLost,S=oo(g),M=`attack-${Date.now()}-${Math.random()}`,V=To(M,S),Q=3+S,he=Vt(Date.now()),re=ro(Q,S,he),oe=ut({probeCount:Q,keepGrid:V.grid,seed:M,probeTypes:re});k(oe),m(V.grid),b("attack");let Te=oe.events[oe.events.length-1];if(Te?.type==="raid_end"){let ie=Te.outcome!=="defense_win",et=oe.events.filter(xe=>xe.type==="treasury_breach").reduce((xe,lt)=>({gold:xe.gold+lt.lootTaken.gold,wood:xe.wood+lt.lootTaken.wood,stone:xe.stone+lt.lootTaken.stone}),{gold:0,wood:0,stone:0}),He=oe.events.filter(xe=>xe.type==="raider_destroyed").length,wt=oe.events.filter(xe=>xe.type==="wall_damaged"&&xe.destroyed).length;$({won:ie,raidType:"attack",outcome:Te.outcome,lootGained:et,lootLost:{gold:0,wood:0,stone:0},raidersKilled:He,raidersTotal:Q,wallsDestroyed:wt,archersActive:V.grid.structures.filter(xe=>xe.kind==="archerTower").length,difficulty:S});let at=ie?rt(Oe(o.keep.resources,et)):o.keep.resources,it=ie?o.progression.currentWinStreak+1:0,Rt={id:M,seed:M,rulesVersion:1,attackerId:o.player.id,defenderKeepId:V.id,startedAtUnixMs:Date.now(),resolvedAtUnixMs:Date.now(),outcome:Te.outcome,lootLost:{gold:0,wood:0,stone:0},lootGained:ie?et:{gold:0,wood:0,stone:0},replay:oe,defenderGrid:V.grid};O({...o,keep:{...o.keep,resources:at},raidHistory:[...o.raidHistory.slice(-19),Rt],progression:{...o.progression,totalRaidsWon:o.progression.totalRaidsWon+(ie?1:0),totalRaidsLost:o.progression.totalRaidsLost+(ie?0:1),currentWinStreak:it,bestWinStreak:Math.max(o.progression.bestWinStreak,it)}})}},[o,O]),Y=ge(()=>{if(!o)return;let g=o.progression.totalRaidsWon+o.progression.totalRaidsLost,S=oo(g),M=`defend-${Date.now()}-${Math.random()}`,V=3+S,Q=Vt(Date.now()),he=ro(V,S,Q),re=ut({probeCount:V,keepGrid:o.keep.grid,seed:M,probeTypes:he});k(re),m(o.keep.grid),b("defend");let oe=re.events[re.events.length-1];if(oe?.type==="raid_end"){let Te=oe.outcome==="defense_win",ie=re.events.filter(te=>te.type==="treasury_breach").reduce((te,dt)=>({gold:te.gold+dt.lootTaken.gold,wood:te.wood+dt.lootTaken.wood,stone:te.stone+dt.lootTaken.stone}),{gold:0,wood:0,stone:0}),et=re.events.filter(te=>te.type==="raider_destroyed").length,He=re.events.filter(te=>te.type==="wall_damaged"&&te.destroyed).length,wt=re.events.filter(te=>te.type==="arrow_hit"&&te.hpRemaining<=0).length,at=Te?{gold:10+S*3,wood:5+S*2,stone:5+S*2}:{gold:0,wood:0,stone:0};$({won:Te,raidType:"defend",outcome:oe.outcome,lootGained:at,lootLost:ie,raidersKilled:et,raidersTotal:V,wallsDestroyed:He,archersActive:o.keep.grid.structures.filter(te=>te.kind==="archerTower").length,difficulty:S});let it={gold:Math.max(0,o.keep.resources.gold-ie.gold),wood:Math.max(0,o.keep.resources.wood-ie.wood),stone:Math.max(0,o.keep.resources.stone-ie.stone)},Rt=rt(Oe(it,at)),xe=Te?o.progression.currentWinStreak+1:0,lt={id:M,seed:M,rulesVersion:1,attackerId:"npc",defenderKeepId:o.keep.id,startedAtUnixMs:Date.now(),resolvedAtUnixMs:Date.now(),outcome:oe.outcome,lootLost:ie,lootGained:at,replay:re};O({...o,keep:{...o.keep,resources:Rt},raidHistory:[...o.raidHistory.slice(-19),lt],progression:{...o.progression,totalRaidsWon:o.progression.totalRaidsWon+(Te?1:0),totalRaidsLost:o.progression.totalRaidsLost+(Te?0:1),currentWinStreak:xe,bestWinStreak:Math.max(o.progression.bestWinStreak,xe),totalRaidersKilledByArcher:o.progression.totalRaidersKilledByArcher+wt}})}},[o,O]),Ie=ge(()=>{if(!o)return;let g=o.progression.totalRaidsWon+o.progression.totalRaidsLost,S=oo(g),M=`quick-${Date.now()}-${Math.random()}`,V=3+S,Q=Vt(Date.now()),he=ro(V,S,Q),re=ut({probeCount:V,keepGrid:o.keep.grid,seed:M,probeTypes:he}),oe=re.events[re.events.length-1];if(oe?.type==="raid_end"){let Te=oe.outcome==="defense_win",ie=re.events.filter(te=>te.type==="treasury_breach").reduce((te,dt)=>({gold:te.gold+dt.lootTaken.gold,wood:te.wood+dt.lootTaken.wood,stone:te.stone+dt.lootTaken.stone}),{gold:0,wood:0,stone:0}),et=re.events.filter(te=>te.type==="arrow_hit"&&te.hpRemaining<=0).length,He=Te?{gold:10+S*3,wood:5+S*2,stone:5+S*2}:{gold:0,wood:0,stone:0},wt={gold:Math.max(0,o.keep.resources.gold-ie.gold),wood:Math.max(0,o.keep.resources.wood-ie.wood),stone:Math.max(0,o.keep.resources.stone-ie.stone)},at=rt(Oe(wt,He)),it=Te?o.progression.currentWinStreak+1:0,Rt={id:M,seed:M,rulesVersion:1,attackerId:"npc",defenderKeepId:o.keep.id,startedAtUnixMs:Date.now(),resolvedAtUnixMs:Date.now(),outcome:oe.outcome,lootLost:ie,lootGained:He,replay:re};O({...o,keep:{...o.keep,resources:at},raidHistory:[...o.raidHistory.slice(-19),Rt],progression:{...o.progression,totalRaidsWon:o.progression.totalRaidsWon+(Te?1:0),totalRaidsLost:o.progression.totalRaidsLost+(Te?0:1),currentWinStreak:it,bestWinStreak:Math.max(o.progression.bestWinStreak,it),totalRaidersKilledByArcher:o.progression.totalRaidersKilledByArcher+et}});let xe=re.events.filter(te=>te.type==="raider_destroyed").length,lt=re.events.filter(te=>te.type==="wall_damaged"&&te.destroyed).length;if(C.current={replay:re,grid:o.keep.grid,summary:{won:Te,raidType:"defend",outcome:oe.outcome,lootGained:He,lootLost:ie,raidersKilled:xe,raidersTotal:V,wallsDestroyed:lt,archersActive:o.keep.grid.structures.filter(te=>te.kind==="archerTower").length,difficulty:S}},Te)W(`Defense WIN! +${He.gold}${B.gold} +${He.wood}${B.wood} +${He.stone}${B.stone} [v] view`);else{let te=ie.gold+ie.wood+ie.stone;W(`Defense BREACH! Lost ${te} res [v] view`)}}},[o,O,W]),L=ge(()=>{let g=C.current;return g?(k(g.replay),m(g.grid),b("defend"),$(g.summary),C.current=null,!0):!1},[]),Z=ge(()=>{if(!o)return;let g=Ho(u,r,o.keep.grid);if(!g){W("!Nothing to collect here");return}K(g.updatedFragments);let S=g.yield,M={...o,keep:{...o.keep,resources:rt(Oe(o.keep.resources,S))}};O(M);let V=[];S.gold>0&&V.push(`+${S.gold}${B.gold}`),S.wood>0&&V.push(`+${S.wood}${B.wood}`),S.stone>0&&V.push(`+${S.stone}${B.stone}`);let Q=g.collected[0]?.type.replace("_"," ")??"fragment",he=g.collected.length>1?` (${g.collected.length}x)`:"";W(`${V.join(" ")} ${Q}${he}`)},[o,u,r,O,W]),G=ge(g=>{if(!o)return!1;let S=g.attackerId!==o.player.id;return k(g.replay),m(S?o.keep.grid:g.defenderGrid??o.keep.grid),b(S?"defend":"attack"),$(null),!0},[o]),X=ge(()=>{k(null),m(null),b(null),$(null)},[]),Ce=ge(()=>{o&&O({...o,tutorialCompleted:!0})},[o,O]),Tt=ge(()=>{if(!o)return;let g=Date.now(),S=g-H.current;if(S<$s){let re=Math.ceil(($s-S)/1e3);W(`Cooldown: ${re}s remaining`);return}H.current=g,y.current++;let M=Kr(),V=Ls({...M.grants},y.current),Q=Wo(o.keep,{...M,grants:V});O({...o,keep:Q});let he=y.current>Ut?" (diminished)":"";W(`+${V.gold}${B.gold} +${V.wood}${B.wood} +${V.stone}${B.stone} (${yr[M.type]??M.type})${he}`)},[o,O,W]),Nt=ge((g,S)=>{g>=0&&g<I&&S>=0&&S<I&&a({x:g,y:S})},[]),Io=ge(g=>{if(!o||o.keep.grid.structures.length===0)return;let S=o.keep.grid.structures,M=`${r.x},${r.y}`,V=S.findIndex(re=>`${re.pos.x},${re.pos.y}`===M),Q=V===-1?0:V+g;Q<0&&(Q=S.length-1),Q>=S.length&&(Q=0);let he=S[Q];a({x:he.pos.x,y:he.pos.y})},[o,r]),Pe=ge(()=>{U(null)},[]),Po=o?.keep.grid.structures.find(g=>g.pos.x===r.x&&g.pos.y===r.y)??null;return{gameSave:o,cursor:r,selectedStructure:A,structureAtCursor:Po,message:d,fragments:u,raidReplay:x,raidGrid:R,raidType:p,raidSummary:c,offlineReport:z,moveCursor:ue,cycleStructure:Se,selectStructure:nt,placeAtCursor:$t,upgradeAtCursor:jt,demolishAtCursor:v,collectAtCursor:Z,startAttackRaid:q,startDefendRaid:Y,quickDefend:Ie,watchLastRaid:L,watchRaidRecord:G,clearRaid:X,completeTutorial:Ce,grantSimResources:Tt,jumpToCoord:Nt,jumpToNextStructure:Io,clearOfflineReport:Pe}}var Mo=class{mode="online";baseUrl;auth=null;constructor(t){this.baseUrl=t.replace(/\/$/,"")}async request(t,o={}){let s={"Content-Type":"application/json",...o.headers??{}};this.auth&&(s.Authorization=`Bearer ${this.auth.token}`);let r=await fetch(`${this.baseUrl}${t}`,{...o,headers:s});if(!r.ok){let a=await r.json().catch(()=>({error:r.statusText}));throw new Error(a.error??`HTTP ${r.status}`)}return r.json()}isAuthenticated(){return this.auth!==null}async register(t){let o=await this.request("/auth/register",{method:"POST",body:JSON.stringify({displayName:t})});return this.auth={token:o.token,apiKey:o.apiKey,playerId:o.playerId},o}async login(t){let o=await this.request("/auth/login",{method:"POST",body:JSON.stringify({apiKey:t})});return this.auth={token:o.token,apiKey:t,playerId:o.playerId},o}async load(){if(!this.auth)return null;try{let t=await this.request("/me");return t.keep?{schemaVersion:1,savedAtUnixMs:Date.now(),player:{id:t.player.id,displayName:t.player.displayName,settings:{asciiMode:!1}},keep:{id:t.keep.id,name:t.keep.name,ownerPlayerId:t.player.id,grid:t.keep.grid,resources:t.keep.resources,createdAtUnixMs:t.player.createdAt,updatedAtUnixMs:Date.now()},raidHistory:[],tutorialCompleted:!0,lastPlayedAtUnixMs:Date.now(),progression:{totalBuildsToday:0,totalCommitsToday:0,lastDailyResetDay:Math.floor(Date.now()/864e5),totalRaidsWon:t.progression?.totalRaidsWon??0,totalRaidsLost:t.progression?.totalRaidsLost??0,totalStructuresPlaced:t.progression?.totalStructuresPlaced??0,currentWinStreak:t.progression?.currentWinStreak??0,bestWinStreak:t.progression?.bestWinStreak??0,achievements:t.progression?.achievements??[],totalRaidersKilledByArcher:0}}:null}catch{return null}}async save(t){this.auth&&await this.request("/keep/save",{method:"POST",body:JSON.stringify({grid:t.keep.grid,resources:t.keep.resources})})}async createNew(t){let o=await this.register(t),s=await this.load();return s||{schemaVersion:1,savedAtUnixMs:Date.now(),player:{id:o.playerId,displayName:t,settings:{asciiMode:!1}},keep:{id:`keep-${o.playerId}`,name:`${t}'s Keep`,ownerPlayerId:o.playerId,grid:{width:16,height:16,structures:[]},resources:{...St},createdAtUnixMs:Date.now(),updatedAtUnixMs:Date.now()},raidHistory:[],tutorialCompleted:!1,lastPlayedAtUnixMs:Date.now(),progression:{totalBuildsToday:0,totalCommitsToday:0,lastDailyResetDay:Math.floor(Date.now()/864e5),totalRaidsWon:0,totalRaidsLost:0,totalStructuresPlaced:0,currentWinStreak:0,bestWinStreak:0,achievements:[],totalRaidersKilledByArcher:0}}}async deleteAll(){return!1}async findMatch(){return(await this.request("/matchmaking/find",{method:"POST"})).targets}async launchPvpRaid(t,o,s){return this.request("/raids/launch",{method:"POST",body:JSON.stringify({defenderPlayerId:t,probeTypes:o,spawnSpecs:s})})}async getIncomingRaids(t){return(await this.request(`/raids/incoming?since=${t}`)).raids}async getPvpProfile(){try{let t=await this.request("/me");return{trophies:t.player.trophies,league:t.player.league,shieldExpiresAt:t.player.shieldExpiresAt,seasonId:"S1",seasonPeakTrophies:t.player.trophies}}catch{return null}}async getLeaderboard(t=50){return(await this.request(`/leaderboard?limit=${t}`)).players}async getWarCamp(){try{return await this.request("/warcamp")}catch{return null}}async trainRaider(t,o){let s=await this.request("/warcamp/train",{method:"POST",body:JSON.stringify({slotId:t,raiderType:o})});return{slots:s.slots,maxSlots:s.maxSlots}}async getBounties(){try{return(await this.request("/bounties")).bounties}catch{return[]}}async claimBounty(t){return(await this.request(`/bounties/${t}/claim`,{method:"POST"})).reward}async registerForMatchmaking(){await this.request("/matchmaking/register",{method:"POST"})}async sync(){this.auth&&await this.registerForMatchmaking()}};import{jsx as ae,jsxs as je}from"react/jsx-runtime";var Os=60,Us=18;function Na(){let{stdout:e}=$a(),[t,o]=Re({columns:e?.columns??process.stdout.columns??80,rows:e?.rows??process.stdout.rows??24});return Pa(()=>{let s=e??process.stdout,r=()=>{o({columns:s.columns,rows:s.rows})};return s.on("resize",r),()=>{s.off("resize",r)}},[e]),t}function Ba({asciiMode:e,compact:t,forceTutorial:o,autoResume:s,serverUrl:r,dryRun:a}){let{exit:T}=Da(),{columns:f,rows:d}=Na(),[l,x]=Re(r?"auth":s?"keep":"menu"),[k,R]=Re(e),[m,p]=Re(!1),[b,c]=Re(null),[$,z]=Re(null),[U,u]=Re(!1),[K,w]=Re(""),[H,y]=Re("keep"),h=Bs(1),E=Bs(r?new Mo(r):null),[C,N]=Re(!1),[A,O]=Re(null),[W,ue]=Re([]),[Se,nt]=Re(!1),[$t,jt]=Re([]),[v,q]=Re(!1),[Y,Ie]=Re({slots:[],maxSlots:3}),[L,Z]=Re(null),[G,X]=Re(null),[Ce,Tt]=Re(!1),Nt=t||f<80||d<24,Io=f<Os||d<Us,{gameSave:Pe,cursor:Po,selectedStructure:g,message:S,fragments:M,raidReplay:V,raidGrid:Q,raidType:he,raidSummary:re,offlineReport:oe,structureAtCursor:Te,moveCursor:ie,cycleStructure:et,selectStructure:He,placeAtCursor:wt,upgradeAtCursor:at,demolishAtCursor:it,collectAtCursor:Rt,startAttackRaid:xe,startDefendRaid:lt,quickDefend:te,watchLastRaid:dt,watchRaidRecord:Do,clearRaid:ir,completeTutorial:Ks,grantSimResources:Hs,jumpToCoord:Fs,jumpToNextStructure:Vs,clearOfflineReport:Ys}=Ns(o,a),lr=Yt(()=>{T()},[T]),js=Yt(()=>{Xr(),T()},[T]),dr=Yt(async(D,ne)=>{let me=E.current;if(me){Tt(!0),X(null);try{D==="login"?await me.login(ne):await me.register(ne),N(!0),x("menu"),me.getPvpProfile?.().then(Fe=>O(Fe)),me.registerForMatchmaking?.()}catch(Fe){X(Fe instanceof Error?Fe.message:"Connection failed")}finally{Tt(!1)}}},[]),qs=Yt(async()=>{let D=E.current;if(D?.findMatch){nt(!0);try{let ne=await D.findMatch();ue(ne)}catch{ue([])}finally{nt(!1)}}},[]),zs=Yt(async(D,ne,me)=>{let Fe=E.current;if(!(!Fe?.launchPvpRaid||!Pe))try{let Bt=await Fe.launchPvpRaid(D.playerId,ne,me);Do({replay:Bt.replay,attackerId:Pe.player.id,defenderKeepId:D.playerId,defenderGrid:D.grid})&&(y("pvp"),x("raid")),O(Lo=>Lo&&{...Lo,trophies:Bt.newTrophies,league:Bt.newLeague})}catch{}},[Pe,Do]),Zs=Yt(async()=>{let D=E.current;if(D?.getLeaderboard){q(!0);try{let ne=await D.getLeaderboard();jt(ne)}catch{jt([])}finally{q(!1)}}},[]);return La((D,ne)=>{if(m){p(!1);return}if(D==="?"){p(!0);return}if(U){if(ne.escape){u(!1),w("");return}if(ne.return){let me=K.split(/[, ]+/);if(me.length===2){let Fe=parseInt(me[0],16),Bt=parseInt(me[1],16);!isNaN(Fe)&&!isNaN(Bt)&&Fs(Fe,Bt)}u(!1),w("");return}if(ne.backspace||ne.delete){w(me=>me.slice(0,-1));return}/^[0-9a-fA-F, ]$/.test(D)&&w(me=>me+D);return}if(oe&&l==="keep"&&Ys(),l!=="menu"&&l!=="tutorial"){if(l==="raid"||l==="friendRaid"){(D==="q"||ne.escape)&&(ir(),c(null),z(null),x("keep"));return}if(!(l==="friendList"||l==="raidLog"||l==="settings"||l==="pvp"||l==="warcamp"||l==="leaderboard"||l==="auth"||l==="raidPlanner")){if(D==="q"){lr();return}if(ne.escape){x("menu");return}D==="h"||D==="a"||ne.leftArrow?ie(-1,0):D==="l"||D==="d"||ne.rightArrow?ie(1,0):D==="k"||D==="w"||ne.upArrow?ie(0,-1):D==="j"||D==="s"||ne.downArrow?ie(0,1):ne.return||D==="e"?wt():D==="u"?at():D==="x"?it():D==="["?et(-1):D==="]"?et(1):D==="f"?Hs():D==="c"?Rt():D==="r"?te():D==="v"?dt()&&x("raid"):D==="g"?(u(!0),w("")):ne.tab?Vs(1):D>="1"&&D<="6"&&He(parseInt(D)-1)}}}),Pe?Io?je(Lt,{flexDirection:"column",padding:1,children:[ae(Me,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep"}),ae(Me,{children:" "}),ae(Me,{color:"red",children:"Terminal too small"}),je(Me,{children:["Need ",je(Me,{bold:!0,children:[Os,"x",Us]}),", got ",je(Me,{bold:!0,children:[f,"x",d]})]}),ae(Me,{children:" "}),ae(Me,{dimColor:!0,children:"Resize your terminal or run with --compact"})]}):m?ae(rs,{}):l==="tutorial"||!Pe.tutorialCompleted?ae(ds,{gameSave:Pe,onComplete:()=>{Ks(),x("keep")}}):l==="auth"?ae(bs,{error:G,isLoading:Ce,onLogin:D=>dr("login",D),onRegister:D=>dr("register",D),onPlayOffline:()=>{N(!1),x("menu")}}):l==="menu"?ae(ns,{gameSave:Pe,onlineMode:C,onKeep:()=>x("keep"),onAttack:()=>{xe(),x("raid")},onDefend:()=>{lt(),x("raid")},onPvp:()=>x("pvp"),onFriendRaid:()=>x("friendList"),onRaidLog:()=>x("raidLog"),onSettings:()=>x("settings"),onQuit:lr}):l==="raidLog"?ae(ms,{gameSave:Pe,onBack:()=>x("menu"),onWatchReplay:D=>{Do(D)&&(y("raidLog"),x("raid"))}}):l==="settings"?ae(Ts,{onBack:()=>x("menu"),onResetGame:js,onReplayTutorial:()=>x("tutorial"),asciiMode:k,onToggleAscii:()=>R(D=>!D)}):l==="pvp"?ae(ys,{pvpProfile:A,targets:W,isSearching:Se,onSearch:qs,onAttack:D=>{Z(D),x("raidPlanner")},onWarCamp:()=>{E.current?.getWarCamp?.().then(ne=>{ne&&Ie(ne)}),x("warcamp")},onLeaderboard:()=>{Zs(),x("leaderboard")},onBack:()=>x("menu")}):l==="raidPlanner"&&L?ae(As,{targetName:L.displayName,targetTrophies:L.trophies,targetGrid:L.grid,warCamp:Y,resources:Pe.keep.resources,onLaunch:D=>{let ne=D.map(me=>me.raiderType);zs(L,ne,D),Z(null)},onBack:()=>{Z(null),x("pvp")}}):l==="warcamp"?ae(Rs,{warCamp:Y,resources:Pe.keep.resources,onTrain:(D,ne)=>{E.current?.trainRaider?.(D,ne).then(Fe=>Ie(Fe))},onBack:()=>x("pvp")}):l==="leaderboard"?ae(Ss,{entries:$t,currentPlayerId:Pe.player.id,isLoading:v,onBack:()=>x("pvp")}):l==="friendList"?ae(ps,{onSelectFriend:D=>{let ne=`friend-raid-${Date.now()}`,me=ut({probeCount:4,keepGrid:D.grid,seed:ne});c(me),z(D.grid),x("friendRaid")},onBack:()=>x("menu")}):l==="friendRaid"&&b&&$?je(Lt,{flexDirection:"column",children:[ae(Me,{bold:!0,color:"yellow",children:"[ LOCAL SIMULATION ]"}),ae(Jo,{replay:b,keepGrid:$,raidType:"attack",initialSpeed:h.current,onSpeedChange:D=>{h.current=D},onDone:()=>{c(null),z(null),x("menu")}})]}):l==="raid"&&V&&Q?ae(Jo,{replay:V,keepGrid:Q,raidType:he||"defend",summary:re||void 0,initialSpeed:h.current,onSpeedChange:D=>{h.current=D},onDone:()=>{ir(),x(H),y("keep")}}):je(Lt,{flexDirection:"column",children:[oe&&je(Lt,{flexDirection:"column",children:[(oe.resources.gold>0||oe.resources.wood>0||oe.resources.stone>0)&&je(Me,{color:"green",children:["Passive income: +",oe.resources.gold,B.gold," +",oe.resources.wood,B.wood," +",oe.resources.stone,B.stone]}),oe.raids.length>0&&je(Me,{color:"yellow",children:["Background raids: ",oe.raids.filter(D=>D.won).length,"W / ",oe.raids.filter(D=>!D.won).length,"L"]}),oe.newAchievements.length>0&&je(Me,{color:"magenta",bold:!0,children:["New achievements: ",oe.newAchievements.join(", ")]}),ae(Me,{dimColor:!0,children:"(press any key to continue)"})]}),ae(os,{resources:Pe.keep.resources,selectedStructure:g,message:U?`Jump to: ${K}_`:S,compact:Nt,structureAtCursor:Te,fragmentCount:M.length,dryRun:a}),je(Lt,{flexDirection:"row",children:[ae(ts,{grid:Pe.keep.grid,cursor:Po,asciiMode:k,compact:Nt,fragments:M}),!Nt&&je(Lt,{flexDirection:"column",marginLeft:2,width:28,children:[ae(us,{selected:g}),ae(Lt,{marginTop:1,children:ae(Me,{dimColor:!0,children:`e place u upgrade x demo
|
|
25
|
+
`);return`https://github.com/tooyipjee/codekeep/issues/new?title=${t}&body=${o}&labels=bug,crash`}import{Fragment as la,jsx as me,jsxs as st}from"react/jsx-runtime";function Ts({onBack:e,onResetGame:t,onReplayTutorial:o,asciiMode:s,onToggleAscii:r}){let[a,T]=eo(0),[p,d]=eo(!1),[l,x]=eo(!1),[b,R]=eo(""),[f,m]=eo(null),C=[{key:"ascii",label:`ASCII Mode: ${s?"ON":"OFF"}`,desc:"Use plain ASCII borders (for basic terminals)"},{key:"tutorial",label:"Replay Tutorial",desc:"Learn how to play again"},{key:"report",label:"Report Bug",desc:"Open a GitHub issue for bugs"},{key:"reset",label:"Reset Game",desc:"Delete save and start over"},{key:"back",label:"Back",desc:"Return to menu"}];return aa((u,v)=>{if(f){(v.escape||v.return||u==="q")&&(m(null),x(!1));return}if(l){if(v.escape){x(!1),R("");return}if(v.return&&b.length>0){let U={timestamp:new Date().toISOString(),error:b,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch,screen:"settings"},B=bo(U);m(B),R("");let c=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";ia(`${c} "${B}"`,()=>{});return}if(v.backspace||v.delete){R(U=>U.slice(0,-1));return}u&&u.length===1&&R(U=>U+u);return}if(p){if(u==="y"||u==="Y"){t();return}d(!1);return}if(u==="q"||v.escape){e();return}if((u==="j"||u==="s"||v.downArrow)&&T(U=>Math.min(U+1,C.length-1)),(u==="k"||u==="w"||v.upArrow)&&T(U=>Math.max(U-1,0)),v.return){let U=C[a];U.key==="ascii"?r():U.key==="tutorial"?o():U.key==="report"?x(!0):U.key==="reset"?d(!0):U.key==="back"&&e()}}),st(to,{flexDirection:"column",padding:1,children:[me(le,{bold:!0,color:"yellow",children:"\u25C6 Settings"}),me(le,{children:" "}),f?st(to,{flexDirection:"column",children:[me(le,{bold:!0,color:"green",children:"Bug report ready!"}),me(le,{children:" "}),me(le,{children:"Opening in your browser..."}),me(le,{children:"If it didn't open, copy this URL:"}),me(le,{children:" "}),me(le,{color:"cyan",wrap:"wrap",children:f}),me(le,{children:" "}),me(le,{dimColor:!0,children:"Press Enter or Esc to go back"})]}):l?st(to,{flexDirection:"column",children:[me(le,{bold:!0,color:"cyan",children:"Report a Bug"}),me(le,{children:"Describe the issue briefly:"}),st(le,{color:"cyan",children:[b,me(le,{dimColor:!0,children:"_"})]}),me(le,{children:" "}),me(le,{dimColor:!0,children:"Enter to generate GitHub issue URL \xB7 Esc cancel"})]}):p?st(to,{flexDirection:"column",children:[me(le,{bold:!0,color:"red",children:"Are you sure you want to reset?"}),me(le,{children:"This will permanently delete your save file."}),me(le,{children:"All structures, resources, and achievements will be lost."}),me(le,{children:" "}),st(le,{bold:!0,children:["Press ",me(le,{color:"red",children:"Y"})," to confirm, any other key to cancel"]})]}):st(la,{children:[C.map((u,v)=>st(to,{children:[st(le,{color:v===a?"yellow":void 0,bold:v===a,children:[v===a?" \u25B8 ":" ",u.label]}),st(le,{dimColor:!0,children:[" ",u.desc]})]},u.key)),me(le,{children:" "}),me(le,{dimColor:!0,children:" \u2191\u2193 navigate \xB7 Enter select \xB7 Esc back"})]})]})}import da from"react";import{Box as ca,Text as Ae}from"ink";import{Fragment as xs,jsx as Ge,jsxs as Ct}from"react/jsx-runtime";var ko=class extends da.Component{constructor(t){super(t),this.state={error:null,crashFilePath:null,issueUrl:null}}static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t){try{let o=hs(t),s={timestamp:new Date().toISOString(),error:t.message,stack:t.stack,version:globalThis.__CODEKEEP_VERSION??"unknown",nodeVersion:process.version,platform:process.platform,arch:process.arch},r=bo(s);this.setState({crashFilePath:o,issueUrl:r})}catch{}}render(){return this.state.error?Ct(ca,{flexDirection:"column",padding:1,children:[Ge(Ae,{bold:!0,color:"red",children:"\u25C6 CodeKeep \u2014 Something went wrong"}),Ge(Ae,{children:" "}),Ge(Ae,{color:"red",children:this.state.error.message}),Ge(Ae,{children:" "}),Ge(Ae,{dimColor:!0,children:"Your save file is at:"}),Ct(Ae,{children:[" ","~/.config/codekeep/game.json"]}),Ge(Ae,{children:" "}),this.state.crashFilePath&&Ct(xs,{children:[Ge(Ae,{dimColor:!0,children:"Crash report saved to:"}),Ct(Ae,{children:[" ",this.state.crashFilePath]}),Ge(Ae,{children:" "})]}),this.state.issueUrl&&Ct(xs,{children:[Ge(Ae,{dimColor:!0,children:"Report this bug:"}),Ct(Ae,{color:"cyan",children:[" ",this.state.issueUrl]}),Ge(Ae,{children:" "})]}),Ge(Ae,{dimColor:!0,children:"If the game won't start, try:"}),Ct(Ae,{bold:!0,children:[" ","codekeep --tutorial"]}),Ge(Ae,{children:" "}),Ge(Ae,{dimColor:!0,children:"Press Ctrl+C to exit"})]}):this.props.children}};import{useState as ua}from"react";import{Box as vo,Text as Qe,useInput as pa}from"ink";import{jsx as yt,jsxs as _t}from"react/jsx-runtime";var fa={copper:"white",iron:"gray",silver:"whiteBright",gold:"yellow",diamond:"cyan"},ma={copper:"\u25CB",iron:"\u25CF",silver:"\u25C6",gold:"\u2605",diamond:"\u25C8"};function ys({pvpProfile:e,targets:t,isSearching:o,onSearch:s,onAttack:r,onWarCamp:a,onLeaderboard:T,onBack:p}){let[d,l]=ua(0),x=[{key:"search",label:"Find Opponent"},{key:"warcamp",label:"War Camp"},{key:"leaderboard",label:"Leaderboard"},...t.map((m,C)=>({key:`target-${C}`,label:`Attack ${m.displayName} (${m.trophies} trophies)`})),{key:"back",label:"Back"}];pa((m,C)=>{if(C.upArrow||m==="k")l(u=>Math.max(0,u-1));else if(C.downArrow||m==="j")l(u=>Math.min(x.length-1,u+1));else if(C.return){let u=x[d];if(u.key==="search")s();else if(u.key==="warcamp")a();else if(u.key==="leaderboard")T();else if(u.key==="back")p();else if(u.key.startsWith("target-")){let v=parseInt(u.key.split("-")[1]),U=t[v];U&&r(U)}}else(C.escape||m==="q")&&p()});let b=e?.league??"copper",R=fa[b]??"white",f=ma[b]??"\u25CB";return _t(vo,{flexDirection:"column",padding:1,children:[yt(Qe,{bold:!0,color:"red",children:"\u2694 PVP ARENA"}),yt(Qe,{children:" "}),e&&_t(vo,{flexDirection:"column",children:[_t(vo,{flexDirection:"row",gap:2,children:[_t(Qe,{color:R,bold:!0,children:[f," ",b.toUpperCase()," League"]}),_t(Qe,{children:["Trophies: ",yt(Qe,{bold:!0,color:"yellow",children:e.trophies})]})]}),e.shieldExpiresAt&&e.shieldExpiresAt>Date.now()&&_t(Qe,{color:"cyan",children:["Shield active (",Math.ceil((e.shieldExpiresAt-Date.now())/36e5),"h remaining)"]}),yt(Qe,{children:" "})]}),o&&yt(Qe,{color:"yellow",children:"Searching for opponents..."}),x.map((m,C)=>yt(vo,{children:_t(Qe,{color:C===d?"yellow":void 0,bold:C===d,children:[C===d?" \u25B8 ":" ",m.label]})},m.key)),yt(Qe,{children:" "}),yt(Qe,{dimColor:!0,children:"\u2191\u2193 select Enter choose Esc back"})]})}import{useState as Eo,useEffect as ga}from"react";import{Box as oo,Text as We,useInput as ha}from"ink";import{jsx as gt,jsxs as ht}from"react/jsx-runtime";var tr=[{type:"raider",name:"Raider",symbol:"R"},{type:"scout",name:"Scout",symbol:"S"},{type:"brute",name:"Brute",symbol:"B"}];function ws(e){let t=Math.ceil(e/1e3),o=Math.floor(t/60),s=t%60;return`${o}:${String(s).padStart(2,"0")}`}function Rs({warCamp:e,resources:t,onTrain:o,onBack:s}){let[r,a]=Eo(0),[T,p]=Eo(!1),[d,l]=Eo(0),[x,b]=Eo(Date.now());return ga(()=>{let R=setInterval(()=>b(Date.now()),1e3);return()=>clearInterval(R)},[]),ha((R,f)=>{if(T){f.upArrow||R==="k"?l(m=>Math.max(0,m-1)):f.downArrow||R==="j"?l(m=>Math.min(tr.length-1,m+1)):f.return?(o(r,tr[d].type),p(!1)):f.escape&&p(!1);return}f.upArrow||R==="k"?a(m=>Math.max(0,m-1)):f.downArrow||R==="j"?a(m=>Math.min(e.maxSlots-1,m+1)):f.return?e.slots[r]?.raiderType||p(!0):(f.escape||R==="q")&&s()}),ht(oo,{flexDirection:"column",padding:1,children:[gt(We,{bold:!0,color:"red",children:"\u2694 WAR CAMP"}),gt(We,{dimColor:!0,children:"Train raiders for PvP attacks"}),gt(We,{children:" "}),ht(oo,{flexDirection:"row",gap:2,children:[ht(We,{children:["Gold: ",t.gold,O.gold]}),ht(We,{children:["Wood: ",t.wood,O.wood]}),ht(We,{children:["Stone: ",t.stone,O.stone]})]}),gt(We,{children:" "}),ht(We,{bold:!0,children:["Slots (",e.slots.filter(R=>R.raiderType).length,"/",e.maxSlots,")"]}),Array.from({length:e.maxSlots},(R,f)=>{let m=e.slots[f],C=r===f,u=m?.readyAtMs?m.readyAtMs<=x:!1,v=m?.readyAtMs?m.readyAtMs>x:!1,U=v&&m?.readyAtMs?m.readyAtMs-x:0,B;return m?.raiderType?v?B=`${m.raiderType} training... ${ws(U)}`:u?B=`${m.raiderType} READY!`:B=`${m.raiderType}`:B="[ Empty ]",gt(oo,{children:ht(We,{color:C?"yellow":void 0,bold:C,children:[C?" \u25B8 ":" ","Slot ",f+1,": ",B]})},f)}),T&&ht(oo,{flexDirection:"column",marginTop:1,children:[gt(We,{bold:!0,color:"cyan",children:"Select raider type:"}),tr.map((R,f)=>{let m=To[R.type],C=Ar[R.type],u=$e[R.type],v=t.gold>=m.gold&&t.wood>=m.wood&&t.stone>=m.stone;return gt(oo,{children:ht(We,{color:d===f?"yellow":v?void 0:"red",bold:d===f,children:[d===f?" \u25B8 ":" ",R.name," \u2014 HP:",u.hp," DMG:",u.damage," SPD:",u.speed," \u2014 ",m.gold,"g ",m.wood,"w ",m.stone,"s \u2014 ",ws(C)]})},R.type)})]}),gt(We,{children:" "}),gt(We,{dimColor:!0,children:"\u2191\u2193 select Enter train Esc back"})]})}import{Box as Ao,Text as Be,useInput as Ta}from"ink";import{jsx as Ke,jsxs as ro}from"react/jsx-runtime";var xa={copper:"white",iron:"gray",silver:"whiteBright",gold:"yellow",diamond:"cyan"};function Ss({entries:e,currentPlayerId:t,isLoading:o,onBack:s}){return Ta((r,a)=>{(a.escape||r==="q")&&s()}),ro(Ao,{flexDirection:"column",padding:1,children:[Ke(Be,{bold:!0,color:"yellow",children:"\u{1F3C6} LEADERBOARD"}),Ke(Be,{children:" "}),o&&Ke(Be,{color:"yellow",children:"Loading..."}),!o&&e.length===0&&Ke(Be,{dimColor:!0,children:"No players on the leaderboard yet."}),!o&&e.length>0&&ro(Ao,{flexDirection:"column",children:[ro(Ao,{children:[Ke(Be,{bold:!0,children:" # "}),Ke(Be,{bold:!0,children:"Name "}),Ke(Be,{bold:!0,children:"League "}),Ke(Be,{bold:!0,children:"Trophies"})]}),e.map(r=>{let a=r.id===t,T=a?"green":void 0,p=xa[r.league]??"white";return ro(Ao,{children:[ro(Be,{color:T,bold:a,children:[String(r.rank).padStart(3," ")," "]}),Ke(Be,{color:T,bold:a,children:(r.displayName||"Unknown").padEnd(20," ")}),Ke(Be,{color:p,children:r.league.padEnd(10," ")}),Ke(Be,{color:"yellow",bold:!0,children:r.trophies})]},r.id)})]}),Ke(Be,{children:" "}),Ke(Be,{dimColor:!0,children:"Esc/q back"})]})}import{useState as or}from"react";import{Box as Co,Text as Ce,useInput as ya}from"ink";import{Fragment as wa,jsx as Le,jsxs as Mt}from"react/jsx-runtime";function bs({onLogin:e,onRegister:t,onPlayOffline:o,error:s,isLoading:r}){let[a,T]=or("menu"),[p,d]=or(""),[l,x]=or(0),b=[{key:"register",label:"Register (new player)"},{key:"login",label:"Login (with API key)"},{key:"offline",label:"Play Offline"}];return ya((R,f)=>{if(!r){if(a==="menu"){if(f.upArrow||R==="k")x(m=>Math.max(0,m-1));else if(f.downArrow||R==="j")x(m=>Math.min(b.length-1,m+1));else if(f.return){let m=b[l];m.key==="register"?T("register"):m.key==="login"?T("login"):m.key==="offline"&&o()}return}if(f.escape){T("menu"),d("");return}if(f.return&&p.length>0){a==="login"?e(p):a==="register"&&t(p),d("");return}if(f.backspace||f.delete){d(m=>m.slice(0,-1));return}R&&R.length===1&&d(m=>m+R)}}),Mt(Co,{flexDirection:"column",padding:1,children:[Le(Ce,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep Online"}),Le(Ce,{children:" "}),s&&Le(Ce,{color:"red",children:s}),r&&Le(Ce,{color:"yellow",children:"Connecting..."}),a==="menu"&&!r&&Mt(wa,{children:[b.map((R,f)=>Le(Co,{children:Mt(Ce,{color:f===l?"yellow":void 0,bold:f===l,children:[f===l?" \u25B8 ":" ",R.label]})},R.key)),Le(Ce,{children:" "}),Le(Ce,{dimColor:!0,children:"\u2191\u2193 navigate Enter select"})]}),a==="login"&&!r&&Mt(Co,{flexDirection:"column",children:[Le(Ce,{children:"Enter your API key:"}),Mt(Ce,{color:"cyan",children:[p,Le(Ce,{dimColor:!0,children:"_"})]}),Le(Ce,{children:" "}),Le(Ce,{dimColor:!0,children:"Enter to submit Esc back"})]}),a==="register"&&!r&&Mt(Co,{flexDirection:"column",children:[Le(Ce,{children:"Choose a display name:"}),Mt(Ce,{color:"cyan",children:[p,Le(Ce,{dimColor:!0,children:"_"})]}),Le(Ce,{children:" "}),Le(Ce,{dimColor:!0,children:"Enter to submit Esc back"})]})]})}import{useState as It}from"react";import{Box as Ye,Text as q,useInput as Ra}from"ink";import{jsx as ee,jsxs as ve}from"react/jsx-runtime";var ks={N:"North",S:"South",E:"East",W:"West"},Ft={raider:"R",scout:"S",brute:"B"},_o={raider:"red",scout:"cyan",brute:"magenta"},vs=8;function Es(e,t){switch(e){case"N":return{x:t,y:0};case"S":return{x:t,y:P-1};case"E":return{x:P-1,y:t};case"W":return{x:0,y:t}}}function As({targetName:e,targetTrophies:t,targetGrid:o,warCamp:s,resources:r,onLaunch:a,onBack:T}){let[p,d]=It("scout"),[l,x]=It([]),[b,R]=It(0),[f,m]=It([]),[C,u]=It("N"),[v,U]=It(Math.floor(P/2)),[B,c]=It(0),H=["raider","scout","brute"];Ra((y,h)=>{if(h.escape||y==="q"){if(p==="scout"){T();return}if(p==="army"){d("scout");return}if(p==="deploy"){d("army");return}if(p==="confirm"){d("deploy");return}return}if(p==="scout"){h.return&&d("army");return}if(p==="army"){h.upArrow||y==="k"?R(E=>Math.max(0,E-1)):h.downArrow||y==="j"?R(E=>Math.min(H.length-1,E+1)):h.return?l.length<vs&&x(E=>[...E,H[b]]):h.backspace||h.delete?x(E=>E.slice(0,-1)):(y==="n"||h.tab)&&l.length>0&&(m(l.map(()=>({edge:"N",offset:Math.floor(P/2)}))),c(0),d("deploy"));return}if(p==="deploy"){h.leftArrow||y==="a"?U(C==="E"||C==="W"?E=>Math.max(0,E-1):E=>Math.max(0,E-1)):h.rightArrow||y==="d"?U(E=>Math.min(P-1,E+1)):h.upArrow||y==="w"?U(E=>Math.max(0,E-1)):h.downArrow||y==="s"?U(E=>Math.min(P-1,E+1)):y==="n"?u("N"):y==="e"?u("E"):y==="z"?u("S"):y==="x"?u("W"):h.return?(m(E=>{let _=[...E];return _[B]={edge:C,offset:v},_}),B<l.length-1?c(E=>E+1):d("confirm")):h.tab&&c(E=>(E+1)%l.length);return}if(p==="confirm"){if(h.return||y==="y"){let E=l.map((_,N)=>({raiderType:_,edge:f[N]?.edge??"N",offset:f[N]?.offset??8,waveDelay:0}));a(E)}return}});let F=(()=>{let y=Array.from({length:P},()=>Array(P).fill("\xB7"));for(let _ of o.structures)_.kind==="trap"||_.kind==="ward"?y[_.pos.y][_.pos.x]="?":y[_.pos.y][_.pos.x]=ge[_.kind];if(p==="deploy"||p==="confirm"){for(let _=0;_<f.length;_++){let N=f[_];if(!N)continue;let A=Es(N.edge,N.offset);y[A.y][A.x]=Ft[l[_]]}if(p==="deploy"){let _=Es(C,v);y[_.y][_.x]="\u25BC"}}let h=" "+Array.from({length:P},(_,N)=>N.toString(16).toUpperCase()).join(" "),E=y.map((_,N)=>{let A=N.toString(16).toUpperCase(),G=_.join(" ");return`${A} ${G}`});return[h,...E]})();return ve(Ye,{flexDirection:"column",padding:1,children:[ee(q,{bold:!0,color:"red",children:"\u2694 RAID PLANNER"}),ve(Ye,{gap:2,children:[ve(q,{children:["Target: ",ee(q,{bold:!0,color:"yellow",children:e})]}),ve(q,{children:["Trophies: ",ee(q,{bold:!0,children:t})]})]}),ee(q,{children:" "}),ee(Ye,{flexDirection:"column",children:F.map((y,h)=>h===0?ee(q,{dimColor:!0,children:y},h):ee(q,{children:y},h))}),ee(q,{children:" "}),ee(Ye,{gap:2,children:ee(q,{dimColor:!0,children:"# wall $ treasury ^ tower ! archer & vault ? hidden"})}),ee(q,{children:" "}),p==="scout"&&ve(Ye,{flexDirection:"column",children:[ee(q,{bold:!0,color:"cyan",children:"SCOUT PHASE"}),ee(q,{children:"Study the enemy keep. Traps and wards are hidden (?)."}),ve(q,{children:["Structures: ",o.structures.length," placed"]}),ee(q,{children:" "}),ee(q,{dimColor:!0,children:"Enter \u2192 pick your army Esc \u2192 back"})]}),p==="army"&&ve(Ye,{flexDirection:"column",children:[ve(q,{bold:!0,color:"cyan",children:["ARMY SELECTION (",l.length,"/",vs,")"]}),ee(q,{children:" "}),H.map((y,h)=>{let E=$e[y],_=To[y];return ee(Ye,{children:ve(q,{color:b===h?"yellow":void 0,bold:b===h,children:[b===h?" \u25B8 ":" ",ee(q,{color:_o[y],children:Ft[y]})," ",y.charAt(0).toUpperCase()+y.slice(1)," ","HP:",E.hp," DMG:",E.damage," SPD:",E.speed," ","(",_.gold,"g ",_.wood,"w ",_.stone,"s)"]})},y)}),ee(q,{children:" "}),ve(Ye,{gap:1,children:[ee(q,{children:"Army: "}),l.length===0?ee(q,{dimColor:!0,children:"(empty)"}):l.map((y,h)=>ee(q,{color:_o[y],bold:!0,children:Ft[y]},h))]}),ee(q,{children:" "}),ee(q,{dimColor:!0,children:"Enter add Backspace remove Tab/N \u2192 deploy Esc back"})]}),p==="deploy"&&ve(Ye,{flexDirection:"column",children:[ve(q,{bold:!0,color:"cyan",children:["DEPLOY \u2014 Placing ",Ft[l[B]]," (",l[B],") [",B+1,"/",l.length,"]"]}),ve(q,{children:["Edge: ",ee(q,{bold:!0,color:"yellow",children:ks[C]})," Position: ",ee(q,{bold:!0,children:v.toString(16).toUpperCase()})]}),ee(q,{children:" "}),ve(Ye,{gap:1,children:[ee(q,{children:"Spawns: "}),l.map((y,h)=>{let E=f[h],_=h<B||h===B&&!1;return ve(q,{color:h===B?"yellow":_?_o[y]:"gray",children:[Ft[y],E?`@${E.edge}${E.offset.toString(16)}`:"",h<l.length-1?" ":""]},h)})]}),ee(q,{children:" "}),ee(q,{dimColor:!0,children:"\u2190\u2192 / \u2191\u2193 position N/E/Z/X edge Enter place Tab next Esc back"})]}),p==="confirm"&&ve(Ye,{flexDirection:"column",children:[ee(q,{bold:!0,color:"green",children:"READY TO ATTACK"}),ee(q,{children:" "}),ee(Ye,{flexDirection:"column",children:l.map((y,h)=>{let E=f[h];return ve(q,{children:[ee(q,{color:_o[y],bold:!0,children:Ft[y]})," ",y," \u2192 ",E?`${ks[E.edge]} edge, pos ${E.offset.toString(16).toUpperCase()}`:"unplaced"]},h)})}),ee(q,{children:" "}),ve(q,{bold:!0,children:["Press ",ee(q,{color:"green",children:"Enter"})," to launch raid or ",ee(q,{color:"red",children:"Esc"})," to adjust"]})]})]})}import{useState as nt,useEffect as ao,useCallback as Te,useRef as Dt}from"react";import{useEffect as ka,useRef as _s,useCallback as va}from"react";import{existsSync as Ms,statSync as Is,openSync as Ea,readSync as Aa,closeSync as Ca,watch as _a}from"fs";import{existsSync as fd,mkdirSync as md,readFileSync as gd,writeFileSync as hd,unlinkSync as Td}from"fs";import{join as Cs}from"path";import{homedir as Sa}from"os";var ba=Cs(Sa(),".config","codekeep"),Pt=Cs(ba,"events.jsonl");var Ma=2e3;function Ps(e){let t=_s(0),o=_s(e);o.current=e;let s=va(()=>{if(!Ms(Pt))return;let r;try{r=Is(Pt).size}catch{return}if(r<t.current&&(t.current=0),r<=t.current)return;let a,T;try{let d=Buffer.alloc(r-t.current);T=Ea(Pt,"r"),Aa(T,d,0,d.length,t.current),a=d.toString("utf-8")}catch{return}finally{T!==void 0&&Ca(T)}t.current=r;let p=a.split(`
|
|
26
|
+
`).filter(Boolean);for(let d of p)try{let l=JSON.parse(d);o.current(l)}catch{}},[]);ka(()=>{if(Ms(Pt))try{t.current=Is(Pt).size}catch{t.current=0}let r=null;try{r=_a(Pt,()=>s())}catch{}let a=setInterval(s,Ma);return()=>{clearInterval(a),r?.close()}},[s])}function so(e){return e<=2?1:e<=5?2:e<=9?3:e<=14?4:5}function no(e,t,o){let s=[];for(let r=0;r<e;r++)t>=3&&o()<.2?s.push("brute"):t>=2&&o()<.3?s.push("scout"):s.push("raider");return s}function Vt(e){let t=e|0;return()=>{t=t+1831565813|0;let o=Math.imul(t^t>>>15,1|t);return o=o+Math.imul(o^o>>>7,61|o)^o,((o^o>>>14)>>>0)/4294967296}}function Ds(e){let t=e.progression;return{...e,progression:{...t,totalRaidsWon:t.totalRaidsWon??0,totalRaidsLost:t.totalRaidsLost??0,totalStructuresPlaced:t.totalStructuresPlaced??0,currentWinStreak:t.currentWinStreak??0,bestWinStreak:t.bestWinStreak??0,achievements:t.achievements??[],totalRaidersKilledByArcher:t.totalRaidersKilledByArcher??t.totalProbesKilledByScanner??0}}}var Ia={first_structure:{gold:20,wood:0,stone:0},defense_win_5:{gold:25,wood:15,stone:15},win_streak_3:{gold:10,wood:10,stone:10},win_streak_5:{gold:30,wood:30,stone:30},all_types:{gold:15,wood:15,stone:15},max_level:{gold:20,wood:0,stone:10},archer_kills_10:{gold:20,wood:10,stone:10},structures_20:{gold:10,wood:10,stone:10},raids_10:{gold:15,wood:15,stone:15},hoarder:{gold:25,wood:0,stone:0}};function rr(e){return Ia[e]??null}function sr(e){let t=e.progression,o=new Set(t.achievements),s=[],r=(T,p)=>{!o.has(T)&&p&&s.push(T)};r("first_structure",t.totalStructuresPlaced>=1),r("defense_win_5",t.totalRaidsWon>=5),r("win_streak_3",t.bestWinStreak>=3),r("win_streak_5",t.bestWinStreak>=5),r("structures_20",t.totalStructuresPlaced>=20),r("raids_10",t.totalRaidsWon+t.totalRaidsLost>=10),r("archer_kills_10",t.totalRaidersKilledByArcher>=10),r("hoarder",e.keep.resources.gold+e.keep.resources.wood+e.keep.resources.stone>=500);let a=new Set(e.keep.grid.structures.map(T=>T.kind));return r("all_types",a.size>=ut.length),r("max_level",e.keep.grid.structures.some(T=>T.level===3)),s}function Ls(e,t){if(t<=Ut)return e;let o=Math.pow(Rr,Math.floor((t-Ut)/Ut)+1);return{gold:Math.max(1,Math.floor(e.gold*o)),wood:Math.max(1,Math.floor(e.wood*o)),stone:Math.max(1,Math.floor(e.stone*o))}}var $s=5e3,nr=6e4;function Pa(e,t){if(e.keep.grid.structures.length<3)return{save:e,results:[]};let o=Math.min(Math.floor(t/qt),wr);if(o===0)return{save:e,results:[]};let s=[],r=e;for(let a=0;a<o;a++){let T=r.progression.totalRaidsWon+r.progression.totalRaidsLost,p=so(T),d=`bg-${r.lastPlayedAtUnixMs}-${a}`,l=3+p,x=Vt(r.lastPlayedAtUnixMs+a),b=no(l,p,x),R=pt({probeCount:l,keepGrid:r.keep.grid,seed:d,probeTypes:b}),f=R.events[R.events.length-1];if(f?.type!=="raid_end")continue;let m=f.outcome==="defense_win",C=R.events.filter(w=>w.type==="treasury_breach").reduce((w,F)=>({gold:w.gold+F.lootTaken.gold,wood:w.wood+F.lootTaken.wood,stone:w.stone+F.lootTaken.stone}),{gold:0,wood:0,stone:0}),u=R.events.filter(w=>w.type==="arrow_hit"&&w.hpRemaining<=0).length,v=m?{gold:10+p*3,wood:5+p*2,stone:5+p*2}:{gold:0,wood:0,stone:0},U={gold:Math.max(0,r.keep.resources.gold-C.gold),wood:Math.max(0,r.keep.resources.wood-C.wood),stone:Math.max(0,r.keep.resources.stone-C.stone)},B=rt(Oe(U,v)),c=m?r.progression.currentWinStreak+1:0,H={id:d,seed:d,rulesVersion:1,attackerId:"npc-bg",defenderKeepId:r.keep.id,startedAtUnixMs:r.lastPlayedAtUnixMs+a*qt,resolvedAtUnixMs:r.lastPlayedAtUnixMs+(a+1)*qt,outcome:f.outcome,lootLost:C,lootGained:v,replay:R};r={...r,keep:{...r.keep,resources:B},raidHistory:[...r.raidHistory.slice(-19),H],progression:{...r.progression,totalRaidsWon:r.progression.totalRaidsWon+(m?1:0),totalRaidsLost:r.progression.totalRaidsLost+(m?0:1),currentWinStreak:c,bestWinStreak:Math.max(r.progression.bestWinStreak,c),totalRaidersKilledByArcher:r.progression.totalRaidersKilledByArcher+u}},s.push({won:m,lootLost:C,defenseBonus:v,difficulty:p})}return{save:r,results:s}}function Ns(e,t){let[o,s]=nt(null),[r,a]=nt({x:8,y:8}),[T,p]=nt(0),[d,l]=nt(""),[x,b]=nt(null),[R,f]=nt(null),[m,C]=nt(null),[u,v]=nt(null),[U,B]=nt(null),[c,H]=nt([]),w=Dt(null),F=Dt(0),y=Dt(0),h=Dt(null),E=Dt(null),_=Dt(null),N=Dt(null),A=ut[T];Ps(g=>{s(S=>{if(!S)return S;let I=Wo(S.keep,g),Y={...S,keep:I,lastPlayedAtUnixMs:Date.now()};return t||Et(Y),Y})}),ao(()=>{let g=Gt();g||(g=Jr(process.env.USER||process.env.USERNAME||"Keeper")),g=Ds(g),g.lastPlayedAtUnixMs||(g={...g,lastPlayedAtUnixMs:g.savedAtUnixMs||Date.now()});let S=Date.now()-g.lastPlayedAtUnixMs,I={gold:0,wood:0,stone:0},Y=[];if(S>nr&&g.keep.grid.structures.length>0&&(I=Ko(g.keep.grid,S),(I.gold>0||I.wood>0||I.stone>0)&&(g={...g,keep:{...g.keep,resources:rt(Oe(g.keep.resources,I))}})),S>qt){let re=Pa(g,S);g=re.save,Y=re.results}let Q=sr(g);if(Q.length>0){g={...g,progression:{...g.progression,achievements:[...g.progression.achievements,...Q]}};for(let re of Q){let oe=rr(re);oe&&(g={...g,keep:{...g.keep,resources:Oe(g.keep.resources,oe)}})}}(I.gold>0||I.wood>0||I.stone>0||Y.length>0||Q.length>0)&&B({resources:I,raids:Y,newAchievements:Q}),g={...g,lastPlayedAtUnixMs:Date.now()},e&&(g={...g,tutorialCompleted:!1}),s(g),t||Et(g)},[]),ao(()=>(h.current=setInterval(()=>{s(g=>{if(!g||g.keep.grid.structures.length===0)return g;let S=Ko(g.keep.grid,nr);if(S.gold===0&&S.wood===0&&S.stone===0)return g;let I={...g,keep:{...g.keep,resources:rt(Oe(g.keep.resources,S))},lastPlayedAtUnixMs:Date.now()};return t||Et(I),I})},nr),()=>{h.current&&clearInterval(h.current)}),[t]),ao(()=>(E.current=setInterval(()=>{H(g=>{if(!o)return g;let S=Vt(Date.now()),I=zr(g,Date.now());return qr(I,o.keep.grid,Date.now(),S)})},Sr),()=>{E.current&&clearInterval(E.current)}),[o]),ao(()=>{let g=N.current;if(!g||!o)return;N.current=null;let S=Ho(c,g,o.keep.grid);if(!S)return;H(S.updatedFragments);let I=S.yield,Y={...o,keep:{...o.keep,resources:rt(Oe(o.keep.resources,I))}};G(Y);let Q=[];I.gold>0&&Q.push(`+${I.gold}${O.gold}`),I.wood>0&&Q.push(`+${I.wood}${O.wood}`),I.stone>0&&Q.push(`+${I.stone}${O.stone}`);let xe=S.collected[0]?.type.replace("_"," ")??"fragment",re=S.collected.length>1?` (${S.collected.length}x)`:"";K(`${Q.join(" ")} ${xe}${re}`)},[r]),ao(()=>()=>{w.current&&clearTimeout(w.current)},[]);let G=Te(g=>{let S=sr(g),I={...g,lastPlayedAtUnixMs:Date.now()};if(S.length>0){I={...I,progression:{...I.progression,achievements:[...I.progression.achievements,...S]}};for(let Y of S){let Q=rr(Y);Q&&(I={...I,keep:{...I.keep,resources:Oe(I.keep.resources,Q)}})}}if(s(I),t||Et(I),S.length>0){let Y=S.map(Q=>zt.find(xe=>xe.id===Q)?.name||Q);K(`\u{1F3C6} ${Y.join(", ")}!`)}},[t]),K=Te(g=>{w.current&&clearTimeout(w.current),l(g),w.current=setTimeout(()=>{l(""),w.current=null},3e3)},[]),pe=Te((g,S)=>{a(I=>{let Y={x:Math.max(0,Math.min(P-1,I.x+g)),y:Math.max(0,Math.min(P-1,I.y+S))};return N.current=Y,Y})},[]),ke=Te(g=>{p(S=>{let I=S+g;return I<0?ut.length-1:I>=ut.length?0:I})},[]),at=Te(g=>{g>=0&&g<ut.length&&p(g)},[]),$t=Te(()=>{if(!o)return;let g=Br(o.keep,r,A);if(g.ok&&g.keep){let S={...o,keep:g.keep,progression:{...o.progression,totalStructuresPlaced:o.progression.totalStructuresPlaced+1}};G(S),K(`Placed ${A}`)}else K(`!${g.reason}`)},[o,r,A,G,K]),jt=Te(()=>{if(!o)return;let g=Or(o.keep,r);g.ok&&g.keep?(G({...o,keep:g.keep}),K("Upgraded!")):K(`!${g.reason}`)},[o,r,G,K]),k=Te(()=>{if(!o)return;let g=Ur(o.keep,r);g.ok&&g.keep?(G({...o,keep:g.keep}),K("Demolished (50% refund)")):K(`!${g.reason}`)},[o,r,G,K]),z=Te(()=>{if(!o)return;let g=o.progression.totalRaidsWon+o.progression.totalRaidsLost,S=so(g),I=`attack-${Date.now()}-${Math.random()}`,Y=yo(I,S),Q=3+S,xe=Vt(Date.now()),re=no(Q,S,xe),oe=pt({probeCount:Q,keepGrid:Y.grid,seed:I,probeTypes:re});b(oe),f(Y.grid),C("attack");let ye=oe.events[oe.events.length-1];if(ye?.type==="raid_end"){let ie=ye.outcome!=="defense_win",et=oe.events.filter(we=>we.type==="treasury_breach").reduce((we,dt)=>({gold:we.gold+dt.lootTaken.gold,wood:we.wood+dt.lootTaken.wood,stone:we.stone+dt.lootTaken.stone}),{gold:0,wood:0,stone:0}),He=oe.events.filter(we=>we.type==="raider_destroyed").length,wt=oe.events.filter(we=>we.type==="wall_damaged"&&we.destroyed).length;v({won:ie,raidType:"attack",outcome:ye.outcome,lootGained:et,lootLost:{gold:0,wood:0,stone:0},raidersKilled:He,raidersTotal:Q,wallsDestroyed:wt,archersActive:Y.grid.structures.filter(we=>we.kind==="archerTower").length,difficulty:S});let it=ie?rt(Oe(o.keep.resources,et)):o.keep.resources,lt=ie?o.progression.currentWinStreak+1:0,Rt={id:I,seed:I,rulesVersion:1,attackerId:o.player.id,defenderKeepId:Y.id,startedAtUnixMs:Date.now(),resolvedAtUnixMs:Date.now(),outcome:ye.outcome,lootLost:{gold:0,wood:0,stone:0},lootGained:ie?et:{gold:0,wood:0,stone:0},replay:oe,defenderGrid:Y.grid};G({...o,keep:{...o.keep,resources:it},raidHistory:[...o.raidHistory.slice(-19),Rt],progression:{...o.progression,totalRaidsWon:o.progression.totalRaidsWon+(ie?1:0),totalRaidsLost:o.progression.totalRaidsLost+(ie?0:1),currentWinStreak:lt,bestWinStreak:Math.max(o.progression.bestWinStreak,lt)}})}},[o,G]),j=Te(()=>{if(!o)return;let g=o.progression.totalRaidsWon+o.progression.totalRaidsLost,S=so(g),I=`defend-${Date.now()}-${Math.random()}`,Y=3+S,Q=Vt(Date.now()),xe=no(Y,S,Q),re=pt({probeCount:Y,keepGrid:o.keep.grid,seed:I,probeTypes:xe});b(re),f(o.keep.grid),C("defend");let oe=re.events[re.events.length-1];if(oe?.type==="raid_end"){let ye=oe.outcome==="defense_win",ie=re.events.filter(te=>te.type==="treasury_breach").reduce((te,ct)=>({gold:te.gold+ct.lootTaken.gold,wood:te.wood+ct.lootTaken.wood,stone:te.stone+ct.lootTaken.stone}),{gold:0,wood:0,stone:0}),et=re.events.filter(te=>te.type==="raider_destroyed").length,He=re.events.filter(te=>te.type==="wall_damaged"&&te.destroyed).length,wt=re.events.filter(te=>te.type==="arrow_hit"&&te.hpRemaining<=0).length,it=ye?{gold:10+S*3,wood:5+S*2,stone:5+S*2}:{gold:0,wood:0,stone:0};v({won:ye,raidType:"defend",outcome:oe.outcome,lootGained:it,lootLost:ie,raidersKilled:et,raidersTotal:Y,wallsDestroyed:He,archersActive:o.keep.grid.structures.filter(te=>te.kind==="archerTower").length,difficulty:S});let lt={gold:Math.max(0,o.keep.resources.gold-ie.gold),wood:Math.max(0,o.keep.resources.wood-ie.wood),stone:Math.max(0,o.keep.resources.stone-ie.stone)},Rt=rt(Oe(lt,it)),we=ye?o.progression.currentWinStreak+1:0,dt={id:I,seed:I,rulesVersion:1,attackerId:"npc",defenderKeepId:o.keep.id,startedAtUnixMs:Date.now(),resolvedAtUnixMs:Date.now(),outcome:oe.outcome,lootLost:ie,lootGained:it,replay:re};G({...o,keep:{...o.keep,resources:Rt},raidHistory:[...o.raidHistory.slice(-19),dt],progression:{...o.progression,totalRaidsWon:o.progression.totalRaidsWon+(ye?1:0),totalRaidsLost:o.progression.totalRaidsLost+(ye?0:1),currentWinStreak:we,bestWinStreak:Math.max(o.progression.bestWinStreak,we),totalRaidersKilledByArcher:o.progression.totalRaidersKilledByArcher+wt}})}},[o,G]),Pe=Te(()=>{if(!o)return;let g=o.progression.totalRaidsWon+o.progression.totalRaidsLost,S=so(g),I=`quick-${Date.now()}-${Math.random()}`,Y=3+S,Q=Vt(Date.now()),xe=no(Y,S,Q),re=pt({probeCount:Y,keepGrid:o.keep.grid,seed:I,probeTypes:xe}),oe=re.events[re.events.length-1];if(oe?.type==="raid_end"){let ye=oe.outcome==="defense_win",ie=re.events.filter(te=>te.type==="treasury_breach").reduce((te,ct)=>({gold:te.gold+ct.lootTaken.gold,wood:te.wood+ct.lootTaken.wood,stone:te.stone+ct.lootTaken.stone}),{gold:0,wood:0,stone:0}),et=re.events.filter(te=>te.type==="arrow_hit"&&te.hpRemaining<=0).length,He=ye?{gold:10+S*3,wood:5+S*2,stone:5+S*2}:{gold:0,wood:0,stone:0},wt={gold:Math.max(0,o.keep.resources.gold-ie.gold),wood:Math.max(0,o.keep.resources.wood-ie.wood),stone:Math.max(0,o.keep.resources.stone-ie.stone)},it=rt(Oe(wt,He)),lt=ye?o.progression.currentWinStreak+1:0,Rt={id:I,seed:I,rulesVersion:1,attackerId:"npc",defenderKeepId:o.keep.id,startedAtUnixMs:Date.now(),resolvedAtUnixMs:Date.now(),outcome:oe.outcome,lootLost:ie,lootGained:He,replay:re};G({...o,keep:{...o.keep,resources:it},raidHistory:[...o.raidHistory.slice(-19),Rt],progression:{...o.progression,totalRaidsWon:o.progression.totalRaidsWon+(ye?1:0),totalRaidsLost:o.progression.totalRaidsLost+(ye?0:1),currentWinStreak:lt,bestWinStreak:Math.max(o.progression.bestWinStreak,lt),totalRaidersKilledByArcher:o.progression.totalRaidersKilledByArcher+et}});let we=re.events.filter(te=>te.type==="raider_destroyed").length,dt=re.events.filter(te=>te.type==="wall_damaged"&&te.destroyed).length;if(_.current={replay:re,grid:o.keep.grid,summary:{won:ye,raidType:"defend",outcome:oe.outcome,lootGained:He,lootLost:ie,raidersKilled:we,raidersTotal:Y,wallsDestroyed:dt,archersActive:o.keep.grid.structures.filter(te=>te.kind==="archerTower").length,difficulty:S}},ye)K(`Defense WIN! +${He.gold}${O.gold} +${He.wood}${O.wood} +${He.stone}${O.stone} [v] view`);else{let te=ie.gold+ie.wood+ie.stone;K(`Defense BREACH! Lost ${te} res [v] view`)}}},[o,G,K]),$=Te(()=>{let g=_.current;return g?(b(g.replay),f(g.grid),C("defend"),v(g.summary),_.current=null,!0):!1},[]),Z=Te(()=>{if(!o)return;let g=Ho(c,r,o.keep.grid);if(!g){K("!Nothing to collect here");return}H(g.updatedFragments);let S=g.yield,I={...o,keep:{...o.keep,resources:rt(Oe(o.keep.resources,S))}};G(I);let Y=[];S.gold>0&&Y.push(`+${S.gold}${O.gold}`),S.wood>0&&Y.push(`+${S.wood}${O.wood}`),S.stone>0&&Y.push(`+${S.stone}${O.stone}`);let Q=g.collected[0]?.type.replace("_"," ")??"fragment",xe=g.collected.length>1?` (${g.collected.length}x)`:"";K(`${Y.join(" ")} ${Q}${xe}`)},[o,c,r,G,K]),W=Te(g=>{if(!o)return!1;let S=g.attackerId!==o.player.id;return b(g.replay),f(S?o.keep.grid:g.defenderGrid??o.keep.grid),C(S?"defend":"attack"),v(null),!0},[o]),X=Te(()=>{b(null),f(null),C(null),v(null)},[]),_e=Te(()=>{o&&G({...o,tutorialCompleted:!0})},[o,G]),Tt=Te(()=>{if(!o)return;let g=Date.now(),S=g-F.current;if(S<$s){let re=Math.ceil(($s-S)/1e3);K(`Cooldown: ${re}s remaining`);return}F.current=g,y.current++;let I=Kr(),Y=Ls({...I.grants},y.current),Q=Wo(o.keep,{...I,grants:Y});G({...o,keep:Q});let xe=y.current>Ut?" (diminished)":"";K(`+${Y.gold}${O.gold} +${Y.wood}${O.wood} +${Y.stone}${O.stone} (${yr[I.type]??I.type})${xe}`)},[o,G,K]),Nt=Te((g,S)=>{g>=0&&g<P&&S>=0&&S<P&&a({x:g,y:S})},[]),Io=Te(g=>{if(!o||o.keep.grid.structures.length===0)return;let S=o.keep.grid.structures,I=`${r.x},${r.y}`,Y=S.findIndex(re=>`${re.pos.x},${re.pos.y}`===I),Q=Y===-1?0:Y+g;Q<0&&(Q=S.length-1),Q>=S.length&&(Q=0);let xe=S[Q];a({x:xe.pos.x,y:xe.pos.y})},[o,r]),De=Te(()=>{B(null)},[]),Po=o?.keep.grid.structures.find(g=>g.pos.x===r.x&&g.pos.y===r.y)??null;return{gameSave:o,cursor:r,selectedStructure:A,structureAtCursor:Po,message:d,fragments:c,raidReplay:x,raidGrid:R,raidType:m,raidSummary:u,offlineReport:U,moveCursor:pe,cycleStructure:ke,selectStructure:at,placeAtCursor:$t,upgradeAtCursor:jt,demolishAtCursor:k,collectAtCursor:Z,startAttackRaid:z,startDefendRaid:j,quickDefend:Pe,watchLastRaid:$,watchRaidRecord:W,clearRaid:X,completeTutorial:_e,grantSimResources:Tt,jumpToCoord:Nt,jumpToNextStructure:Io,clearOfflineReport:De}}var Mo=class{mode="online";baseUrl;auth=null;constructor(t){this.baseUrl=t.replace(/\/$/,"")}async request(t,o={}){let s={"Content-Type":"application/json",...o.headers??{}};this.auth&&(s.Authorization=`Bearer ${this.auth.token}`);let r=await fetch(`${this.baseUrl}${t}`,{...o,headers:s});if(!r.ok){let a=await r.json().catch(()=>({error:r.statusText}));throw new Error(a.error??`HTTP ${r.status}`)}return r.json()}isAuthenticated(){return this.auth!==null}async register(t){let o=await this.request("/auth/register",{method:"POST",body:JSON.stringify({displayName:t})});return this.auth={token:o.token,apiKey:o.apiKey,playerId:o.playerId},o}async login(t){let o=await this.request("/auth/login",{method:"POST",body:JSON.stringify({apiKey:t})});return this.auth={token:o.token,apiKey:t,playerId:o.playerId},o}async load(){if(!this.auth)return null;try{let t=await this.request("/me");return t.keep?{schemaVersion:1,savedAtUnixMs:Date.now(),player:{id:t.player.id,displayName:t.player.displayName,settings:{asciiMode:!1}},keep:{id:t.keep.id,name:t.keep.name,ownerPlayerId:t.player.id,grid:t.keep.grid,resources:t.keep.resources,createdAtUnixMs:t.player.createdAt,updatedAtUnixMs:Date.now()},raidHistory:[],tutorialCompleted:!0,lastPlayedAtUnixMs:Date.now(),progression:{totalBuildsToday:0,totalCommitsToday:0,lastDailyResetDay:Math.floor(Date.now()/864e5),totalRaidsWon:t.progression?.totalRaidsWon??0,totalRaidsLost:t.progression?.totalRaidsLost??0,totalStructuresPlaced:t.progression?.totalStructuresPlaced??0,currentWinStreak:t.progression?.currentWinStreak??0,bestWinStreak:t.progression?.bestWinStreak??0,achievements:t.progression?.achievements??[],totalRaidersKilledByArcher:0}}:null}catch{return null}}async save(t){this.auth&&await this.request("/keep/save",{method:"POST",body:JSON.stringify({grid:t.keep.grid,resources:t.keep.resources})})}async createNew(t){let o=await this.register(t),s=await this.load();return s||{schemaVersion:1,savedAtUnixMs:Date.now(),player:{id:o.playerId,displayName:t,settings:{asciiMode:!1}},keep:{id:`keep-${o.playerId}`,name:`${t}'s Keep`,ownerPlayerId:o.playerId,grid:{width:16,height:16,structures:[]},resources:{...St},createdAtUnixMs:Date.now(),updatedAtUnixMs:Date.now()},raidHistory:[],tutorialCompleted:!1,lastPlayedAtUnixMs:Date.now(),progression:{totalBuildsToday:0,totalCommitsToday:0,lastDailyResetDay:Math.floor(Date.now()/864e5),totalRaidsWon:0,totalRaidsLost:0,totalStructuresPlaced:0,currentWinStreak:0,bestWinStreak:0,achievements:[],totalRaidersKilledByArcher:0}}}async deleteAll(){return!1}async findMatch(){return(await this.request("/matchmaking/find",{method:"POST"})).targets}async launchPvpRaid(t,o,s){return this.request("/raids/launch",{method:"POST",body:JSON.stringify({defenderPlayerId:t,probeTypes:o,spawnSpecs:s})})}async getIncomingRaids(t){return(await this.request(`/raids/incoming?since=${t}`)).raids}async getPvpProfile(){try{let t=await this.request("/me");return{trophies:t.player.trophies,league:t.player.league,shieldExpiresAt:t.player.shieldExpiresAt,seasonId:"S1",seasonPeakTrophies:t.player.trophies}}catch{return null}}async getLeaderboard(t=50){return(await this.request(`/leaderboard?limit=${t}`)).players}async getWarCamp(){try{return await this.request("/warcamp")}catch{return null}}async trainRaider(t,o){let s=await this.request("/warcamp/train",{method:"POST",body:JSON.stringify({slotId:t,raiderType:o})});return{slots:s.slots,maxSlots:s.maxSlots}}async getBounties(){try{return(await this.request("/bounties")).bounties}catch{return[]}}async claimBounty(t){return(await this.request(`/bounties/${t}/claim`,{method:"POST"})).reward}async registerForMatchmaking(){await this.request("/matchmaking/register",{method:"POST"})}async sync(){this.auth&&await this.registerForMatchmaking()}};import{jsx as ae,jsxs as je}from"react/jsx-runtime";var Os=60,Us=18;function Ba(){let{stdout:e}=Na(),[t,o]=be({columns:e?.columns??process.stdout.columns??80,rows:e?.rows??process.stdout.rows??24});return Da(()=>{let s=e??process.stdout,r=()=>{o({columns:s.columns,rows:s.rows})};return s.on("resize",r),()=>{s.off("resize",r)}},[e]),t}function Oa({asciiMode:e,compact:t,forceTutorial:o,autoResume:s,serverUrl:r,dryRun:a}){let{exit:T}=La(),{columns:p,rows:d}=Ba(),[l,x]=be(r?"auth":s?"keep":"menu"),[b,R]=be(e),[f,m]=be(!1),[C,u]=be(null),[v,U]=be(null),[B,c]=be(!1),[H,w]=be(""),[F,y]=be("keep"),h=Bs(1),E=Bs(r?new Mo(r):null),[_,N]=be(!1),[A,G]=be(null),[K,pe]=be([]),[ke,at]=be(!1),[$t,jt]=be([]),[k,z]=be(!1),[j,Pe]=be({slots:[],maxSlots:3}),[$,Z]=be(null),[W,X]=be(null),[_e,Tt]=be(!1),Nt=t||p<80||d<24,Io=p<Os||d<Us,{gameSave:De,cursor:Po,selectedStructure:g,message:S,fragments:I,raidReplay:Y,raidGrid:Q,raidType:xe,raidSummary:re,offlineReport:oe,structureAtCursor:ye,moveCursor:ie,cycleStructure:et,selectStructure:He,placeAtCursor:wt,upgradeAtCursor:it,demolishAtCursor:lt,collectAtCursor:Rt,startAttackRaid:we,startDefendRaid:dt,quickDefend:te,watchLastRaid:ct,watchRaidRecord:Do,clearRaid:ir,completeTutorial:Ks,grantSimResources:Hs,jumpToCoord:Fs,jumpToNextStructure:Vs,clearOfflineReport:Ys}=Ns(o,a),lr=Yt(()=>{T()},[T]),js=Yt(()=>{Xr(),T()},[T]),dr=Yt(async(L,ne)=>{let he=E.current;if(he){Tt(!0),X(null);try{L==="login"?await he.login(ne):await he.register(ne),N(!0),x("menu"),he.getPvpProfile?.().then(Fe=>G(Fe)),he.registerForMatchmaking?.()}catch(Fe){X(Fe instanceof Error?Fe.message:"Connection failed")}finally{Tt(!1)}}},[]),qs=Yt(async()=>{let L=E.current;if(L?.findMatch){at(!0);try{let ne=await L.findMatch();pe(ne)}catch{pe([])}finally{at(!1)}}},[]),zs=Yt(async(L,ne,he)=>{let Fe=E.current;if(!(!Fe?.launchPvpRaid||!De))try{let Bt=await Fe.launchPvpRaid(L.playerId,ne,he);Do({replay:Bt.replay,attackerId:De.player.id,defenderKeepId:L.playerId,defenderGrid:L.grid})&&(y("pvp"),x("raid")),G(Lo=>Lo&&{...Lo,trophies:Bt.newTrophies,league:Bt.newLeague})}catch{}},[De,Do]),Zs=Yt(async()=>{let L=E.current;if(L?.getLeaderboard){z(!0);try{let ne=await L.getLeaderboard();jt(ne)}catch{jt([])}finally{z(!1)}}},[]);return $a((L,ne)=>{if(f){m(!1);return}if(L==="?"){m(!0);return}if(B){if(ne.escape){c(!1),w("");return}if(ne.return){let he=H.split(/[, ]+/);if(he.length===2){let Fe=parseInt(he[0],16),Bt=parseInt(he[1],16);!isNaN(Fe)&&!isNaN(Bt)&&Fs(Fe,Bt)}c(!1),w("");return}if(ne.backspace||ne.delete){w(he=>he.slice(0,-1));return}/^[0-9a-fA-F, ]$/.test(L)&&w(he=>he+L);return}if(oe&&l==="keep"&&Ys(),l!=="menu"&&l!=="tutorial"){if(l==="raid"||l==="friendRaid"){(L==="q"||ne.escape)&&(ir(),u(null),U(null),x("keep"));return}if(!(l==="friendList"||l==="raidLog"||l==="settings"||l==="pvp"||l==="warcamp"||l==="leaderboard"||l==="auth"||l==="raidPlanner")){if(L==="q"){lr();return}if(ne.escape){x("menu");return}L==="h"||L==="a"||ne.leftArrow?ie(-1,0):L==="l"||L==="d"||ne.rightArrow?ie(1,0):L==="k"||L==="w"||ne.upArrow?ie(0,-1):L==="j"||L==="s"||ne.downArrow?ie(0,1):ne.return||L==="e"?wt():L==="u"?it():L==="x"?lt():L==="["?et(-1):L==="]"?et(1):L==="f"?Hs():L==="c"?Rt():L==="r"?te():L==="v"?ct()&&x("raid"):L==="g"?(c(!0),w("")):ne.tab?Vs(1):L>="1"&&L<="6"&&He(parseInt(L)-1)}}}),De?Io?je(Lt,{flexDirection:"column",padding:1,children:[ae(Ie,{bold:!0,color:"yellow",children:"\u25C6 CodeKeep"}),ae(Ie,{children:" "}),ae(Ie,{color:"red",children:"Terminal too small"}),je(Ie,{children:["Need ",je(Ie,{bold:!0,children:[Os,"x",Us]}),", got ",je(Ie,{bold:!0,children:[p,"x",d]})]}),ae(Ie,{children:" "}),ae(Ie,{dimColor:!0,children:"Resize your terminal or run with --compact"})]}):f?ae(rs,{}):l==="tutorial"||!De.tutorialCompleted?ae(ds,{gameSave:De,onComplete:()=>{Ks(),x("keep")}}):l==="auth"?ae(bs,{error:W,isLoading:_e,onLogin:L=>dr("login",L),onRegister:L=>dr("register",L),onPlayOffline:()=>{N(!1),x("menu")}}):l==="menu"?ae(ns,{gameSave:De,onlineMode:_,onKeep:()=>x("keep"),onAttack:()=>{we(),x("raid")},onDefend:()=>{dt(),x("raid")},onPvp:()=>x("pvp"),onFriendRaid:()=>x("friendList"),onRaidLog:()=>x("raidLog"),onSettings:()=>x("settings"),onQuit:lr}):l==="raidLog"?ae(ms,{gameSave:De,onBack:()=>x("menu"),onWatchReplay:L=>{Do(L)&&(y("raidLog"),x("raid"))}}):l==="settings"?ae(Ts,{onBack:()=>x("menu"),onResetGame:js,onReplayTutorial:()=>x("tutorial"),asciiMode:b,onToggleAscii:()=>R(L=>!L)}):l==="pvp"?ae(ys,{pvpProfile:A,targets:K,isSearching:ke,onSearch:qs,onAttack:L=>{Z(L),x("raidPlanner")},onWarCamp:()=>{E.current?.getWarCamp?.().then(ne=>{ne&&Pe(ne)}),x("warcamp")},onLeaderboard:()=>{Zs(),x("leaderboard")},onBack:()=>x("menu")}):l==="raidPlanner"&&$?ae(As,{targetName:$.displayName,targetTrophies:$.trophies,targetGrid:$.grid,warCamp:j,resources:De.keep.resources,onLaunch:L=>{let ne=L.map(he=>he.raiderType);zs($,ne,L),Z(null)},onBack:()=>{Z(null),x("pvp")}}):l==="warcamp"?ae(Rs,{warCamp:j,resources:De.keep.resources,onTrain:(L,ne)=>{E.current?.trainRaider?.(L,ne).then(Fe=>Pe(Fe))},onBack:()=>x("pvp")}):l==="leaderboard"?ae(Ss,{entries:$t,currentPlayerId:De.player.id,isLoading:k,onBack:()=>x("pvp")}):l==="friendList"?ae(ps,{onSelectFriend:L=>{let ne=`friend-raid-${Date.now()}`,he=pt({probeCount:4,keepGrid:L.grid,seed:ne});u(he),U(L.grid),x("friendRaid")},onBack:()=>x("menu")}):l==="friendRaid"&&C&&v?je(Lt,{flexDirection:"column",children:[ae(Ie,{bold:!0,color:"yellow",children:"[ LOCAL SIMULATION ]"}),ae(Jo,{replay:C,keepGrid:v,raidType:"attack",initialSpeed:h.current,onSpeedChange:L=>{h.current=L},onDone:()=>{u(null),U(null),x("menu")}})]}):l==="raid"&&Y&&Q?ae(Jo,{replay:Y,keepGrid:Q,raidType:xe||"defend",summary:re||void 0,initialSpeed:h.current,onSpeedChange:L=>{h.current=L},onDone:()=>{ir(),x(F),y("keep")}}):je(Lt,{flexDirection:"column",children:[oe&&je(Lt,{flexDirection:"column",children:[(oe.resources.gold>0||oe.resources.wood>0||oe.resources.stone>0)&&je(Ie,{color:"green",children:["Passive income: +",oe.resources.gold,O.gold," +",oe.resources.wood,O.wood," +",oe.resources.stone,O.stone]}),oe.raids.length>0&&je(Ie,{color:"yellow",children:["Background raids: ",oe.raids.filter(L=>L.won).length,"W / ",oe.raids.filter(L=>!L.won).length,"L"]}),oe.newAchievements.length>0&&je(Ie,{color:"magenta",bold:!0,children:["New achievements: ",oe.newAchievements.join(", ")]}),ae(Ie,{dimColor:!0,children:"(press any key to continue)"})]}),ae(os,{resources:De.keep.resources,selectedStructure:g,message:B?`Jump to: ${H}_`:S,compact:Nt,structureAtCursor:ye,fragmentCount:I.length,dryRun:a}),je(Lt,{flexDirection:"row",children:[ae(ts,{grid:De.keep.grid,cursor:Po,asciiMode:b,compact:Nt,fragments:I}),!Nt&&je(Lt,{flexDirection:"column",marginLeft:2,width:28,children:[ae(us,{selected:g}),ae(Lt,{marginTop:1,children:ae(Ie,{dimColor:!0,children:`e place u upgrade x demo
|
|
31
27
|
[ ] cycle 1-6 select
|
|
32
28
|
c collect r raid v replay
|
|
33
29
|
g jump Tab next f +res
|
|
34
|
-
?help Esc menu q quit`})})]})]}),Nt&&ae(
|
|
35
|
-
`),process.exit(1));let d=
|
|
30
|
+
?help Esc menu q quit`})})]})]}),Nt&&ae(Ie,{dimColor:!0,children:"e/u/x build c collect r raid v replay f +res q quit"})]}):ae(Ie,{children:"Loading..."})}function Gs(e){return ae(ko,{children:ae(Oa,{...e})})}import{jsx as Wa}from"react/jsx-runtime";var ar="0.2.3";globalThis.__CODEKEEP_VERSION=ar;var Ws=new Ua;Ws.name("codekeep").description("Async tower defense terminal game powered by your coding activity").version(ar).option("--ascii","Force ASCII-only rendering (no Unicode box drawing)").option("--compact","Compact layout for narrow terminals").option("--tutorial","Replay the tutorial").option("--resume","Skip menu, jump straight to keep").option("--no-save","Dry-run mode: play without writing to disk").option("--stats","Print save file stats as JSON (headless, no TUI)").option("--online <url>","Connect to a CodeKeep server for multiplayer").option("--server <url>","Alias for --online").action(e=>{if(e.stats){let p=Gt();p||(process.stdout.write(JSON.stringify({error:"No save file found"})+`
|
|
31
|
+
`),process.exit(1));let d=p.progression,l={version:ar,player:p.player.displayName,keepAge:Math.max(1,Math.floor((Date.now()-p.keep.createdAtUnixMs)/864e5)),structures:p.keep.grid.structures.length,resources:p.keep.resources,raids:{won:d.totalRaidsWon,lost:d.totalRaidsLost,winStreak:d.currentWinStreak,bestStreak:d.bestWinStreak},achievements:d.achievements?.length??0,tutorialCompleted:p.tutorialCompleted,lastPlayed:new Date(p.lastPlayedAtUnixMs).toISOString()};process.stdout.write(JSON.stringify(l,null,2)+`
|
|
36
32
|
`),process.exit(0)}(!process.stdin.isTTY||!process.stdout.isTTY)&&(process.stderr.write(`codekeep requires an interactive terminal.
|
|
37
33
|
Run it directly in your terminal (not piped or in CI).
|
|
38
34
|
Use --stats for headless save file info.
|
|
39
|
-
`),process.exit(1));let t=!!Gt(),o=e.online||e.server||process.env.CODEKEEP_SERVER,{waitUntilExit:s,unmount:r}=
|
|
35
|
+
`),process.exit(1));let t=!!Gt(),o=e.online||e.server||process.env.CODEKEEP_SERVER,{waitUntilExit:s,unmount:r}=Ga(Wa(Gs,{asciiMode:e.ascii??!1,compact:e.compact??!1,forceTutorial:e.tutorial??!1,autoResume:(e.resume??!1)&&t,serverUrl:o,dryRun:e.save===!1}),{exitOnCtrlC:!1}),a=!1;function T(){if(!a){if(a=!0,e.save!==!1){process.stderr.write(`
|
|
40
36
|
Saving...
|
|
41
|
-
`);try{let
|
|
37
|
+
`);try{let p=Gt();p&&Et(p)}catch{}}r()}}process.on("SIGINT",T),process.on("SIGTERM",T),process.on("SIGHUP",T),s().then(()=>{process.exit(0)}).catch(()=>{process.exit(1)})});Ws.parse();export{ar as CLI_VERSION};
|