diff --git a/dist/game.js b/dist/game.js index bb87228..9acffed 100644 --- a/dist/game.js +++ b/dist/game.js @@ -1,61 +1,61 @@ -var q={version:"v27.6 direct conveyor chick movement",time:{daySeconds:60,farmShutdownGraceSeconds:10},cash:{starting:250},grid:{x:170,y:78,cols:20,rows:10,cell:46,expansion:{colsPerPurchase:10,rowsPerPurchase:10,costBase:1e3,costMultiplier:1.3333333333333333,truncateUnit:100}},map:{blockedRatio:.075},economy:{income:{mixer:5,truck:10},poopFine:10,zundaTax:{exemption:500,legalWorkExemptionBonus:250,maxRateCash:1e4,stepAmount:500,stepRate:.05,maxRate:.95},fairiesTribute:{perDay:10},incomeUpgradeRate:1.1,explosionDamageDivisor:30,maleTruckFinePerHalfDay:30,shredderBonus:{maxCards:30,probabilityPercentDivisor:2}},production:{poopRate:.08,autoScannerCooldown:3.5,autoScannerUpgradeRate:.95,autoScannerMinCooldown:.5,eggSpawnRanges:[[1.95,4.35],[1.55,3.45],[1.25,2.8],[1.05,2.35]]},conveyor:{baseSpeed:52,maxSpeed:300,bearingSpeedMultiplier:1.075},cards:{definitions:[{id:"upgradeEgg",title:"EGG Improvement",rarity:"common",type:"equipmentUpgrade",target:"eggFarm",description:"Choose one EGG FARM on the map and raise it by 1 level.",tags:["EGG","ACTIVE"]},{id:"upgradeAutoScanner",title:"AUTO SCANNER Improvement",rarity:"common",type:"equipmentUpgrade",target:"autoScanner",description:"Choose one AUTO SCANNER on the map and raise it by 1 level.",tags:["SCANNER","ACTIVE"]},{id:"upgradeMixer",title:"MIXER Improvement",rarity:"common",type:"equipmentUpgrade",target:"mixer",description:"Choose MIXER on the map and raise it by 1 level.",tags:["ECONOMY","ACTIVE"]},{id:"upgradeTruck",title:"TRUCK Improvement",rarity:"common",type:"equipmentUpgrade",target:"truck",description:"Choose TRUCK on the map and raise it by 1 level.",tags:["ECONOMY","ACTIVE"]},{id:"upgradeTrash",title:"SHREDDER Improvement",rarity:"common",type:"equipmentUpgrade",target:"trash",description:"Shred poops for a little money. Max 30 upgrades.",tags:["POOP","ACTIVE"]},{id:"crowdedFarming",title:"Crowded Farming Contract",rarity:"common",type:"instant",description:"EGG production interval -5%. Equipment deterioration +4%. Max 10 cards.",tags:["EGG","RISK","PASSIVE"]},{id:"hatchingFeed",title:"Hatching Feed",rarity:"common",type:"instant",description:"EGG production interval -4%. Poop rate +3%. No card limit.",tags:["EGG","POOP","PASSIVE"]},{id:"safetyCover",title:"Safety Cover",rarity:"common",type:"instant",description:"Explosion damage -5%. No card limit.",tags:["RISK","PASSIVE"]},{id:"preventiveMaintenance",title:"Preventive Maintenance Manual",rarity:"common",type:"instant",description:"All equipment deterioration speed -5%. No card limit.",tags:["MAINTENANCE","PASSIVE"]},{id:"dudFilter",title:"DUD Filter",rarity:"common",type:"instant",description:"DUD chance -5 percentage points. Stacks until DUD chance reaches 0%.",tags:["CARD","PASSIVE"]},{id:"durabilityCoating",title:"Durability Coating",rarity:"common",type:"instant",description:"All equipment maximum durability +3%. No card limit.",tags:["MAINTENANCE","PASSIVE"]},{id:"bearing",title:"High-Quality Bearing",rarity:"common",type:"instant",description:"Conveyor speed +7.5%.",tags:["CONVEYOR","PASSIVE"]},{id:"sparePartsBin",title:"SPARE PARTS BIN",rarity:"common",type:"instant",description:"Restores 2% durability to every machine. A box of almost-compatible parts.",tags:["MAINTENANCE","ACTIVE"],weightBase:100,weightLossPerCopy:2},{id:"dudRefund",title:"DUD REFUND",rarity:"common",type:"instant",description:"DUDs now come with paperwork. Gain JPY 50 per copy when you pick a DUD.",tags:["CARD","ECONOMY","PASSIVE"]},{id:"freeReroll",title:"FREE REROLL",rarity:"common",type:"instant",description:"The first bad ideas are free.",tags:["CARD","PASSIVE"]},{id:"composter",title:"COMPOSTER",rarity:"common",type:"instant",description:"Turns shredder waste into suspicious fertilizer. Gain JPY 3 per copy when SHREDDER processes poop.",tags:["POOP","ECONOMY","PASSIVE"]},{id:"scannerQueueSpacing",title:"Scanner Queue Spacing",rarity:"common",type:"instant",description:"Manual and Auto Scanner queue spacing -15%. No card limit.",tags:["SCANNER","PASSIVE"]},{id:"extraEggOutlet",title:"Extra Egg Outlet",rarity:"rare",type:"equipmentUpgrade",target:"eggOutlet",description:"Choose one EGG FARM and add +1 output port to that EGG. Max +3 per EGG.",tags:["EGG","RARE","ACTIVE"]},{id:"recyclingSubsidy",title:"Recycling Subsidy",rarity:"rare",type:"instant",description:"SELL refund rate +10 points. Max 3 cards; normal SELL can reach 80%.",tags:["ECONOMY","RARE","PASSIVE"]},{id:"dynamite",title:"Dynamite",rarity:"rare",type:"cellAction",target:"blockedCell",description:"Remove any 3 unbuildable cells",tags:["RISK","RARE","ONE-SHOT"]},{id:"usedMachine",title:"Used Machines",rarity:"rare",type:"instant",description:"Future equipment costs half. New purchases have no refund and 60% durability. New Machines cancels it.",tags:["ECONOMY","RISK","RARE"]},{id:"newMachine",title:"New Machines",rarity:"common",type:"instant",description:"Cancel Used Machines and restore normal prices, refunds, and durability.",tags:["ECONOMY","ONE-SHOT"]},{id:"flattery",title:"Flattery",rarity:"rare",type:"instant",description:"Next Fairies tribute -50%.",tags:["ECONOMY","RARE","ONE-SHOT"]},{id:"loan",title:"Loan",rarity:"rare",type:"instant",description:"Gain JPY 300 per elapsed day now. Pay JPY 400 per day for that many days starting tomorrow.",tags:["ECONOMY","RISK","RARE"]},{id:"extraCards",title:"Extra Cards",rarity:"rare",type:"instant",description:"Draw 2 cards now and choose both.",tags:["RARE","ACTIVE"]},{id:"legalWork",title:"Legal Work",rarity:"rare",type:"instant",description:"ZUNDA TAX exemption +\uFFE5250. The 95% cap point also moves up by \uFFE5250.",tags:["ECONOMY","RARE","PASSIVE"]},{id:"chemicalWeaponSubsidy",title:"Chemical Weapons Subsidy",rarity:"ultraRare",type:"instant",description:"Each day, gain JPY 2000 per 100 poop shipped by truck. No card limit.",tags:["POOP","ULTRA RARE","PASSIVE"]},{id:"laborExploitation",title:"Motivational Exploitation",rarity:"ultraRare",type:"instant",description:"Repairman labor cost -30%. The repairman smiles with bloodshot eyes. One time only.",tags:["MAINTENANCE","ULTRA RARE","PASSIVE"]},{id:"rescueLoan",title:"Emergency Loan",rarity:"ultraRare",type:"instant",description:"If cash is below JPY 0 at day-end settlement, gain JPY 500. Consumed on use.",tags:["ECONOMY","ULTRA RARE","ONE-SHOT"]}],defaultEffects:{bearing:0,legalWork:0,fairiesFlatteryNext:0,crowdedFarming:0,extraEggOutlet:0,hatchingFeed:0,safetyCover:0,recyclingSubsidy:0,chemicalWeaponSubsidy:0,preventiveMaintenance:0,dudFilter:0,durabilityCoating:0,sparePartsBin:0,dudRefund:0,freeReroll:0,composter:0,scannerQueueSpacing:0,laborExploitation:0,usedMachineActive:!1,rescueLoanCharges:0,loans:[]},commonWeight:8,rareWeight:2,ultraRareWeight:.45,baseDraftSize:3,maxDudsPerDraft:2,dudChancePerCard:.3,dudChanceReductionPerFilter:.05,extraCardsAddedPicks:2,rerollCost:{dayDivisor:2,multiplier:100}},contracts:{eventChance:.7,firstTurn:8},maintenance:{fullPerformanceUntilWear:.5,minimumPerformance:.5,durability:{conveyor:1300,eggFarm:475,autoScanner:850,mixer:775,trash:950},processingDelayMaxSeconds:{mixer:1.2,trash:1},repairman:{dailyCost:100,secondsPerOnePercent:.5,walkSpeed:90}},facilities:{conveyor:{id:"conveyor",type:"conveyor",name:"Conveyor",shortName:"Belt",price:30,buildable:!0,upgradeable:!1},boostConveyor:{id:"boostConveyor",type:"conveyor",name:"Boost Conveyor",shortName:"BOOST BELT",price:75,buildable:!0,upgradeable:!1,speedMultiplier:2},eggFarm:{id:"eggFarm",type:"eggFarm",name:"Egg Farm",shortName:"EGG",price:250,buildable:!0,upgradeable:!0,maxLevel:4,upgradeCosts:[null,300,720,1600]},manualScanner:{id:"manualScanner",type:"scanner",kind:"manual",name:"Manual Scanner",shortName:"Manual Scanner",price:260,buildable:!0,upgradeable:!1},autoScanner:{id:"autoScanner",type:"scanner",kind:"auto",name:"Auto Scanner",shortName:"Auto Scanner",price:360,buildable:!0,upgradeable:!1},mixer:{id:"mixer",type:"facility",name:"Mixer",shortName:"MIXER",price:450,buildable:!0,upgradeable:!0,incomeKey:"mixer",body:{w:164,h:126}},trash:{id:"trash",type:"facility",name:"Waste Shredder",shortName:"SHREDDER",price:240,buildable:!0,upgradeable:!0,body:{w:180,h:100}},truck:{id:"truck",type:"facility",name:"Truck",shortName:"TRUCK",price:450,buildable:!0,upgradeable:!0,incomeKey:"truck",body:{w:176,h:138}}}};var yn=q.version,Bt=q.time.daySeconds,Jt=q.cash.starting,Ce=q.economy,tr=q.time.farmShutdownGraceSeconds,hn=q.production.autoScannerCooldown,nr=q.production.autoScannerUpgradeRate,eo=q.production.autoScannerMinCooldown,or=q.contracts.eventChance,ft=q.contracts.firstTurn,rr=q.production.poopRate,mn=q.conveyor.baseSpeed,Lt=q.conveyor.maxSpeed,bn=q.conveyor.bearingSpeedMultiplier,Ee=q.cards,ir=q.cards.definitions,to=q.cards.defaultEffects,et=q.production.eggSpawnRanges,y=q.grid,we=q.facilities,no=["conveyor","boostConveyor","eggFarm","autoScanner","manualScanner","mixer","trash","truck"],Zt=["mixer","trash","truck"],sr=["mixer","truck"],pt=Object.fromEntries(Object.entries(we).map(([e,t])=>[e,t.price])),pe=[{name:"right",dc:1,dr:0,angle:0,opposite:"left"},{name:"left",dc:-1,dr:0,angle:Math.PI,opposite:"right"},{name:"down",dc:0,dr:1,angle:Math.PI/2,opposite:"up"},{name:"up",dc:0,dr:-1,angle:-Math.PI/2,opposite:"down"}],f={bg:"#eefbea",ink:"#102015",muted:"#526456",green:"#22b94f",greenSoft:"#b7f5c6",mixerBlue:"#2477ff",truckPink:"#ff6aa8",wasteGreen:"#22b94f",danger:"#c42424",warn:"#f0a020",white:"#ffffff",poop:"#7b4a23",male:"#62a9ff",female:"#ff8bc6"},Se={ambient:0,important:1,critical:2};function S(e){let t=Number(e)||0;return`${t<0?"-":""}\uFFE5${Math.ceil(Math.abs(t)).toLocaleString("ja-JP")}`}function I(e,t){return`${e},${t}`}function J(e){let[t,n]=String(e).split(",").map(Number);return{col:t,row:n}}function jt(e,t,n){return Math.max(t,Math.min(n,e))}function W(e,t){return e+Math.random()*(t-e)}function Pe(e,t){return e>=0&&e=0&&tMath.round(jt(r,0,255)).toString(16).padStart(2,"0");return`#${o(e)}${o(t)}${o(n)}`}function ro(e,t,n){let o=lr(e),r=lr(t);return As(oo(o.r,r.r,n),oo(o.g,r.g,n),oo(o.b,r.b,n))}var re={appTitle:"Chick Sorter",versionTitle:`Chick Sorter ${yn}`,currency:"\uFFE5",phases:{title:"Title",build:"Build",running:"Sorting",clearing:"Clearing",loanDecision:"Emergency Loan",gameover:"Game Over"},actions:{startGame:"Start Game",nextDay:"Next Day",buildPhase:"Build Phase",restart:"Restart",close:"Close",remove:"Remove",refund:"Refund",autoMenu:"Auto Menu"},status:{build:e=>`BUILD: ${(e==="erase"?"SELL":e||"SELECT").toUpperCase()} | Right-drag pan | Hover equipment for info`,sorting:"Sorting: use scanner keys. Build after clearing the day.",allPortsConnected:"Start condition met: one EGG route reaches an exit.",equipmentPanelEmpty:"Hover equipment in Build phase.",eventPanelRunning:"Forced events appear in Build phase."},routeLabels:{mixer:"MIXER",truck:"TRUCK",trash:"WASTE",scanner:"NEXT","scanner-role-1":"NEXT",input:"IN"},fail:{outOfGrid:"Out of grid",cellOccupied:"Cell occupied",notEnoughCash:"Not enough cash",facilityExists:"Facility already exists",facilityOverlap:"Facility overlap",nothingToErase:"Nothing to sell",noUpgrade:"No upgrade available"}};function ht(e){return we[e]?.name||String(e||"").replace(/^[a-z]/,t=>t.toUpperCase())}function $s(e){let t=we[e];return t?e==="conveyor"||e==="boostConveyor"?`${S(t.price)} / tile`:e==="autoScanner"?`${S(t.price)} / ${hn.toFixed(1)}s cooldown`:e==="trash"?S(t.price):S(t.price):""}function Is(e){return{conveyor:"A narrow green belt. Routes decide whether chicks live, ship, or become invoices.",boostConveyor:"Moves chicks twice as fast. Wears down like a normal belt.",eggFarm:"A tiny gatehouse producing questionable eggs on schedule.",autoScanner:"Slower than hands without upgrades.",manualScanner:"A manual checkpoint. The operator is the algorithm.",mixer:"Male chicks become revenue here. Do not feed it poop.",trash:"A polite shredder for poop and other regrets.",truck:"Ships the target cargo. Wrong cargo still leaves a paper trail."}[e]||""}function cr(e){let t=Is(e);return`${ht(e)}${$s(e)}${t?`${t}`:""}`}function ur(e){return re.routeLabels[e]||String(e).toUpperCase()}function dr(e){let t=2166136261;for(let n=0;n>>0}function fr(e){let t=e>>>0;return()=>{t+=1831565813;let n=Math.imul(t^t>>>15,1|t);return n^=n+Math.imul(n^n>>>7,61|n),((n^n>>>14)>>>0)/4294967296}}function vn(){return`RUN-${Date.now().toString(36)}-${Math.floor(Math.random()*1e6).toString(36)}`.toUpperCase()}function pr({seed:e,targetRatio:t=.2,reserved:n=[]}={}){let o=fr(dr(e||vn())),r=new Set(n.map(d=>typeof d=="string"?d:I(d.col,d.row))),s=[];for(let d=0;d0;d-=1){let k=Math.floor(o()*(d+1));[s[d],s[k]]=[s[k],s[d]]}let l=Math.round(y.cols*y.rows*t);return new Set(s.slice(0,Math.min(l,s.length)).map(d=>d.k))}function yr({seed:e,colStart:t=0,colEnd:n=y.cols,rowStart:o=0,rowEnd:r=y.rows,targetRatio:s=.2,reserved:l=[]}={}){let d=fr(dr(e||vn())),k=new Set(l.map(X=>typeof X=="string"?X:I(X.col,X.row))),M=[],D=Math.max(0,Math.floor(t)),L=Math.max(D,Math.floor(n)),F=Math.max(0,Math.floor(o)),z=Math.max(F,Math.floor(r));for(let X=F;X0;X-=1){let ae=Math.floor(d()*(X+1));[M[X],M[ae]]=[M[ae],M[X]]}let oe=Math.round((L-D)*(z-F)*s);return new Set(M.slice(0,Math.min(oe,M.length)).map(X=>X.k))}var Ot=10,hr=2,mr=1;function Nt(e,t){return`${e},${t}`}function Fs(e){let[t,n]=String(e).split(",").map(Number);return{chunkX:t,chunkY:n}}function xn(e){let t=q.grid.expansion||{},n=Math.max(1,(e.gridExpansionPurchases||0)+1),o=(t.costBase||1e3)*n*(t.costMultiplier||4/3),r=Math.max(1,t.truncateUnit||100);return Math.floor(o/r)*r}function br(e){e.ownedCells=new Set,e.ownedChunks=new Set,e.gridChunkOriginY=0,e.gridExpansionPurchases=0,e.gridExpansionPurchasesByDirection={right:0,up:0};for(let t=0;t=0&&t=0&&n0)continue;let M=Nt(k.chunkX,k.chunkY);t.has(M)||n.has(M)||n.set(M,io(e,k.chunkX,k.chunkY,k.direction))}}return[...n.values()].sort((o,r)=>{let s=o.direction===r.direction?0:o.direction==="right"?-1:1;return s||(o.chunkY!==r.chunkY?o.chunkY-r.chunkY:o.chunkX-r.chunkX)})}function xr(e,t="right"){let n=mt(e);return n.find(o=>o.direction===t)||n[0]||null}function bt(e){return{x:y.x+e.colStart*y.cell,y:y.y+e.rowStart*y.cell,w:e.cols*y.cell,h:e.rows*y.cell}}function so(e){let t=bt(e);return{x:t.x+t.w/2-116,y:t.y+t.h/2-36,w:232,h:72,cx:t.x+t.w/2,cy:t.y+t.h/2}}function kr(e,t){let n=so(e);return t.x>=n.x&&t.x<=n.x+n.w&&t.y>=n.y&&t.y<=n.y+n.h}function Cr(e){let t=[];for(let n=e.rowStart;nPe(t.col,t.row))}function _s(e){return[{col:e.col,row:e.row-1},{col:e.col+1,row:e.row-1},{col:e.col,row:e.row},{col:e.col+1,row:e.row},{col:e.col,row:e.row+1},{col:e.col+1,row:e.row+1}].filter(t=>Pe(t.col,t.row))}function Ks(e){return[{col:e.col,row:e.row+1},{col:e.col-1,row:e.row},{col:e.col+1,row:e.row},{col:e.col,row:e.row-1}].filter(t=>Pe(t.col,t.row))}function Pr(e){let t=new Set([...e.conveyorTiles]),n=o=>{o&&Pe(o.col,o.row)&&t.add(I(o.col,o.row))};for(let o of e.scanners){for(let r of _s(o))n(r);for(let r of Ws(o))n(r)}for(let o of e.eggFarms){n(o);for(let r of Ks(o))n(r)}for(let o of Object.values(e.facilities))n(o.entry);return t}function gr(e){let t=Os.map(([n,o])=>({col:n,row:o}));for(let{col:n,row:o}of t)Er(e,n,o);for(let n=0;n({...n,keys:n.keys?{left:{...n.keys.left},right:{...n.keys.right}}:null,queue:[]})),e.eggFarms=Gs.map(n=>{let o=wt(n);return{...n,nextSpawn:o,lastInterval:o}});let t=Pr(e);e.blockedCells=pr({seed:e.runSeed,targetRatio:q.map.blockedRatio,reserved:[...t]}),Hs(e),gr(e),e.routingVersion=(e.routingVersion||0)+1,e.factoryGraph=null,e.factoryGraphCommittedVersion=-1}function xt(e){return e?[{col:e.col,row:e.row-1},{col:e.col+1,row:e.row-1},{col:e.col,row:e.row},{col:e.col+1,row:e.row},{col:e.col,row:e.row+1},{col:e.col+1,row:e.row+1}]:[]}function de(e){let t=$(e.col,e.row);return{x:t.x+y.cell/2,y:t.y}}function kt(e,t,n){return e.eggFarms.find(o=>o.col===t&&o.row===n)||null}function Ct(e,t,n){return e.scanners.find(o=>xt(o).some(r=>r.col===t&&r.row===n))||null}function St(e,t){return e.scanners.find(n=>n.id===t)||null}function fo(e,t){return e.scanners.find(n=>n.kind==="manual"&&n.slot===t)||null}function Mn(e){return e?.baseId||e?.id||""}function po(e,t){return Object.entries(e.facilities||{}).filter(([,n])=>Mn(n)===t)}function Me(e,t){return{inputA:{col:e.col,row:e.row-2},inputB:null,left:{col:e.col-1,row:e.row},right:{col:e.col+2,row:e.row}}[t]}function Ar(e){return e?pe.map(t=>({col:e.col+t.dc,row:e.row+t.dr})).filter(t=>Pe(t.col,t.row)):[]}function $r(e,t,n=[],o=[]){if(!t||!Pe(t.col,t.row))return[];let r=[],s=l=>{!l||!Pe(l.col,l.row)||!e.conveyorTiles.has(I(l.col,l.row))||n.some(d=>d&&wn(l,d))||r.some(d=>wn(d,l))||r.push({...l,viaTolerance:!wn(l,t)})};s(t);for(let l of o)s(l);for(let l of Ar(t))s(l);return r}function Rr(e,t,n=[],o=[]){let r=$r(e,t,n,o),s=r.filter(l=>!l.viaTolerance);return s.length?s:r}function qs(e,t){let n=Me(t,"inputA");return!n||!Pe(n.col,n.row)?[]:e.conveyorTiles.has(I(n.col,n.row))?[{...n,viaTolerance:!1}]:[]}function Ys(e,t,n){let o=Me(t,n);return $r(e,o,xt(t))}function zs(e,t){let n=e.facilities[t]||po(e,t)[0]?.[1];if(!n)return null;if(n.entry)return $(n.entry.col,n.entry.row);let o=Mn(n)||t;return o==="mixer"?{x:n.x+n.w,y:n.y+n.h*.52}:o==="truck"?{x:n.x,y:n.y+n.h*.52}:o==="trash"?{x:n.x+n.w/2,y:n.y+8}:{x:n.x+n.w/2,y:n.y+n.h/2}}function Ir(e,t,n){return n==="inputA"?qs(e,t).length>0:Ys(e,t,n).length>0}function be(e){e.branchCounters?.clear?.(),e.routeCache=null,e.routingVersion=(e.routingVersion||0)+1,e.factoryGraph=null,e.factoryGraphCommittedVersion=-1}function Re(e,t){return I(e,t)}function vt(e){return J(e)}function Vs(e,t){let n=e.conveyorMeta?.get?.(t)||{},o=[];return Array.isArray(n.outDirs)&&o.push(...n.outDirs),n.dir&&o.push(n.dir),[...new Set(o)].filter(r=>pe.some(s=>s.name===r))}function Xs(e,t){let n=vt(t),o=pe.filter(l=>e.conveyorTiles.has(Re(n.col+l.dc,n.row+l.dr))).map(l=>l.name),r=Vs(e,t).filter(l=>o.includes(l)),s=o;return r.length?r:s}function Qs(e){return{version:e.routingVersion||0,cells:new Set,adjacency:new Map,components:new Map,cellToComponent:new Map,scannerInputs:new Map,scannerPorts:new Map,facilityPorts:new Map,farmOutputs:new Map,pathCache:new Map,metrics:{conveyorCells:0,edges:0,components:0,deadEnds:0,crosses:0,scannerPorts:0,connectedScannerPorts:0,facilityPorts:0,connectedFacilityPorts:0,farmOutputs:0},issues:[]}}function Js(e){let t=Qs(e);for(let o of e.conveyorTiles)t.cells.add(o),t.adjacency.set(o,[]);for(let o of t.cells){let r=vt(o),s=Xs(e,o),l=pe.filter(d=>s.includes(d.name));for(let d of l){let k=Re(r.col+d.dc,r.row+d.dr);t.cells.has(k)&&t.adjacency.get(o).push({key:k,dir:d.name})}}let n=0;for(let[o,r]of t.adjacency.entries()){n+=r.length;let s=new Set(r.map(l=>l.dir));r.length===1&&(t.metrics.deadEnds+=1),r.length===4&&s.has("left")&&s.has("right")&&s.has("up")&&s.has("down")&&(t.metrics.crosses+=1)}return t.metrics.conveyorCells=t.cells.size,t.metrics.edges=Math.floor(n/2),Zs(t),js(e,t),t.issues=dl(e,t,{includeRoutes:!1}),t}function Zs(e){let t=1;for(let n of e.cells){if(e.cellToComponent.has(n))continue;let o=t++,r=[n],s=[];for(e.cellToComponent.set(n,o);r.length;){let l=r.shift();s.push(l);for(let d of e.adjacency.get(l)||[])e.cellToComponent.has(d.key)||(e.cellToComponent.set(d.key,o),r.push(d.key))}e.components.set(o,{id:o,cells:s,capacity:Math.max(1,s.length),count:0,ratio:0})}e.metrics.components=e.components.size}function js(e,t){for(let n of e.scanners){let o={};for(let r of["inputA","left","right"]){let s=Me(n,r),l=r==="inputA"?s&&t.cells.has(Re(s.col,s.row))?[{...s}]:[]:Rr(e,s,xt(n)).filter(M=>t.cells.has(Re(M.col,M.row))),d=l.map(M=>Re(M.col,M.row)),k=d.length>0;if(o[r]={type:r,cell:s,cells:l,keys:d,key:d[0]||null,connected:k},t.metrics.scannerPorts+=1,k&&(t.metrics.connectedScannerPorts+=1),r==="inputA")for(let M of d)t.scannerInputs.set(M,n.id)}t.scannerPorts.set(n.id,o)}for(let[n,o]of Object.entries(e.facilities)){if(!o.entry)continue;let r=Rr(e,o.entry).filter(d=>t.cells.has(Re(d.col,d.row))),s=r.map(d=>Re(d.col,d.row)),l=s.length>0;t.facilityPorts.set(n,{id:n,cell:{...o.entry},cells:r,keys:s,key:s[0]||null,connected:l}),t.metrics.facilityPorts+=1,l&&(t.metrics.connectedFacilityPorts+=1)}for(let n of e.eggFarms){let o=Ar({col:n.col,row:n.row}).map(r=>Re(r.col,r.row)).filter(r=>t.cells.has(r));t.farmOutputs.set(n.id,o),t.metrics.farmOutputs+=o.length}}function De(e){let t=e.routingVersion||0;return(!e.factoryGraph||e.factoryGraph.version!==t)&&(e.factoryGraph=Js(e)),e.factoryGraph}function Fr(e){let t=De(e);return e.factoryGraphCommittedVersion=t.version,e.factoryGraphSnapshot=el(t),t}function el(e){return e?{version:e.version,conveyorCells:e.metrics.conveyorCells,edges:e.metrics.edges,components:e.metrics.components,deadEnds:e.metrics.deadEnds,crosses:e.metrics.crosses,scannerPorts:`${e.metrics.connectedScannerPorts}/${e.metrics.scannerPorts}`,facilityPorts:`${e.metrics.connectedFacilityPorts}/${e.metrics.facilityPorts}`,farmOutputs:e.metrics.farmOutputs,issues:[...e.issues||[]]}:null}function Br(e,t,n){let o=De(e),r=Re(t,n);return(o.adjacency.get(r)||[]).map(s=>({...vt(s.key),dir:pe.find(l=>l.name===s.dir)||{name:s.dir,dc:0,dr:0}}))}function tl(e,t){let n=t.col-e.col,o=t.row-e.row;return pe.find(r=>r.dc===n&&r.dr===o)?.name||null}function nl(e,t,n){if(e.conveyorMeta?.get?.(n)?.branchMode!=="pass")return!1;let o=t.adjacency.get(n)||[],r=new Set(o.map(s=>s.dir));return o.length===4&&r.has("left")&&r.has("right")&&r.has("up")&&r.has("down")}function yo(e,t,n){let o=De(e),r=Re(t.col,t.row);if(!o.cells.has(r))return[];let s=`${r}|${n.cacheKey||"dynamic"}`;if(n.cacheKey&&o.pathCache.has(s))return o.pathCache.get(s).map(g=>g.map(_=>({..._})));let l=`${r}|none`,d=[{key:r,col:t.col,row:t.row,incoming:"none",depth:0}],k=new Map([[l,0]]),M=new Map,D=[],L=1/0,F=8,z=24,oe=80;for(;d.length;){let g=d.shift();if(g.depth>L)break;let _=`${g.key}|${g.incoming}`;if(n(g)){if(D.push(_),L=g.depth,D.length>=z)break;continue}for(let U of o.adjacency.get(g.key)||[]){let le=vt(U.key),me=tl(g,le);if(g.incoming!=="none"&&nl(e,o,g.key)&&me!==g.incoming)continue;let fe=g.depth+1;if(fe>L)continue;let E=`${U.key}|${me}`,G=k.get(E);if(G==null)k.set(E,fe),M.set(E,[_]),d.push({key:U.key,col:le.col,row:le.row,incoming:me,depth:fe});else if(G===fe){let w=M.get(E)||[];w.length{if(X.length>=oe)return;let[U]=g.split("|"),le=[vt(U),..._];if(g===l||U===r){X.push(le);return}for(let me of M.get(g)||[])ae(me,le)};for(let g of D)ae(g,[]);let he=[],j=new Set;for(let g of X){let _=g.map(U=>Re(U.col,U.row)).join(">");j.has(_)||(j.add(_),he.push(g))}return n.cacheKey&&o.pathCache.set(s,he.map(g=>g.map(_=>({..._})))),he}function Lr(e){let t=De(e),n=new Map;for(let[o,r]of t.components.entries())n.set(o,{id:o,cells:[...r.cells],capacity:r.capacity,count:0,ratio:0});return{components:n,cellToComponent:new Map(t.cellToComponent)}}function Or(e,t,n){let o=De(e),r=null,s=1/0;for(let l of o.cells){let d=J(l),k=$(d.col,d.row),M=Math.hypot(k.x-t,k.y-n);M$(o.col,o.row));return t&&n.push(t),n}function tt(e,t){t&&(!e.length||ar(e[e.length-1],t)>1)&&e.push(t)}function ol(e,t,n,o=null){let r=[{x:e.x,y:e.y}];tt(r,$(t.col,t.row));for(let s of Tn(n,o))tt(r,s);return r}function rl(e,t){let n=Tn(e),o=Me(t,"inputA");return o&&tt(n,$(o.col,o.row)),tt(n,de(t)),n}function il(e,t,n,o){let r=[{x:e.x,y:e.y}];tt(r,$(t.col,t.row));for(let l of Tn(n))tt(r,l);let s=Me(o,"inputA");return s&&tt(r,$(s.col,s.row)),tt(r,de(o)),r}function sl(e,t,n=0){let o=$(t.col,t.row),r=0;for(let s of e.chicks||[]){if(s.stage==="flying"||s.stage==="queued")continue;Math.hypot(s.x-o.x,s.y-o.y){let L=M.roleBias??0,F=D.roleBias??0;if(L!==F)return L-F;let z=M.cells?.length??M.route?.length??Number.POSITIVE_INFINITY,oe=D.cells?.length??D.route?.length??Number.POSITIVE_INFINITY;if(z!==oe)return z-oe;let X=M.key||JSON.stringify(M.route||M.cells||M),ae=D.key||JSON.stringify(D.route||D.cells||D);return X.localeCompare(ae)});if(!o||r.length===1)return r[0];let s=r[0].roleBias??0,l=r.filter(M=>(M.roleBias??0)===s),d=Math.min(...l.map(M=>Dr(e,M)));l=l.filter(M=>Dr(e,M)===d);let k=e.branchCounters.get(t)||0;return e.branchCounters.set(t,k+1),l[Math.floor(Math.random()*l.length)]}function Nr(e){return e.role===0?{left:{match:"male",destination:"mixer"},right:{match:"default",destination:"scanner-role-1"}}:{left:{match:"poop",destination:"trash"},right:{match:"default",destination:"truck"}}}function ho(e){let t=Nr(e);return{left:t.left.destination,right:t.right.destination}}function mo(e,t){return Nr(e).left.match===t.sex?"left":"right"}function bo(e){return ur(e)}function wo(e){return{mixer:"#2477ff",truck:"#ff6aa8",trash:"#22b94f","scanner-role-1":"#526456",scanner:"#526456",input:"#526456"}[e]||"#102015"}function Mt(e,t,n=!1){let o=De(e),r=(o.farmOutputs.get(t.id)||[]).map(vt),s=[];for(let k of r){let M=L=>o.scannerInputs.has(Re(L.col,L.row));M.cacheKey=`farmInput:${k.col},${k.row}:v${o.version}`;let D=yo(e,k,M);for(let L of D){let F=L[L.length-1],z=o.scannerInputs.get(Re(F.col,F.row)),oe=St(e,z);oe&&s.push({key:`${oe.id}:${F.col},${F.row}:${L.length}`,scanner:oe,cells:L})}}let l=Sn(e,`farm:${t.id}`,s,n);if(l)return{scannerId:l.scanner.id,route:rl(l.cells,l.scanner)};let d=Sn(e,`farm:${t.id}:output`,r.map(k=>({key:I(k.col,k.row),cells:[k]})),n);return d?{scannerId:null,route:Tn(d.cells)}:null}function Gr(e,t,n,o,r=!1){let s=St(e,o);if(!s)return null;let d=De(e).scannerPorts.get(s.id)?.[t],k=Me(s,t),M=d?.connected?(d.keys||[d.key]).filter(Boolean).map(vt):[];if(!k||!M.length)return null;let D=ho(s)[t];return D==="scanner-role-1"?ll(e,s,t,n,k,M,r):al(e,s,t,n,k,M,D,r)}function ll(e,t,n,o,r,s,l){let d=De(e),k=e.scanners.filter(L=>L.id!==t.id&&d.scannerPorts.get(L.id)?.inputA?.connected),M=[];for(let L of s)for(let F of k){let z=d.scannerPorts.get(F.id)?.inputA?.key;if(!z)continue;let oe=(d.scannerPorts.get(F.id)?.inputA?.keys||[z]).filter(Boolean),X=j=>oe.includes(Re(j.col,j.row));X.cacheKey=`toScanner:${L.col},${L.row}:${F.id}:v${d.version}`;let ae=yo(e,L,X),he=F.role===1?0:1;for(let j of ae)M.push({key:`${he}:start${I(L.col,L.row)}:s${F.id}:${j.length}:${I(j[j.length-1].col,j[j.length-1].row)}`,target:F,cells:j,roleBias:he})}let D=Sn(e,`scanner:${t.id}:${n}:toRole1`,M,l);return D?{destination:"scanner",nextScannerId:D.target.id,route:il(o,r,D.cells,D.target)}:null}function al(e,t,n,o,r,s,l,d){let k=De(e),M=[...k.facilityPorts.entries()].filter(([F,z])=>z?.connected&&Mn(e.facilities?.[F])===l);if(!M.length)return null;let D=[];for(let F of s)for(let[z,oe]of M){let X=(oe.keys||[oe.key]).filter(Boolean);if(!X.length)continue;let ae=j=>X.includes(Re(j.col,j.row));ae.cacheKey=`toFacility:${F.col},${F.row}:${z}:v${k.version}`;let he=yo(e,F,ae);for(let j of he)D.push({key:`start${I(F.col,F.row)}:${z}:${j.length}:${I(j[j.length-1].col,j[j.length-1].row)}`,cells:j,facilityId:z})}let L=Sn(e,`scanner:${t.id}:${n}:${l}`,D,d);return L?{destination:l,facilityId:L.facilityId,route:ol(o,r,L.cells,zs(e,L.facilityId))}:null}function Ur(e,t,n=new Set){if(!t||n.has(t.id))return!1;n.add(t.id);for(let o of["left","right"]){let r=Gr(e,o,de(t),t.id,!1);if(r){if(["mixer","trash","truck"].includes(r.destination))return!0;if(r.destination==="scanner"){let s=St(e,r.nextScannerId);if(Ur(e,s,n))return!0}}}return!1}function cl(e,t){return!!Mt(e,t,!1)?.scannerId}function gn(e){if(e.phase!=="build")return[];let t=[];for(let n of e.eggFarms||[])cl(e,n)||t.push({type:"eggFarm",id:n.id,ref:n,message:"This Egg will not produce chicks"});for(let n of Zt)for(let[o,r]of po(e,n))De(e).facilityPorts.get(o)?.connected||t.push({type:"facility",id:o,ref:r,message:"This facility cannot receive items"});return t}function ul(e){let t=De(e);for(let r of e.eggFarms||[]){let s=Mt(e,r,!1);if(!s?.scannerId)continue;let l=St(e,s.scannerId);if(Ur(e,l))return[]}let n=e.eggFarms?.length||0,o=[...t.facilityPorts.entries()].filter(([r,s])=>s?.connected&&["mixer","trash","truck"].includes(Mn(e.facilities?.[r]))).length;return n?t.metrics.farmOutputs?e.scanners?.length?o?["Connect at least one EGG through a scanner to any valid exit."]:["Connect at least one receiver exit: Mixer, Shredder, or Truck."]:["Build at least one scanner and connect it to an EGG route."]:["Connect at least one EGG to a conveyor."]:["Build at least one EGG before starting the next day."]}function Cn(e){return e.kind==="manual"?`S${(e.slot??0)+1}`:`Auto Scanner #${e.id}`}function dl(e,t=De(e),n={}){let o=n.includeRoutes!==!1,r=[],s={mixer:"Mixer",trash:"Shredder",truck:"Truck"};for(let l of Zt){let d=po(e,l);if(!d.length){r.push(`${s[l]||l} is missing`);continue}for(let[k]of d)t.facilityPorts.get(k)?.connected||r.push(`${s[l]||l} receiver has no conveyor`)}for(let l of e.scanners){let d=t.scannerPorts.get(l.id)||{};if(d.inputA?.connected||r.push(`${Cn(l)} input has no conveyor`),d.left?.connected||r.push(`${Cn(l)} left output has no conveyor`),d.right?.connected||r.push(`${Cn(l)} right output has no conveyor`),!!o)for(let k of["left","right"])d[k]?.connected&&!Gr(e,k,de(l),l.id)&&r.push(`${Cn(l)} ${k} route is incomplete`)}if(o)for(let l of e.eggFarms)Mt(e,l)?.scannerId||r.push(`Egg Farm #${l.id} has no scanner route`);else for(let l of e.eggFarms)(t.farmOutputs.get(l.id)||[]).length||r.push(`Egg Farm #${l.id} has no output conveyor`);return[...new Set(r)]}function tn(e){return ul(e)}function Wr(e){return tn(e).length===0}var _r=[{target:"female",title:"Premium Female Shipment",route:"Female -> Truck",note:"Only female chicks are valid truck cargo this turn."},{target:"male",title:"Male Export Trial",route:"Male -> Truck",note:"Male chicks are temporarily valid truck cargo. Male truck fines are suspended for valid male cargo."},{target:"poop",title:"Contaminant Sample Export",route:"Poop -> Truck",note:"Poop is temporarily valid truck cargo. Shipped poop does not soil this contract shipment."}];function vo(e){return[...e.eggFarms.map(t=>t.price||0),...e.scanners.map(t=>t.price||0),...Object.values(e.facilities).map(t=>t.price||0),...[...e.conveyorMeta.values()].map(t=>t.price||0)].reduce((t,n)=>t+n,0)}function fl(e,t=1){return e.eggFarms.reduce((n,o)=>{let[r,s]=en(o),l=Math.max(.1,(r+s)/2);return n+Bt/l*t},0)}function Kr(e){if(e.turn+1or)return null;let n=_r[Math.floor(Math.random()*_r.length)],o=2,r=3,s=fl(e,o),l=Math.max(1,Math.ceil(s*.9)),d=Math.round((.08+Math.random()*.08)*100)/100,k=Math.floor(vo(e)*d),M=Math.floor(Math.max(0,e.cash)*.25),D=Math.max(k,M);return{id:`contract-${e.turn}-${Date.now()}-${Math.floor(Math.random()*9999)}`,...n,forced:!0,productionMultiplier:o,poopMultiplier:r,targetAmount:l,estimatedOutput:Math.round(s),rewardRate:d,reward:D,rewardByFactory:k,rewardByCash:M,createdTurn:e.turn,oneTurnOnly:!0}}function Hr(e){e.contractActive=e.contractOffer?{...e.contractOffer}:null,e.contractOffer=null}function qr(e){e.contractActive=null}function Yr(e){return e.contractActive?.productionMultiplier||1}function zr(e){let t=Math.max(0,Number(e?.cardEffects?.hatchingFeed)||0),n=Math.pow(1.03,t);return Math.min(.95,rr*(e.contractActive?.poopMultiplier||1)*n)}function ze(e){return e.contractActive?.target||"female"}function xo(e,t){return t==="male"?e.truckMale||0:t==="poop"?e.poopTruck||0:e.truckFemale||0}function Vr(e,t){return ze(e)===t}function Xr(e){return ze(e)!=="male"}function Qr(e){let t=e.contractActive;if(!t)return null;let n=xo(e.stats,t.target),o=n>=t.targetAmount,r=o?t.reward:0;r>0?(Ae(e,r),e.stats.contractBonus=r,e.totals.contractBonus+=r,e.totals.contractSuccess+=1):(e.stats.contractBonus=0,e.totals.contractFailed+=1);let s={title:t.title,target:t.target,route:t.route,targetAmount:t.targetAmount,count:n,success:o,bonus:r,reward:t.reward,productionMultiplier:t.productionMultiplier,poopMultiplier:t.poopMultiplier};return e.stats.contractResult=s,s}function ko(e,t){return Math.max(0,Number(e?.cardEffects?.[t])||0)}function pl(e){return!!e?.cardEffects?.usedMachineActive}function yl(e,t){return t?1:Math.min(.8,.5+.1*Math.min(3,ko(e,"recyclingSubsidy")))}function hl(e){return Math.pow(.95,ko(e,"safetyCover"))}function En(e){let t=e?.ref||e||{},n=e?.type||t.type;return n==="conveyor"?t.price||we[t.kind]?.price||we.conveyor.price:n==="eggFarm"?t.price||we.eggFarm.price:n==="scanner"?t.price||(t.kind==="auto"?we.autoScanner.price:we.manualScanner.price):n==="facility"?t.price||we[t.id]?.price||0:we[t.id]?.price||0}function Be(e,t=null){let n=we[e]?.price||0;return pl(t)?Math.ceil(n*.5):n}function ml(e,t){return e?.facilities?Object.values(e.facilities).filter(n=>(n?.baseId||n?.id)===t).reduce((n,o)=>n+Math.max(0,(o.level||1)-1),0):0}function nt(e,t){return sr.includes(t)?Math.pow(Ce.incomeUpgradeRate,ml(e,t)):1}function _e(e){return Math.ceil(Ce.income.mixer*nt(e,"mixer"))}function Le(e){return Math.ceil(Ce.income.truck*nt(e,"truck"))}function nn(e){return Math.ceil(Ce.poopFine*nt(e,"mixer"))}function on(e){return Math.ceil(Ce.poopFine*nt(e,"truck"))}function Co(e){let t=Ce.maleTruckFinePerHalfDay*(e.turn/2);return Math.ceil(t*nt(e,"truck"))}function Tt(e){let t=Math.max(0,(e.facilities?.trash?.level||1)-1);return Math.min(Ce.shredderBonus.maxCards,t)}function Jr(e){return Math.max(0,(e.facilities?.trash?.level||1)-1)}function Gt(){return Ce.shredderBonus.maxCards}function ot(e){let t=typeof e=="number"?e:Tt(e),n=Math.min(Ce.shredderBonus.maxCards,Math.max(0,t)),o=Math.sqrt(n)/Ce.shredderBonus.probabilityPercentDivisor*n;return Math.min(1,o/100)}function bl(e){let t=Tt(e),n=ot(t),o=t>0&&Math.random()0&&(e.stats.shredderBonus=(e.stats.shredderBonus||0)+t.amount,e.totals.shredderBonus=(e.totals.shredderBonus||0)+t.amount,Ae(e,t.amount)),t}function Pn(e,t=null){let n=Ce.zundaTax,o=Math.max(0,t?.cardEffects?.legalWork||0),r=n.exemption+o*n.legalWorkExemptionBonus,s=n.maxRateCash+o*n.legalWorkExemptionBonus,l=Math.max(0,Math.floor(Number(e)||0)),d=Math.max(0,l-r);if(d<=0)return{profit:l,exemption:r,maxRateCash:s,taxable:0,rate:0,ratePercent:0,tax:0,step:0,legalWork:o};let k=Math.max(1,Math.ceil(d/n.stepAmount)),M=Math.min(n.maxRate,k*n.stepRate),D=Math.ceil(d*M);return{profit:l,exemption:r,maxRateCash:s,taxable:d,rate:M,ratePercent:Math.round(M*100),tax:D,step:k,legalWork:o}}function So(e,t=e.turn){let n=Math.max(1,Math.floor(Number(t)||1)),o=Math.ceil(n*Ce.fairiesTribute.perDay),r=Math.max(0,e?.cardEffects?.fairiesFlatteryNext||e?.cardEffects?.flattery||0),s=r>0?Math.pow(.5,r):1,l=Math.max(0,Math.ceil(o*s)),d=Math.max(0,o-l);return{day:n,base:o,flatteryCount:r,perCardReduction:Math.ceil(o*.5),reduction:d,amount:l}}function gt(e,t){let n=e?.ref||e||{},o=e?.type==="conveyor"?t?.conveyorMeta?.get(e.oldKey||n.id):n,r=o?.price||En(e||n),s=o?.builtSession===t?.buildSession;if(o?.noRefund||n?.noRefund)return{amount:0,sameBuild:s,price:r,rate:0,noRefund:!0};let l=yl(t,s);return{amount:Math.max(0,Math.ceil(r*l)),sameBuild:s,price:r,rate:l,noRefund:!1}}function wl(e,t=null){let n=Math.max(1,Math.ceil((Number(e)||0)/Ce.explosionDamageDivisor));return Math.max(1,Math.ceil(n*hl(t)))}function vl(e,t){return Math.max(0,Math.ceil(t))}function jr(e,t){let n=e.cash;e.cash+=t,e.stats.profit+=t,e.totals.profit+=t,t>=0?(e.stats.revenue+=t,e.totals.revenue+=t):(e.stats.penalty+=Math.abs(t),e.totals.penalty+=Math.abs(t)),e.totals.maxCash=Math.max(e.totals.maxCash,e.cash)}function Ae(e,t){let n=Math.max(0,Math.ceil(t));return n>0&&jr(e,n),n}function Ve(e,t){let n=Math.max(0,Math.ceil(t));return n>0&&jr(e,-n),n}function rt(e,t){Ve(e,t)}function ei(e,t){if(t<=0)return;let n=e.cash;e.cash+=t,e.stats.profit+=t,e.totals.profit+=t,e.totals.maxCash=Math.max(e.totals.maxCash,e.cash)}function ti(e){let t=_e(e);return e.stats.mixerRevenue+=t,Ae(e,t),t}function ni(e){let t=nn(e);return e.stats.mixerPoopFine+=t,e.totals.mixerPoopFine+=t,Ve(e,t),t}function oi(e){let t=on(e);return e.stats.truckPoopFine+=t,e.totals.truckPoopFine+=t,Ve(e,t),t}function ri(e){let t=Co(e);return e.stats.maleTruckFine+=t,e.totals.maleTruckFine+=t,Ve(e,t),t}function ii(e,t){let n=wl(t,e);return e.stats.explosionDamage+=n,e.totals.explosionDamage+=n,Ve(e,n),n}function si(e){let t=ze(e),n=xo(e.stats,t),o=Le(e),r=n*o,s=vl(e,r);return e.stats.pendingTruckRevenue=s,e.stats.truckRevenue=s,t==="poop"?e.stats.poopShipmentIncome=s:e.stats.chickShipmentIncome=s,s>0&&Ae(e,s),{base:r,adjusted:s,target:t,targetCount:n,unitPrice:o}}function li(e,t=0){let n=Pn(t,e);return n.tax<=0||(e.totals.zundaTax=(e.totals.zundaTax||0)+n.tax,e.totals.penalty=(e.totals.penalty||0)+n.tax,e.totals.profit=(e.totals.profit||0)-n.tax,e.cash-=n.tax),n}function ai(e,t=e.turn){let n=So(e,t);return e.cardEffects&&(e.cardEffects.fairiesFlatteryNext=0,e.cardEffects.flattery!=null&&delete e.cardEffects.flattery),n.amount<=0||(e.stats.fairiesTribute=(e.stats.fairiesTribute||0)+n.amount,e.totals.fairiesTribute=(e.totals.fairiesTribute||0)+n.amount,Ve(e,n.amount)),n}function ci(e){let t=ko(e,"chemicalWeaponSubsidy"),n=Math.max(0,e?.stats?.poopTruck||0),o=Math.floor(n/100),r=t>0&&o>0?o*2e3*t:0;return r>0&&(e.stats.chemicalWeaponSubsidy=(e.stats.chemicalWeaponSubsidy||0)+r,e.totals.chemicalWeaponSubsidy=(e.totals.chemicalWeaponSubsidy||0)+r,Ae(e,r)),{cards:t,poopTruck:n,blocks:o,amount:r}}function ui(e){let t=e?.cardEffects;if(!t||!Array.isArray(t.loans)||!t.loans.length)return{amount:0,paidLoans:0,remainingLoans:0};let n=0,o=0,r=[];for(let s of t.loans){if((s.daysRemaining||0)<=0)continue;let l=Math.max(0,Math.ceil(s.dailyPayment||400));n+=l,o+=1;let d=Math.max(0,(s.daysRemaining||0)-1);d>0&&r.push({...s,daysRemaining:d})}return t.loans=r,n>0&&(e.stats.loanRepayment=(e.stats.loanRepayment||0)+n,e.totals.loanRepayment=(e.totals.loanRepayment||0)+n,Ve(e,n)),{amount:n,paidLoans:o,remainingLoans:r.length}}function di(e){let t=vo(e),n=Math.max(1,e.totals.turnsCompleted||e.turn||1),o=Math.max(0,e.cash+e.totals.revenue+t*.5+e.totals.contractBonus*.5),r=Math.ceil(o/n),s=Math.floor(n*35+Math.max(0,n-5)*15),l=e.totals.correct*5,d=e.totals.explosionDamage;return{score:Math.max(0,Math.floor(o+r+l-e.totals.penalty-s)),factoryValue:t,base:o,dailyEarned:r,dayPenalty:s,correctBonus:l,explosionPenalty:d,days:n}}function xl(e){return{...e,keys:e.keys?{left:{...e.keys.left},right:{...e.keys.right}}:null,queue:[],cooldown:e.cooldown||0}}function rn(e){return JSON.stringify({cash:e.cash,stats:e.stats,totals:e.totals,nextId:e.nextId,gridX:y.x,gridY:y.y,gridCols:e.gridCols||y.cols,gridRows:e.gridRows||y.rows,gridExpansionPurchases:e.gridExpansionPurchases||0,gridExpansionPurchasesByDirection:e.gridExpansionPurchasesByDirection||{right:0,up:0},gridChunkOriginY:e.gridChunkOriginY||0,ownedCells:[...e.ownedCells||[]],ownedChunks:[...e.ownedChunks||[]],blockedCells:[...e.blockedCells||[]],selected:e.selected,multiSelected:e.multiSelected||[],buildTool:e.buildTool,view:e.view,facilities:e.facilities,eggFarms:e.eggFarms,scanners:e.scanners.map(xl),conveyorTiles:[...e.conveyorTiles],conveyorMeta:[...e.conveyorMeta.entries()],branchCounters:[...e.branchCounters.entries()],cardEffects:e.cardEffects,cardDraft:e.cardDraft,cardTargetPick:e.cardTargetPick})}function fi(e,t){let n=JSON.parse(t);e.cash=n.cash,e.stats=n.stats,e.totals=n.totals,e.nextId=n.nextId,y.x=n.gridX??q.grid.x,y.y=n.gridY??q.grid.y,y.cols=n.gridCols||q.grid.cols,y.rows=n.gridRows||q.grid.rows,e.gridCols=y.cols,e.gridRows=y.rows,e.gridExpansionPurchases=n.gridExpansionPurchases||0,e.gridExpansionPurchasesByDirection=n.gridExpansionPurchasesByDirection||{right:0,up:0},e.gridChunkOriginY=n.gridChunkOriginY||0,e.ownedCells=new Set(n.ownedCells||[]),e.ownedChunks=new Set(n.ownedChunks||[]),e.gridOwnedCells=e.ownedCells.size||y.cols*y.rows,e.blockedCells=new Set(n.blockedCells||[]),e.selected=n.selected,e.multiSelected=n.multiSelected||[],e.buildTool=n.buildTool,e.view=n.view||{x:0,y:0,scale:1},e.view.scale==null&&(e.view.scale=1),e.facilities=n.facilities||{},e.eggFarms=n.eggFarms||[],e.scanners=(n.scanners||[]).map(o=>({...o,queue:[],cooldown:o.cooldown||0})),e.conveyorTiles=new Set(n.conveyorTiles||[]),e.conveyorMeta=new Map(n.conveyorMeta||[]),e.branchCounters=new Map(n.branchCounters||[]),e.cardEffects=n.cardEffects||{bearing:0,legalWork:0,fairiesFlatteryNext:0,scannerQueueSpacing:0},e.cardEffects.drawBonus!=null&&delete e.cardEffects.drawBonus,e.cardDraft=n.cardDraft||{pending:!1,choices:[],rerolls:0,picksRemaining:0},e.cardDraft.picksRemaining==null&&(e.cardDraft.picksRemaining=e.cardDraft.pending?1:0),e.cardDraft.freeRerolls==null&&(e.cardDraft.freeRerolls=0),e.cardTargetPick=n.cardTargetPick||null,e.groupDrag=null,e.selectionBox=null,e.pan=null,e.routingVersion=(e.routingVersion||0)+1,e.factoryGraph=null,e.factoryGraphCommittedVersion=-1}function Oe(e){e.phase==="build"&&(e.undoStack.push(rn(e)),e.undoStack.length>80&&e.undoStack.shift(),e.redoStack=[])}function Mo(e){return e.phase!=="build"||!e.undoStack.length?!1:(e.redoStack.push(rn(e)),fi(e,e.undoStack.pop()),!0)}function To(e){return e.phase!=="build"||!e.redoStack.length?!1:(e.undoStack.push(rn(e)),fi(e,e.redoStack.pop()),!0)}function N(e,t,n,o,r=f.ink){e.floatingTexts.push({x:t,y:n,text:o,color:r,life:1,maxLife:1})}function Et(e,t,n){e.shake.strength=Math.max(e.shake.strength,t),e.shake.time=Math.max(e.shake.time,n)}function pi(e,t,n){e.effects.push({type:"spawn",priority:Se.ambient,x:t,y:n,life:.42,maxLife:.42,size:12})}function yi(e,t,n,o=f.green){e.effects.push({type:"scannerPulse",priority:Se.important,x:t,y:n,color:o,life:.42,maxLife:.42,size:24})}function hi(e,t,n){for(let o=0;o<12;o+=1)e.effects.push({type:"meat",priority:Se.important,x:t+W(-18,18),y:n+W(-12,16),vx:W(-60,60),vy:W(-90,-25),life:W(.45,.85),maxLife:.85,size:W(4,8)})}function mi(e,t,n){for(let o=0;o<16;o+=1)e.effects.push({type:"sludge",priority:Se.important,x:t+W(-18,18),y:n+W(-12,16),vx:W(-46,46),vy:W(-70,-20),life:W(.45,.9),maxLife:.9,size:W(4,8)})}function bi(e,t,n,o){let r=o==="poop"?f.poop:o==="male"?f.male:f.female;for(let s=0;s<22;s+=1)e.effects.push({type:"shred",priority:Se.important,x:t+W(-16,16),y:n+W(-8,8),vx:W(-70,70),vy:W(40,160),color:s%3===0?f.white:r,life:W(.45,.95),maxLife:.95,w:W(3,7),h:W(10,24),rot:W(-Math.PI,Math.PI)});Xe(e,t,n,f.green,34,Se.important)}function wi(e,t,n,o){let r=o==="poop"?f.poop:o==="male"?f.danger:f.green;for(let s=0;s<8;s+=1)e.effects.push({type:"load",priority:Se.important,x:t+W(-18,18),y:n+W(-18,18),vx:W(-42,42),vy:W(-90,-25),color:r,life:W(.35,.65),maxLife:.65,size:8})}function Xe(e,t,n,o=f.danger,r=52,s=Se.critical){e.effects.push({type:"shockwave",priority:s,x:t,y:n,color:o,life:.55,maxLife:.55,size:8,maxSize:r})}function it(e,t,n,o=12,r=f.danger,s=Se.critical){for(let l=0;l0&&(e.shake.time=Math.max(0,e.shake.time-t),e.shake.time<=0&&(e.shake.strength=0));for(let r of e.effects)if(r.life-=t,["meat","sludge","spark","smoke","shred","load","rage"].includes(r.type))r.x+=(r.vx||0)*t,r.y+=(r.vy||0)*t,r.vy=(r.vy||0)+150*t;else if(r.type==="flyingChick"){r.x+=r.vx*t,r.y+=r.vy*t,r.vy+=360*t,r.noDamage||kl(e,r,o);let s=r.x+e.view.x,l=r.y+e.view.y;(s<-100||l<-100||s>n.width+100||l>n.height+100)&&(r.life=0)}e.effects=e.effects.filter(r=>r.life>0);for(let r of e.floatingTexts)r.life-=t,r.y-=34*t;e.floatingTexts=e.floatingTexts.filter(r=>r.life>0)}function kl(e,t,n){t.hit||(t.hit=new Set);for(let o of n()){if(t.hit.has(o.id)||t.xo.x+o.w||t.yo.y+o.h)continue;t.hit.add(o.id);let r=ii(e,o.price);N(e,t.x,t.y-10,`-${S(r)}`,f.danger),Xe(e,t.x,t.y,f.danger,30),it(e,t.x,t.y,5,f.danger),Et(e,5,.18)}}var $e=q.maintenance;function Eo(e,t){return Math.max(0,Number(e?.cardEffects?.[t])||0)}function $n(e){let t=Math.min(1.4,1+.04*Math.min(10,Eo(e,"crowdedFarming"))),n=Math.pow(.95,Eo(e,"preventiveMaintenance"));return t*n}function Cl(e){return Math.pow(1.03,Eo(e,"durabilityCoating"))}function In(e,t,n=1){let o=$e.durability[t]||1e3;return Math.max(1,Math.ceil(o*Math.max(.1,n||1)*Cl(e)))}function Do(e){let t=$e.repairman.dailyCost;return e?.cardEffects?.laborExploitation?Math.ceil(t*.7):t}function ki(e){return Math.max(.1,Number(e?.durabilityBaseMultiplier)||1)}function ye(e){e.repairman||(e.repairman={hiredForNextDay:!1,active:!1,x:y.x+y.cell*.5,y:y.y+y.rows*y.cell+84,target:null,repairedToday:0});for(let[t,n]of e.conveyorMeta||[]){n.uses==null&&(n.uses=0),n.maintenanceType==null&&(n.maintenanceType="conveyor"),n.kind||(n.kind="conveyor"),n.speedMultiplier||(n.speedMultiplier=n.kind==="boostConveyor"?2:1),n.branchMode||(n.branchMode="random");let o=In(e,"conveyor",ki(n));(n.durability==null||n.durability0&&(n.processingCooldown=Math.max(0,n.processingCooldown-t))}function On(e){ye(e);let t=[];for(let[n,o]of e.conveyorMeta||[])t.push({type:"conveyor",key:n,meta:o,label:`${o.kind==="boostConveyor"?"Boost Belt":"Belt"} ${n}`,center:$(J(n).col,J(n).row)});for(let n of e.eggFarms||[])t.push({type:"eggFarm",ref:n,label:`EGG #${n.id}`,center:$(n.col,n.row)});for(let n of(e.scanners||[]).filter(o=>o.kind==="auto"))t.push({type:"autoScanner",ref:n,label:`AUTO #${n.id}`,center:$(n.col,n.row)});for(let n of["mixer","trash"]){let o=e.facilities?.[n];o&&t.push({type:n,ref:o,label:o.shortName||o.name||n,center:{x:o.x+o.w/2,y:o.y+o.h/2}})}return t}function Dn(e){return e.meta?Ut(e):Ut(e.ref)}function sn(e){return e.meta?Po(e):Po(e.ref)}function An(e){return e.meta?Ro(e):Ro(e.ref)}function Ei(e,t){Sl(e.meta?e:e.ref,Math.max(0,sn(e)-t))}function Pi(e){return e.type==="conveyor"?`conveyor:${e.key}`:`${e.type}:${e.ref?.id||e.ref?.type||e.type}`}function Ri(e,t=.02){ye(e);let n=Math.max(0,Number(t)||0),o=0;for(let r of On(e)){let s=sn(r);Ei(r,An(r)*n),o+=Math.max(0,s-sn(r))/An(r)*100}return o}function Di(e){if(ye(e),e.repairman.hiredForNextDay)return{ok:!1,reason:"Repairman already hired."};let t=Do(e);return e.cashPi(o)===t.key)||null}function gl(e){let t=null;for(let n of On(e)){let o=Dn(n);o<=.001||(!t||o>Dn(t))&&(t=n)}return t}function Ii(e,t){ye(e);let n=e.repairman;if(!n.active||e.phase!=="running")return;let o=Tl(e,n.target)||gl(e);if(!o){n.target=null;return}n.target={key:Pi(o),label:o.label};let r=$e.repairman.walkSpeed,s=o.center.x-n.x,l=o.center.y-n.y,d=Math.hypot(s,l);if(d>8){let F=Math.min(d,r*t);n.x+=s/d*F,n.y+=l/d*F;return}let k=t/($e.repairman.secondsPerOnePercent*100),M=sn(o),D=k*An(o);Ei(o,D);let L=Math.max(0,(M-sn(o))/An(o)*100);n.repairedToday=(n.repairedToday||0)+L,L>0&&Math.random()<.02&&N(e,n.x,n.y-28,"REPAIR",f.green),Dn(o)<=.001&&(n.target=null)}function El(e){let t=0,n="none";for(let o of On(e)){let r=Dn(o);r>t&&(t=r,n=o.label)}return{ratio:t,percent:Math.round(t*100),label:n}}function No(e){let t=El(e),n=$o(e);return{worst:t,avgBeltPerformance:n,repairman:e.repairman||null}}var Pl=Ee.commonWeight,Rl=Ee.rareWeight,Dl=Ee.ultraRareWeight||.45,Pt=Ee.baseDraftSize,Kt=ir;function _t(e,t){let n=Ie(e);return Math.max(0,Number(n[t])||0)}function Ie(e){e.cardEffects||(e.cardEffects={...to,loans:[]});for(let[t,n]of Object.entries(to))e.cardEffects[t]==null&&(e.cardEffects[t]=Array.isArray(n)?[]:n);return e.cardEffects.flattery!=null&&(e.cardEffects.fairiesFlatteryNext=Math.max(0,e.cardEffects.fairiesFlatteryNext||0)+Math.max(0,e.cardEffects.flattery||0),delete e.cardEffects.flattery),Array.isArray(e.cardEffects.loans)||(e.cardEffects.loans=[]),e.cardEffects.drawBonus!=null&&delete e.cardEffects.drawBonus,e.cardDraft||(e.cardDraft={pending:!1,choices:[],rerolls:0,picksRemaining:0}),e.cardDraft.picksRemaining==null&&(e.cardDraft.picksRemaining=e.cardDraft.pending?1:0),e.cardDraft.freeRerolls==null&&(e.cardDraft.freeRerolls=0),e.cardEffects}function Wi(e){let t=Math.max(.5,1-.05*Math.min(10,_t(e,"crowdedFarming"))),n=Math.pow(.96,_t(e,"hatchingFeed"));return t*n}function _i(e,t=null){return Math.min(3,Math.max(0,Number(t?.extraEggOutlet)||0))}function Ki(e){return Math.pow(.85,_t(e,"scannerQueueSpacing"))}function Gn(e,t=null){let n=Ie(e),o=mn*Math.pow(bn,Math.max(0,n.bearing||0)),r=t?Math.max(.1,Number(e?.conveyorMeta?.get?.(t)?.speedMultiplier)||1):1,s=t?1:$o(e);return Math.min(Lt,o*s)*r}function Qe(e,t=null){let n=Math.max(0,(e?.level||1)-1),o=Math.max(eo,hn*Math.pow(nr,n));return t?Math.max(eo,o*Bn(e)):o}function Fi(e){let t=e.cardDraft||{rerolls:0},n=Math.max(1,(t.rerolls||0)+1);return Math.ceil(Math.max(1,e.turn||1)/Ee.rerollCost.dayDivisor*n*Ee.rerollCost.multiplier)}function Uo(e){return Kt.find(t=>t.id===e)||null}function Wo(e,t){let n=typeof t=="string"?Uo(t):t;if(Ie(e),!n||n.type==="dud")return{ok:!1,reason:"Card not found."};if(n.id==="extraCards")return e.cardDraft=e.cardDraft||{pending:!1,choices:[],rerolls:0,picksRemaining:0},e.cardDraft.choices.push(...Nn(e,2)),{ok:!0,reason:`${n.title} added draft cards.`};if(n.type==="instant")return qi(e,n),{ok:!0,reason:`${n.title} granted.`};if(n.type==="equipmentUpgrade"){let o=Ue(e,n)[0];return o?(Hi(e,o),{ok:!0,reason:`${n.title} applied.`}):{ok:!1,reason:`No valid target for ${n.title}.`}}if(n.type==="cellAction"&&n.target==="blockedCell"){let o=Ue(e,n).slice(0,3);if(!o.length)return{ok:!1,reason:"No blocked cells to remove."};for(let r of o){e.blockedCells.delete(I(r.col,r.row));let s=$(r.col,r.row);st(e,s.x,s.y)}return be(e),{ok:!0,reason:`${n.title} removed ${o.length} blocked cells.`}}return{ok:!1,reason:`${n.title} cannot be debug-granted.`}}function Al(e,t){if(t.type==="blockedCell")return`Blocked cell ${t.col},${t.row}`;if(t.type==="eggFarm")return`EGG #${t.id} L${t.level} -> L${t.level+1}`;if(t.type==="eggOutlet"){let n=Math.max(0,Number(t.farm?.extraEggOutlet)||0);return`EGG #${t.id} OUTLET ${1+n} -> ${2+n}`}if(t.type==="scanner")return`AUTO #${t.id} L${t.level} -> L${t.level+1} | CD ${Qe(t,e).toFixed(1)}s -> ${Qe({...t,level:t.level+1},e).toFixed(1)}s`;if(t.type==="facility"&&(t.baseId||t.id)==="mixer"){let n=t.level,o=_e(e);t.level=n+1;let r=_e(e);return t.level=n,`MIXER L${t.level} -> L${t.level+1} | ${S(o)} -> ${S(r)}`}if(t.type==="facility"&&(t.baseId||t.id)==="truck"){let n=t.level,o=Le(e);t.level=n+1;let r=Le(e);return t.level=n,`TRUCK L${t.level} -> L${t.level+1} | ${S(o)} -> ${S(r)}`}if(t.type==="facility"&&t.id==="trash"){let n=Tt(e),o=Math.min(Gt(),n+1),r=(ot(n)*100).toFixed(2),s=(ot(o)*100).toFixed(2);return`SHREDDER L${t.level||1} -> L${(t.level||1)+1} | ${r}% -> ${s}% per item`}return`${t.name||t.id} L${t.level||1} -> L${(t.level||1)+1}`}function $l(e){return[...e.blockedCells||[]].map(t=>{let[n,o]=String(t).split(",").map(Number);return{type:"blockedCell",id:t,col:n,row:o}}).filter(t=>Number.isFinite(t.col)&&Number.isFinite(t.row))}function Ue(e,t){let n=typeof t=="string"?Uo(t):t;return Ie(e),n?n.target==="blockedCell"?$l(e):n.target==="eggFarm"?e.eggFarms.filter(o=>(o.level||1)<4):n.target==="eggOutlet"?e.eggFarms.filter(o=>Math.max(0,Number(o.extraEggOutlet)||0)<3).map(o=>({type:"eggOutlet",id:o.id,col:o.col,row:o.row,farm:o})):n.target==="autoScanner"?e.scanners.filter(o=>o.kind==="auto"):n.target==="mixer"?Object.values(e.facilities||{}).filter(o=>(o.baseId||o.id)==="mixer"):n.target==="truck"?Object.values(e.facilities||{}).filter(o=>(o.baseId||o.id)==="truck"):n.target==="trash"?Jr(e)(o.baseId||o.id)==="trash"):[]:[]:[]}function Il(e,t){let n=Ie(e);if(t.id==="crowdedFarming")return(n.crowdedFarming||0)>=10;if(t.id==="recyclingSubsidy")return(n.recyclingSubsidy||0)>=3;if(t.id==="dudFilter"){let o=Math.max(0,Math.min(1,Ee.dudChancePerCard||0)),r=Math.max(1e-4,Math.min(1,Ee.dudChanceReductionPerFilter||.05));return(n.dudFilter||0)>=Math.ceil(o/r)}return t.id==="laborExploitation"?!!n.laborExploitation:t.id==="usedMachine"?!!n.usedMachineActive:t.id==="newMachine"?!n.usedMachineActive:!1}function Fl(e){let t=e.repairman||{};return!!(t.active||t.hiredForNextDay||e.pendingRepairWorkerWages>0)}function Bl(e){return Kt.filter(t=>!(t.id==="laborExploitation"&&!Fl(e)||Il(e,t)||(t.type==="equipmentUpgrade"||t.type==="cellAction")&&Ue(e,t).length<=0))}function Bi(e,t){if(t.weightBase!=null){let n=_t(e,t.id);return Math.max(0,Number(t.weightBase)-Math.max(0,Number(t.weightLossPerCopy)||0)*n)}return t.rarity==="ultraRare"?Dl:t.rarity==="rare"?Rl:Pl}function Ll(e,t){let n=t.reduce((r,s)=>r+Bi(e,s),0);if(n<=0)return t[Math.floor(Math.random()*t.length)];let o=Math.random()*n;for(let r of t)if(o-=Bi(e,r),o<=0)return r;return t[t.length-1]}var Li=0;function Ol(){return Li+=1,{id:`dud:${Date.now()}:${Li}`,title:"Dud",rarity:"dud",type:"dud",description:"Click to flick this dud away. It does not consume a pick."}}function Nl(e){let t=Ie(e),n=Math.max(0,Math.min(1,Ee.dudChancePerCard||0)),o=Math.max(0,Math.min(1,Ee.dudChanceReductionPerFilter||0));return Math.max(0,n-o*Math.max(0,t.dudFilter||0))}function Gl(e,t){let n=[...t],o=0,r=Math.max(0,Ee.maxDudsPerDraft||0),s=Nl(e);for(let l=0;ld.id===s.id);l>=0&&r.splice(l,1)}return Gl(e,o)}function Wt(e){return e?e.type==="blockedCell"?`blockedCell:${e.id}`:e.type==="eggFarm"?`eggFarm:${e.id}`:e.type==="eggOutlet"?`eggOutlet:${e.id}`:e.type==="scanner"?`scanner:${e.id}`:e.type==="facility"?`facility:${e.id}`:`${e.type}:${e.id}`:""}function Oi(e){if(!e)return null;if(e.type==="blockedCell"){let t=$(e.col,e.row);return{x:t.x-y.cell*.48,y:t.y-y.cell*.48,w:y.cell*.96,h:y.cell*.96,cx:t.x,cy:t.y}}if(e.type==="eggFarm"||e.type==="eggOutlet"){let t=$(e.col,e.row);return{x:t.x-34,y:t.y-34,w:68,h:68,cx:t.x,cy:t.y}}if(e.type==="scanner"){let t=de(e);return{x:t.x-54,y:t.y-77,w:108,h:154,cx:t.x,cy:t.y}}return e.type==="facility"?{x:e.x,y:e.y,w:e.w,h:e.h,cx:e.x+e.w/2,cy:e.y+e.h/2}:null}function _o(e){if(!e.cardTargetPick?.pending)return[];if(e.cardTargetPick.mode==="autoScannerMenu"){let n=new Set(e.cardTargetPick.targetKeys||[]);return e.scanners.filter(o=>o.kind==="auto"&&n.has(Wt(o))).map(o=>({key:Wt(o),label:"AUTO ROUTE SETTINGS",target:o,bounds:Oi(o)})).filter(o=>o.bounds)}let t=new Set(e.cardTargetPick.targetKeys||[]);return Ue(e,e.cardTargetPick.cardId).filter(n=>t.has(Wt(n))).map(n=>({key:Wt(n),label:Al(e,n),target:n,bounds:Oi(n)})).filter(n=>n.bounds)}function Ul(e,t){let n=_o(e);for(let o of n){let r=o.bounds;if(t.x>=r.x&&t.x<=r.x+r.w&&t.y>=r.y&&t.y<=r.y+r.h)return o.target}return null}function Hi(e,t){if(t.type==="eggOutlet"){let n=t.farm||e.eggFarms.find(r=>r.id===t.id);if(!n)return;n.extraEggOutlet=Math.min(3,Math.max(0,Number(n.extraEggOutlet)||0)+1);let o=$(n.col,n.row);N(e,o.x,o.y-30,`OUTLET ${1+n.extraEggOutlet}`,f.green),be(e);return}if(t.level=(t.level||1)+1,t.type==="eggFarm"){t.nextSpawn=wt(t),t.lastInterval=t.nextSpawn;let n=$(t.col,t.row);N(e,n.x,n.y-30,`LV ${t.level}`,f.green);return}if(t.type==="scanner"){let n=de(t);N(e,n.x,n.y-34,`LV ${t.level}`,f.green);return}t.type==="facility"&&N(e,t.x+t.w/2,t.y+18,`LV ${t.level}`,f.green)}function qi(e,t){let n=Ie(e),o=r=>{n[r]=Math.max(0,n[r]||0)+1};if(t.id==="bearing"&&o("bearing"),t.id==="legalWork"&&o("legalWork"),t.id==="crowdedFarming"&&(n.crowdedFarming=Math.min(10,(n.crowdedFarming||0)+1)),t.id==="hatchingFeed"&&o("hatchingFeed"),t.id==="safetyCover"&&o("safetyCover"),t.id==="recyclingSubsidy"&&(n.recyclingSubsidy=Math.min(3,(n.recyclingSubsidy||0)+1)),t.id==="chemicalWeaponSubsidy"&&o("chemicalWeaponSubsidy"),t.id==="preventiveMaintenance"&&o("preventiveMaintenance"),t.id==="dudFilter"&&o("dudFilter"),t.id==="durabilityCoating"&&(o("durabilityCoating"),ye(e)),t.id==="sparePartsBin"){o("sparePartsBin");let r=Ri(e,.02);e.stats.sparePartsRepair=(e.stats.sparePartsRepair||0)+r,e.totals.sparePartsRepair=(e.totals.sparePartsRepair||0)+r,N(e,y.x+y.cols*y.cell/2,y.y-30,"REPAIR +2%",f.green)}if(t.id==="dudRefund"&&o("dudRefund"),t.id==="freeReroll"&&o("freeReroll"),t.id==="composter"&&o("composter"),t.id==="scannerQueueSpacing"&&o("scannerQueueSpacing"),t.id==="flattery"&&o("fairiesFlatteryNext"),t.id==="usedMachine"&&(n.usedMachineActive=!0),t.id==="newMachine"&&(n.usedMachineActive=!1),t.id==="laborExploitation"&&(n.laborExploitation=1),t.id==="rescueLoan"&&(o("rescueLoanCharges"),N(e,y.x+y.cols*y.cell/2,y.y-30,"RESCUE HELD",f.green)),t.id==="loan"){let r=Math.max(1,Math.floor(Number(e.turn)||1)),s=300*r;Ae(e,s),n.loans.push({daysRemaining:r,dailyPayment:400,borrowedOnTurn:e.turn||1}),N(e,y.x+y.cols*y.cell/2,y.y-30,`LOAN +${S(s)}`,f.green)}}function Ne(e){return`${Math.round(Number(e||0)*100)}%`}function Ni(e){let t=et[Math.max(0,Math.min(et.length-1,e-1))]||et[0]||[0,0];return`${t[0].toFixed(2)}-${t[1].toFixed(2)}s`}function Gi(e,t){return Math.ceil(e*Math.pow(Ce.incomeUpgradeRate,Math.max(0,t-1)))}function Ui(e,t,n){let r=Ue(e,t==="mixer"?"upgradeMixer":"upgradeTruck")[0];if(!r)return{before:Gi(n,1),after:Gi(n,2)};let s=t==="mixer"?_e(e):Le(e),l=r.level||1;r.level=l+1;let d=t==="mixer"?_e(e):Le(e);return r.level=l,{before:s,after:d}}function Wl(e,t){let n=Ie(e);if(t.id==="upgradeEgg"){let o=Ue(e,t),r=Math.min(3,Math.max(1,o[0]?.level||1));return`Choose one EGG. Level +1. Production interval ${Ni(r)} -> ${Ni(r+1)}.`}if(t.id==="upgradeAutoScanner"){let o=Ue(e,t)[0]||{level:1},r=Qe(o,e),s=Qe({...o,level:(o.level||1)+1},e);return`Choose one AUTO SCANNER. Level +1. Cooldown ${r.toFixed(2)}s -> ${s.toFixed(2)}s.`}if(t.id==="upgradeMixer"){let o=Ui(e,"mixer",Ce.income.mixer);return`Choose one MIXER. Earning +10%. Male-chick income ${S(o.before)} -> ${S(o.after)}.`}if(t.id==="upgradeTruck"){let o=Ui(e,"truck",Ce.income.truck);return`Choose one TRUCK. Earning +10%. Correct shipment income ${S(o.before)} -> ${S(o.after)}.`}if(t.id==="upgradeTrash"){let o=Tt(e),r=Math.min(Gt(),o+1);return`SHREDDER level +1. Bonus chance per item ${Ne(ot(o))} -> ${Ne(ot(r))}.`}if(t.id==="bearing"){let o=Math.min(Lt,mn*Math.pow(bn,Math.max(0,n.bearing||0))),r=Math.min(Lt,mn*Math.pow(bn,Math.max(0,(n.bearing||0)+1)));return`Conveyor speed +7.5%. After pick: ${o.toFixed(1)} -> ${r.toFixed(1)} px/s.`}if(t.id==="legalWork"){let o=Math.max(0,n.legalWork||0);return`ZUNDA TAX exemption +${S(250)}. After pick: exemption bonus ${S(250*(o+1))}.`}if(t.id==="crowdedFarming"){let o=Math.min(10,(n.crowdedFarming||0)+1);return`Held ${n.crowdedFarming||0}/10. After pick: EGG interval ${Ne(Math.max(.5,1-.05*o))}, deterioration ${Ne(Math.min(1.4,1+.04*o))}.`}if(t.id==="extraEggOutlet"){let o=Ue(e,t),r=Math.max(0,Number(o[0]?.farm?.extraEggOutlet)||0);return`Choose one EGG. Output ports ${1+r} -> ${2+r}. Max 4 ports per EGG.`}if(t.id==="hatchingFeed")return`Held ${n.hatchingFeed||0}. After pick: EGG interval ${Ne(Math.pow(.96,(n.hatchingFeed||0)+1))}, poop rate ${Ne(Math.pow(1.03,(n.hatchingFeed||0)+1))}.`;if(t.id==="safetyCover")return`Held ${n.safetyCover||0}. After pick: explosion damage ${Ne(Math.pow(.95,(n.safetyCover||0)+1))}.`;if(t.id==="recyclingSubsidy")return`Held ${n.recyclingSubsidy||0}/3. After pick: normal SELL refund ${Math.round(Math.min(.8,.5+.1*((n.recyclingSubsidy||0)+1))*100)}%.`;if(t.id==="chemicalWeaponSubsidy")return`Held ${n.chemicalWeaponSubsidy||0}. After pick: ${S(2e3*((n.chemicalWeaponSubsidy||0)+1))} per 100 truck-shipped poop each day.`;if(t.id==="preventiveMaintenance")return`Held ${n.preventiveMaintenance||0}. After pick: deterioration speed ${Ne(Math.pow(.95,(n.preventiveMaintenance||0)+1))}.`;if(t.id==="dudFilter"){let o=Math.max(0,Math.min(1,Ee.dudChancePerCard||0)),r=Math.max(0,Math.min(1,Ee.dudChanceReductionPerFilter||.05)),s=Math.max(0,n.dudFilter||0),l=Math.max(0,o-r*s),d=Math.max(0,o-r*(s+1));return`Held ${s}. DUD chance per card ${Ne(l)} -> ${Ne(d)}.`}if(t.id==="durabilityCoating")return`Held ${n.durabilityCoating||0}. After pick: maximum durability ${Ne(Math.pow(1.03,(n.durabilityCoating||0)+1))}.`;if(t.id==="sparePartsBin")return`Held ${n.sparePartsBin||0}. Immediately restores 2% durability to every maintained machine. Offer weight: ${Math.max(0,100-2*Math.max(0,n.sparePartsBin||0))}.`;if(t.id==="dudRefund")return`Held ${n.dudRefund||0}. After pick: DUD clicks award ${S(50*((n.dudRefund||0)+1))}.`;if(t.id==="freeReroll")return`Held ${n.freeReroll||0}. After pick: each card screen starts with ${(n.freeReroll||0)+1} free reroll${(n.freeReroll||0)+1===1?"":"s"}.`;if(t.id==="composter")return`Held ${n.composter||0}. After pick: SHREDDER poop grants ${S(3*((n.composter||0)+1))}.`;if(t.id==="scannerQueueSpacing")return`Held ${n.scannerQueueSpacing||0}. After pick: Manual/Auto Scanner queue spacing ${Ne(Math.pow(.85,(n.scannerQueueSpacing||0)+1))}.`;if(t.id==="flattery")return`Next Fairies tribute -50%. Held for next tribute: ${n.fairiesFlatteryNext||0}.`;if(t.id==="usedMachine")return"Enable Used Machines: future equipment costs 50%, has no SELL refund, and starts with 60% durability.";if(t.id==="newMachine")return"Cancel Used Machines. Future equipment returns to normal price, refund, and durability.";if(t.id==="laborExploitation")return"Repairman labor cost -30% permanently. One time only.";if(t.id==="rescueLoan")return`At day-end bankruptcy check, gain ${S(500)} if cash is below ${S(0)}. Held: ${n.rescueLoanCharges||0}.`;if(t.id==="loan"){let o=Math.max(1,Math.floor(Number(e.turn)||1));return`Gain ${S(300*o)} now. Starting tomorrow, pay ${S(400)} per day for ${o} days.`}return t.description}function Go(e,t){let n=e.cardDraft?.choices||[],o=n.findIndex(r=>r.id===t.id);o>=0&&n.splice(o,1)}function Yi({game:e,ui:t,onUpdatePanels:n}){function o(){n&&n()}function r(){Ie(e),e.cardTargetPick=null,e.cardDraft={pending:!0,choices:Nn(e,Pt),rerolls:0,freeRerolls:_t(e,"freeReroll"),picksRemaining:1}}function s(){Ie(e),e.cardDraft.pending=!1,e.cardDraft.choices=[],e.cardDraft.picksRemaining=0,e.cardTargetPick=null,t.modal.classList.remove("visible","equipment-popover"),o()}function l(g=Pt){Ie(e),e.cardDraft.choices=Nn(e,g)}function d(){Ie(e),!e.cardDraft.choices.length&&e.cardDraft.picksRemaining>0&&l(Pt)}function k(g){Go(e,g),e.cardDraft.picksRemaining=Math.max(0,(e.cardDraft.picksRemaining||1)-1),e.cardTargetPick=null,d(),e.cardDraft.picksRemaining<=0?s():j()}function M(g){Go(e,g),e.cardDraft.picksRemaining=Math.max(0,(e.cardDraft.picksRemaining||1)-1)+Ee.extraCardsAddedPicks,e.cardDraft.choices.push(...Nn(e,2)),j()}function D(g,_){let U=_t(e,"dudRefund")*50;if(U>0){let le=Ae(e,U);e.stats.dudRefundIncome=(e.stats.dudRefundIncome||0)+le,e.totals.dudRefundIncome=(e.totals.dudRefundIncome||0)+le,N(e,y.x+y.cols*y.cell/2,y.y-30,`DUD REFUND +${S(le)}`,f.green)}Et(e,34,.62),document.body?.animate&&document.body.animate([{transform:"translate(0, 0)"},{transform:"translate(-10px, 7px)"},{transform:"translate(12px, -8px)"},{transform:"translate(-8px, -5px)"},{transform:"translate(9px, 6px)"},{transform:"translate(0, 0)"}],{duration:260,easing:"steps(5, end)"}),_&&(_.style.setProperty("--fling-x",`${260+Math.random()*260}px`),_.style.setProperty("--fling-y",`${-260-Math.random()*170}px`),_.style.setProperty("--fling-r",`${(Math.random()<.5?-1:1)*(52+Math.random()*38)}deg`),_.classList.add("flung")),window.setTimeout(()=>{Go(e,g),d(),j()},340)}function L(g,_=null){if(g.type==="dud")return D(g,_);if(g.id==="extraCards")return M(g);if(g.type==="equipmentUpgrade"||g.type==="cellAction")return F(g);qi(e,g),k(g)}function F(g){let _=Ue(e,g);_.length&&(e.cardTargetPick={pending:!0,cardId:g.id,mode:g.target==="blockedCell"?"blockedCell":"equipment",targetKeys:_.map(Wt),remaining:g.id==="dynamite"?Math.min(3,_.length):1},t.modal.classList.remove("visible","equipment-popover"),o())}function z(g){if(!g||g.type!=="blockedCell")return!1;let _=I(g.col,g.row);if(!e.blockedCells?.has(_))return!1;e.blockedCells.delete(_);let U=$(g.col,g.row);return st(e,U.x,U.y),Xe(e,U.x,U.y,f.danger,62),it(e,U.x,U.y,18,f.warn),Rn(e,U.x,U.y,8),Et(e,16,.35),N(e,U.x,U.y-18,"BOOM",f.danger),be(e),e.cardTargetPick.remaining=Math.max(0,(e.cardTargetPick.remaining||1)-1),e.cardTargetPick.targetKeys=Ue(e,"dynamite").map(Wt),o(),!0}function oe(g){if(!e.cardTargetPick?.pending)return!1;let _=Uo(e.cardTargetPick.cardId),U=Ul(e,g);return!_||!U?(X(),!0):_.id==="dynamite"?(z(U)&&((e.cardTargetPick.remaining||0)<=0||!Ue(e,"dynamite").length)&&k(_),!0):(Hi(e,U),k(_),!0)}function X(){e.cardTargetPick?.pending&&(e.cardTargetPick=null,j())}function ae(){let g=Math.max(0,e.cardDraft?.freeRerolls||0);if(g>0){e.cardDraft.freeRerolls=g-1,e.cardDraft.rerolls+=1,l(Pt),j();return}let _=Fi(e);e.cash<_||(Ve(e,_),e.stats.cardRerollCost=(e.stats.cardRerollCost||0)+_,e.totals.cardRerollCost=(e.totals.cardRerollCost||0)+_,e.cardDraft.rerolls+=1,l(Pt),j())}function he(g,_){let U=document.createElement("button");U.type="button";let le=g.type==="dud"?"dud":g.rarity==="ultraRare"?"ultra-rare":g.rarity==="rare"?"rare":"common";U.className=`card-choice ${le}`;let me=g.type==="dud"?"MISS":g.rarity==="ultraRare"?"ULTRA RARE":g.rarity==="rare"?"RARE":"COMMON",fe=[me,...(g.tags||[]).filter(E=>E!==me)].slice(0,5);return U.innerHTML=`${g.title}
${fe.map(E=>`${E}`).join("")}
${Wl(e,g)}`,U.addEventListener("click",()=>L(g,U)),U.dataset.index=String(_),U}function j(){Ie(e),e.cardTargetPick=null,e.cardDraft?.pending||r(),e.cardDraft.choices?.length||l(Pt);let g=e.cardDraft.choices||[],_=Fi(e),U=Math.max(0,e.cardDraft?.freeRerolls||0),le=Math.max(1,e.cardDraft.picksRemaining||1);t.modalTitle.textContent="Choose Upgrade Card",t.modalBody.innerHTML=`

Choose ${le} card${le===1?"":"s"} before Build phase. Dud cards can be flicked away without consuming a pick. Equipment cards target machines; Dynamite targets blocked cells.

`;let me=t.modalBody.querySelector("#cardChoices");g.forEach((G,w)=>me.appendChild(he(G,w))),t.modalActions.innerHTML="";let fe=document.createElement("button");fe.type="button",fe.className="facility-action warn reroll-button",fe.textContent=U>0?`Reroll (FREE x${U})`:`Reroll ${S(_)}`,fe.disabled=U<=0&&e.cash<_,fe.title=U>0?`${U} free reroll${U===1?"":"s"} remaining.`:fe.disabled?"Not enough cash":"",fe.addEventListener("click",ae),t.modalActions.appendChild(fe);let E=document.createElement("button");E.type="button",E.className="facility-action",E.textContent="No Card",E.title="Skip this upgrade pick and enter Build phase.",E.addEventListener("click",s),t.modalActions.appendChild(E),t.modal.classList.remove("equipment-popover"),t.modal.classList.add("visible")}return{prepareDraft:r,showDraft:j,finishDraft:s,chooseTargetAtPoint:oe,cancelTargetPick:X}}var _l=!1,Kl={chickMale:"./assets/images/chick_male.png",chickFemale:"./assets/images/chick_female.png",poop:new URL("../../assets/images/poop.png",import.meta.url).href,conveyor:"./assets/images/conveyor.png",scannerManual:"./assets/images/scanner_manual.png",scannerAuto:"./assets/images/scanner_auto.png",eggFarm:"./assets/images/egg_farm.png",mixer:"./assets/images/mixer.png",shredder:"./assets/images/shredder.png",truck:"./assets/images/truck.png"},He={};for(let[e,t]of Object.entries(Kl)){let n=new Image;n.loaded=!1,n.onerror=()=>{n.loaded=!1},(_l||e==="poop")&&t&&(n.onload=()=>{n.loaded=!0},n.src=t),He[e]=n}function Xi(e,t,n,o){e.clearRect(0,0,t.width,t.height),Yl(e,t),e.save();let r=n.shake.time>0?W(-n.shake.strength,n.shake.strength):0,s=n.shake.time>0?W(-n.shake.strength,n.shake.strength):0,l=n.view?.scale||1;e.translate((n.view?.x||0)+r,(n.view?.y||0)+s),e.scale(l,l),Vl(e,n),Xl(e,n),Ql(e,n),sa(e,n),pa(e,n),la(e,n),ca(e,n),ql(e,n),ba(e,n,o.activeQueuedChick),ga(e,n),Ca(e,t,n),ka(e,n,o.selectedObject,o.selectedTitle),Sa(e,n),Ma(e,n),Ta(e,n),e.restore(),Ea(e,n,t)}function Hl(e){if(e.type==="eggFarm"){let n=$(e.ref.col,e.ref.row);return{x:n.x-34,y:n.y-34,w:68,h:68,cx:n.x,cy:n.y}}let t=e.ref;return{x:t.x,y:t.y,w:t.w,h:t.h,cx:t.x+t.w/2,cy:t.y+t.h/2}}function ql(e,t){if(t.phase!=="build")return;let n=gn(t);for(let o of n){let r=Hl(o);e.save(),e.fillStyle="rgba(196,36,36,.22)",e.strokeStyle=f.danger,e.lineWidth=5,e.setLineDash([10,6]),ee(e,r.x,r.y,r.w,r.h,!0,!0),e.setLineDash([]),e.fillStyle=f.danger,e.strokeStyle=f.white,e.lineWidth=4,e.font="900 16px ui-monospace, monospace",e.textAlign="center",e.strokeText("!",r.cx,r.y+24),e.fillText("!",r.cx,r.y+24),e.font="900 11px ui-monospace, monospace",e.strokeText("NO ROUTE",r.cx,r.y+r.h-8),e.fillText("NO ROUTE",r.cx,r.y+r.h-8),e.restore()}}function ee(e,t,n,o,r,s=!0,l=!0){e.beginPath(),e.rect(t,n,o,r),s&&e.fill(),l&&e.stroke()}function Ho(e,t,n,o,r=f.ink){e.save(),e.font="900 12px ui-monospace, monospace",e.textAlign="center",e.lineWidth=4,e.strokeStyle=f.white,e.fillStyle=r,e.strokeText(o,t,n),e.fillText(o,t,n),e.restore()}function Rt(e,t,n,o,r,s){return t?.loaded?(e.drawImage(t,n,o,r,s),!0):!1}function Yl(e,t){e.fillStyle=f.bg,e.fillRect(0,0,t.width,t.height),e.save(),e.strokeStyle="rgba(16, 32, 21, 0.035)",e.lineWidth=1;for(let n=0;n=n;for(let r of mt(t)){let s=bt(r);e.save(),e.fillStyle=o?"rgba(183,245,198,.34)":"rgba(255,255,255,.22)",e.strokeStyle=o?f.green:f.muted,e.lineWidth=5,e.setLineDash([16,10]),ee(e,s.x,s.y,s.w,s.h,!0,!0),e.setLineDash([]);let l=so(r),d=l.cx,k=l.cy;e.fillStyle="rgba(255,255,255,.94)",e.strokeStyle=f.ink,e.lineWidth=4,ee(e,l.x,l.y,l.w,l.h,!0,!0),e.fillStyle=o?f.green:f.danger,e.font="900 16px ui-monospace, monospace",e.textAlign="center",e.fillText(r.direction==="right"?"BUY RIGHT LOT":"BUY UPPER LOT",d,k-12),e.fillStyle=f.ink,e.font="900 16px ui-monospace, monospace",e.fillText(`${S(n)} / 10\xD710`,d,k+15),e.fillStyle=f.muted,e.font="900 11px ui-monospace, monospace",e.fillText("blocked tiles included",d,k+30),e.restore()}}function Je(e,t,n,o,r,s,l=0){let d=Ut(s);d<.5||(e.save(),e.globalAlpha=Math.min(.62,.12+d*.54),e.fillStyle="#6b4a23",l>0?(e.beginPath(),e.roundRect?.(t,n,o,r,l),e.roundRect?e.fill():ee(e,t,n,o,r,!0,!1)):ee(e,t,n,o,r,!0,!1),e.globalAlpha=Math.min(.9,.25+d*.6),e.strokeStyle="#3a2a16",e.lineWidth=Math.max(2,Math.floor(2+d*5)),e.setLineDash([5,6]),ee(e,t+3,n+3,o-6,r-6,!1,!0),e.restore())}function Ko(e,t){let n=e.componentLookup?.get(t);return(n?e.congestion.get(n):null)?.ratio||0}function Ql(e,t){let n=[],o=new Set;for(let s of t.conveyorTiles){let{col:l,row:d}=J(s);for(let k of Br(t,l,d)){let M=I(k.col,k.row),D=[s,M].sort().join("|");if(o.has(D))continue;o.add(D);let L=Math.max(Ko(t,s),Ko(t,M));n.push({a:$(l,d),b:$(k.col,k.row),ratio:L})}}e.save(),e.lineCap="round",e.lineJoin="round";for(let s of[{width:33,color:f.ink,alpha:1},{width:25,color:f.white,alpha:1},{width:17,color:f.greenSoft,alpha:1}]){e.globalAlpha=s.alpha,e.strokeStyle=s.color,e.lineWidth=s.width;for(let l of n)e.beginPath(),e.moveTo(l.a.x,l.a.y),e.lineTo(l.b.x,l.b.y),e.stroke()}for(let s of n){let l=Math.max(0,(s.ratio-.5)/.5);e.strokeStyle=ro(f.green,f.danger,l),e.globalAlpha=s.ratio>.5?.35+l*.55:.8,e.lineWidth=6,e.beginPath(),e.moveTo(s.a.x,s.a.y),e.lineTo(s.b.x,s.b.y),e.stroke()}let r=oa(t);for(let s of t.conveyorTiles){let l=$(...Object.values(J(s))),d=t.conveyorMeta.get(s)||{},k=Ko(t,s),M=d.kind==="boostConveyor"?"#fff2b8":f.white;e.fillStyle=k>.5?ro(M,"#ffd6d6",Math.max(0,(k-.5)/.5)):M,e.strokeStyle=f.ink,e.lineWidth=2,ee(e,l.x-8,l.y-8,16,16,!0,!0),d.kind==="boostConveyor"&&(e.save(),e.strokeStyle=f.warn,e.lineWidth=3,e.beginPath(),e.moveTo(l.x-15,l.y+13),e.lineTo(l.x-3,l.y-13),e.lineTo(l.x+2,l.y-2),e.lineTo(l.x+15,l.y-13),e.stroke(),e.restore()),Je(e,l.x-14,l.y-14,28,28,{meta:d}),We(e,t,"conveyor",s,l.x,l.y,38,38),ra(e,l,r.get(s)||[]),ta(e,l,d,Zl(t,s,d),jl(t,s)),k>.5&&Ho(e,l.x,l.y-15,`${Math.floor(k*100)}%`,f.danger)}e.restore()}function Jl(e){return{random:"RND",pass:"PASS",up:"UP",right:"RT",down:"DN",left:"LF"}[e||"random"]||"RND"}function Zl(e,t,n){let o=J(t),r=pe.filter(d=>e.conveyorTiles.has(I(o.col+d.dc,o.row+d.dr))).map(d=>d.name),s=[...new Set([...Array.isArray(n.outDirs)?n.outDirs:[],n.dir].filter(Boolean))].filter(d=>r.includes(d)),l=r;return s.length?s:l}function jl(e,t){let n=J(t);return pe.filter(o=>e.conveyorTiles.has(I(n.col+o.dc,n.row+o.dr))).map(o=>o.name)}function ea(e,t,n){let o=J(t),r=pe.filter(d=>e.conveyorTiles.has(I(o.col+d.dc,o.row+d.dr))).map(d=>d.name),s=[...new Set([...Array.isArray(n.outDirs)?n.outDirs:[],n.dir].filter(Boolean))].filter(d=>r.includes(d)),l=r;return s.length?s:l}function ta(e,t,n,o,r=o){if(r.length<3||o.length<2)return;e.save();let s=n.branchMode==="pass"&&o.length>=4?"pass":o.includes(n.branchMode)?n.branchMode:"random",l=s!=="random",d=Jl(s);e.lineJoin="round",e.lineCap="round",e.lineWidth=4,e.fillStyle="rgba(255,255,255,.9)",e.strokeStyle=l?f.warn:f.green,ee(e,t.x-28,t.y-19,56,38,!0,!0),e.strokeStyle=f.ink,e.lineWidth=2,e.beginPath(),e.moveTo(t.x-18,t.y),e.lineTo(t.x+18,t.y),e.moveTo(t.x,t.y-13),e.lineTo(t.x,t.y+13),e.stroke(),e.fillStyle=l?f.warn:f.green,e.beginPath(),e.arc(t.x,t.y,12,0,Math.PI*2),e.fill(),e.strokeStyle=f.ink,e.lineWidth=2,e.stroke(),e.font="900 9px ui-monospace, monospace",e.textAlign="center",e.textBaseline="middle",e.fillStyle=f.ink,e.fillText(d,t.x,t.y+.5),e.restore()}function na(e,t,n){if(!n)return[];let o=J(t),r=pe.filter(d=>e.conveyorTiles.has(I(o.col+d.dc,o.row+d.dr))).map(d=>d.name),s=[...new Set([...Array.isArray(n.outDirs)?n.outDirs:[],n.dir].filter(Boolean))].filter(d=>r.includes(d));return(s.length?s:ea(e,t,n).slice(0,1)).map(d=>pe.find(k=>k.name===d)?.angle).filter(d=>Number.isFinite(d))}function oa(e){let t=new Map;for(let[n,o]of e.conveyorMeta||[]){let r=na(e,n,o);r.length&&t.set(n,r)}return t}function ra(e,t,n){let o=n.slice(0,3);o.length&&(e.save(),e.fillStyle=f.ink,e.globalAlpha=.86,o.forEach((r,s)=>{let l=(s-(o.length-1)/2)*4,d=Math.cos(r+Math.PI/2),k=Math.sin(r+Math.PI/2);ia(e,t.x+d*l,t.y+k*l,r,6,5)}),e.restore())}function ia(e,t,n,o,r=11,s=8){e.save(),e.translate(t,n),e.rotate(o),e.beginPath(),e.moveTo(r,0),e.lineTo(-r*.72,-s),e.lineTo(-r*.72,s),e.closePath(),e.fill(),e.restore()}function sa(e,t){e.save(),e.lineWidth=2,e.font="900 12px ui-monospace, monospace",e.textAlign="center";for(let n of t.scanners){let o=ho(n),r=[{type:"inputA",p:Me(n,"inputA"),label:"IN",color:f.green},{type:"left",p:Me(n,"left"),label:bo(o.left),color:wo(o.left)},{type:"right",p:Me(n,"right"),label:bo(o.right),color:wo(o.right)}];for(let s of r){if(!s.p)continue;let l=$(s.p.col,s.p.row),d=Ir(t,n,s.type||(s.label==="IN"?"inputA":null))||t.conveyorTiles.has(I(s.p.col,s.p.row));e.fillStyle=d?s.color:"#f4c2c2",e.strokeStyle=f.ink,ee(e,l.x-18,l.y-12,36,24,!0,!0),e.fillStyle=d&&s.color===f.white?f.ink:f.white,e.fillText(s.label,l.x,l.y+4)}}e.restore()}function la(e,t){for(let n of t.eggFarms)aa(e,n,t)}function aa(e,t,n){let o=$(t.col,t.row);if(e.save(),Rt(e,He.eggFarm,o.x-29,o.y-29,58,58)){Je(e,o.x-29,o.y-29,58,58,t);let l=1+Math.max(0,Number(t.extraEggOutlet)||0);e.fillStyle="rgba(255,255,255,.92)",e.strokeStyle=f.ink,e.lineWidth=2,ee(e,o.x+9,o.y+12,30,18,!0,!0),e.fillStyle=f.ink,e.font="900 10px ui-monospace, monospace",e.textAlign="center",e.fillText(`O${l}`,o.x+24,o.y+24),We(e,n,"eggFarm",t.id,o.x,o.y,68,68),e.restore();return}e.fillStyle=f.white,e.strokeStyle=f.ink,e.lineWidth=4,ee(e,o.x-28,o.y-28,56,56,!0,!0),e.fillStyle=f.green,e.font="900 19px ui-monospace, monospace",e.textAlign="center",e.fillText("EGG",o.x,o.y-4);let r=1+Math.max(0,Number(t.extraEggOutlet)||0);e.font="900 11px ui-monospace, monospace",e.fillStyle=f.ink,e.fillText(`L${t.level} O${r}`,o.x,o.y+13),e.fillText(`${t.nextSpawn.toFixed(1)}s`,o.x,o.y+26);let s=Math.max(0,Math.min(1,t.shutterProgress||0));if(s>0){let l=56*s;e.fillStyle=f.ink,ee(e,o.x-28,o.y-28,56,l,!0,!1),e.strokeStyle=f.white,e.lineWidth=2;for(let d=o.y-24;d=1&&(e.fillStyle=f.white,e.font="900 12px ui-monospace, monospace",e.fillText("SHUT",o.x,o.y+4))}Je(e,o.x-28,o.y-28,56,56,t),We(e,n,"eggFarm",t.id,o.x,o.y,68,68),e.restore()}function ca(e,t){for(let n of t.scanners)ua(e,n,t)}function ua(e,t,n){let o=de(t);if(e.save(),t.kind==="manual"&&(n.manualCombo?.count||0)>=10){let k=n.manualCombo.count;e.save(),e.globalAlpha=Math.min(.55,.16+k/120),e.strokeStyle=k>=30?f.warn:f.green,e.lineWidth=Math.min(14,5+Math.floor(k/10)),e.shadowColor=e.strokeStyle,e.shadowBlur=Math.min(28,8+k),ee(e,o.x-54,o.y-76,108,152,!1,!0),e.restore()}let r=t.kind==="auto"?He.scannerAuto:He.scannerManual;if(Rt(e,r,o.x-50,o.y-73,100,146)){t.kind==="auto"?Je(e,o.x-50,o.y-73,100,146,t):zi(e,t,o),We(e,n,"scanner",t.id,o.x,o.y,108,154),e.restore();return}e.fillStyle=t.kind==="auto"?"#d8ffe2":f.white,e.strokeStyle=f.ink,e.lineWidth=4,ee(e,o.x-50,o.y-73,100,146,!0,!0),e.fillStyle=t.kind==="auto"?f.green:f.ink,e.font="900 19px ui-monospace, monospace",e.textAlign="center",e.fillText(`${t.kind==="auto"?"AUTO":`S${(t.slot??0)+1}`}`,o.x,o.y-48),e.font="900 11px ui-monospace, monospace";let s=t.keys?.left?.label||(t.slot===0?"A":"Left"),l=t.keys?.right?.label||(t.slot===0?"D":"Right");e.fillText(t.role===0?`${s}:M ${l}:NEXT`:`${s}:WASTE ${l}:TRUCK`,o.x,o.y-28);let d=t.queue.length;t.kind==="manual"?(zi(e,t,o),e.fillStyle=d>0?f.green:f.muted,e.font="900 10px ui-monospace, monospace",e.fillText(`Q:${d}`,o.x,o.y+54)):(e.fillStyle=d>0?f.green:f.muted,e.font="900 12px ui-monospace, monospace",e.fillText(`Q:${d} CD:${t.cooldown.toFixed(1)}`,o.x,o.y+54),Je(e,o.x-50,o.y-73,100,146,t)),We(e,n,"scanner",t.id,o.x,o.y,108,154),e.restore()}function zi(e,t,n){if(t.kind!=="manual")return;let r=(t.slot??0)===0?[{side:"left",text:"A"},{side:"right",text:"D"}]:[{side:"left",text:"\u2190"},{side:"right",text:"\u2192"}];r[0].text=t.keys?.left?.label||r[0].text,r[1].text=t.keys?.right?.label||r[1].text;let s=t.keyPressTime>0?t.keyPressSide:null,l=n.x-30,d=n.y+18;e.save(),e.lineWidth=3,e.fillStyle="#f7fff5",e.strokeStyle=f.ink,ee(e,l-4,d-4,68,25,!0,!0),e.font="900 19px ui-monospace, monospace",e.textAlign="center";for(let k=0;k.8,s=t.y+Math.sin(t.bob)*(r?5:1.5);if(e.save(),n&&(e.strokeStyle=f.green,e.lineWidth=5,e.beginPath(),e.arc(t.x,s,t.radius+8,0,Math.PI*2),e.stroke()),r&&(e.strokeStyle=f.danger,e.lineWidth=3,e.beginPath(),e.arc(t.x,s,t.radius+12+Math.sin(t.bob*2)*4,0,Math.PI*2),e.stroke()),t.sex==="poop")Vo(e,t.x,s,t.radius);else{let l=t.sex==="male"?He.chickMale:He.chickFemale;Rt(e,l,t.x-t.radius,s-t.radius,t.radius*2,t.radius*2)||(e.fillStyle=t.sex==="male"?f.male:f.female,e.strokeStyle=f.ink,e.lineWidth=5,e.beginPath(),e.arc(t.x,s,t.radius,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle=f.ink,e.beginPath(),e.arc(t.x-6,s-5,2.5,0,Math.PI*2),e.arc(t.x+6,s-5,2.5,0,Math.PI*2),e.fill(),e.fillStyle="#ffaa2e",e.beginPath(),e.moveTo(t.x,s+2),e.lineTo(t.x+9,s+6),e.lineTo(t.x,s+10),e.closePath(),e.fill())}e.restore()}function va(e,t){let n=null,o=1/0;for(let[r,s]of e.componentLookup.entries()){let l=J(r),d=$(l.col,l.row),k=Math.hypot(t.x-d.x,t.y-d.y);kVi(o.type,o.id)===Vi(t,n))}function We(e,t,n,o,r,s,l,d){xa(t,n,o)&&(e.save(),e.fillStyle="rgba(34,185,79,.18)",e.strokeStyle=f.green,e.lineWidth=(t.multiSelected||[]).length>1?6:4,e.setLineDash((t.multiSelected||[]).length>1?[]:[8,5]),ee(e,r-l/2,s-d/2,l,d,!0,!0),e.restore())}function ka(e,t,n,o){if(t.phase==="build"&&(t.multiSelected||[]).length>1){e.save(),e.fillStyle="rgba(255,255,255,.96)",e.strokeStyle=f.ink,e.lineWidth=3,ee(e,54-t.view.x,66-t.view.y,214,42,!0,!0),e.fillStyle=f.ink,e.font="900 11px ui-monospace, monospace",e.fillText(`${t.multiSelected.length} ITEMS SELECTED`,65-t.view.x,83-t.view.y),e.fillStyle=f.muted,e.font="900 11px ui-monospace, monospace",e.fillText("drag any selected item to move group",65-t.view.x,99-t.view.y),e.restore();return}}function Ca(e,t,n){if(!n.cardTargetPick?.pending)return;let o=_o(n);e.save(),e.fillStyle="rgba(0,0,0,.66)",e.beginPath();let r=n.view?.scale||1;e.rect(-(n.view?.x||0)/r,-(n.view?.y||0)/r,t.width/r,t.height/r);for(let k of o){let M=k.bounds,D=8;e.rect(M.x-D,M.y-D,M.w+D*2,M.h+D*2)}e.fill("evenodd");let s=n.cardTargetPick?.mode==="blockedCell";if(!o.length){e.fillStyle="rgba(255,255,255,.95)",e.strokeStyle=f.ink,e.lineWidth=3,ee(e,(260-(n.view?.x||0))/r,(96-(n.view?.y||0))/r,420/r,42/r,!0,!0),e.fillStyle=f.danger,e.font="900 12px ui-monospace, monospace",e.textAlign="center",e.fillText(s?"NO BLOCKED CELL. PRESS ESC.":"NO VALID UPGRADE TARGET. PRESS ESC.",(470-(n.view?.x||0))/r,(122-(n.view?.y||0))/r),e.restore();return}e.fillStyle="rgba(255,255,255,.95)",e.strokeStyle=f.ink,e.lineWidth=3;let l=Math.max(1,n.cardTargetPick?.remaining||1),d=n.cardTargetPick?.mode==="autoScannerMenu"?"AUTO SCANNER ROUTE SETTINGS. CLICK EMPTY AREA TO CLOSE.":s?`CLICK BLOCKED CELL: ${l} LEFT. PRESS ESC TO CANCEL.`:"CLICK AN UPGRADEABLE MACHINE. PRESS ESC TO CANCEL.";ee(e,(216-(n.view?.x||0))/r,(96-(n.view?.y||0))/r,560/r,42/r,!0,!0),e.fillStyle=f.ink,e.font="900 12px ui-monospace, monospace",e.textAlign="center",e.fillText(d,(496-(n.view?.x||0))/r,(122-(n.view?.y||0))/r),e.restore()}function Sa(e,t){if(t.phase!=="build"||!t.selectionBox)return;let n=t.selectionBox,o=Math.min(n.x1,n.x2),r=Math.min(n.y1,n.y2),s=Math.abs(n.x2-n.x1),l=Math.abs(n.y2-n.y1);e.save(),e.fillStyle="rgba(34,185,79,.12)",e.strokeStyle=f.green,e.lineWidth=2,e.setLineDash([7,4]),ee(e,o,r,s,l,!0,!0),e.restore()}function Ma(e,t){let n=[...t.effects].sort((o,r)=>(o.priority??Se.ambient)-(r.priority??Se.ambient));for(let o of n){let r=Math.max(0,o.life/o.maxLife);e.save(),e.globalAlpha=r,o.type==="meat"?(e.fillStyle=f.green,ee(e,o.x-o.size/2,o.y-o.size/2,o.size*1.5,o.size,!0,!1)):o.type==="sludge"?(e.fillStyle=f.poop,ee(e,o.x-o.size/2,o.y-o.size/2,o.size*1.4,o.size,!0,!1)):o.type==="spawn"?(e.strokeStyle=f.green,e.lineWidth=3,e.beginPath(),e.arc(o.x,o.y,o.size+(1-r)*20,0,Math.PI*2),e.stroke()):o.type==="spark"?(e.fillStyle=o.color||f.danger,ee(e,o.x-o.size/2,o.y-o.size/2,o.size,o.size,!0,!1)):o.type==="smoke"?(e.fillStyle="rgba(16,32,21,.24)",e.beginPath(),e.arc(o.x,o.y,o.size*(1.2-r*.2),0,Math.PI*2),e.fill()):o.type==="shockwave"?(e.strokeStyle=o.color||f.danger,e.lineWidth=4,e.beginPath(),e.arc(o.x,o.y,o.size+(1-r)*(o.maxSize||44),0,Math.PI*2),e.stroke()):o.type==="scannerPulse"?(e.strokeStyle=o.color||f.green,e.lineWidth=5,e.setLineDash([8,6]),e.beginPath(),e.arc(o.x,o.y,o.size+(1-r)*38,0,Math.PI*2),e.stroke()):o.type==="shred"?(e.fillStyle=o.color||f.ink,e.save(),e.translate(o.x,o.y),e.rotate(o.rot||0),ee(e,-o.w/2,-o.h/2,o.w,o.h,!0,!1),e.restore()):o.type==="load"?(e.strokeStyle=o.color||f.green,e.lineWidth=3,e.beginPath(),e.moveTo(o.x-14,o.y),e.lineTo(o.x+14,o.y),e.moveTo(o.x,o.y-14),e.lineTo(o.x,o.y+14),e.stroke()):o.type==="erase"?(e.strokeStyle=f.ink,e.lineWidth=3,e.setLineDash([4,4]),ee(e,o.x-o.size/2,o.y-o.size/2,o.size,o.size,!1,!0)):o.type==="rage"?(e.fillStyle=o.color||f.danger,e.font="900 16px ui-monospace, monospace",e.textAlign="center",e.fillText("!",o.x,o.y)):o.type==="flyingChick"&&(o.sex==="poop"?Vo(e,o.x,o.y,o.radius||11):Xo(e,o.x,o.y,o.sex,o.sex==="male")),e.restore()}}function Ta(e,t){e.save(),e.font="900 19px ui-monospace, monospace",e.textAlign="center";for(let n of t.floatingTexts){let o=Math.max(0,n.life/n.maxLife);e.globalAlpha=o,e.lineWidth=5,e.strokeStyle=f.white,e.fillStyle=n.color,e.strokeText(n.text,n.x,n.y),e.fillText(n.text,n.x,n.y)}e.restore()}function ga(e,t){let n=t.repairman;if(!(!!n?.active||t.phase==="build"&&!!n?.hiredForNextDay))return;e.save(),e.lineWidth=3,e.fillStyle="#fff0d1",e.strokeStyle=f.ink,e.beginPath(),e.arc(n.x,n.y,17,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle="#ffd6e7",e.beginPath(),e.arc(n.x-6,n.y-2,3.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(n.x+6,n.y-2,3.2,0,Math.PI*2),e.fill();let r=!!t.cardEffects?.laborExploitation;e.fillStyle=r?f.danger:f.ink,e.beginPath(),e.arc(n.x-5,n.y-5,r?2.8:2.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(n.x+5,n.y-5,r?2.8:2.2,0,Math.PI*2),e.fill(),e.strokeStyle=f.ink,e.lineWidth=r?3:2,e.beginPath(),e.arc(n.x,n.y+(r?0:1),r?8:7,.1*Math.PI,.9*Math.PI),e.stroke(),e.fillStyle=f.green,ee(e,n.x-9,n.y+14,18,8,!0,!0),e.strokeStyle=f.ink,e.lineWidth=3,e.beginPath(),e.moveTo(n.x-11,n.y+18),e.lineTo(n.x-20,n.y+24),e.stroke(),e.beginPath(),e.moveTo(n.x+11,n.y+18),e.lineTo(n.x+20,n.y+24),e.stroke(),e.fillStyle=f.ink,e.font="900 11px ui-monospace, monospace",e.textAlign="center",e.fillText(n.active?"FIX":"HIRED",n.x,n.y+33),n.target?.label?Ho(e,n.x,n.y-24,n.target.label,f.green):!n.active&&n.hiredForNextDay&&Ho(e,n.x,n.y-24,"NEXT DAY",f.green),e.restore()}function Ea(e,t,n){e.save(),e.font="900 19px ui-monospace, monospace",e.textAlign="left",e.fillStyle=f.ink,e.fillStyle="rgba(255,255,255,.92)",e.strokeStyle=f.ink,e.lineWidth=3,ee(e,n.width-284,18,260,34,!0,!0),e.fillStyle=f.green,e.font="900 12px ui-monospace, monospace",e.fillText(yn,n.width-270,40),e.font="900 19px ui-monospace, monospace",e.fillStyle=f.ink;let o=[];t.phase==="running"&&o.push(t.timeLeft<=0?"TIME UP: FARMS SHUT. CLEAR LINE.":"A/D: S1 \u2190/\u2192: S2"),t.phase==="build"&&o.push(`BUILD: ${t.buildTool||"SELECT"} | connect 1 EGG route to any exit`);let r=76;for(let s of o)e.strokeStyle=f.white,e.lineWidth=4,e.strokeText(s,24,r),e.fillText(s,24,r),r+=20;e.restore()}var ji=[[{key:"a",code:"KeyA",label:"A"},{key:"d",code:"KeyD",label:"D"}],[{key:"arrowleft",code:"ArrowLeft",label:"Left"},{key:"arrowright",code:"ArrowRight",label:"Right"}],[{key:"j",code:"KeyJ",label:"J"},{key:"l",code:"KeyL",label:"L"}],[{key:"f",code:"KeyF",label:"F"},{key:"h",code:"KeyH",label:"H"}],[{key:"q",code:"KeyQ",label:"Q"},{key:"e",code:"KeyE",label:"E"}],[{key:"z",code:"KeyZ",label:"Z"},{key:"x",code:"KeyX",label:"X"}],[{key:"u",code:"KeyU",label:"U"},{key:"o",code:"KeyO",label:"O"}],[{key:"n",code:"KeyN",label:"N"},{key:"m",code:"KeyM",label:"M"}]];function Pa(e){let t=ji[e]||ji[0];return{left:{...t[0]},right:{...t[1]}}}function es(e,t,n){let o={type:"eggFarm",id:e.nextId++,col:t,row:n,level:1,nextSpawn:2,lastInterval:2,extraEggOutlet:0,price:pt.eggFarm,builtSession:e.buildSession};return o.nextSpawn=wt(o),o.lastInterval=o.nextSpawn,o}function ts(e,t,n,o){let r=o==="auto"?we.autoScanner.price:we.manualScanner.price,s=new Set(e.scanners.filter(d=>d.kind==="manual").map(d=>d.slot)),l=0;for(;s.has(l);)l+=1;return{type:"scanner",id:e.nextId++,kind:o,slot:o==="manual"?l:null,role:o==="manual"?l%2:0,col:t,row:n,level:1,queue:[],cooldown:0,price:r,builtSession:e.buildSession,autoMode:"standard",keys:o==="manual"?Pa(l):null}}function Un(e){let t=(e.x-y.x)/y.cell,n=(e.y-y.y)/y.cell,o=Math.max(0,Math.min(y.cols-1,Math.round(t-.5))),r=Math.max(0,Math.min(y.rows-1,Math.round(n-.5))),s=Math.abs(e.x-y.x),l=Math.abs(e.x-(y.x+y.cols*y.cell)),d=Math.abs(e.y-y.y),k=Math.abs(e.y-(y.y+y.rows*y.cell)),M=[["left",s],["right",l],["top",d],["bottom",k]].sort((D,L)=>D[1]-L[1])[0][0];return M==="left"?{side:M,entry:{col:0,row:r}}:M==="right"?{side:M,entry:{col:y.cols-1,row:r}}:M==="top"?{side:M,entry:{col:o,row:0}}:{side:M,entry:{col:o,row:y.rows-1}}}function Wn(e,t,n){let o={x:y.x+t.col*y.cell+y.cell/2,y:y.y+t.row*y.cell+y.cell/2},r={left:y.x,right:y.x+y.cols*y.cell,top:y.y,bottom:y.y+y.rows*y.cell};return e.entry={...t},e.side=n,n==="left"?(e.x=r.left-e.w-18,e.y=o.y-e.h/2):n==="right"?(e.x=r.right+18,e.y=o.y-e.h/2):n==="top"?(e.x=o.x-e.w/2,e.y=r.top-e.h-18):(e.x=o.x-e.w/2,e.y=r.bottom+18),e}function ns(e,t,n,o){let{entry:r,side:s}=Un(n),l=we[t],d=l?{name:l.name,w:l.body?.w||168,h:l.body?.h||110,price:l.price}:{name:t[0].toUpperCase()+t.slice(1),w:168,h:110,price:o},k={type:"facility",id:t,name:d.name,x:0,y:0,w:d.w,h:d.h,level:1,price:o||d.price,builtSession:e.buildSession};return Wn(k,r,s)}function Ra(e){return Math.random()({x:s.x,y:s.y})),r=o[0];return{id:e.nextId++,sex:n,x:r.x,y:r.y,route:o,targetIndex:o.length>1?1:o.length,stage:t.stage||"belt",scannerId:t.scannerId??null,nextScannerId:null,pendingScannerId:null,radius:n==="poop"?15:17,bob:Math.random()*Math.PI*2,queueIndex:-1,queueRoute:null}}function rs({game:e,canvasPoint:t,updatePanels:n,equipmentAtPoint:o,fail:r,pointInGrid:s,rectOfFacility:l,rectsOverlap:d}){function k(w){return w?w.type==="conveyor"?`conveyor:${w.id||w.oldKey}`:w.type==="facility"?`facility:${w.id||w.ref?.id}`:`${w.type}:${w.id||w.ref?.id}`:""}function M(w){return w?w.type==="conveyor"?{type:"conveyor",id:w.oldKey}:{type:w.type,id:w.ref.id}:null}function D(w){let C=k(M(w));return(e.multiSelected||[]).some(O=>k(O)===C)}function L(w){return w?w.type==="eggFarm"?e.eggFarms.find(C=>C.id===w.id)||null:w.type==="scanner"?e.scanners.find(C=>C.id===w.id)||null:w.type==="facility"?e.facilities[w.id]||null:w.type==="conveyor"&&e.conveyorTiles.has(w.id)?{type:"conveyor",id:w.id}:null:null}function F(){let w=[];for(let C of e.conveyorTiles){let O=J(C),Y=$(O.col,O.row);w.push({type:"conveyor",id:C,center:Y,cell:O})}for(let C of e.eggFarms)w.push({type:"eggFarm",id:C.id,ref:C,center:$(C.col,C.row),cell:{col:C.col,row:C.row}});for(let C of e.scanners)w.push({type:"scanner",id:C.id,ref:C,center:de(C),cell:{col:C.col,row:C.row}});for(let C of Object.values(e.facilities)){let O={x:C.x+C.w/2,y:C.y+C.h/2},Y=C.entry?$(C.entry.col,C.entry.row):O;w.push({type:"facility",id:C.id,ref:C,center:O,portCenter:Y})}return w}function z(w,C){let O=Math.min(C.x1,C.x2),Y=Math.max(C.x1,C.x2),te=Math.min(C.y1,C.y2),se=Math.max(C.y1,C.y2);return w.x>=O&&w.x<=Y&&w.y>=te&&w.y<=se}function oe(w){let C=[];for(let O of F())(z(O.center,w)||O.portCenter&&z(O.portCenter,w))&&C.push({type:O.type,id:O.id});return e.multiSelected=C,e.selected=C[0]||null,n(),C.length}function X(){e.multiSelected=[],e.selected=null,n()}function ae(w){let C=t(w);return e.selectionBox={x1:C.x,y1:C.y,x2:C.x,y2:C.y},e.multiSelected=[],e.selected=null,n(),!0}function he(w){if(!e.selectionBox)return;let C=t(w);e.selectionBox.x2=C.x,e.selectionBox.y2=C.y}function j(){if(!e.selectionBox)return 0;let w=e.selectionBox,C=Math.abs(w.x2-w.x1),O=Math.abs(w.y2-w.y1);return e.selectionBox=null,C<6&&O<6?(X(),0):oe(w)}function g(w){let C=M(w);return C?(e.multiSelected||[]).length&&D(w)?[...e.multiSelected]:[C]:[]}function _(w){let C=L(w);if(!C)return null;if(w.type==="conveyor"){let O=J(w.id);return{...w,obj:C,oldKey:w.id,currentKey:w.id,meta:e.conveyorMeta.get(w.id)||{price:Be("conveyor"),builtSession:null},col:O.col,row:O.row}}return w.type==="eggFarm"||w.type==="scanner"?{...w,obj:C,col:C.col,row:C.row}:w.type==="facility"?{...w,obj:C,x:C.x,y:C.y,w:C.w,h:C.h,entry:C.entry?{...C.entry}:null,side:C.side,center:{x:C.x+C.w/2,y:C.y+C.h/2}}:null}function U(w){let C=t(w),O=o(C);if(!O)return!1;let Y=g(O),te=Y.map(_).filter(Boolean);return te.length?(e.groupDrag={start:C,selections:Y,origins:te,historySnapshot:rn(e),committed:!1,lastDCol:0,lastDRow:0},e.multiSelected=Y,e.selected=Y[0]||null,n(),!0):!1}function le(){!e.groupDrag||e.groupDrag.committed||(e.undoStack.push(e.groupDrag.historySnapshot),e.redoStack=[],e.groupDrag.committed=!0)}function me(w,C,O){let Y=I(w,C),te=kt(e,w,C);if(te&&!O.has(`eggFarm:${te.id}`))return!0;let se=Ct(e,w,C);return!!(se&&!O.has(`scanner:${se.id}`)||e.conveyorTiles.has(Y)&&!O.has(`conveyor:${Y}`))}function fe(w,C,O){let Y=[];for(let te of e.groupDrag.origins){if(te.type!=="facility")continue;let se={x:te.center.x+w,y:te.center.y+C},{entry:ve,side:ce}=Un(se);if(!s(ve.col,ve.row)||e.blockedCells?.has?.(I(ve.col,ve.row)))return!0;let je={...te.obj,entry:{...ve},side:ce};Wn(je,ve,ce),Y.push({id:te.id,rect:l(je)})}for(let te of Y)for(let se of Object.values(e.facilities))if(!O.has(`facility:${se.id}`)&&d(te.rect,l(se),12))return!0;for(let te=0;teQ.type==="conveyor");for(let Q of je){let xe=Q.currentKey||Q.oldKey;e.conveyorTiles.delete(xe),e.conveyorMeta.delete(xe)}for(let Q of je){let xe=I(Q.col+te,Q.row+se);e.conveyorTiles.add(xe),e.conveyorMeta.set(xe,Q.meta||{price:Be("conveyor"),builtSession:null,uses:0}),Q.currentKey=xe}for(let Q of e.groupDrag.origins)if(Q.type==="eggFarm")Q.obj.col=Q.col+te,Q.obj.row=Q.row+se;else if(Q.type==="scanner")Q.obj.col=Q.col+te,Q.obj.row=Q.row+se;else if(Q.type==="facility"){let xe={x:Q.center.x+O,y:Q.center.y+Y},{entry:ut,side:qe}=Un(xe);Wn(Q.obj,ut,qe)}e.groupDrag.selections=e.groupDrag.origins.map(Q=>Q.type==="conveyor"?{type:"conveyor",id:Q.currentKey||Q.oldKey}:{type:Q.type,id:Q.id}),e.multiSelected=[...e.groupDrag.selections],e.selected=e.multiSelected[0]||null,be(e),n()}function G(){e.groupDrag&&(be(e),e.groupDrag=null,n())}return{selectionFromHit:M,startSelectionBox:ae,updateSelectionBox:he,finishSelectionBox:j,startGroupDrag:U,updateGroupDrag:E,finishGroupDrag:G}}function is({game:e,ui:t,canvas:n,canvasPoint:o,onUpdatePanels:r}){function s(){r&&r()}let l=null,d=!1,k=null,M=!1,D=["random","pass","up","right","down","left"],L={random:"RND",pass:"PASS",up:"UP",right:"RT",down:"DN",left:"LF"},F={w:56,h:38,y:0};function z(){return xn(e)}function oe(){e.gridCols=y.cols,e.gridRows=y.rows,e.gridOwnedCells=vr(e)}function X(i){if(i){y.y-=i*y.cell,y.rows+=i,e.gridChunkOriginY=(e.gridChunkOriginY||0)-Math.floor(i/10),e.ownedCells=kn(e.ownedCells,i),e.blockedCells=kn(e.blockedCells,i),e.conveyorTiles=kn(e.conveyorTiles,i),e.conveyorMeta=Sr(e.conveyorMeta,i);for(let c of e.eggFarms||[])c.row+=i;for(let c of e.scanners||[])c.row+=i;for(let c of Object.values(e.facilities||{}))c.entry&&(c.entry.row+=i)}}function ae(i){e.ownedCells=e.ownedCells||new Set;for(let c of Cr(i))e.ownedCells.add(I(c.col,c.row))}function he(i){return e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending?null:mt(e).find(c=>c&&kr(c,i))||null}function j(i="right"){if(i&&typeof i=="object"&&Number.isFinite(i.chunkX)&&Number.isFinite(i.chunkY))return i;let c=["right","up"].includes(i)?i:"right";return xr(e,c)}function g(i="right"){let c=j(i);if(e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending)return{ok:!1,reason:"Cannot expand now."};if(!c)return{ok:!1,reason:"Invalid expansion lot."};let h=c.direction||"right",m=z();if(e.cashy.cols&&(y.cols=A.colStart+A.cols),A.rowStart+A.rows>y.rows&&(y.rows=A.rowStart+A.rows),ae(A),Mr(e,A.chunkX,A.chunkY),e.gridExpansionPurchases=(e.gridExpansionPurchases||0)+1,e.gridExpansionPurchasesByDirection[h]=(e.gridExpansionPurchasesByDirection[h]||0)+1,oe();let ne=yr({seed:`${e.runSeed}:grid-expansion:${A.chunkX},${A.chunkY}:${e.gridExpansionPurchases}`,colStart:A.colStart,colEnd:A.colStart+A.cols,rowStart:A.rowStart,rowEnd:A.rowStart+A.rows,targetRatio:q.map.blockedRatio});for(let Ft of ne)e.blockedCells.add(Ft);be(e);let ue=A.cols*A.rows,ke=bt(A);return N(e,ke.x+ke.w/2,ke.y+ke.h/2,`${h.toUpperCase()} +${ue} CELLS -${S(m)}`,f.green),s(),{ok:!0,direction:h,cost:m,addedCells:ue,blocked:ne.size,chunkX:A.chunkX,chunkY:A.chunkY}}function _(i,c){return i>=0&&i=0&&c!c.has(m.id)&&me(h,le(m),12))}function E(i,c,h=null){if(!_(i,c))return!1;if(U(i,c))return!0;let m=I(i,c),T=kt(e,i,c);if(T&&!(h?.type==="eggFarm"&&h.ref?.id===T.id))return!0;let A=Ct(e,i,c);return!!(A&&!(h?.type==="scanner"&&h.ref?.id===A.id)||e.conveyorTiles.has(m)&&!(h?.type==="conveyor"&&h.oldKey===m))}function G(i,c,h=null){let m={col:i,row:c};for(let T of xt(m)){if(!_(T.col,T.row)||U(T.col,T.row))return!0;let A=I(T.col,T.row);if(kt(e,T.col,T.row))return!0;let ue=Ct(e,T.col,T.row);if(ue&&!(h?.type==="scanner"&&h.ref?.id===ue.id)||e.conveyorTiles.has(A))return!0}return!1}function w(){return e.selected?e.selected.type==="eggFarm"?e.eggFarms.find(i=>i.id===e.selected.id)||null:e.selected.type==="scanner"?e.scanners.find(i=>i.id===e.selected.id)||null:e.selected.type==="conveyor"?e.conveyorTiles.has(e.selected.id)?{type:"conveyor",id:e.selected.id}:null:e.selected.type==="facility"&&e.facilities[e.selected.id]||null:null}function C(i){return i?i.type==="eggFarm"?`${ht("eggFarm")} #${i.id}`:i.type==="scanner"?`${i.kind==="auto"?ht("autoScanner"):ht("manualScanner")} #${i.id}`:i.type==="conveyor"?e.conveyorMeta.get(i.id)?.kind==="boostConveyor"?ht("boostConveyor"):ht("conveyor"):i.type==="facility"?i.name:"Equipment":"Equipment"}function O(i){return i?.type==="conveyor"?e.conveyorMeta?.get(i.oldKey||i.ref?.id)?.price||En(i):En(i)}function Y(){return!!e.cardEffects?.usedMachineActive}function te(){return Y()?{usedMachine:!0,noRefund:!0,durabilityBaseMultiplier:.6}:{usedMachine:!1,noRefund:!1,durabilityBaseMultiplier:1}}function se(i){let c=te();return Object.assign(i,c),i}function ve(i){let c=i?.ref||i||{};if(i?.type==="eggFarm"||c.type==="eggFarm")return e.eggFarms.length<=1?"Cannot sell the last EGG FARM.":"";if((i?.type==="facility"||c.type==="facility")&&["mixer","trash","truck"].includes(c.baseId||c.id)){let h=c.baseId||c.id;if(Object.values(e.facilities).filter(T=>(T.baseId||T.id)===h).length<=1)return`Cannot sell the last ${c.shortName||c.name||c.id}.`}return""}function ce(i){t.buildStatus.textContent=i,N(e,n.width/2-e.view.x,86-e.view.y,i.toUpperCase(),f.danger)}function je(i){if(!i)return null;let c=I(i.col,i.row);return e.conveyorMeta.get(c)||null}function Q(i){return i?(Array.isArray(i.outDirs)||(i.outDirs=[]),i.outDirs=[...new Set(i.outDirs.filter(c=>["right","left","down","up"].includes(c)))],D.includes(i.branchMode)||(i.branchMode="random"),i.kind||(i.kind="conveyor"),i.speedMultiplier||(i.speedMultiplier=i.kind==="boostConveyor"?2:1),i.outDirs):[]}function xe(i){return i?pe.filter(c=>e.conveyorTiles.has(I(i.col+c.dc,i.row+c.dr))).map(c=>c.name):[]}function ut(i,c){let h=pe.find(ue=>ue.name===c);if(!i||!h)return!1;let m=I(i.col+h.dc,i.row+h.dr),T=e.conveyorMeta.get(m);if(!T)return!1;let A=h.opposite;return[...new Set([...Array.isArray(T.outDirs)?T.outDirs:[],T.dir].filter(Boolean))].includes(A)}function qe(i){if(!e.conveyorTiles.has(i))return[];let c=J(i),h=e.conveyorMeta.get(i),m=Q(h),T=xe(c),A=[...new Set([...m,h?.dir].filter(Boolean))].filter(ke=>T.includes(ke)),ne=T,ue=A.length?A:ne;return h&&(h.outDirs=m.filter(ke=>T.includes(ke)),h.dir&&!T.includes(h.dir)&&(h.dir=h.outDirs[0]||null),h.branchMode!=="random"&&h.branchMode!=="pass"&&!ue.includes(h.branchMode)&&(h.branchMode="random"),h.branchMode==="pass"&&ue.length<4&&(h.branchMode="random")),ue}function Fe(i,c){let h=yt(i,c);if(!h)return!1;let m=I(i.col,i.row),T=I(c.col,c.row),A=e.conveyorMeta.get(m),ne=e.conveyorMeta.get(T);if(!A||!ne)return!1;let ue=Q(A),ke=!ue.includes(h)||A.dir!==h||!ne.dir;return ue.includes(h)||ue.push(h),A.dir=h,ne.dir||(ne.dir=h),ke}function At(i){if(!i)return[];let c=xe(i);return c.length?c:pe.map(h=>h.name)}function cn(i){if(!i)return!1;let c=I(i.col,i.row),h=e.conveyorMeta.get(c);if(!h)return!1;let m=At(i);if(!m.length)return!1;let T=Q(h),A=h.dir&&m.includes(h.dir)?h.dir:T.find(ke=>m.includes(ke)),ne=m[(Math.max(-1,m.indexOf(A))+1)%m.length];if(!ne)return!1;Oe(e),h.dir=ne,h.outDirs=[ne],h.branchMode="random",e.selected={type:"conveyor",id:c};let ue=$(i.col,i.row);return N(e,ue.x,ue.y-22,pn(ne),f.ink),be(e),s(),!0}function un(){return q.facilities[e.buildTool==="boostConveyor"?"boostConveyor":"conveyor"]}function dt(i,c=null){if(!i)return{ok:!1,reason:re.fail.outOfGrid};let{col:h,row:m}=i,T=I(h,m);if(e.conveyorTiles.has(T))return{ok:!1,exists:!0};if(U(h,m))return{ok:!1,reason:"Cannot build on blocked ground."};if(kt(e,h,m)||Ct(e,h,m))return{ok:!1,reason:re.fail.cellOccupied};let A=un(),ne=A.id||"conveyor",ue=Be(ne,e);if(e.cash=Math.abs(m)?{col:i.col+Math.sign(h),row:i.row}:{col:i.col,row:i.row+Math.sign(m)}}function zn(i){d=!1,k=i?{col:i.col,row:i.row}:null,M=!1,l=null,zt(i)}function $t(i){if(!i)return;if(!l)return zt(i);let c=l,h=0;for(;(c.col!==i.col||c.row!==i.row)&&h<64;){let m=Yn(c,i);if(zt(m),c=l||m,h+=1,!l)break}}function dn(){!M&&k&&e.conveyorTiles.has(I(k.col,k.row))&&cn(k),k=null,M=!1,l=null,d=!1}function Vt(i){if(!i)return ce(re.fail.outOfGrid);let{col:c,row:h}=i;if(e.buildTool==="conveyor"||e.buildTool==="boostConveyor"){let m=dt(i,null);if(!m.ok)return ce(m.reason||re.fail.cellOccupied);be(e);return}if(U(c,h))return ce("Cannot build on blocked ground.");if(e.buildTool==="manualScanner"||e.buildTool==="autoScanner"?G(c,h):E(c,h))return ce(re.fail.cellOccupied);e.buildTool==="eggFarm"?Vn(c,h):e.buildTool==="manualScanner"?It(c,h,"manual"):e.buildTool==="autoScanner"&&It(c,h,"auto")}function Xt(i){let c=Ge(i.x,i.y);if(["conveyor","boostConveyor","eggFarm","manualScanner","autoScanner"].includes(e.buildTool))return Vt(c);if(Zt.includes(e.buildTool))return Xn(i,e.buildTool)}function Vn(i,c){let h=Be("eggFarm",e);if(e.cashA.kind==="manual").length>=8)return ce("Manual Scanner limit reached (8 max).");let m=h==="auto"?Be("autoScanner",e):Be("manualScanner",e);if(e.cashT.id!==c.ref.id),st(e,m.x,m.y),be(e)}if(c.type==="scanner"){let m=de(c.ref);u(c,m.x,m.y),e.scanners=e.scanners.filter(T=>T.id!==c.ref.id),st(e,m.x,m.y),be(e)}if(c.type==="facility"){let m=c.ref;u(c,m.x+m.w/2,m.y+m.h/2),delete e.facilities[m.id],st(e,m.x+m.w/2,m.y+m.h/2),be(e)}e.selected=null,e.multiSelected=[],s()}function b(i){let c=Object.values(e.facilities).find(ne=>{if(i.x>=ne.x&&i.x<=ne.x+ne.w&&i.y>=ne.y&&i.y<=ne.y+ne.h)return!0;if(!ne.entry)return!1;let ke=$(ne.entry.col,ne.entry.row);return Math.abs(i.x-ke.x)<=y.cell*.46&&Math.abs(i.y-ke.y)<=y.cell*.46});if(c)return{type:"facility",ref:c};let h=Ge(i.x,i.y);if(!h)return null;let m=kt(e,h.col,h.row);if(m)return{type:"eggFarm",ref:m};let T=Ct(e,h.col,h.row);if(T)return{type:"scanner",ref:T};let A=I(h.col,h.row);return e.conveyorTiles.has(A)?{type:"conveyor",oldKey:A,ref:{type:"conveyor",id:A}}:null}let x=rs({game:e,canvasPoint:o,updatePanels:s,equipmentAtPoint:b,fail:ce,pointInGrid:_,rectOfFacility:le,rectsOverlap:me});function v(i){return null}function P(){let i=w();i&&(i.type==="conveyor"?p($(...Object.values(J(i.id)))):i.type==="eggFarm"?p($(i.col,i.row)):i.type==="scanner"?p(de(i)):i.type==="facility"&&p({x:i.x+i.w/2,y:i.y+i.h/2}))}function R(i,c,h="primary-button"){let m=document.createElement("button");return m.type="button",m.className=h,m.textContent=i,m.addEventListener("click",c),m}function V(){e.cardTargetPick?.mode==="autoScannerMenu"&&(e.cardTargetPick=null),t.modal.classList.remove("visible","equipment-popover"),t.modal.style.removeProperty("--popover-x"),t.modal.style.removeProperty("--popover-y")}function Z(i){t.modalTitle.textContent="Auto Scanner",e.cardTargetPick={pending:!0,mode:"autoScannerMenu",cardId:null,targetKeys:[`scanner:${i.id}`],remaining:1},t.modalBody.innerHTML=` +var Z={version:"v27.8 repair routing and acquired-card UI",time:{daySeconds:60,farmShutdownGraceSeconds:10},cash:{starting:250},grid:{x:170,y:78,cols:20,rows:10,cell:46,expansion:{colsPerPurchase:10,rowsPerPurchase:10,costBase:1e3,costMultiplier:1.3333333333333333,truncateUnit:100}},map:{blockedRatio:.075},economy:{income:{mixer:5,truck:10},poopFine:10,zundaTax:{exemption:500,legalWorkExemptionBonus:250,maxRateCash:1e4,stepAmount:500,stepRate:.05,maxRate:.95},fairiesTribute:{perDay:10},incomeUpgradeRate:1.1,explosionDamageDivisor:30,maleTruckFinePerHalfDay:30,shredderBonus:{maxCards:30,probabilityPercentDivisor:2}},production:{poopRate:.08,autoScannerCooldown:3.5,autoScannerUpgradeRate:.95,autoScannerMinCooldown:.5,eggSpawnRanges:[[1.95,4.35],[1.55,3.45],[1.25,2.8],[1.05,2.35]]},conveyor:{baseSpeed:52,maxSpeed:300,bearingSpeedMultiplier:1.075},cards:{definitions:[{id:"upgradeEgg",title:"EGG Improvement",rarity:"common",type:"equipmentUpgrade",target:"eggFarm",description:"Choose one EGG FARM on the map and raise it by 1 level.",tags:["EGG","ACTIVE"]},{id:"upgradeAutoScanner",title:"AUTO SCANNER Improvement",rarity:"common",type:"equipmentUpgrade",target:"autoScanner",description:"Choose one AUTO SCANNER on the map and raise it by 1 level.",tags:["SCANNER","ACTIVE"]},{id:"upgradeMaintenanceRoom",title:"MAINTENANCE ROOM Improvement",rarity:"common",type:"equipmentUpgrade",target:"maintenanceRoom",description:"Choose one MAINTENANCE ROOM and add +1 repair worker. Each worker costs JPY 80 per day.",tags:["MAINTENANCE","ACTIVE"]},{id:"upgradeMixer",title:"MIXER Improvement",rarity:"common",type:"equipmentUpgrade",target:"mixer",description:"Choose MIXER on the map and raise it by 1 level.",tags:["ECONOMY","ACTIVE"]},{id:"upgradeTruck",title:"TRUCK Improvement",rarity:"common",type:"equipmentUpgrade",target:"truck",description:"Choose TRUCK on the map and raise it by 1 level.",tags:["ECONOMY","ACTIVE"]},{id:"upgradeTrash",title:"SHREDDER Improvement",rarity:"common",type:"equipmentUpgrade",target:"trash",description:"Shred poops for a little money. Max 30 upgrades.",tags:["POOP","ACTIVE"]},{id:"crowdedFarming",title:"Crowded Farming Contract",rarity:"common",type:"instant",description:"EGG production interval -5%. Equipment deterioration +4%. Max 10 cards.",tags:["EGG","RISK","PASSIVE"]},{id:"hatchingFeed",title:"Hatching Feed",rarity:"common",type:"instant",description:"EGG production interval -4%. Poop rate +3%. No card limit.",tags:["EGG","POOP","PASSIVE"]},{id:"safetyCover",title:"Safety Cover",rarity:"common",type:"instant",description:"Explosion damage -5%. No card limit.",tags:["RISK","PASSIVE"]},{id:"preventiveMaintenance",title:"Preventive Maintenance Manual",rarity:"common",type:"instant",description:"All equipment deterioration speed -5%. No card limit.",tags:["MAINTENANCE","PASSIVE"]},{id:"dudFilter",title:"DUD Filter",rarity:"common",type:"instant",description:"DUD chance -5 percentage points. Stacks until DUD chance reaches 0%.",tags:["CARD","PASSIVE"]},{id:"durabilityCoating",title:"Durability Coating",rarity:"common",type:"instant",description:"All equipment maximum durability +3%. No card limit.",tags:["MAINTENANCE","PASSIVE"]},{id:"bearing",title:"High-Quality Bearing",rarity:"common",type:"instant",description:"Conveyor speed +7.5%.",tags:["CONVEYOR","PASSIVE"]},{id:"sparePartsBin",title:"SPARE PARTS BIN",rarity:"common",type:"instant",description:"Restores 2% durability to every machine. A box of almost-compatible parts.",tags:["MAINTENANCE","ACTIVE"],weightBase:100,weightLossPerCopy:2},{id:"dudRefund",title:"DUD REFUND",rarity:"common",type:"instant",description:"DUDs now come with paperwork. Gain JPY 50 per copy when you pick a DUD.",tags:["CARD","ECONOMY","PASSIVE"]},{id:"freeReroll",title:"FREE REROLL",rarity:"common",type:"instant",description:"The first bad ideas are free.",tags:["CARD","PASSIVE"]},{id:"composter",title:"COMPOSTER",rarity:"common",type:"instant",description:"Turns shredder waste into suspicious fertilizer. Gain JPY 3 per copy when SHREDDER processes poop.",tags:["POOP","ECONOMY","PASSIVE"]},{id:"scannerQueueSpacing",title:"Scanner Queue Spacing",rarity:"common",type:"instant",description:"Manual and Auto Scanner queue spacing -15%. No card limit.",tags:["SCANNER","PASSIVE"]},{id:"extraEggOutlet",title:"Extra Egg Outlet",rarity:"rare",type:"equipmentUpgrade",target:"eggOutlet",description:"Choose one EGG FARM and add +1 output port to that EGG. Max +3 per EGG.",tags:["EGG","RARE","ACTIVE"]},{id:"recyclingSubsidy",title:"Recycling Subsidy",rarity:"rare",type:"instant",description:"SELL refund rate +10 points. Max 3 cards; normal SELL can reach 80%.",tags:["ECONOMY","RARE","PASSIVE"]},{id:"dynamite",title:"Dynamite",rarity:"rare",type:"cellAction",target:"blockedCell",description:"Remove any 3 unbuildable cells",tags:["RISK","RARE","ONE-SHOT"]},{id:"usedMachine",title:"Used Machines",rarity:"rare",type:"instant",description:"Future equipment costs half. New purchases have no refund and 60% durability. New Machines cancels it.",tags:["ECONOMY","RISK","RARE"]},{id:"newMachine",title:"New Machines",rarity:"common",type:"instant",description:"Cancel Used Machines and restore normal prices, refunds, and durability.",tags:["ECONOMY","ONE-SHOT"]},{id:"flattery",title:"Flattery",rarity:"rare",type:"instant",description:"Next Fairies tribute -50%.",tags:["ECONOMY","RARE","ONE-SHOT"]},{id:"loan",title:"Loan",rarity:"rare",type:"instant",description:"Gain JPY 300 per elapsed day now. Pay JPY 400 per day for that many days starting tomorrow.",tags:["ECONOMY","RISK","RARE"]},{id:"extraCards",title:"Extra Cards",rarity:"rare",type:"instant",description:"Draw 2 cards now and choose both.",tags:["RARE","ACTIVE"]},{id:"legalWork",title:"Legal Work",rarity:"rare",type:"instant",description:"ZUNDA TAX exemption +\uFFE5250. The 95% cap point also moves up by \uFFE5250.",tags:["ECONOMY","RARE","PASSIVE"]},{id:"chemicalWeaponSubsidy",title:"Chemical Weapons Subsidy",rarity:"ultraRare",type:"instant",description:"Each day, gain JPY 2000 per 100 poop shipped by truck. No card limit.",tags:["POOP","ULTRA RARE","PASSIVE"]},{id:"laborExploitation",title:"Motivational Exploitation",rarity:"ultraRare",type:"instant",description:"Repairman labor cost -30%. The repairman smiles with bloodshot eyes. One time only.",tags:["MAINTENANCE","ULTRA RARE","PASSIVE"]},{id:"rescueLoan",title:"Emergency Loan",rarity:"ultraRare",type:"instant",description:"If cash is below JPY 0 at day-end settlement, gain JPY 500. Consumed on use.",tags:["ECONOMY","ULTRA RARE","ONE-SHOT"]}],defaultEffects:{bearing:0,legalWork:0,fairiesFlatteryNext:0,crowdedFarming:0,extraEggOutlet:0,hatchingFeed:0,safetyCover:0,recyclingSubsidy:0,chemicalWeaponSubsidy:0,preventiveMaintenance:0,dudFilter:0,durabilityCoating:0,sparePartsBin:0,dudRefund:0,freeReroll:0,composter:0,scannerQueueSpacing:0,laborExploitation:0,usedMachineActive:!1,rescueLoanCharges:0,loans:[]},commonWeight:8,rareWeight:2,ultraRareWeight:.45,baseDraftSize:3,maxDudsPerDraft:2,dudChancePerCard:.3,dudChanceReductionPerFilter:.05,extraCardsAddedPicks:2,rerollCost:{dayDivisor:2,multiplier:100}},contracts:{eventChance:.7,firstTurn:8},maintenance:{fullPerformanceUntilWear:.5,minimumPerformance:.5,durability:{conveyor:1300,eggFarm:475,autoScanner:850,mixer:775,trash:950},processingDelayMaxSeconds:{mixer:1.2,trash:1},repairman:{dailyCost:80,secondsPerOnePercent:.5,walkSpeed:90}},facilities:{conveyor:{id:"conveyor",type:"conveyor",name:"Conveyor",shortName:"Belt",price:30,buildable:!0,upgradeable:!1},boostConveyor:{id:"boostConveyor",type:"conveyor",name:"Boost Conveyor",shortName:"BOOST BELT",price:75,buildable:!0,upgradeable:!1,speedMultiplier:2},eggFarm:{id:"eggFarm",type:"eggFarm",name:"Egg Farm",shortName:"EGG",price:250,buildable:!0,upgradeable:!0,maxLevel:4,upgradeCosts:[null,300,720,1600]},manualScanner:{id:"manualScanner",type:"scanner",kind:"manual",name:"Manual Scanner",shortName:"Manual Scanner",price:260,buildable:!0,upgradeable:!1},autoScanner:{id:"autoScanner",type:"scanner",kind:"auto",name:"Auto Scanner",shortName:"Auto Scanner",price:360,buildable:!0,upgradeable:!1},maintenanceRoom:{id:"maintenanceRoom",type:"maintenanceRoom",name:"Maintenance Room",shortName:"MAINT ROOM",price:400,buildable:!0,upgradeable:!0,footprint:{cols:2,rows:2}},mixer:{id:"mixer",type:"facility",name:"Mixer",shortName:"MIXER",price:450,buildable:!0,upgradeable:!0,incomeKey:"mixer",body:{w:164,h:126}},trash:{id:"trash",type:"facility",name:"Waste Shredder",shortName:"SHREDDER",price:240,buildable:!0,upgradeable:!0,body:{w:180,h:100}},truck:{id:"truck",type:"facility",name:"Truck",shortName:"TRUCK",price:450,buildable:!0,upgradeable:!0,incomeKey:"truck",body:{w:176,h:138}}}};var Tn=Z.version,Kt=Z.time.daySeconds,ln=Z.cash.starting,Se=Z.economy,vr=Z.time.farmShutdownGraceSeconds,gn=Z.production.autoScannerCooldown,kr=Z.production.autoScannerUpgradeRate,ao=Z.production.autoScannerMinCooldown,Cr=Z.contracts.eventChance,Tt=Z.contracts.firstTurn,Sr=Z.production.poopRate,En=Z.conveyor.baseSpeed,_t=Z.conveyor.maxSpeed,Rn=Z.conveyor.bearingSpeedMultiplier,$e=Z.cards,Mr=Z.cards.definitions,co=Z.cards.defaultEffects,at=Z.production.eggSpawnRanges,y=Z.grid,ve=Z.facilities,uo=["conveyor","boostConveyor","eggFarm","autoScanner","manualScanner","maintenanceRoom","mixer","trash","truck"],an=["mixer","trash","truck"],Tr=["mixer","truck"],gt=Object.fromEntries(Object.entries(ve).map(([e,t])=>[e,t.price])),ue=[{name:"right",dc:1,dr:0,angle:0,opposite:"left"},{name:"left",dc:-1,dr:0,angle:Math.PI,opposite:"right"},{name:"down",dc:0,dr:1,angle:Math.PI/2,opposite:"up"},{name:"up",dc:0,dr:-1,angle:-Math.PI/2,opposite:"down"}],f={bg:"#eefbea",ink:"#102015",muted:"#526456",green:"#22b94f",greenSoft:"#b7f5c6",mixerBlue:"#2477ff",truckPink:"#ff6aa8",wasteGreen:"#22b94f",danger:"#c42424",warn:"#f0a020",white:"#ffffff",poop:"#7b4a23",male:"#62a9ff",female:"#ff8bc6"},Me={ambient:0,important:1,critical:2};function T(e){let t=Number(e)||0;return`${t<0?"-":""}\uFFE5${Math.ceil(Math.abs(t)).toLocaleString("ja-JP")}`}function P(e,t){return`${e},${t}`}function j(e){let[t,n]=String(e).split(",").map(Number);return{col:t,row:n}}function cn(e,t,n){return Math.max(t,Math.min(n,e))}function H(e,t){return e+Math.random()*(t-e)}function Te(e,t){return e>=0&&e=0&&tMath.round(cn(r,0,255)).toString(16).padStart(2,"0");return`#${o(e)}${o(t)}${o(n)}`}function po(e,t,n){let o=gr(e),r=gr(t);return cl(fo(o.r,r.r,n),fo(o.g,r.g,n),fo(o.b,r.b,n))}var re={appTitle:"Chick Sorter",versionTitle:`Chick Sorter ${Tn}`,currency:"\uFFE5",phases:{title:"Title",build:"Build",running:"Sorting",clearing:"Clearing",loanDecision:"Emergency Loan",gameover:"Game Over"},actions:{startGame:"Start Game",nextDay:"Next Day",buildPhase:"Build Phase",restart:"Restart",close:"Close",remove:"Remove",refund:"Refund",autoMenu:"Auto Menu"},status:{build:e=>`BUILD: ${(e==="erase"?"SELL":e||"SELECT").toUpperCase()} | Right/middle-drag pan | Hover equipment for info`,sorting:"Sorting: use scanner keys. Right/middle-drag to pan. Build after clearing the day.",allPortsConnected:"Start condition met: one EGG route reaches an exit.",equipmentPanelEmpty:"Hover equipment in Build phase.",eventPanelRunning:"Forced events appear in Build phase."},routeLabels:{mixer:"MIXER",truck:"TRUCK",trash:"WASTE",scanner:"NEXT","scanner-role-1":"NEXT",input:"IN"},fail:{outOfGrid:"Out of grid",cellOccupied:"Cell occupied",notEnoughCash:"Not enough cash",facilityExists:"Facility already exists",facilityOverlap:"Facility overlap",nothingToErase:"Nothing to sell",noUpgrade:"No upgrade available"}};function Et(e){return ve[e]?.name||String(e||"").replace(/^[a-z]/,t=>t.toUpperCase())}function ul(e){let t=ve[e];return t?e==="conveyor"||e==="boostConveyor"?`${T(t.price)} / tile`:e==="autoScanner"?`${T(t.price)} / ${gn.toFixed(1)}s cooldown`:e==="trash"?T(t.price):T(t.price):""}function dl(e){return{conveyor:"A narrow green belt. Routes decide whether chicks live, ship, or become invoices.",boostConveyor:"Moves chicks twice as fast. Wears down like a normal belt.",eggFarm:"A tiny gatehouse producing questionable eggs on schedule.",autoScanner:"Slower than hands without upgrades.",manualScanner:"A manual checkpoint. The operator is the algorithm.",maintenanceRoom:"2x2 repair base. Spawns repair workers during sorting.",mixer:"Male chicks become revenue here. Do not feed it poop.",trash:"A polite shredder for poop and other regrets.",truck:"Ships the target cargo. Wrong cargo still leaves a paper trail."}[e]||""}function Rr(e){let t=dl(e);return`${Et(e)}${ul(e)}${t?`${t}`:""}`}function Pr(e){return re.routeLabels[e]||String(e).toUpperCase()}function Ar(e){let t=2166136261;for(let n=0;n>>0}function Dr(e){let t=e>>>0;return()=>{t+=1831565813;let n=Math.imul(t^t>>>15,1|t);return n^=n+Math.imul(n^n>>>7,61|n),((n^n>>>14)>>>0)/4294967296}}function An(){return`RUN-${Date.now().toString(36)}-${Math.floor(Math.random()*1e6).toString(36)}`.toUpperCase()}function Ir({seed:e,targetRatio:t=.2,reserved:n=[]}={}){let o=Dr(Ar(e||An())),r=new Set(n.map(d=>typeof d=="string"?d:P(d.col,d.row))),s=[];for(let d=0;d0;d-=1){let v=Math.floor(o()*(d+1));[s[d],s[v]]=[s[v],s[d]]}let l=Math.round(y.cols*y.rows*t);return new Set(s.slice(0,Math.min(l,s.length)).map(d=>d.k))}function $r({seed:e,colStart:t=0,colEnd:n=y.cols,rowStart:o=0,rowEnd:r=y.rows,targetRatio:s=.2,reserved:l=[]}={}){let d=Dr(Ar(e||An())),v=new Set(l.map(q=>typeof q=="string"?q:P(q.col,q.row))),M=[],g=Math.max(0,Math.floor(t)),A=Math.max(g,Math.floor(n)),L=Math.max(0,Math.floor(o)),V=Math.max(L,Math.floor(r));for(let q=L;q0;q-=1){let le=Math.floor(d()*(q+1));[M[q],M[le]]=[M[le],M[q]]}let ee=Math.round((A-g)*(V-L)*s);return new Set(M.slice(0,Math.min(ee,M.length)).map(q=>q.k))}var Ht=10,Fr=2,Br=1;function qt(e,t){return`${e},${t}`}function fl(e){let[t,n]=String(e).split(",").map(Number);return{chunkX:t,chunkY:n}}function Dn(e){let t=Z.grid.expansion||{},n=Math.max(1,(e.gridExpansionPurchases||0)+1),o=(t.costBase||1e3)*n*(t.costMultiplier||4/3),r=Math.max(1,t.truncateUnit||100);return Math.floor(o/r)*r}function Lr(e){e.ownedCells=new Set,e.ownedChunks=new Set,e.gridChunkOriginY=0,e.gridExpansionPurchases=0,e.gridExpansionPurchasesByDirection={right:0,up:0};for(let t=0;t=0&&t=0&&n0)continue;let M=qt(v.chunkX,v.chunkY);t.has(M)||n.has(M)||n.set(M,yo(e,v.chunkX,v.chunkY,v.direction))}}return[...n.values()].sort((o,r)=>{let s=o.direction===r.direction?0:o.direction==="right"?-1:1;return s||(o.chunkY!==r.chunkY?o.chunkY-r.chunkY:o.chunkX-r.chunkX)})}function Wr(e,t="right"){let n=Rt(e);return n.find(o=>o.direction===t)||n[0]||null}function Pt(e){return{x:y.x+e.colStart*y.cell,y:y.y+e.rowStart*y.cell,w:e.cols*y.cell,h:e.rows*y.cell}}function ho(e){let t=Pt(e);return{x:t.x+t.w/2-116,y:t.y+t.h/2-36,w:232,h:72,cx:t.x+t.w/2,cy:t.y+t.h/2}}function Gr(e,t){let n=ho(e);return t.x>=n.x&&t.x<=n.x+n.w&&t.y>=n.y&&t.y<=n.y+n.h}function Ur(e){let t=[];for(let n=e.rowStart;nTe(t.col,t.row))}function vl(e){return[{col:e.col,row:e.row-1},{col:e.col+1,row:e.row-1},{col:e.col,row:e.row},{col:e.col+1,row:e.row},{col:e.col,row:e.row+1},{col:e.col+1,row:e.row+1}].filter(t=>Te(t.col,t.row))}function kl(e){return[{col:e.col,row:e.row+1},{col:e.col-1,row:e.row},{col:e.col+1,row:e.row},{col:e.col,row:e.row-1}].filter(t=>Te(t.col,t.row))}function Vr(e){let t=new Set([...e.conveyorTiles]),n=o=>{o&&Te(o.col,o.row)&&t.add(P(o.col,o.row))};for(let o of e.scanners){for(let r of vl(o))n(r);for(let r of xl(o))n(r)}for(let o of e.eggFarms){n(o);for(let r of kl(o))n(r)}for(let o of Object.values(e.facilities))n(o.entry);return t}function qr(e){let t=hl.map(([n,o])=>({col:n,row:o}));for(let{col:n,row:o}of t)Yr(e,n,o);for(let n=0;n({...n,keys:n.keys?{left:{...n.keys.left},right:{...n.keys.right}}:null,queue:[]})),e.eggFarms=wl.map(n=>{let o=At(n);return{...n,nextSpawn:o,lastInterval:o}});let t=Vr(e);e.blockedCells=Ir({seed:e.runSeed,targetRatio:Z.map.blockedRatio,reserved:[...t]}),Cl(e),qr(e),e.routingVersion=(e.routingVersion||0)+1,e.factoryGraph=null,e.factoryGraphCommittedVersion=-1}function Dt(e){return e?[{col:e.col,row:e.row-1},{col:e.col+1,row:e.row-1},{col:e.col,row:e.row},{col:e.col+1,row:e.row},{col:e.col,row:e.row+1},{col:e.col+1,row:e.row+1}]:[]}function pe(e){let t=$(e.col,e.row);return{x:t.x+y.cell/2,y:t.y}}function ut(e,t,n){return e.eggFarms.find(o=>o.col===t&&o.row===n)||null}function dt(e,t,n){return e.scanners.find(o=>Dt(o).some(r=>r.col===t&&r.row===n))||null}function It(e,t){return e.scanners.find(n=>n.id===t)||null}function vo(e,t){return e.scanners.find(n=>n.kind==="manual"&&n.slot===t)||null}function Bn(e){return e?.baseId||e?.id||""}function ko(e,t){return Object.entries(e.facilities||{}).filter(([,n])=>Bn(n)===t)}function Pe(e,t){return{inputA:{col:e.col,row:e.row-2},inputB:null,left:{col:e.col-1,row:e.row},right:{col:e.col+2,row:e.row}}[t]}function Qr(e){return e?ue.map(t=>({col:e.col+t.dc,row:e.row+t.dr})).filter(t=>Te(t.col,t.row)):[]}function Jr(e,t,n=[],o=[]){if(!t||!Te(t.col,t.row))return[];let r=[],s=l=>{!l||!Te(l.col,l.row)||!e.conveyorTiles.has(P(l.col,l.row))||n.some(d=>d&&Pn(l,d))||r.some(d=>Pn(d,l))||r.push({...l,viaTolerance:!Pn(l,t)})};s(t);for(let l of o)s(l);for(let l of Qr(t))s(l);return r}function zr(e,t,n=[],o=[]){let r=Jr(e,t,n,o),s=r.filter(l=>!l.viaTolerance);return s.length?s:r}function Sl(e,t){let n=Pe(t,"inputA");return!n||!Te(n.col,n.row)?[]:e.conveyorTiles.has(P(n.col,n.row))?[{...n,viaTolerance:!1}]:[]}function Ml(e,t,n){let o=Pe(t,n);return Jr(e,o,Dt(t))}function Tl(e,t){let n=e.facilities[t]||ko(e,t)[0]?.[1];if(!n)return null;if(n.entry)return $(n.entry.col,n.entry.row);let o=Bn(n)||t;return o==="mixer"?{x:n.x+n.w,y:n.y+n.h*.52}:o==="truck"?{x:n.x,y:n.y+n.h*.52}:o==="trash"?{x:n.x+n.w/2,y:n.y+8}:{x:n.x+n.w/2,y:n.y+n.h/2}}function Zr(e,t,n){return n==="inputA"?Sl(e,t).length>0:Ml(e,t,n).length>0}function be(e){e.branchCounters?.clear?.(),e.routeCache=null,e.routingVersion=(e.routingVersion||0)+1,e.factoryGraph=null,e.factoryGraphCommittedVersion=-1}function Fe(e,t){return P(e,t)}function Yt(e){return j(e)}function gl(e,t){let n=e.conveyorMeta?.get?.(t)||{},o=[];return Array.isArray(n.outDirs)&&o.push(...n.outDirs),n.dir&&o.push(n.dir),[...new Set(o)].filter(r=>ue.some(s=>s.name===r))}function El(e,t,n){let o=ue.find(l=>l.name===n);if(!o)return!1;let r=e.conveyorMeta?.get?.(Fe(t.col+o.dc,t.row+o.dr));return r?[...new Set([...Array.isArray(r.outDirs)?r.outDirs:[],r.dir].filter(Boolean))].includes(o.opposite):!1}function Rl(e,t){let n=Yt(t),o=e.conveyorMeta?.get?.(t)||{},r=ue.filter(A=>e.conveyorTiles.has(Fe(n.col+A.dc,n.row+A.dr))).map(A=>A.name),s=gl(e,t).filter(A=>r.includes(A)),l=r,d=s.length?s:l;if(r.length<3)return d;if(o.branchMode&&o.branchMode!=="random"&&r.includes(o.branchMode))return[o.branchMode];let v=new Set(r.filter(A=>El(e,n,A))),M=r.filter(A=>!v.has(A));if(M.length)return M;let g=d.filter(A=>!v.has(A));return g.length?g:d}function Pl(e){return{version:e.routingVersion||0,cells:new Set,adjacency:new Map,components:new Map,cellToComponent:new Map,scannerInputs:new Map,scannerPorts:new Map,facilityPorts:new Map,farmOutputs:new Map,pathCache:new Map,metrics:{conveyorCells:0,edges:0,components:0,deadEnds:0,crosses:0,scannerPorts:0,connectedScannerPorts:0,facilityPorts:0,connectedFacilityPorts:0,farmOutputs:0},issues:[]}}function Al(e){let t=Pl(e);for(let o of e.conveyorTiles)t.cells.add(o),t.adjacency.set(o,[]);for(let o of t.cells){let r=Yt(o),s=Rl(e,o),l=ue.filter(d=>s.includes(d.name));for(let d of l){let v=Fe(r.col+d.dc,r.row+d.dr);t.cells.has(v)&&t.adjacency.get(o).push({key:v,dir:d.name})}}let n=0;for(let[o,r]of t.adjacency.entries()){n+=r.length;let s=new Set(r.map(l=>l.dir));r.length===1&&(t.metrics.deadEnds+=1),r.length===4&&s.has("left")&&s.has("right")&&s.has("up")&&s.has("down")&&(t.metrics.crosses+=1)}return t.metrics.conveyorCells=t.cells.size,t.metrics.edges=Math.floor(n/2),Dl(t),Il(e,t),t.issues=_l(e,t,{includeRoutes:!1}),t}function Dl(e){let t=1;for(let n of e.cells){if(e.cellToComponent.has(n))continue;let o=t++,r=[n],s=[];for(e.cellToComponent.set(n,o);r.length;){let l=r.shift();s.push(l);for(let d of e.adjacency.get(l)||[])e.cellToComponent.has(d.key)||(e.cellToComponent.set(d.key,o),r.push(d.key))}e.components.set(o,{id:o,cells:s,capacity:Math.max(1,s.length),count:0,ratio:0})}e.metrics.components=e.components.size}function Il(e,t){for(let n of e.scanners){let o={};for(let r of["inputA","left","right"]){let s=Pe(n,r),l=r==="inputA"?s&&t.cells.has(Fe(s.col,s.row))?[{...s}]:[]:zr(e,s,Dt(n)).filter(M=>t.cells.has(Fe(M.col,M.row))),d=l.map(M=>Fe(M.col,M.row)),v=d.length>0;if(o[r]={type:r,cell:s,cells:l,keys:d,key:d[0]||null,connected:v},t.metrics.scannerPorts+=1,v&&(t.metrics.connectedScannerPorts+=1),r==="inputA")for(let M of d)t.scannerInputs.set(M,n.id)}t.scannerPorts.set(n.id,o)}for(let[n,o]of Object.entries(e.facilities)){if(!o.entry)continue;let r=zr(e,o.entry).filter(d=>t.cells.has(Fe(d.col,d.row))),s=r.map(d=>Fe(d.col,d.row)),l=s.length>0;t.facilityPorts.set(n,{id:n,cell:{...o.entry},cells:r,keys:s,key:s[0]||null,connected:l}),t.metrics.facilityPorts+=1,l&&(t.metrics.connectedFacilityPorts+=1)}for(let n of e.eggFarms){let o=Qr({col:n.col,row:n.row}).map(r=>Fe(r.col,r.row)).filter(r=>t.cells.has(r));t.farmOutputs.set(n.id,o),t.metrics.farmOutputs+=o.length}}function Ue(e){let t=e.routingVersion||0;return(!e.factoryGraph||e.factoryGraph.version!==t)&&(e.factoryGraph=Al(e)),e.factoryGraph}function jr(e){let t=Ue(e);return e.factoryGraphCommittedVersion=t.version,e.factoryGraphSnapshot=$l(t),t}function $l(e){return e?{version:e.version,conveyorCells:e.metrics.conveyorCells,edges:e.metrics.edges,components:e.metrics.components,deadEnds:e.metrics.deadEnds,crosses:e.metrics.crosses,scannerPorts:`${e.metrics.connectedScannerPorts}/${e.metrics.scannerPorts}`,facilityPorts:`${e.metrics.connectedFacilityPorts}/${e.metrics.facilityPorts}`,farmOutputs:e.metrics.farmOutputs,issues:[...e.issues||[]]}:null}function Fl(e,t){let n=t.col-e.col,o=t.row-e.row;return ue.find(r=>r.dc===n&&r.dr===o)?.name||null}function Co(e,t,n){let o=Ue(e),r=Fe(t.col,t.row);if(!o.cells.has(r))return[];let s=`${r}|${n.cacheKey||"dynamic"}`;if(n.cacheKey&&o.pathCache.has(s))return o.pathCache.get(s).map(I=>I.map(_=>({..._})));let l=`${r}|none`,d=[{key:r,col:t.col,row:t.row,incoming:"none",depth:0}],v=new Map([[l,0]]),M=new Map,g=[],A=1/0,L=8,V=24,ee=80;for(;d.length;){let I=d.shift();if(I.depth>A)break;let _=`${I.key}|${I.incoming}`;if(n(I)){if(g.push(_),A=I.depth,g.length>=V)break;continue}for(let W of o.adjacency.get(I.key)||[]){let fe=Yt(W.key),Ce=Fl(I,fe),ye=I.depth+1;if(ye>A)continue;let he=`${W.key}|${Ce}`,ge=v.get(he);if(ge==null)v.set(he,ye),M.set(he,[_]),d.push({key:W.key,col:fe.col,row:fe.row,incoming:Ce,depth:ye});else if(ge===ye){let De=M.get(he)||[];De.length{if(q.length>=ee)return;let[W]=I.split("|"),fe=[Yt(W),..._];if(I===l||W===r){q.push(fe);return}for(let Ce of M.get(I)||[])le(Ce,fe)};for(let I of g)le(I,[]);let me=[],ne=new Set;for(let I of q){let _=I.map(W=>Fe(W.col,W.row)).join(">");ne.has(_)||(ne.add(_),me.push(I))}return n.cacheKey&&o.pathCache.set(s,me.map(I=>I.map(_=>({..._})))),me}function ei(e){let t=Ue(e),n=new Map;for(let[o,r]of t.components.entries())n.set(o,{id:o,cells:[...r.cells],capacity:r.capacity,count:0,ratio:0});return{components:n,cellToComponent:new Map(t.cellToComponent)}}function ti(e,t,n){let o=Ue(e),r=null,s=1/0;for(let l of o.cells){let d=j(l),v=$(d.col,d.row),M=Math.hypot(v.x-t,v.y-n);M$(o.col,o.row));return t&&n.push(t),n}function ct(e,t){t&&(!e.length||Er(e[e.length-1],t)>1)&&e.push(t)}function Bl(e,t,n,o=null){let r=[{x:e.x,y:e.y}];ct(r,$(t.col,t.row));for(let s of Ln(n,o))ct(r,s);return r}function Ll(e,t){let n=Ln(e),o=Pe(t,"inputA");return o&&ct(n,$(o.col,o.row)),ct(n,pe(t)),n}function Ol(e,t,n,o){let r=[{x:e.x,y:e.y}];ct(r,$(t.col,t.row));for(let l of Ln(n))ct(r,l);let s=Pe(o,"inputA");return s&&ct(r,$(s.col,s.row)),ct(r,pe(o)),r}function Nl(e,t,n=0){let o=$(t.col,t.row),r=0;for(let s of e.chicks||[]){if(s.stage==="flying"||s.stage==="queued")continue;Math.hypot(s.x-o.x,s.y-o.y){let A=M.roleBias??0,L=g.roleBias??0;if(A!==L)return A-L;let V=M.cells?.length??M.route?.length??Number.POSITIVE_INFINITY,ee=g.cells?.length??g.route?.length??Number.POSITIVE_INFINITY;if(V!==ee)return V-ee;let q=M.key||JSON.stringify(M.route||M.cells||M),le=g.key||JSON.stringify(g.route||g.cells||g);return q.localeCompare(le)});if(!o||r.length===1)return r[0];let s=r[0].roleBias??0,l=r.filter(M=>(M.roleBias??0)===s),d=Math.min(...l.map(M=>Xr(e,M)));l=l.filter(M=>Xr(e,M)===d);let v=e.branchCounters.get(t)||0;return e.branchCounters.set(t,v+1),l[Math.floor(Math.random()*l.length)]}function ni(e){return e.role===0?{left:{match:"male",destination:"mixer"},right:{match:"default",destination:"scanner-role-1"}}:{left:{match:"poop",destination:"trash"},right:{match:"default",destination:"truck"}}}function So(e){let t=ni(e);return{left:t.left.destination,right:t.right.destination}}function Mo(e,t){return ni(e).left.match===t.sex?"left":"right"}function To(e){return Pr(e)}function go(e){return{mixer:"#2477ff",truck:"#ff6aa8",trash:"#22b94f","scanner-role-1":"#526456",scanner:"#526456",input:"#526456"}[e]||"#102015"}function $t(e,t,n=!1){let o=Ue(e),r=(o.farmOutputs.get(t.id)||[]).map(Yt),s=[];for(let v of r){let M=A=>o.scannerInputs.has(Fe(A.col,A.row));M.cacheKey=`farmInput:${v.col},${v.row}:v${o.version}`;let g=Co(e,v,M);for(let A of g){let L=A[A.length-1],V=o.scannerInputs.get(Fe(L.col,L.row)),ee=It(e,V);ee&&s.push({key:`${ee.id}:${L.col},${L.row}:${A.length}`,scanner:ee,cells:A})}}let l=Fn(e,`farm:${t.id}`,s,n);if(l)return{scannerId:l.scanner.id,route:Ll(l.cells,l.scanner)};let d=Fn(e,`farm:${t.id}:output`,r.map(v=>({key:P(v.col,v.row),cells:[v]})),n);return d?{scannerId:null,route:Ln(d.cells)}:null}function oi(e,t,n,o,r=!1){let s=It(e,o);if(!s)return null;let d=Ue(e).scannerPorts.get(s.id)?.[t],v=Pe(s,t),M=d?.connected?(d.keys||[d.key]).filter(Boolean).map(Yt):[];if(!v||!M.length)return null;let g=So(s)[t];return g==="scanner-role-1"?Wl(e,s,t,n,v,M,r):Gl(e,s,t,n,v,M,g,r)}function Wl(e,t,n,o,r,s,l){let d=Ue(e),v=e.scanners.filter(A=>A.id!==t.id&&d.scannerPorts.get(A.id)?.inputA?.connected),M=[];for(let A of s)for(let L of v){let V=d.scannerPorts.get(L.id)?.inputA?.key;if(!V)continue;let ee=(d.scannerPorts.get(L.id)?.inputA?.keys||[V]).filter(Boolean),q=ne=>ee.includes(Fe(ne.col,ne.row));q.cacheKey=`toScanner:${A.col},${A.row}:${L.id}:v${d.version}`;let le=Co(e,A,q),me=L.role===1?0:1;for(let ne of le)M.push({key:`${me}:start${P(A.col,A.row)}:s${L.id}:${ne.length}:${P(ne[ne.length-1].col,ne[ne.length-1].row)}`,target:L,cells:ne,roleBias:me})}let g=Fn(e,`scanner:${t.id}:${n}:toRole1`,M,l);return g?{destination:"scanner",nextScannerId:g.target.id,route:Ol(o,r,g.cells,g.target)}:null}function Gl(e,t,n,o,r,s,l,d){let v=Ue(e),M=[...v.facilityPorts.entries()].filter(([L,V])=>V?.connected&&Bn(e.facilities?.[L])===l);if(!M.length)return null;let g=[];for(let L of s)for(let[V,ee]of M){let q=(ee.keys||[ee.key]).filter(Boolean);if(!q.length)continue;let le=ne=>q.includes(Fe(ne.col,ne.row));le.cacheKey=`toFacility:${L.col},${L.row}:${V}:v${v.version}`;let me=Co(e,L,le);for(let ne of me)g.push({key:`start${P(L.col,L.row)}:${V}:${ne.length}:${P(ne[ne.length-1].col,ne[ne.length-1].row)}`,cells:ne,facilityId:V})}let A=Fn(e,`scanner:${t.id}:${n}:${l}`,g,d);return A?{destination:l,facilityId:A.facilityId,route:Bl(o,r,A.cells,Tl(e,A.facilityId))}:null}function ri(e,t,n=new Set){if(!t||n.has(t.id))return!1;n.add(t.id);for(let o of["left","right"]){let r=oi(e,o,pe(t),t.id,!1);if(r){if(["mixer","trash","truck"].includes(r.destination))return!0;if(r.destination==="scanner"){let s=It(e,r.nextScannerId);if(ri(e,s,n))return!0}}}return!1}function Ul(e,t){return!!$t(e,t,!1)?.scannerId}function On(e){if(e.phase!=="build")return[];let t=[];for(let n of e.eggFarms||[])Ul(e,n)||t.push({type:"eggFarm",id:n.id,ref:n,message:"This Egg will not produce chicks"});for(let n of an)for(let[o,r]of ko(e,n))Ue(e).facilityPorts.get(o)?.connected||t.push({type:"facility",id:o,ref:r,message:"This facility cannot receive items"});return t}function Kl(e){let t=Ue(e);for(let r of e.eggFarms||[]){let s=$t(e,r,!1);if(!s?.scannerId)continue;let l=It(e,s.scannerId);if(ri(e,l))return[]}let n=e.eggFarms?.length||0,o=[...t.facilityPorts.entries()].filter(([r,s])=>s?.connected&&["mixer","trash","truck"].includes(Bn(e.facilities?.[r]))).length;return n?t.metrics.farmOutputs?e.scanners?.length?o?["Connect at least one EGG through a scanner to any valid exit."]:["Connect at least one receiver exit: Mixer, Shredder, or Truck."]:["Build at least one scanner and connect it to an EGG route."]:["Connect at least one EGG to a conveyor."]:["Build at least one EGG before starting the next day."]}function $n(e){return e.kind==="manual"?`S${(e.slot??0)+1}`:`Auto Scanner #${e.id}`}function _l(e,t=Ue(e),n={}){let o=n.includeRoutes!==!1,r=[],s={mixer:"Mixer",trash:"Shredder",truck:"Truck"};for(let l of an){let d=ko(e,l);if(!d.length){r.push(`${s[l]||l} is missing`);continue}for(let[v]of d)t.facilityPorts.get(v)?.connected||r.push(`${s[l]||l} receiver has no conveyor`)}for(let l of e.scanners){let d=t.scannerPorts.get(l.id)||{};if(d.inputA?.connected||r.push(`${$n(l)} input has no conveyor`),d.left?.connected||r.push(`${$n(l)} left output has no conveyor`),d.right?.connected||r.push(`${$n(l)} right output has no conveyor`),!!o)for(let v of["left","right"])d[v]?.connected&&!oi(e,v,pe(l),l.id)&&r.push(`${$n(l)} ${v} route is incomplete`)}if(o)for(let l of e.eggFarms)$t(e,l)?.scannerId||r.push(`Egg Farm #${l.id} has no scanner route`);else for(let l of e.eggFarms)(t.farmOutputs.get(l.id)||[]).length||r.push(`Egg Farm #${l.id} has no output conveyor`);return[...new Set(r)]}function dn(e){return Kl(e)}function ii(e){return dn(e).length===0}var si=[{target:"female",title:"Premium Female Shipment",route:"Female -> Truck",note:"Only female chicks are valid truck cargo this turn."},{target:"male",title:"Male Export Trial",route:"Male -> Truck",note:"Male chicks are temporarily valid truck cargo. Male truck fines are suspended for valid male cargo."},{target:"poop",title:"Contaminant Sample Export",route:"Poop -> Truck",note:"Poop is temporarily valid truck cargo. Shipped poop does not soil this contract shipment."}];function Eo(e){return[...e.eggFarms.map(t=>t.price||0),...e.scanners.map(t=>t.price||0),...Object.values(e.facilities).map(t=>t.price||0),...[...e.conveyorMeta.values()].map(t=>t.price||0)].reduce((t,n)=>t+n,0)}function Hl(e,t=1){return e.eggFarms.reduce((n,o)=>{let[r,s]=un(o),l=Math.max(.1,(r+s)/2);return n+Kt/l*t},0)}function li(e){if(e.turn+1Cr)return null;let n=si[Math.floor(Math.random()*si.length)],o=2,r=3,s=Hl(e,o),l=Math.max(1,Math.ceil(s*.9)),d=Math.round((.08+Math.random()*.08)*100)/100,v=Math.floor(Eo(e)*d),M=Math.floor(Math.max(0,e.cash)*.25),g=Math.max(v,M);return{id:`contract-${e.turn}-${Date.now()}-${Math.floor(Math.random()*9999)}`,...n,forced:!0,productionMultiplier:o,poopMultiplier:r,targetAmount:l,estimatedOutput:Math.round(s),rewardRate:d,reward:g,rewardByFactory:v,rewardByCash:M,createdTurn:e.turn,oneTurnOnly:!0}}function ai(e){e.contractActive=e.contractOffer?{...e.contractOffer}:null,e.contractOffer=null}function ci(e){e.contractActive=null}function ui(e){return e.contractActive?.productionMultiplier||1}function di(e){let t=Math.max(0,Number(e?.cardEffects?.hatchingFeed)||0),n=Math.pow(1.03,t);return Math.min(.95,Sr*(e.contractActive?.poopMultiplier||1)*n)}function nt(e){return e.contractActive?.target||"female"}function Ro(e,t){return t==="male"?e.truckMale||0:t==="poop"?e.poopTruck||0:e.truckFemale||0}function fi(e,t){return nt(e)===t}function pi(e){return nt(e)!=="male"}function yi(e){let t=e.contractActive;if(!t)return null;let n=Ro(e.stats,t.target),o=n>=t.targetAmount,r=o?t.reward:0;r>0?(Le(e,r),e.stats.contractBonus=r,e.totals.contractBonus+=r,e.totals.contractSuccess+=1):(e.stats.contractBonus=0,e.totals.contractFailed+=1);let s={title:t.title,target:t.target,route:t.route,targetAmount:t.targetAmount,count:n,success:o,bonus:r,reward:t.reward,productionMultiplier:t.productionMultiplier,poopMultiplier:t.poopMultiplier};return e.stats.contractResult=s,s}function Po(e,t){return Math.max(0,Number(e?.cardEffects?.[t])||0)}function ql(e){return!!e?.cardEffects?.usedMachineActive}function Yl(e,t){return t?1:Math.min(.8,.5+.1*Math.min(3,Po(e,"recyclingSubsidy")))}function Vl(e){return Math.pow(.95,Po(e,"safetyCover"))}function Nn(e){let t=e?.ref||e||{},n=e?.type||t.type;return n==="conveyor"?t.price||ve[t.kind]?.price||ve.conveyor.price:n==="eggFarm"?t.price||ve.eggFarm.price:n==="scanner"?t.price||(t.kind==="auto"?ve.autoScanner.price:ve.manualScanner.price):n==="facility"?t.price||ve[t.baseId||t.id]?.price||0:ve[t.id]?.price||0}function Oe(e,t=null){let n=ve[e]?.price||0;return ql(t)?Math.ceil(n*.5):n}function zl(e,t){return e?.facilities?Object.values(e.facilities).filter(n=>(n?.baseId||n?.id)===t).reduce((n,o)=>n+Math.max(0,(o.level||1)-1),0):0}function ft(e,t){return Tr.includes(t)?Math.pow(Se.incomeUpgradeRate,zl(e,t)):1}function Qe(e){return Math.ceil(Se.income.mixer*ft(e,"mixer"))}function Ke(e){return Math.ceil(Se.income.truck*ft(e,"truck"))}function fn(e){return Math.ceil(Se.poopFine*ft(e,"mixer"))}function pn(e){return Math.ceil(Se.poopFine*ft(e,"truck"))}function Ao(e){let t=Se.maleTruckFinePerHalfDay*(e.turn/2);return Math.ceil(t*ft(e,"truck"))}function Ft(e){let t=Math.max(0,(e.facilities?.trash?.level||1)-1);return Math.min(Se.shredderBonus.maxCards,t)}function hi(e){return Math.max(0,(e.facilities?.trash?.level||1)-1)}function Vt(){return Se.shredderBonus.maxCards}function pt(e){let t=typeof e=="number"?e:Ft(e),n=Math.min(Se.shredderBonus.maxCards,Math.max(0,t)),o=Math.sqrt(n)/Se.shredderBonus.probabilityPercentDivisor*n;return Math.min(1,o/100)}function Xl(e){let t=Ft(e),n=pt(t),o=t>0&&Math.random()0&&(e.stats.shredderBonus=(e.stats.shredderBonus||0)+t.amount,e.totals.shredderBonus=(e.totals.shredderBonus||0)+t.amount,Le(e,t.amount)),t}function Wn(e,t=null){let n=Se.zundaTax,o=Math.max(0,t?.cardEffects?.legalWork||0),r=n.exemption+o*n.legalWorkExemptionBonus,s=n.maxRateCash+o*n.legalWorkExemptionBonus,l=Math.max(0,Math.floor(Number(e)||0)),d=Math.max(0,l-r);if(d<=0)return{profit:l,exemption:r,maxRateCash:s,taxable:0,rate:0,ratePercent:0,tax:0,step:0,legalWork:o};let v=Math.max(1,Math.ceil(d/n.stepAmount)),M=Math.min(n.maxRate,v*n.stepRate),g=Math.ceil(d*M);return{profit:l,exemption:r,maxRateCash:s,taxable:d,rate:M,ratePercent:Math.round(M*100),tax:g,step:v,legalWork:o}}function Do(e,t=e.turn){let n=Math.max(1,Math.floor(Number(t)||1)),o=Math.ceil(n*Se.fairiesTribute.perDay),r=Math.max(0,e?.cardEffects?.fairiesFlatteryNext||e?.cardEffects?.flattery||0),s=r>0?Math.pow(.5,r):1,l=Math.max(0,Math.ceil(o*s)),d=Math.max(0,o-l);return{day:n,base:o,flatteryCount:r,perCardReduction:Math.ceil(o*.5),reduction:d,amount:l}}function Bt(e,t){let n=e?.ref||e||{},o=e?.type==="conveyor"?t?.conveyorMeta?.get(e.oldKey||n.id):n,r=o?.price||Nn(e||n),s=o?.builtSession===t?.buildSession;if(o?.noRefund||n?.noRefund)return{amount:0,sameBuild:s,price:r,rate:0,noRefund:!0};let l=Yl(t,s);return{amount:Math.max(0,Math.ceil(r*l)),sameBuild:s,price:r,rate:l,noRefund:!1}}function Ql(e,t=null){let n=Math.max(1,Math.ceil((Number(e)||0)/Se.explosionDamageDivisor));return Math.max(1,Math.ceil(n*Vl(t)))}function Jl(e,t){return Math.max(0,Math.ceil(t))}function wi(e,t){let n=e.cash;e.cash+=t,e.stats.profit+=t,e.totals.profit+=t,t>=0?(e.stats.revenue+=t,e.totals.revenue+=t):(e.stats.penalty+=Math.abs(t),e.totals.penalty+=Math.abs(t)),e.totals.maxCash=Math.max(e.totals.maxCash,e.cash)}function Le(e,t){let n=Math.max(0,Math.ceil(t));return n>0&&wi(e,n),n}function ot(e,t){let n=Math.max(0,Math.ceil(t));return n>0&&wi(e,-n),n}function yt(e,t){ot(e,t)}function bi(e,t){if(t<=0)return;let n=e.cash;e.cash+=t,e.stats.profit+=t,e.totals.profit+=t,e.totals.maxCash=Math.max(e.totals.maxCash,e.cash)}function xi(e){let t=Qe(e);return e.stats.mixerRevenue+=t,Le(e,t),t}function vi(e){let t=fn(e);return e.stats.mixerPoopFine+=t,e.totals.mixerPoopFine+=t,ot(e,t),t}function ki(e){let t=pn(e);return e.stats.truckPoopFine+=t,e.totals.truckPoopFine+=t,ot(e,t),t}function Ci(e){let t=Ao(e);return e.stats.maleTruckFine+=t,e.totals.maleTruckFine+=t,ot(e,t),t}function Si(e,t){let n=Ql(t,e);return e.stats.explosionDamage+=n,e.totals.explosionDamage+=n,ot(e,n),n}function Mi(e){let t=nt(e),n=Ro(e.stats,t),o=Ke(e),r=n*o,s=Jl(e,r);return e.stats.pendingTruckRevenue=s,e.stats.truckRevenue=s,t==="poop"?e.stats.poopShipmentIncome=s:e.stats.chickShipmentIncome=s,s>0&&Le(e,s),{base:r,adjusted:s,target:t,targetCount:n,unitPrice:o}}function Ti(e,t=0){let n=Wn(t,e);return n.tax<=0||(e.totals.zundaTax=(e.totals.zundaTax||0)+n.tax,e.totals.penalty=(e.totals.penalty||0)+n.tax,e.totals.profit=(e.totals.profit||0)-n.tax,e.cash-=n.tax),n}function gi(e,t=e.turn){let n=Do(e,t);return e.cardEffects&&(e.cardEffects.fairiesFlatteryNext=0,e.cardEffects.flattery!=null&&delete e.cardEffects.flattery),n.amount<=0||(e.stats.fairiesTribute=(e.stats.fairiesTribute||0)+n.amount,e.totals.fairiesTribute=(e.totals.fairiesTribute||0)+n.amount,ot(e,n.amount)),n}function Ei(e){let t=Po(e,"chemicalWeaponSubsidy"),n=Math.max(0,e?.stats?.poopTruck||0),o=Math.floor(n/100),r=t>0&&o>0?o*2e3*t:0;return r>0&&(e.stats.chemicalWeaponSubsidy=(e.stats.chemicalWeaponSubsidy||0)+r,e.totals.chemicalWeaponSubsidy=(e.totals.chemicalWeaponSubsidy||0)+r,Le(e,r)),{cards:t,poopTruck:n,blocks:o,amount:r}}function Ri(e){let t=e?.cardEffects;if(!t||!Array.isArray(t.loans)||!t.loans.length)return{amount:0,paidLoans:0,remainingLoans:0};let n=0,o=0,r=[];for(let s of t.loans){if((s.daysRemaining||0)<=0)continue;let l=Math.max(0,Math.ceil(s.dailyPayment||400));n+=l,o+=1;let d=Math.max(0,(s.daysRemaining||0)-1);d>0&&r.push({...s,daysRemaining:d})}return t.loans=r,n>0&&(e.stats.loanRepayment=(e.stats.loanRepayment||0)+n,e.totals.loanRepayment=(e.totals.loanRepayment||0)+n,ot(e,n)),{amount:n,paidLoans:o,remainingLoans:r.length}}function Pi(e){let t=Eo(e),n=Math.max(1,e.totals.turnsCompleted||e.turn||1),o=Math.max(0,e.cash+e.totals.revenue+t*.5+e.totals.contractBonus*.5),r=Math.ceil(o/n),s=Math.floor(n*35+Math.max(0,n-5)*15),l=e.totals.correct*5,d=e.totals.explosionDamage;return{score:Math.max(0,Math.floor(o+r+l-e.totals.penalty-s)),factoryValue:t,base:o,dailyEarned:r,dayPenalty:s,correctBonus:l,explosionPenalty:d,days:n}}function Zl(e){return{...e,keys:e.keys?{left:{...e.keys.left},right:{...e.keys.right}}:null,queue:[],cooldown:e.cooldown||0}}function yn(e){return JSON.stringify({cash:e.cash,stats:e.stats,totals:e.totals,nextId:e.nextId,gridX:y.x,gridY:y.y,gridCols:e.gridCols||y.cols,gridRows:e.gridRows||y.rows,gridExpansionPurchases:e.gridExpansionPurchases||0,gridExpansionPurchasesByDirection:e.gridExpansionPurchasesByDirection||{right:0,up:0},gridChunkOriginY:e.gridChunkOriginY||0,ownedCells:[...e.ownedCells||[]],ownedChunks:[...e.ownedChunks||[]],blockedCells:[...e.blockedCells||[]],selected:e.selected,multiSelected:e.multiSelected||[],buildTool:e.buildTool,view:e.view,facilities:e.facilities,eggFarms:e.eggFarms,scanners:e.scanners.map(Zl),conveyorTiles:[...e.conveyorTiles],conveyorMeta:[...e.conveyorMeta.entries()],branchCounters:[...e.branchCounters.entries()],cardEffects:e.cardEffects,cardDraft:e.cardDraft,cardTargetPick:e.cardTargetPick})}function Ai(e,t){let n=JSON.parse(t);e.cash=n.cash,e.stats=n.stats,e.totals=n.totals,e.nextId=n.nextId,y.x=n.gridX??Z.grid.x,y.y=n.gridY??Z.grid.y,y.cols=n.gridCols||Z.grid.cols,y.rows=n.gridRows||Z.grid.rows,e.gridCols=y.cols,e.gridRows=y.rows,e.gridExpansionPurchases=n.gridExpansionPurchases||0,e.gridExpansionPurchasesByDirection=n.gridExpansionPurchasesByDirection||{right:0,up:0},e.gridChunkOriginY=n.gridChunkOriginY||0,e.ownedCells=new Set(n.ownedCells||[]),e.ownedChunks=new Set(n.ownedChunks||[]),e.gridOwnedCells=e.ownedCells.size||y.cols*y.rows,e.blockedCells=new Set(n.blockedCells||[]),e.selected=n.selected,e.multiSelected=n.multiSelected||[],e.buildTool=n.buildTool,e.view=n.view||{x:0,y:0,scale:1},e.view.scale==null&&(e.view.scale=1),e.facilities=n.facilities||{},e.eggFarms=n.eggFarms||[],e.scanners=(n.scanners||[]).map(o=>({...o,queue:[],cooldown:o.cooldown||0})),e.conveyorTiles=new Set(n.conveyorTiles||[]),e.conveyorMeta=new Map(n.conveyorMeta||[]),e.branchCounters=new Map(n.branchCounters||[]),e.cardEffects=n.cardEffects||{bearing:0,legalWork:0,fairiesFlatteryNext:0,scannerQueueSpacing:0},e.cardEffects.drawBonus!=null&&delete e.cardEffects.drawBonus,e.cardDraft=n.cardDraft||{pending:!1,choices:[],rerolls:0,picksRemaining:0},e.cardDraft.picksRemaining==null&&(e.cardDraft.picksRemaining=e.cardDraft.pending?1:0),e.cardDraft.freeRerolls==null&&(e.cardDraft.freeRerolls=0),e.cardTargetPick=n.cardTargetPick||null,e.groupDrag=null,e.selectionBox=null,e.pan=null,e.routingVersion=(e.routingVersion||0)+1,e.factoryGraph=null,e.factoryGraphCommittedVersion=-1}function Be(e){e.phase==="build"&&(e.undoStack.push(yn(e)),e.undoStack.length>80&&e.undoStack.shift(),e.redoStack=[])}function Io(e){return e.phase!=="build"||!e.undoStack.length?!1:(e.redoStack.push(yn(e)),Ai(e,e.undoStack.pop()),!0)}function $o(e){return e.phase!=="build"||!e.redoStack.length?!1:(e.undoStack.push(yn(e)),Ai(e,e.redoStack.pop()),!0)}function K(e,t,n,o,r=f.ink){e.floatingTexts.push({x:t,y:n,text:o,color:r,life:1,maxLife:1})}function Lt(e,t,n){e.shake.strength=Math.max(e.shake.strength,t),e.shake.time=Math.max(e.shake.time,n)}function Di(e,t,n){e.effects.push({type:"spawn",priority:Me.ambient,x:t,y:n,life:.42,maxLife:.42,size:12})}function Ii(e,t,n,o=f.green){e.effects.push({type:"scannerPulse",priority:Me.important,x:t,y:n,color:o,life:.42,maxLife:.42,size:24})}function $i(e,t,n){for(let o=0;o<12;o+=1)e.effects.push({type:"meat",priority:Me.important,x:t+H(-18,18),y:n+H(-12,16),vx:H(-60,60),vy:H(-90,-25),life:H(.45,.85),maxLife:.85,size:H(4,8)})}function Fi(e,t,n){for(let o=0;o<16;o+=1)e.effects.push({type:"sludge",priority:Me.important,x:t+H(-18,18),y:n+H(-12,16),vx:H(-46,46),vy:H(-70,-20),life:H(.45,.9),maxLife:.9,size:H(4,8)})}function Bi(e,t,n,o){let r=o==="poop"?f.poop:o==="male"?f.male:f.female;for(let s=0;s<22;s+=1)e.effects.push({type:"shred",priority:Me.important,x:t+H(-16,16),y:n+H(-8,8),vx:H(-70,70),vy:H(40,160),color:s%3===0?f.white:r,life:H(.45,.95),maxLife:.95,w:H(3,7),h:H(10,24),rot:H(-Math.PI,Math.PI)});rt(e,t,n,f.green,34,Me.important)}function Li(e,t,n,o){let r=o==="poop"?f.poop:o==="male"?f.danger:f.green;for(let s=0;s<8;s+=1)e.effects.push({type:"load",priority:Me.important,x:t+H(-18,18),y:n+H(-18,18),vx:H(-42,42),vy:H(-90,-25),color:r,life:H(.35,.65),maxLife:.65,size:8})}function rt(e,t,n,o=f.danger,r=52,s=Me.critical){e.effects.push({type:"shockwave",priority:s,x:t,y:n,color:o,life:.55,maxLife:.55,size:8,maxSize:r})}function ht(e,t,n,o=12,r=f.danger,s=Me.critical){for(let l=0;l0&&(e.shake.time=Math.max(0,e.shake.time-t),e.shake.time<=0&&(e.shake.strength=0));for(let r of e.effects)if(r.life-=t,["meat","sludge","spark","smoke","shred","load","rage"].includes(r.type))r.x+=(r.vx||0)*t,r.y+=(r.vy||0)*t,r.vy=(r.vy||0)+150*t;else if(r.type==="flyingChick"){r.x+=r.vx*t,r.y+=r.vy*t,r.vy+=360*t,r.noDamage||jl(e,r,o);let s=r.x+e.view.x,l=r.y+e.view.y;(s<-100||l<-100||s>n.width+100||l>n.height+100)&&(r.life=0)}e.effects=e.effects.filter(r=>r.life>0);for(let r of e.floatingTexts)r.life-=t,r.y-=34*t;e.floatingTexts=e.floatingTexts.filter(r=>r.life>0)}function jl(e,t,n){t.hit||(t.hit=new Set);for(let o of n()){if(t.hit.has(o.id)||t.xo.x+o.w||t.yo.y+o.h)continue;t.hit.add(o.id);let r=Si(e,o.price);K(e,t.x,t.y-10,`-${T(r)}`,f.danger),rt(e,t.x,t.y,f.danger,30),ht(e,t.x,t.y,5,f.danger),Lt(e,5,.18)}}var Ne=Z.maintenance,Gi=.2;function Bo(e,t){return Math.max(0,Number(e?.cardEffects?.[t])||0)}function Kn(e){let t=Math.min(1.4,1+.04*Math.min(10,Bo(e,"crowdedFarming"))),n=Math.pow(.95,Bo(e,"preventiveMaintenance"));return t*n}function ea(e){return Math.pow(1.03,Bo(e,"durabilityCoating"))}function _n(e,t,n=1){let o=Ne.durability[t]||1e3;return Math.max(1,Math.ceil(o*Math.max(.1,n||1)*ea(e)))}function wt(e){let t=Ne.repairman.dailyCost;return e?.cardEffects?.laborExploitation?Math.ceil(t*.7):t}function mn(e){return e?.baseId||e?.id||e}function Ui(e){return Object.values(e?.facilities||{}).filter(t=>mn(t)==="maintenanceRoom")}function wn(e){return Math.max(1,Math.floor(Number(e?.level)||1))}function Ki(e){return Ui(e).reduce((t,n)=>t+wn(n),0)}function ta(e){return Ki(e)*wt(e)}function na(e,t=0,n=1){let o=Math.max(1,Math.ceil(Math.sqrt(n))),r=Math.floor(t/o),s=t%o,l=12;return{x:e.x+e.w/2+(s-(o-1)/2)*l,y:e.y+e.h/2+(r-Math.floor((n-1)/o)/2)*l}}function Uo(){return{x:y.x+y.cell*.5,y:y.y+y.rows*y.cell+84}}function _i(e){return Math.max(.1,Number(e?.durabilityBaseMultiplier)||1)}function ke(e){e.repairman||(e.repairman={hiredForNextDay:!1,active:!1,...Uo(),target:null,repairedToday:0}),Array.isArray(e.repairmen)||(e.repairmen=[]);for(let[t,n]of e.conveyorMeta||[]){n.uses==null&&(n.uses=0),n.maintenanceType==null&&(n.maintenanceType="conveyor"),n.kind||(n.kind="conveyor"),n.speedMultiplier||(n.speedMultiplier=n.kind==="boostConveyor"?2:1),n.branchMode||(n.branchMode="random");let o=_n(e,"conveyor",_i(n));(n.durability==null||n.durability0&&(n.processingCooldown=Math.max(0,n.processingCooldown-t))}function Vn(e){ke(e);let t=[];for(let[n,o]of e.conveyorMeta||[])t.push({type:"conveyor",key:n,meta:o,label:`${o.kind==="boostConveyor"?"Boost Belt":"Belt"} ${n}`,center:$(j(n).col,j(n).row)});for(let n of e.eggFarms||[])t.push({type:"eggFarm",ref:n,label:`EGG #${n.id}`,center:$(n.col,n.row)});for(let n of(e.scanners||[]).filter(o=>o.kind==="auto"))t.push({type:"autoScanner",ref:n,label:`AUTO #${n.id}`,center:$(n.col,n.row)});for(let n of Object.values(e.facilities||{})){let o=mn(n);["mixer","trash"].includes(o)&&t.push({type:o,ref:n,label:n.shortName||n.name||o,center:{x:n.x+n.w/2,y:n.y+n.h/2}})}return t}function Un(e){return e.meta?Xt(e):Xt(e.ref)}function No(e){return Un(e)>Gi}function zt(e){return e.meta?Lo(e):Lo(e.ref)}function hn(e){return e.meta?Oo(e):Oo(e.ref)}function ra(e,t){Hi(e.meta?e:e.ref,Math.max(0,zt(e)-t))}function Xi(e){return e.type==="conveyor"?`conveyor:${e.key}`:`${e.type}:${e.ref?.id||e.ref?.type||e.type}`}function Qi(e,t=.02){ke(e);let n=Math.max(0,Number(t)||0),o=0;for(let r of Vn(e)){let s=zt(r);ra(r,hn(r)*n),o+=Math.max(0,s-zt(r))/hn(r)*100}return o}function Ji(e){ke(e);let t=Ui(e),n=[];for(let r of t){let s=wn(r);for(let l=0;lXi(o)===t.key)||null}function Wo(e,t,n){return Te(t,n)&&Ve(e,t,n)&&!e.blockedCells?.has?.(P(t,n))}function Wi(e,t){let n=Ge(t.x,t.y);if(n&&Wo(e,n.col,n.row))return n;let o=null,r=1/0;for(let s=0;s$(d.col,d.row)),l=s[s.length-1]||$(r[0].col,r[0].row);return Math.hypot(l.x-o.x,l.y-o.y)>2&&s.push(o),s.length||s.push(o),s}function es(e,t,n){return!!ji(e,t||Uo(),n)}function la(e,t){let n=null;for(let o of Vn(e)){let r=Un(o);No(o)&&(t&&!es(e,t,o)||(!n||r>Un(n))&&(n=o))}return n}function aa(e,t,n){if(!t.active||e.phase!=="running")return;let o=ia(e,t.target);if(o&&(!No(o)||!es(e,t,o))&&(o=null),o=o||la(e,t),!o){t.target=null;return}t.target={key:Xi(o),label:o.label};let r=ji(e,t,o);if(!r?.length){t.target=null;return}let s=r[0],l=Ne.repairman.walkSpeed,d=s.x-t.x,v=s.y-t.y,M=Math.hypot(d,v);if(M>8){let q=Math.min(M,l*n);t.x+=d/M*q,t.y+=v/M*q;return}if(r.length>1||Math.hypot((Go(e,o)?.x||s.x)-t.x,(Go(e,o)?.y||s.y)-t.y)>10)return;let g=n/(Ne.repairman.secondsPerOnePercent*100),A=zt(o),L=hn(o)*Gi,V=g*hn(o);Hi(o.meta?o:o.ref,Math.max(L,zt(o)-V));let ee=Math.max(0,(A-zt(o))/hn(o)*100);t.repairedToday=(t.repairedToday||0)+ee,ee>0&&Math.random()<.02&&K(e,t.x,t.y-28,"REPAIR",f.green),No(o)||(t.target=null)}function ts(e,t){ke(e);let n=Array.isArray(e.repairmen)&&e.repairmen.length?e.repairmen:e.repairman?[e.repairman]:[];for(let o of n)aa(e,o,t);n.length&&(e.repairman=n[0])}function ca(e){let t=0,n="none";for(let o of Vn(e)){let r=Un(o);r>t&&(t=r,n=o.label)}return{ratio:t,percent:Math.round(t*100),label:n}}function ns(e){let t=ca(e),n=_o(e);return{worst:t,avgBeltPerformance:n,repairman:e.repairman||null,repairmen:Array.isArray(e.repairmen)?e.repairmen:[],workers:Ki(e),dailyWage:ta(e)}}var ua=$e.commonWeight,da=$e.rareWeight,fa=$e.ultraRareWeight||.45,Ot=$e.baseDraftSize,bt=Mr;function Jt(e,t){let n=We(e);return Math.max(0,Number(n[t])||0)}function We(e){e.cardEffects||(e.cardEffects={...co,loans:[]});for(let[t,n]of Object.entries(co))e.cardEffects[t]==null&&(e.cardEffects[t]=Array.isArray(n)?[]:n);return e.cardEffects.flattery!=null&&(e.cardEffects.fairiesFlatteryNext=Math.max(0,e.cardEffects.fairiesFlatteryNext||0)+Math.max(0,e.cardEffects.flattery||0),delete e.cardEffects.flattery),Array.isArray(e.cardEffects.loans)||(e.cardEffects.loans=[]),e.cardEffects.drawBonus!=null&&delete e.cardEffects.drawBonus,e.cardDraft||(e.cardDraft={pending:!1,choices:[],rerolls:0,picksRemaining:0}),e.cardDraft.picksRemaining==null&&(e.cardDraft.picksRemaining=e.cardDraft.pending?1:0),e.cardDraft.freeRerolls==null&&(e.cardDraft.freeRerolls=0),e.cardEffects}function us(e){let t=Math.max(.5,1-.05*Math.min(10,Jt(e,"crowdedFarming"))),n=Math.pow(.96,Jt(e,"hatchingFeed"));return t*n}function ds(e,t=null){return Math.min(3,Math.max(0,Number(t?.extraEggOutlet)||0))}function fs(e){return Math.pow(.85,Jt(e,"scannerQueueSpacing"))}function Xn(e,t=null){let n=We(e),o=En*Math.pow(Rn,Math.max(0,n.bearing||0)),r=t?Math.max(.1,Number(e?.conveyorMeta?.get?.(t)?.speedMultiplier)||1):1,s=t?1:_o(e);return Math.min(_t,o*s)*r}function it(e,t=null){let n=Math.max(0,(e?.level||1)-1),o=Math.max(ao,gn*Math.pow(kr,n));return t?Math.max(ao,o*qn(e)):o}function os(e){let t=e.cardDraft||{rerolls:0},n=Math.max(1,(t.rerolls||0)+1);return Math.ceil(Math.max(1,e.turn||1)/$e.rerollCost.dayDivisor*n*$e.rerollCost.multiplier)}function Qo(e){return bt.find(t=>t.id===e)||null}function Jo(e,t){let n=typeof t=="string"?Qo(t):t;if(We(e),!n||n.type==="dud")return{ok:!1,reason:"Card not found."};if(n.id==="extraCards")return e.cardDraft=e.cardDraft||{pending:!1,choices:[],rerolls:0,picksRemaining:0},e.cardDraft.choices.push(...zn(e,2)),{ok:!0,reason:`${n.title} added draft cards.`};if(n.type==="instant")return ys(e,n),{ok:!0,reason:`${n.title} granted.`};if(n.type==="equipmentUpgrade"){let o=He(e,n)[0];return o?(ps(e,o),{ok:!0,reason:`${n.title} applied.`}):{ok:!1,reason:`No valid target for ${n.title}.`}}if(n.type==="cellAction"&&n.target==="blockedCell"){let o=He(e,n).slice(0,3);if(!o.length)return{ok:!1,reason:"No blocked cells to remove."};for(let r of o){e.blockedCells.delete(P(r.col,r.row));let s=$(r.col,r.row);mt(e,s.x,s.y)}return be(e),{ok:!0,reason:`${n.title} removed ${o.length} blocked cells.`}}return{ok:!1,reason:`${n.title} cannot be debug-granted.`}}function pa(e,t){if(t.type==="blockedCell")return`Blocked cell ${t.col},${t.row}`;if(t.type==="eggFarm")return`EGG #${t.id} L${t.level} -> L${t.level+1}`;if(t.type==="eggOutlet"){let n=Math.max(0,Number(t.farm?.extraEggOutlet)||0);return`EGG #${t.id} OUTLET ${1+n} -> ${2+n}`}if(t.type==="scanner")return`AUTO #${t.id} L${t.level} -> L${t.level+1} | CD ${it(t,e).toFixed(1)}s -> ${it({...t,level:t.level+1},e).toFixed(1)}s`;if(t.type==="facility"&&(t.baseId||t.id)==="maintenanceRoom"){let n=wn(t);return`MAINT ROOM workers ${n} -> ${n+1} | wage ${T(wt(e))}/worker/day`}if(t.type==="facility"&&(t.baseId||t.id)==="mixer"){let n=t.level,o=Qe(e);t.level=n+1;let r=Qe(e);return t.level=n,`MIXER L${t.level} -> L${t.level+1} | ${T(o)} -> ${T(r)}`}if(t.type==="facility"&&(t.baseId||t.id)==="truck"){let n=t.level,o=Ke(e);t.level=n+1;let r=Ke(e);return t.level=n,`TRUCK L${t.level} -> L${t.level+1} | ${T(o)} -> ${T(r)}`}if(t.type==="facility"&&t.id==="trash"){let n=Ft(e),o=Math.min(Vt(),n+1),r=(pt(n)*100).toFixed(2),s=(pt(o)*100).toFixed(2);return`SHREDDER L${t.level||1} -> L${(t.level||1)+1} | ${r}% -> ${s}% per item`}return`${t.name||t.id} L${t.level||1} -> L${(t.level||1)+1}`}function ya(e){return[...e.blockedCells||[]].map(t=>{let[n,o]=String(t).split(",").map(Number);return{type:"blockedCell",id:t,col:n,row:o}}).filter(t=>Number.isFinite(t.col)&&Number.isFinite(t.row))}function He(e,t){let n=typeof t=="string"?Qo(t):t;return We(e),n?n.target==="blockedCell"?ya(e):n.target==="eggFarm"?e.eggFarms.filter(o=>(o.level||1)<4):n.target==="eggOutlet"?e.eggFarms.filter(o=>Math.max(0,Number(o.extraEggOutlet)||0)<3).map(o=>({type:"eggOutlet",id:o.id,col:o.col,row:o.row,farm:o})):n.target==="autoScanner"?e.scanners.filter(o=>o.kind==="auto"):n.target==="maintenanceRoom"?Object.values(e.facilities||{}).filter(o=>(o.baseId||o.id)==="maintenanceRoom"):n.target==="mixer"?Object.values(e.facilities||{}).filter(o=>(o.baseId||o.id)==="mixer"):n.target==="truck"?Object.values(e.facilities||{}).filter(o=>(o.baseId||o.id)==="truck"):n.target==="trash"?hi(e)(o.baseId||o.id)==="trash"):[]:[]:[]}function ha(e,t){let n=We(e);if(t.id==="crowdedFarming")return(n.crowdedFarming||0)>=10;if(t.id==="recyclingSubsidy")return(n.recyclingSubsidy||0)>=3;if(t.id==="dudFilter"){let o=Math.max(0,Math.min(1,$e.dudChancePerCard||0)),r=Math.max(1e-4,Math.min(1,$e.dudChanceReductionPerFilter||.05));return(n.dudFilter||0)>=Math.ceil(o/r)}return t.id==="laborExploitation"?!!n.laborExploitation:t.id==="usedMachine"?!!n.usedMachineActive:t.id==="newMachine"?!n.usedMachineActive:!1}function ma(e){let t=e.repairman||{};return!!(Object.values(e.facilities||{}).some(o=>(o.baseId||o.id)==="maintenanceRoom")||t.active||t.hiredForNextDay||e.pendingRepairWorkerWages>0)}function wa(e){return bt.filter(t=>!(t.id==="laborExploitation"&&!ma(e)||ha(e,t)||(t.type==="equipmentUpgrade"||t.type==="cellAction")&&He(e,t).length<=0))}function rs(e,t){if(t.weightBase!=null){let n=Jt(e,t.id);return Math.max(0,Number(t.weightBase)-Math.max(0,Number(t.weightLossPerCopy)||0)*n)}return t.rarity==="ultraRare"?fa:t.rarity==="rare"?da:ua}function ba(e,t){let n=t.reduce((r,s)=>r+rs(e,s),0);if(n<=0)return t[Math.floor(Math.random()*t.length)];let o=Math.random()*n;for(let r of t)if(o-=rs(e,r),o<=0)return r;return t[t.length-1]}var is=0;function xa(){return is+=1,{id:`dud:${Date.now()}:${is}`,title:"Dud",rarity:"dud",type:"dud",description:"Click to flick this dud away. It does not consume a pick."}}function va(e){let t=We(e),n=Math.max(0,Math.min(1,$e.dudChancePerCard||0)),o=Math.max(0,Math.min(1,$e.dudChanceReductionPerFilter||0));return Math.max(0,n-o*Math.max(0,t.dudFilter||0))}function ka(e,t){let n=[...t],o=0,r=Math.max(0,$e.maxDudsPerDraft||0),s=va(e);for(let l=0;ld.id===s.id);l>=0&&r.splice(l,1)}return ka(e,o)}function Qt(e){return e?e.type==="blockedCell"?`blockedCell:${e.id}`:e.type==="eggFarm"?`eggFarm:${e.id}`:e.type==="eggOutlet"?`eggOutlet:${e.id}`:e.type==="scanner"?`scanner:${e.id}`:e.type==="facility"?`facility:${e.id}`:`${e.type}:${e.id}`:""}function ss(e){if(!e)return null;if(e.type==="blockedCell"){let t=$(e.col,e.row);return{x:t.x-y.cell*.48,y:t.y-y.cell*.48,w:y.cell*.96,h:y.cell*.96,cx:t.x,cy:t.y}}if(e.type==="eggFarm"||e.type==="eggOutlet"){let t=$(e.col,e.row);return{x:t.x-34,y:t.y-34,w:68,h:68,cx:t.x,cy:t.y}}if(e.type==="scanner"){let t=pe(e);return{x:t.x-54,y:t.y-77,w:108,h:154,cx:t.x,cy:t.y}}return e.type==="facility"?{x:e.x,y:e.y,w:e.w,h:e.h,cx:e.x+e.w/2,cy:e.y+e.h/2}:null}function Zo(e){if(!e.cardTargetPick?.pending)return[];if(e.cardTargetPick.mode==="autoScannerMenu"){let n=new Set(e.cardTargetPick.targetKeys||[]);return e.scanners.filter(o=>o.kind==="auto"&&n.has(Qt(o))).map(o=>({key:Qt(o),label:"AUTO ROUTE SETTINGS",target:o,bounds:ss(o)})).filter(o=>o.bounds)}let t=new Set(e.cardTargetPick.targetKeys||[]);return He(e,e.cardTargetPick.cardId).filter(n=>t.has(Qt(n))).map(n=>({key:Qt(n),label:pa(e,n),target:n,bounds:ss(n)})).filter(n=>n.bounds)}function Ca(e,t){let n=Zo(e);for(let o of n){let r=o.bounds;if(t.x>=r.x&&t.x<=r.x+r.w&&t.y>=r.y&&t.y<=r.y+r.h)return o.target}return null}function ps(e,t){if(t.type==="eggOutlet"){let n=t.farm||e.eggFarms.find(r=>r.id===t.id);if(!n)return;n.extraEggOutlet=Math.min(3,Math.max(0,Number(n.extraEggOutlet)||0)+1);let o=$(n.col,n.row);K(e,o.x,o.y-30,`OUTLET ${1+n.extraEggOutlet}`,f.green),be(e);return}if(t.level=(t.level||1)+1,t.type==="eggFarm"){t.nextSpawn=At(t),t.lastInterval=t.nextSpawn;let n=$(t.col,t.row);K(e,n.x,n.y-30,`LV ${t.level}`,f.green);return}if(t.type==="scanner"){let n=pe(t);K(e,n.x,n.y-34,`LV ${t.level}`,f.green);return}t.type==="facility"&&K(e,t.x+t.w/2,t.y+18,`LV ${t.level}`,f.green)}function ys(e,t){let n=We(e),o=r=>{n[r]=Math.max(0,n[r]||0)+1};if(t.id==="bearing"&&o("bearing"),t.id==="legalWork"&&o("legalWork"),t.id==="crowdedFarming"&&(n.crowdedFarming=Math.min(10,(n.crowdedFarming||0)+1)),t.id==="hatchingFeed"&&o("hatchingFeed"),t.id==="safetyCover"&&o("safetyCover"),t.id==="recyclingSubsidy"&&(n.recyclingSubsidy=Math.min(3,(n.recyclingSubsidy||0)+1)),t.id==="chemicalWeaponSubsidy"&&o("chemicalWeaponSubsidy"),t.id==="preventiveMaintenance"&&o("preventiveMaintenance"),t.id==="dudFilter"&&o("dudFilter"),t.id==="durabilityCoating"&&(o("durabilityCoating"),ke(e)),t.id==="sparePartsBin"){o("sparePartsBin");let r=Qi(e,.02);e.stats.sparePartsRepair=(e.stats.sparePartsRepair||0)+r,e.totals.sparePartsRepair=(e.totals.sparePartsRepair||0)+r,K(e,y.x+y.cols*y.cell/2,y.y-30,"REPAIR +2%",f.green)}if(t.id==="dudRefund"&&o("dudRefund"),t.id==="freeReroll"&&o("freeReroll"),t.id==="composter"&&o("composter"),t.id==="scannerQueueSpacing"&&o("scannerQueueSpacing"),t.id==="flattery"&&o("fairiesFlatteryNext"),t.id==="usedMachine"&&(n.usedMachineActive=!0),t.id==="newMachine"&&(n.usedMachineActive=!1),t.id==="laborExploitation"&&(n.laborExploitation=1),t.id==="rescueLoan"&&(o("rescueLoanCharges"),K(e,y.x+y.cols*y.cell/2,y.y-30,"RESCUE HELD",f.green)),t.id==="loan"){let r=Math.max(1,Math.floor(Number(e.turn)||1)),s=300*r;Le(e,s),n.loans.push({daysRemaining:r,dailyPayment:400,borrowedOnTurn:e.turn||1}),K(e,y.x+y.cols*y.cell/2,y.y-30,`LOAN +${T(s)}`,f.green)}}function _e(e){return`${Math.round(Number(e||0)*100)}%`}function ls(e){let t=at[Math.max(0,Math.min(at.length-1,e-1))]||at[0]||[0,0];return`${t[0].toFixed(2)}-${t[1].toFixed(2)}s`}function as(e,t){return Math.ceil(e*Math.pow(Se.incomeUpgradeRate,Math.max(0,t-1)))}function cs(e,t,n){let r=He(e,t==="mixer"?"upgradeMixer":"upgradeTruck")[0];if(!r)return{before:as(n,1),after:as(n,2)};let s=t==="mixer"?Qe(e):Ke(e),l=r.level||1;r.level=l+1;let d=t==="mixer"?Qe(e):Ke(e);return r.level=l,{before:s,after:d}}function Sa(e,t){let n=We(e);if(t.id==="upgradeEgg"){let o=He(e,t),r=Math.min(3,Math.max(1,o[0]?.level||1));return`Choose one EGG. Level +1. Production interval ${ls(r)} -> ${ls(r+1)}.`}if(t.id==="upgradeAutoScanner"){let o=He(e,t)[0]||{level:1},r=it(o,e),s=it({...o,level:(o.level||1)+1},e);return`Choose one AUTO SCANNER. Level +1. Cooldown ${r.toFixed(2)}s -> ${s.toFixed(2)}s.`}if(t.id==="upgradeMaintenanceRoom"){let o=He(e,t)[0]||{level:1},r=wn(o);return`Choose one MAINTENANCE ROOM. Repair workers ${r} -> ${r+1}. Wage ${T(wt(e))} per worker per day.`}if(t.id==="upgradeMixer"){let o=cs(e,"mixer",Se.income.mixer);return`Choose one MIXER. Earning +10%. Male-chick income ${T(o.before)} -> ${T(o.after)}.`}if(t.id==="upgradeTruck"){let o=cs(e,"truck",Se.income.truck);return`Choose one TRUCK. Earning +10%. Correct shipment income ${T(o.before)} -> ${T(o.after)}.`}if(t.id==="upgradeTrash"){let o=Ft(e),r=Math.min(Vt(),o+1);return`SHREDDER level +1. Bonus chance per item ${_e(pt(o))} -> ${_e(pt(r))}.`}if(t.id==="bearing"){let o=Math.min(_t,En*Math.pow(Rn,Math.max(0,n.bearing||0))),r=Math.min(_t,En*Math.pow(Rn,Math.max(0,(n.bearing||0)+1)));return`Conveyor speed +7.5%. After pick: ${o.toFixed(1)} -> ${r.toFixed(1)} px/s.`}if(t.id==="legalWork"){let o=Math.max(0,n.legalWork||0);return`ZUNDA TAX exemption +${T(250)}. After pick: exemption bonus ${T(250*(o+1))}.`}if(t.id==="crowdedFarming"){let o=Math.min(10,(n.crowdedFarming||0)+1);return`Held ${n.crowdedFarming||0}/10. After pick: EGG interval ${_e(Math.max(.5,1-.05*o))}, deterioration ${_e(Math.min(1.4,1+.04*o))}.`}if(t.id==="extraEggOutlet"){let o=He(e,t),r=Math.max(0,Number(o[0]?.farm?.extraEggOutlet)||0);return`Choose one EGG. Output ports ${1+r} -> ${2+r}. Max 4 ports per EGG.`}if(t.id==="hatchingFeed")return`Held ${n.hatchingFeed||0}. After pick: EGG interval ${_e(Math.pow(.96,(n.hatchingFeed||0)+1))}, poop rate ${_e(Math.pow(1.03,(n.hatchingFeed||0)+1))}.`;if(t.id==="safetyCover")return`Held ${n.safetyCover||0}. After pick: explosion damage ${_e(Math.pow(.95,(n.safetyCover||0)+1))}.`;if(t.id==="recyclingSubsidy")return`Held ${n.recyclingSubsidy||0}/3. After pick: normal SELL refund ${Math.round(Math.min(.8,.5+.1*((n.recyclingSubsidy||0)+1))*100)}%.`;if(t.id==="chemicalWeaponSubsidy")return`Held ${n.chemicalWeaponSubsidy||0}. After pick: ${T(2e3*((n.chemicalWeaponSubsidy||0)+1))} per 100 truck-shipped poop each day.`;if(t.id==="preventiveMaintenance")return`Held ${n.preventiveMaintenance||0}. After pick: deterioration speed ${_e(Math.pow(.95,(n.preventiveMaintenance||0)+1))}.`;if(t.id==="dudFilter"){let o=Math.max(0,Math.min(1,$e.dudChancePerCard||0)),r=Math.max(0,Math.min(1,$e.dudChanceReductionPerFilter||.05)),s=Math.max(0,n.dudFilter||0),l=Math.max(0,o-r*s),d=Math.max(0,o-r*(s+1));return`Held ${s}. DUD chance per card ${_e(l)} -> ${_e(d)}.`}if(t.id==="durabilityCoating")return`Held ${n.durabilityCoating||0}. After pick: maximum durability ${_e(Math.pow(1.03,(n.durabilityCoating||0)+1))}.`;if(t.id==="sparePartsBin")return`Held ${n.sparePartsBin||0}. Immediately restores 2% durability to every maintained machine. Offer weight: ${Math.max(0,100-2*Math.max(0,n.sparePartsBin||0))}.`;if(t.id==="dudRefund")return`Held ${n.dudRefund||0}. After pick: DUD clicks award ${T(50*((n.dudRefund||0)+1))}.`;if(t.id==="freeReroll")return`Held ${n.freeReroll||0}. After pick: each card screen starts with ${(n.freeReroll||0)+1} free reroll${(n.freeReroll||0)+1===1?"":"s"}.`;if(t.id==="composter")return`Held ${n.composter||0}. After pick: SHREDDER poop grants ${T(3*((n.composter||0)+1))}.`;if(t.id==="scannerQueueSpacing")return`Held ${n.scannerQueueSpacing||0}. After pick: Manual/Auto Scanner queue spacing ${_e(Math.pow(.85,(n.scannerQueueSpacing||0)+1))}.`;if(t.id==="flattery")return`Next Fairies tribute -50%. Held for next tribute: ${n.fairiesFlatteryNext||0}.`;if(t.id==="usedMachine")return"Enable Used Machines: future equipment costs 50%, has no SELL refund, and starts with 60% durability.";if(t.id==="newMachine")return"Cancel Used Machines. Future equipment returns to normal price, refund, and durability.";if(t.id==="laborExploitation")return"Repairman labor cost -30% permanently. One time only.";if(t.id==="rescueLoan")return`At day-end bankruptcy check, gain ${T(500)} if cash is below ${T(0)}. Held: ${n.rescueLoanCharges||0}.`;if(t.id==="loan"){let o=Math.max(1,Math.floor(Number(e.turn)||1));return`Gain ${T(300*o)} now. Starting tomorrow, pay ${T(400)} per day for ${o} days.`}return t.description}function Xo(e,t){let n=e.cardDraft?.choices||[],o=n.findIndex(r=>r.id===t.id);o>=0&&n.splice(o,1)}function hs({game:e,ui:t,onUpdatePanels:n}){function o(){n&&n()}function r(){We(e),e.cardTargetPick=null,e.cardDraft={pending:!0,choices:zn(e,Ot),rerolls:0,freeRerolls:Jt(e,"freeReroll"),picksRemaining:1}}function s(){We(e),e.cardDraft.pending=!1,e.cardDraft.choices=[],e.cardDraft.picksRemaining=0,e.cardTargetPick=null,t.modal.classList.remove("visible","equipment-popover"),o()}function l(I=Ot){We(e),e.cardDraft.choices=zn(e,I)}function d(){We(e),!e.cardDraft.choices.length&&e.cardDraft.picksRemaining>0&&l(Ot)}function v(I){Xo(e,I),e.cardDraft.picksRemaining=Math.max(0,(e.cardDraft.picksRemaining||1)-1),e.cardTargetPick=null,d(),e.cardDraft.picksRemaining<=0?s():ne()}function M(I){Xo(e,I),e.cardDraft.picksRemaining=Math.max(0,(e.cardDraft.picksRemaining||1)-1)+$e.extraCardsAddedPicks,e.cardDraft.choices.push(...zn(e,2)),ne()}function g(I,_){let W=Jt(e,"dudRefund")*50;if(W>0){let fe=Le(e,W);e.stats.dudRefundIncome=(e.stats.dudRefundIncome||0)+fe,e.totals.dudRefundIncome=(e.totals.dudRefundIncome||0)+fe,K(e,y.x+y.cols*y.cell/2,y.y-30,`DUD REFUND +${T(fe)}`,f.green)}Lt(e,34,.62),document.body?.animate&&document.body.animate([{transform:"translate(0, 0)"},{transform:"translate(-10px, 7px)"},{transform:"translate(12px, -8px)"},{transform:"translate(-8px, -5px)"},{transform:"translate(9px, 6px)"},{transform:"translate(0, 0)"}],{duration:260,easing:"steps(5, end)"}),_&&(_.style.setProperty("--fling-x",`${260+Math.random()*260}px`),_.style.setProperty("--fling-y",`${-260-Math.random()*170}px`),_.style.setProperty("--fling-r",`${(Math.random()<.5?-1:1)*(52+Math.random()*38)}deg`),_.classList.add("flung")),window.setTimeout(()=>{Xo(e,I),d(),ne()},340)}function A(I,_=null){if(I.type==="dud")return g(I,_);if(I.id==="extraCards")return M(I);if(I.type==="equipmentUpgrade"||I.type==="cellAction")return L(I);ys(e,I),v(I)}function L(I){let _=He(e,I);_.length&&(e.cardTargetPick={pending:!0,cardId:I.id,mode:I.target==="blockedCell"?"blockedCell":"equipment",targetKeys:_.map(Qt),remaining:I.id==="dynamite"?Math.min(3,_.length):1},t.modal.classList.remove("visible","equipment-popover"),o())}function V(I){if(!I||I.type!=="blockedCell")return!1;let _=P(I.col,I.row);if(!e.blockedCells?.has(_))return!1;e.blockedCells.delete(_);let W=$(I.col,I.row);return mt(e,W.x,W.y),rt(e,W.x,W.y,f.danger,62),ht(e,W.x,W.y,18,f.warn),Gn(e,W.x,W.y,8),Lt(e,16,.35),K(e,W.x,W.y-18,"BOOM",f.danger),be(e),e.cardTargetPick.remaining=Math.max(0,(e.cardTargetPick.remaining||1)-1),e.cardTargetPick.targetKeys=He(e,"dynamite").map(Qt),o(),!0}function ee(I){if(!e.cardTargetPick?.pending)return!1;let _=Qo(e.cardTargetPick.cardId),W=Ca(e,I);return!_||!W?(q(),!0):_.id==="dynamite"?(V(W)&&((e.cardTargetPick.remaining||0)<=0||!He(e,"dynamite").length)&&v(_),!0):(ps(e,W),v(_),!0)}function q(){e.cardTargetPick?.pending&&(e.cardTargetPick=null,ne())}function le(){let I=Math.max(0,e.cardDraft?.freeRerolls||0);if(I>0){e.cardDraft.freeRerolls=I-1,e.cardDraft.rerolls+=1,l(Ot),ne();return}let _=os(e);e.cash<_||(ot(e,_),e.stats.cardRerollCost=(e.stats.cardRerollCost||0)+_,e.totals.cardRerollCost=(e.totals.cardRerollCost||0)+_,e.cardDraft.rerolls+=1,l(Ot),ne())}function me(I,_){let W=document.createElement("button");W.type="button";let fe=I.type==="dud"?"dud":I.rarity==="ultraRare"?"ultra-rare":I.rarity==="rare"?"rare":"common";W.className=`card-choice ${fe}`;let Ce=I.type==="dud"?"MISS":I.rarity==="ultraRare"?"ULTRA RARE":I.rarity==="rare"?"RARE":"COMMON",ye=[Ce,...(I.tags||[]).filter(he=>he!==Ce)].slice(0,5);return W.innerHTML=`${I.title}
${ye.map(he=>`${he}`).join("")}
${Sa(e,I)}`,W.addEventListener("click",()=>A(I,W)),W.dataset.index=String(_),W}function ne(){We(e),e.cardTargetPick=null,e.cardDraft?.pending||r(),e.cardDraft.choices?.length||l(Ot);let I=e.cardDraft.choices||[],_=os(e),W=Math.max(0,e.cardDraft?.freeRerolls||0),fe=Math.max(1,e.cardDraft.picksRemaining||1);t.modalTitle.textContent="Choose Upgrade Card",t.modalBody.innerHTML=`

Choose ${fe} card${fe===1?"":"s"} before Build phase. Dud cards can be flicked away without consuming a pick. Equipment cards target machines; Dynamite targets blocked cells.

`;let Ce=t.modalBody.querySelector("#cardChoices");I.forEach((ge,De)=>Ce.appendChild(me(ge,De))),t.modalActions.innerHTML="";let ye=document.createElement("button");ye.type="button",ye.className="facility-action warn reroll-button",ye.textContent=W>0?`Reroll (FREE x${W})`:`Reroll ${T(_)}`,ye.disabled=W<=0&&e.cash<_,ye.title=W>0?`${W} free reroll${W===1?"":"s"} remaining.`:ye.disabled?"Not enough cash":"",ye.addEventListener("click",le),t.modalActions.appendChild(ye);let he=document.createElement("button");he.type="button",he.className="facility-action",he.textContent="No Card",he.title="Skip this upgrade pick and enter Build phase.",he.addEventListener("click",s),t.modalActions.appendChild(he),t.modal.classList.remove("equipment-popover"),t.modal.classList.add("visible")}return{prepareDraft:r,showDraft:ne,finishDraft:s,chooseTargetAtPoint:ee,cancelTargetPick:q}}var Ma=!1,Ta={chickMale:"./assets/images/chick_male.png",chickFemale:"./assets/images/chick_female.png",poop:new URL("../../assets/images/poop.png",import.meta.url).href,conveyor:"./assets/images/conveyor.png",scannerManual:"./assets/images/scanner_manual.png",scannerAuto:"./assets/images/scanner_auto.png",eggFarm:"./assets/images/egg_farm.png",mixer:"./assets/images/mixer.png",shredder:"./assets/images/shredder.png",truck:"./assets/images/truck.png"},Ze={};for(let[e,t]of Object.entries(Ta)){let n=new Image;n.loaded=!1,n.onerror=()=>{n.loaded=!1},(Ma||e==="poop")&&t&&(n.onload=()=>{n.loaded=!0},n.src=t),Ze[e]=n}function bs(e,t,n,o){e.clearRect(0,0,t.width,t.height),Ra(e,t),e.save();let r=n.shake.time>0?H(-n.shake.strength,n.shake.strength):0,s=n.shake.time>0?H(-n.shake.strength,n.shake.strength):0,l=n.view?.scale||1;e.translate((n.view?.x||0)+r,(n.view?.y||0)+s),e.scale(l,l),Aa(e,n),Da(e,n),Ia(e,n),Ga(e,n),Va(e,n),Ua(e,n),_a(e,n),Ea(e,n),Za(e,n,o.activeQueuedChick),lc(e,n),oc(e,t,n),nc(e,n,o.selectedObject,o.selectedTitle),rc(e,n),ic(e,n),sc(e,n),e.restore(),ac(e,n,t)}function ga(e){if(e.type==="eggFarm"){let n=$(e.ref.col,e.ref.row);return{x:n.x-34,y:n.y-34,w:68,h:68,cx:n.x,cy:n.y}}let t=e.ref;return{x:t.x,y:t.y,w:t.w,h:t.h,cx:t.x+t.w/2,cy:t.y+t.h/2}}function Ea(e,t){if(t.phase!=="build")return;let n=On(t);for(let o of n){let r=ga(o);e.save(),e.fillStyle="rgba(196,36,36,.22)",e.strokeStyle=f.danger,e.lineWidth=5,e.setLineDash([10,6]),te(e,r.x,r.y,r.w,r.h,!0,!0),e.setLineDash([]),e.fillStyle=f.danger,e.strokeStyle=f.white,e.lineWidth=4,e.font="900 16px ui-monospace, monospace",e.textAlign="center",e.strokeText("!",r.cx,r.y+24),e.fillText("!",r.cx,r.y+24),e.font="900 11px ui-monospace, monospace",e.strokeText("NO ROUTE",r.cx,r.y+r.h-8),e.fillText("NO ROUTE",r.cx,r.y+r.h-8),e.restore()}}function te(e,t,n,o,r,s=!0,l=!0){e.beginPath(),e.rect(t,n,o,r),s&&e.fill(),l&&e.stroke()}function er(e,t,n,o,r=f.ink){e.save(),e.font="900 12px ui-monospace, monospace",e.textAlign="center",e.lineWidth=4,e.strokeStyle=f.white,e.fillStyle=r,e.strokeText(o,t,n),e.fillText(o,t,n),e.restore()}function Nt(e,t,n,o,r,s){return t?.loaded?(e.drawImage(t,n,o,r,s),!0):!1}function Ra(e,t){e.fillStyle=f.bg,e.fillRect(0,0,t.width,t.height),e.save(),e.strokeStyle="rgba(16, 32, 21, 0.035)",e.lineWidth=1;for(let n=0;n=n;for(let r of Rt(t)){let s=Pt(r);e.save(),e.fillStyle=o?"rgba(183,245,198,.34)":"rgba(255,255,255,.22)",e.strokeStyle=o?f.green:f.muted,e.lineWidth=5,e.setLineDash([16,10]),te(e,s.x,s.y,s.w,s.h,!0,!0),e.setLineDash([]);let l=ho(r),d=l.cx,v=l.cy;e.fillStyle="rgba(255,255,255,.94)",e.strokeStyle=f.ink,e.lineWidth=4,te(e,l.x,l.y,l.w,l.h,!0,!0),e.fillStyle=o?f.green:f.danger,e.font="900 16px ui-monospace, monospace",e.textAlign="center",e.fillText(r.direction==="right"?"BUY RIGHT LOT":"BUY UPPER LOT",d,v-12),e.fillStyle=f.ink,e.font="900 16px ui-monospace, monospace",e.fillText(`${T(n)} / 10\xD710`,d,v+15),e.fillStyle=f.muted,e.font="900 11px ui-monospace, monospace",e.fillText("blocked tiles included",d,v+30),e.restore()}}function st(e,t,n,o,r,s,l=0){let d=Xt(s);d<.5||(e.save(),e.globalAlpha=Math.min(.62,.12+d*.54),e.fillStyle="#6b4a23",l>0?(e.beginPath(),e.roundRect?.(t,n,o,r,l),e.roundRect?e.fill():te(e,t,n,o,r,!0,!1)):te(e,t,n,o,r,!0,!1),e.globalAlpha=Math.min(.9,.25+d*.6),e.strokeStyle="#3a2a16",e.lineWidth=Math.max(2,Math.floor(2+d*5)),e.setLineDash([5,6]),te(e,t+3,n+3,o-6,r-6,!1,!0),e.restore())}function jo(e,t){let n=e.componentLookup?.get(t);return(n?e.congestion.get(n):null)?.ratio||0}function Ia(e,t){let n=[],o=new Set;for(let s of t.conveyorTiles){let{col:l,row:d}=j(s);for(let v of ue){let M={col:l+v.dc,row:d+v.dr},g=P(M.col,M.row);if(!t.conveyorTiles.has(g))continue;let A=[s,g].sort().join("|");if(o.has(A))continue;o.add(A);let L=Math.max(jo(t,s),jo(t,g));n.push({a:$(l,d),b:$(M.col,M.row),ratio:L})}}e.save(),e.lineCap="round",e.lineJoin="round";for(let s of[{width:33,color:f.ink,alpha:1},{width:25,color:f.white,alpha:1},{width:17,color:f.greenSoft,alpha:1}]){e.globalAlpha=s.alpha,e.strokeStyle=s.color,e.lineWidth=s.width;for(let l of n)e.beginPath(),e.moveTo(l.a.x,l.a.y),e.lineTo(l.b.x,l.b.y),e.stroke()}for(let s of n){let l=Math.max(0,(s.ratio-.5)/.5);e.strokeStyle=po(f.green,f.danger,l),e.globalAlpha=s.ratio>.5?.35+l*.55:.8,e.lineWidth=6,e.beginPath(),e.moveTo(s.a.x,s.a.y),e.lineTo(s.b.x,s.b.y),e.stroke()}let r=Na(t);for(let s of t.conveyorTiles){let l=$(...Object.values(j(s))),d=t.conveyorMeta.get(s)||{},v=jo(t,s),M=d.kind==="boostConveyor"?"#fff2b8":f.white;e.fillStyle=v>.5?po(M,"#ffd6d6",Math.max(0,(v-.5)/.5)):M,e.strokeStyle=f.ink,e.lineWidth=2,te(e,l.x-8,l.y-8,16,16,!0,!0),d.kind==="boostConveyor"&&(e.save(),e.strokeStyle=f.warn,e.lineWidth=3,e.beginPath(),e.moveTo(l.x-15,l.y+13),e.lineTo(l.x-3,l.y-13),e.lineTo(l.x+2,l.y-2),e.lineTo(l.x+15,l.y-13),e.stroke(),e.restore()),st(e,l.x-14,l.y-14,28,28,{meta:d}),qe(e,t,"conveyor",s,l.x,l.y,38,38),Wa(e,l,r.get(s)||[]);let g=nr(t,s);La(e,l,d,Fa(t,s,g),g),v>.5&&er(e,l.x,l.y-15,`${Math.floor(v*100)}%`,f.danger)}e.restore()}function nr(e,t){let n=j(t);return ue.filter(o=>e.conveyorTiles.has(P(n.col+o.dc,n.row+o.dr))).map(o=>o.name)}function xs(e,t,n){let o=ue.find(l=>l.name===n);if(!o)return!1;let r=e.conveyorMeta?.get?.(P(t.col+o.dc,t.row+o.dr));return r?[...new Set([...Array.isArray(r.outDirs)?r.outDirs:[],r.dir].filter(Boolean))].includes(o.opposite):!1}function $a(e,t,n,o=nr(e,t),r="random"){if(o.length<3)return n;if(r&&r!=="random"&&o.includes(r))return[r];let s=j(t),l=new Set(o.filter(M=>xs(e,s,M))),d=o.filter(M=>!l.has(M));if(d.length)return d;let v=n.filter(M=>!l.has(M));return v.length?v:n}function Fa(e,t,n=nr(e,t)){if(n.length<3)return[];let o=j(t),r=new Set(n.filter(l=>xs(e,o,l))),s=n.filter(l=>!r.has(l));return s.length>=2?s:n}function Ba(e,t,n){let o=j(t),r=ue.filter(d=>e.conveyorTiles.has(P(o.col+d.dc,o.row+d.dr))).map(d=>d.name),s=[...new Set([...Array.isArray(n.outDirs)?n.outDirs:[],n.dir].filter(Boolean))].filter(d=>r.includes(d)),l=r;return s.length?s:l}function La(e,t,n,o,r=o){if(r.length<3||o.length<2)return;e.save();let s=o.includes(n.branchMode)?n.branchMode:"random",l=s!=="random";if(e.lineJoin="round",e.lineCap="round",e.lineWidth=2,e.fillStyle="rgba(255,255,255,.9)",e.strokeStyle=l?f.warn:f.green,te(e,t.x-17,t.y-17,34,34,!0,!0),e.strokeStyle=f.ink,e.lineWidth=2,e.fillStyle=f.ink,s!=="random"){let d=ue.find(v=>v.name===s);Qn(e,t.x,t.y,d?.angle||0,9,6)}else e.beginPath(),e.moveTo(t.x-10,t.y),e.lineTo(t.x-2,t.y),e.lineTo(t.x+8,t.y-8),e.moveTo(t.x-2,t.y),e.lineTo(t.x+8,t.y+8),e.stroke(),Qn(e,t.x+9,t.y-9,-Math.PI/4,4,3),Qn(e,t.x+9,t.y+9,Math.PI/4,4,3);e.restore()}function Oa(e,t,n){if(!n)return[];let o=j(t),r=ue.filter(g=>e.conveyorTiles.has(P(o.col+g.dc,o.row+g.dr))).map(g=>g.name),s=[...new Set([...Array.isArray(n.outDirs)?n.outDirs:[],n.dir].filter(Boolean))].filter(g=>ue.some(A=>A.name===g)),l=s.filter(g=>r.includes(g)),d=n.dir&&s.includes(n.dir)?[n.dir]:[],v=l.length?l:d.length?d:Ba(e,t,n);return $a(e,t,v,r,n.branchMode||"random").map(g=>ue.find(A=>A.name===g)).filter(Boolean)}function Na(e){let t=new Map;for(let[n,o]of e.conveyorMeta||[]){let r=Oa(e,n,o);r.length&&t.set(n,r)}return t}function Wa(e,t,n){let o=n.slice(0,4);o.length&&(e.save(),e.fillStyle=f.ink,e.globalAlpha=.86,o.forEach(r=>{Qn(e,t.x+r.dc*12,t.y+r.dr*12,r.angle,6,5)}),e.restore())}function Qn(e,t,n,o,r=11,s=8){e.save(),e.translate(t,n),e.rotate(o),e.beginPath(),e.moveTo(r,0),e.lineTo(-r*.72,-s),e.lineTo(-r*.72,s),e.closePath(),e.fill(),e.restore()}function Ga(e,t){e.save(),e.lineWidth=2,e.font="900 12px ui-monospace, monospace",e.textAlign="center";for(let n of t.scanners){let o=So(n),r=[{type:"inputA",p:Pe(n,"inputA"),label:"IN",color:f.green},{type:"left",p:Pe(n,"left"),label:To(o.left),color:go(o.left)},{type:"right",p:Pe(n,"right"),label:To(o.right),color:go(o.right)}];for(let s of r){if(!s.p)continue;let l=$(s.p.col,s.p.row),d=Zr(t,n,s.type||(s.label==="IN"?"inputA":null))||t.conveyorTiles.has(P(s.p.col,s.p.row));e.fillStyle=d?s.color:"#f4c2c2",e.strokeStyle=f.ink,te(e,l.x-18,l.y-12,36,24,!0,!0),e.fillStyle=d&&s.color===f.white?f.ink:f.white,e.fillText(s.label,l.x,l.y+4)}}e.restore()}function Ua(e,t){for(let n of t.eggFarms)Ka(e,n,t)}function Ka(e,t,n){let o=$(t.col,t.row);if(e.save(),Nt(e,Ze.eggFarm,o.x-29,o.y-29,58,58)){st(e,o.x-29,o.y-29,58,58,t);let l=1+Math.max(0,Number(t.extraEggOutlet)||0);e.fillStyle="rgba(255,255,255,.92)",e.strokeStyle=f.ink,e.lineWidth=2,te(e,o.x+9,o.y+12,30,18,!0,!0),e.fillStyle=f.ink,e.font="900 10px ui-monospace, monospace",e.textAlign="center",e.fillText(`O${l}`,o.x+24,o.y+24),qe(e,n,"eggFarm",t.id,o.x,o.y,68,68),e.restore();return}e.fillStyle=f.white,e.strokeStyle=f.ink,e.lineWidth=4,te(e,o.x-28,o.y-28,56,56,!0,!0),e.fillStyle=f.green,e.font="900 19px ui-monospace, monospace",e.textAlign="center",e.fillText("EGG",o.x,o.y-4);let r=1+Math.max(0,Number(t.extraEggOutlet)||0);e.font="900 11px ui-monospace, monospace",e.fillStyle=f.ink,e.fillText(`L${t.level} O${r}`,o.x,o.y+13),e.fillText(`${t.nextSpawn.toFixed(1)}s`,o.x,o.y+26);let s=Math.max(0,Math.min(1,t.shutterProgress||0));if(s>0){let l=56*s;e.fillStyle=f.ink,te(e,o.x-28,o.y-28,56,l,!0,!1),e.strokeStyle=f.white,e.lineWidth=2;for(let d=o.y-24;d=1&&(e.fillStyle=f.white,e.font="900 12px ui-monospace, monospace",e.fillText("SHUT",o.x,o.y+4))}st(e,o.x-28,o.y-28,56,56,t),qe(e,n,"eggFarm",t.id,o.x,o.y,68,68),e.restore()}function _a(e,t){for(let n of t.scanners)Ha(e,n,t)}function Ha(e,t,n){let o=pe(t);if(e.save(),t.kind==="manual"&&(n.manualCombo?.count||0)>=10){let v=n.manualCombo.count;e.save(),e.globalAlpha=Math.min(.55,.16+v/120),e.strokeStyle=v>=30?f.warn:f.green,e.lineWidth=Math.min(14,5+Math.floor(v/10)),e.shadowColor=e.strokeStyle,e.shadowBlur=Math.min(28,8+v),te(e,o.x-54,o.y-76,108,152,!1,!0),e.restore()}let r=t.kind==="auto"?Ze.scannerAuto:Ze.scannerManual;if(Nt(e,r,o.x-50,o.y-73,100,146)){t.kind==="auto"?st(e,o.x-50,o.y-73,100,146,t):ms(e,t,o),qe(e,n,"scanner",t.id,o.x,o.y,108,154),e.restore();return}e.fillStyle=t.kind==="auto"?"#d8ffe2":f.white,e.strokeStyle=f.ink,e.lineWidth=4,te(e,o.x-50,o.y-73,100,146,!0,!0),e.fillStyle=t.kind==="auto"?f.green:f.ink,e.font="900 19px ui-monospace, monospace",e.textAlign="center",e.fillText(`${t.kind==="auto"?"AUTO":`S${(t.slot??0)+1}`}`,o.x,o.y-48),e.font="900 11px ui-monospace, monospace";let s=t.keys?.left?.label||(t.slot===0?"A":"Left"),l=t.keys?.right?.label||(t.slot===0?"D":"Right");e.fillText(t.role===0?`${s}:M ${l}:NEXT`:`${s}:WASTE ${l}:TRUCK`,o.x,o.y-28);let d=t.queue.length;t.kind==="manual"?(ms(e,t,o),e.fillStyle=d>0?f.green:f.muted,e.font="900 10px ui-monospace, monospace",e.fillText(`Q:${d}`,o.x,o.y+54)):(e.fillStyle=d>0?f.green:f.muted,e.font="900 12px ui-monospace, monospace",e.fillText(`Q:${d} CD:${t.cooldown.toFixed(1)}`,o.x,o.y+54),st(e,o.x-50,o.y-73,100,146,t)),qe(e,n,"scanner",t.id,o.x,o.y,108,154),e.restore()}function ms(e,t,n){if(t.kind!=="manual")return;let r=(t.slot??0)===0?[{side:"left",text:"A"},{side:"right",text:"D"}]:[{side:"left",text:"\u2190"},{side:"right",text:"\u2192"}];r[0].text=t.keys?.left?.label||r[0].text,r[1].text=t.keys?.right?.label||r[1].text;let s=t.keyPressTime>0?t.keyPressSide:null,l=n.x-30,d=n.y+18;e.save(),e.lineWidth=3,e.fillStyle="#f7fff5",e.strokeStyle=f.ink,te(e,l-4,d-4,68,25,!0,!0),e.font="900 19px ui-monospace, monospace",e.textAlign="center";for(let v=0;v.8,s=t.y+Math.sin(t.bob)*(r?5:1.5);if(e.save(),n&&(e.strokeStyle=f.green,e.lineWidth=5,e.beginPath(),e.arc(t.x,s,t.radius+8,0,Math.PI*2),e.stroke()),r&&(e.strokeStyle=f.danger,e.lineWidth=3,e.beginPath(),e.arc(t.x,s,t.radius+12+Math.sin(t.bob*2)*4,0,Math.PI*2),e.stroke()),t.sex==="poop")ir(e,t.x,s,t.radius);else{let l=t.sex==="male"?Ze.chickMale:Ze.chickFemale;Nt(e,l,t.x-t.radius,s-t.radius,t.radius*2,t.radius*2)||(e.fillStyle=t.sex==="male"?f.male:f.female,e.strokeStyle=f.ink,e.lineWidth=5,e.beginPath(),e.arc(t.x,s,t.radius,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle=f.ink,e.beginPath(),e.arc(t.x-6,s-5,2.5,0,Math.PI*2),e.arc(t.x+6,s-5,2.5,0,Math.PI*2),e.fill(),e.fillStyle="#ffaa2e",e.beginPath(),e.moveTo(t.x,s+2),e.lineTo(t.x+9,s+6),e.lineTo(t.x,s+10),e.closePath(),e.fill())}e.restore()}function ec(e,t){let n=null,o=1/0;for(let[r,s]of e.componentLookup.entries()){let l=j(r),d=$(l.col,l.row),v=Math.hypot(t.x-d.x,t.y-d.y);vws(o.type,o.id)===ws(t,n))}function qe(e,t,n,o,r,s,l,d){tc(t,n,o)&&(e.save(),e.fillStyle="rgba(34,185,79,.18)",e.strokeStyle=f.green,e.lineWidth=(t.multiSelected||[]).length>1?6:4,e.setLineDash((t.multiSelected||[]).length>1?[]:[8,5]),te(e,r-l/2,s-d/2,l,d,!0,!0),e.restore())}function nc(e,t,n,o){if(t.phase==="build"&&(t.multiSelected||[]).length>1){e.save(),e.fillStyle="rgba(255,255,255,.96)",e.strokeStyle=f.ink,e.lineWidth=3,te(e,54-t.view.x,66-t.view.y,214,42,!0,!0),e.fillStyle=f.ink,e.font="900 11px ui-monospace, monospace",e.fillText(`${t.multiSelected.length} ITEMS SELECTED`,65-t.view.x,83-t.view.y),e.fillStyle=f.muted,e.font="900 11px ui-monospace, monospace",e.fillText("drag any selected item to move group",65-t.view.x,99-t.view.y),e.restore();return}}function oc(e,t,n){if(!n.cardTargetPick?.pending)return;let o=Zo(n);e.save(),e.fillStyle="rgba(0,0,0,.66)",e.beginPath();let r=n.view?.scale||1;e.rect(-(n.view?.x||0)/r,-(n.view?.y||0)/r,t.width/r,t.height/r);for(let v of o){let M=v.bounds,g=8;e.rect(M.x-g,M.y-g,M.w+g*2,M.h+g*2)}e.fill("evenodd");let s=n.cardTargetPick?.mode==="blockedCell";if(!o.length){e.fillStyle="rgba(255,255,255,.95)",e.strokeStyle=f.ink,e.lineWidth=3,te(e,(260-(n.view?.x||0))/r,(96-(n.view?.y||0))/r,420/r,42/r,!0,!0),e.fillStyle=f.danger,e.font="900 12px ui-monospace, monospace",e.textAlign="center",e.fillText(s?"NO BLOCKED CELL. PRESS ESC.":"NO VALID UPGRADE TARGET. PRESS ESC.",(470-(n.view?.x||0))/r,(122-(n.view?.y||0))/r),e.restore();return}e.fillStyle="rgba(255,255,255,.95)",e.strokeStyle=f.ink,e.lineWidth=3;let l=Math.max(1,n.cardTargetPick?.remaining||1),d=n.cardTargetPick?.mode==="autoScannerMenu"?"AUTO SCANNER ROUTE SETTINGS. CLICK EMPTY AREA TO CLOSE.":s?`CLICK BLOCKED CELL: ${l} LEFT. PRESS ESC TO CANCEL.`:"CLICK AN UPGRADEABLE MACHINE. PRESS ESC TO CANCEL.";te(e,(216-(n.view?.x||0))/r,(96-(n.view?.y||0))/r,560/r,42/r,!0,!0),e.fillStyle=f.ink,e.font="900 12px ui-monospace, monospace",e.textAlign="center",e.fillText(d,(496-(n.view?.x||0))/r,(122-(n.view?.y||0))/r),e.restore()}function rc(e,t){if(t.phase!=="build"||!t.selectionBox)return;let n=t.selectionBox,o=Math.min(n.x1,n.x2),r=Math.min(n.y1,n.y2),s=Math.abs(n.x2-n.x1),l=Math.abs(n.y2-n.y1);e.save(),e.fillStyle="rgba(34,185,79,.12)",e.strokeStyle=f.green,e.lineWidth=2,e.setLineDash([7,4]),te(e,o,r,s,l,!0,!0),e.restore()}function ic(e,t){let n=[...t.effects].sort((o,r)=>(o.priority??Me.ambient)-(r.priority??Me.ambient));for(let o of n){let r=Math.max(0,o.life/o.maxLife);e.save(),e.globalAlpha=r,o.type==="meat"?(e.fillStyle=f.green,te(e,o.x-o.size/2,o.y-o.size/2,o.size*1.5,o.size,!0,!1)):o.type==="sludge"?(e.fillStyle=f.poop,te(e,o.x-o.size/2,o.y-o.size/2,o.size*1.4,o.size,!0,!1)):o.type==="spawn"?(e.strokeStyle=f.green,e.lineWidth=3,e.beginPath(),e.arc(o.x,o.y,o.size+(1-r)*20,0,Math.PI*2),e.stroke()):o.type==="spark"?(e.fillStyle=o.color||f.danger,te(e,o.x-o.size/2,o.y-o.size/2,o.size,o.size,!0,!1)):o.type==="smoke"?(e.fillStyle="rgba(16,32,21,.24)",e.beginPath(),e.arc(o.x,o.y,o.size*(1.2-r*.2),0,Math.PI*2),e.fill()):o.type==="shockwave"?(e.strokeStyle=o.color||f.danger,e.lineWidth=4,e.beginPath(),e.arc(o.x,o.y,o.size+(1-r)*(o.maxSize||44),0,Math.PI*2),e.stroke()):o.type==="scannerPulse"?(e.strokeStyle=o.color||f.green,e.lineWidth=5,e.setLineDash([8,6]),e.beginPath(),e.arc(o.x,o.y,o.size+(1-r)*38,0,Math.PI*2),e.stroke()):o.type==="shred"?(e.fillStyle=o.color||f.ink,e.save(),e.translate(o.x,o.y),e.rotate(o.rot||0),te(e,-o.w/2,-o.h/2,o.w,o.h,!0,!1),e.restore()):o.type==="load"?(e.strokeStyle=o.color||f.green,e.lineWidth=3,e.beginPath(),e.moveTo(o.x-14,o.y),e.lineTo(o.x+14,o.y),e.moveTo(o.x,o.y-14),e.lineTo(o.x,o.y+14),e.stroke()):o.type==="erase"?(e.strokeStyle=f.ink,e.lineWidth=3,e.setLineDash([4,4]),te(e,o.x-o.size/2,o.y-o.size/2,o.size,o.size,!1,!0)):o.type==="rage"?(e.fillStyle=o.color||f.danger,e.font="900 16px ui-monospace, monospace",e.textAlign="center",e.fillText("!",o.x,o.y)):o.type==="flyingChick"&&(o.sex==="poop"?ir(e,o.x,o.y,o.radius||11):sr(e,o.x,o.y,o.sex,o.sex==="male")),e.restore()}}function sc(e,t){e.save(),e.font="900 19px ui-monospace, monospace",e.textAlign="center";for(let n of t.floatingTexts){let o=Math.max(0,n.life/n.maxLife);e.globalAlpha=o,e.lineWidth=5,e.strokeStyle=f.white,e.fillStyle=n.color,e.strokeText(n.text,n.x,n.y),e.fillText(n.text,n.x,n.y)}e.restore()}function lc(e,t){let o=(Array.isArray(t.repairmen)&&t.repairmen.length?t.repairmen:t.repairman?[t.repairman]:[]).filter(s=>s?.active||t.phase==="build"&&!!s?.hiredForNextDay);if(!o.length)return;let r=!!t.cardEffects?.laborExploitation;for(let s of o)e.save(),e.lineWidth=3,e.fillStyle="#fff0d1",e.strokeStyle=f.ink,e.beginPath(),e.arc(s.x,s.y,17,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle="#ffd6e7",e.beginPath(),e.arc(s.x-6,s.y-2,3.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(s.x+6,s.y-2,3.2,0,Math.PI*2),e.fill(),e.fillStyle=r?f.danger:f.ink,e.beginPath(),e.arc(s.x-5,s.y-5,r?2.8:2.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(s.x+5,s.y-5,r?2.8:2.2,0,Math.PI*2),e.fill(),e.strokeStyle=f.ink,e.lineWidth=r?3:2,e.beginPath(),e.arc(s.x,s.y+(r?0:1),r?8:7,.1*Math.PI,.9*Math.PI),e.stroke(),e.fillStyle=f.green,te(e,s.x-9,s.y+14,18,8,!0,!0),e.strokeStyle=f.ink,e.lineWidth=3,e.beginPath(),e.moveTo(s.x-11,s.y+18),e.lineTo(s.x-20,s.y+24),e.stroke(),e.beginPath(),e.moveTo(s.x+11,s.y+18),e.lineTo(s.x+20,s.y+24),e.stroke(),e.fillStyle=f.ink,e.font="900 11px ui-monospace, monospace",e.textAlign="center",e.fillText(s.active?"FIX":"HIRED",s.x,s.y+33),s.target?.label?er(e,s.x,s.y-24,s.target.label,f.green):!s.active&&s.hiredForNextDay&&er(e,s.x,s.y-24,"NEXT DAY",f.green),e.restore()}function ac(e,t,n){e.save(),e.font="900 19px ui-monospace, monospace",e.textAlign="left",e.fillStyle=f.ink,e.fillStyle="rgba(255,255,255,.92)",e.strokeStyle=f.ink,e.lineWidth=3,te(e,n.width-284,18,260,34,!0,!0),e.fillStyle=f.green,e.font="900 12px ui-monospace, monospace",e.fillText(Tn,n.width-270,40),e.font="900 19px ui-monospace, monospace",e.fillStyle=f.ink;let o=[];t.phase==="running"&&o.push(t.timeLeft<=0?"TIME UP: FARMS SHUT. CLEAR LINE.":"A/D: S1 \u2190/\u2192: S2"),t.phase==="build"&&o.push(`BUILD: ${t.buildTool||"SELECT"} | connect 1 EGG route to any exit`);let r=76;for(let s of o)e.strokeStyle=f.white,e.lineWidth=4,e.strokeText(s,24,r),e.fillText(s,24,r),r+=20;e.restore()}var Ss=[[{key:"a",code:"KeyA",label:"A"},{key:"d",code:"KeyD",label:"D"}],[{key:"arrowleft",code:"ArrowLeft",label:"Left"},{key:"arrowright",code:"ArrowRight",label:"Right"}],[{key:"j",code:"KeyJ",label:"J"},{key:"l",code:"KeyL",label:"L"}],[{key:"f",code:"KeyF",label:"F"},{key:"h",code:"KeyH",label:"H"}],[{key:"q",code:"KeyQ",label:"Q"},{key:"e",code:"KeyE",label:"E"}],[{key:"z",code:"KeyZ",label:"Z"},{key:"x",code:"KeyX",label:"X"}],[{key:"u",code:"KeyU",label:"U"},{key:"o",code:"KeyO",label:"O"}],[{key:"n",code:"KeyN",label:"N"},{key:"m",code:"KeyM",label:"M"}]];function cc(e){let t=Ss[e]||Ss[0];return{left:{...t[0]},right:{...t[1]}}}function Ms(e,t,n){let o={type:"eggFarm",id:e.nextId++,col:t,row:n,level:1,nextSpawn:2,lastInterval:2,extraEggOutlet:0,price:gt.eggFarm,builtSession:e.buildSession};return o.nextSpawn=At(o),o.lastInterval=o.nextSpawn,o}function Ts(e,t,n,o){let r=o==="auto"?ve.autoScanner.price:ve.manualScanner.price,s=new Set(e.scanners.filter(d=>d.kind==="manual").map(d=>d.slot)),l=0;for(;s.has(l);)l+=1;return{type:"scanner",id:e.nextId++,kind:o,slot:o==="manual"?l:null,role:o==="manual"?l%2:0,col:t,row:n,level:1,queue:[],cooldown:0,price:r,builtSession:e.buildSession,autoMode:"standard",keys:o==="manual"?cc(l):null}}function gs(e,t,n,o){let r=ve.maintenanceRoom||{},s=r.footprint?.cols||2,l=r.footprint?.rows||2;return{type:"facility",id:"maintenanceRoom",baseId:"maintenanceRoom",name:r.name||"Maintenance Room",shortName:r.shortName||"MAINT ROOM",col:t,row:n,cols:s,rows:l,x:y.x+t*y.cell,y:y.y+n*y.cell,w:s*y.cell,h:l*y.cell,level:1,price:o||r.price||400,builtSession:e.buildSession}}function Jn(e){let t=(e.x-y.x)/y.cell,n=(e.y-y.y)/y.cell,o=Math.max(0,Math.min(y.cols-1,Math.round(t-.5))),r=Math.max(0,Math.min(y.rows-1,Math.round(n-.5))),s=Math.abs(e.x-y.x),l=Math.abs(e.x-(y.x+y.cols*y.cell)),d=Math.abs(e.y-y.y),v=Math.abs(e.y-(y.y+y.rows*y.cell)),M=[["left",s],["right",l],["top",d],["bottom",v]].sort((g,A)=>g[1]-A[1])[0][0];return M==="left"?{side:M,entry:{col:0,row:r}}:M==="right"?{side:M,entry:{col:y.cols-1,row:r}}:M==="top"?{side:M,entry:{col:o,row:0}}:{side:M,entry:{col:o,row:y.rows-1}}}function Zn(e,t,n){let o={x:y.x+t.col*y.cell+y.cell/2,y:y.y+t.row*y.cell+y.cell/2},r={left:y.x,right:y.x+y.cols*y.cell,top:y.y,bottom:y.y+y.rows*y.cell};return e.entry={...t},e.side=n,n==="left"?(e.x=r.left-e.w-18,e.y=o.y-e.h/2):n==="right"?(e.x=r.right+18,e.y=o.y-e.h/2):n==="top"?(e.x=o.x-e.w/2,e.y=r.top-e.h-18):(e.x=o.x-e.w/2,e.y=r.bottom+18),e}function Es(e,t,n,o){let{entry:r,side:s}=Jn(n),l=ve[t],d=l?{name:l.name,w:l.body?.w||168,h:l.body?.h||110,price:l.price}:{name:t[0].toUpperCase()+t.slice(1),w:168,h:110,price:o},v={type:"facility",id:t,name:d.name,x:0,y:0,w:d.w,h:d.h,level:1,price:o||d.price,builtSession:e.buildSession};return Zn(v,r,s)}function uc(e){return Math.random()({x:s.x,y:s.y})),r=o[0];return{id:e.nextId++,sex:n,x:r.x,y:r.y,route:o,targetIndex:o.length>1?1:o.length,stage:t.stage||"belt",scannerId:t.scannerId??null,nextScannerId:null,pendingScannerId:null,radius:n==="poop"?15:17,bob:Math.random()*Math.PI*2,queueIndex:-1,queueRoute:null}}function Ps({game:e,canvasPoint:t,updatePanels:n,equipmentAtPoint:o,fail:r,pointInGrid:s,rectOfFacility:l,rectsOverlap:d}){function v(x){return x?x.type==="conveyor"?`conveyor:${x.id||x.oldKey}`:x.type==="facility"?`facility:${x.id||x.ref?.id}`:`${x.type}:${x.id||x.ref?.id}`:""}function M(x){return x?.baseId||x?.id||""}function g(x){return M(x)==="maintenanceRoom"}function A(x,C=x?.col,N=x?.row){let U=[],Y=x?.cols||2,ie=x?.rows||2;for(let ae=0;aev(N)===C)}function q(x){return x?x.type==="eggFarm"?e.eggFarms.find(C=>C.id===x.id)||null:x.type==="scanner"?e.scanners.find(C=>C.id===x.id)||null:x.type==="facility"?e.facilities[x.id]||null:x.type==="conveyor"&&e.conveyorTiles.has(x.id)?{type:"conveyor",id:x.id}:null:null}function le(){let x=[];for(let C of e.conveyorTiles){let N=j(C),U=$(N.col,N.row);x.push({type:"conveyor",id:C,center:U,cell:N})}for(let C of e.eggFarms)x.push({type:"eggFarm",id:C.id,ref:C,center:$(C.col,C.row),cell:{col:C.col,row:C.row}});for(let C of e.scanners)x.push({type:"scanner",id:C.id,ref:C,center:pe(C),cell:{col:C.col,row:C.row}});for(let C of Object.values(e.facilities)){let N={x:C.x+C.w/2,y:C.y+C.h/2},U=C.entry?$(C.entry.col,C.entry.row):N;x.push({type:"facility",id:C.id,ref:C,center:N,portCenter:U})}return x}function me(x,C){let N=Math.min(C.x1,C.x2),U=Math.max(C.x1,C.x2),Y=Math.min(C.y1,C.y2),ie=Math.max(C.y1,C.y2);return x.x>=N&&x.x<=U&&x.y>=Y&&x.y<=ie}function ne(x){let C=[];for(let N of le())(me(N.center,x)||N.portCenter&&me(N.portCenter,x))&&C.push({type:N.type,id:N.id});return e.multiSelected=C,e.selected=C[0]||null,n(),C.length}function I(){e.multiSelected=[],e.selected=null,n()}function _(x){let C=t(x);return e.selectionBox={x1:C.x,y1:C.y,x2:C.x,y2:C.y},e.multiSelected=[],e.selected=null,n(),!0}function W(x){if(!e.selectionBox)return;let C=t(x);e.selectionBox.x2=C.x,e.selectionBox.y2=C.y}function fe(){if(!e.selectionBox)return 0;let x=e.selectionBox,C=Math.abs(x.x2-x.x1),N=Math.abs(x.y2-x.y1);return e.selectionBox=null,C<6&&N<6?(I(),0):ne(x)}function Ce(x){let C=V(x);return C?(e.multiSelected||[]).length&&ee(x)?[...e.multiSelected]:[C]:[]}function ye(x){let C=q(x);if(!C)return null;if(x.type==="conveyor"){let N=j(x.id);return{...x,obj:C,oldKey:x.id,currentKey:x.id,meta:e.conveyorMeta.get(x.id)||{price:Oe("conveyor"),builtSession:null},col:N.col,row:N.row}}return x.type==="eggFarm"||x.type==="scanner"?{...x,obj:C,col:C.col,row:C.row}:x.type==="facility"?g(C)?{...x,obj:C,col:C.col,row:C.row,cols:C.cols||2,rows:C.rows||2,maintenanceRoom:!0,center:{x:C.x+C.w/2,y:C.y+C.h/2}}:{...x,obj:C,x:C.x,y:C.y,w:C.w,h:C.h,entry:C.entry?{...C.entry}:null,side:C.side,center:{x:C.x+C.w/2,y:C.y+C.h/2}}:null}function he(x){let C=t(x),N=o(C);if(!N)return!1;let U=Ce(N),Y=U.map(ye).filter(Boolean);return Y.length?(e.groupDrag={start:C,selections:U,origins:Y,historySnapshot:yn(e),committed:!1,lastDCol:0,lastDRow:0},e.multiSelected=U,e.selected=U[0]||null,n(),!0):!1}function ge(){!e.groupDrag||e.groupDrag.committed||(e.undoStack.push(e.groupDrag.historySnapshot),e.redoStack=[],e.groupDrag.committed=!0)}function De(x,C,N){let U=P(x,C),Y=ut(e,x,C);if(Y&&!N.has(`eggFarm:${Y.id}`))return!0;let ie=dt(e,x,C);if(ie&&!N.has(`scanner:${ie.id}`)||e.conveyorTiles.has(U)&&!N.has(`conveyor:${U}`))return!0;for(let ae of Object.values(e.facilities||{}))if(!(!g(ae)||N.has(`facility:${ae.id}`))&&A(ae).some(we=>we.col===x&&we.row===C))return!0;return!1}function Ct(x,C,N){let U=[];for(let Y of e.groupDrag.origins){if(Y.type!=="facility"||Y.maintenanceRoom)continue;let ie={x:Y.center.x+x,y:Y.center.y+C},{entry:ae,side:we}=Jn(ie);if(!s(ae.col,ae.row)||e.blockedCells?.has?.(P(ae.col,ae.row)))return!0;let Ee={...Y.obj,entry:{...ae},side:we};Zn(Ee,ae,we),U.push({id:Y.id,rect:l(Ee)})}for(let Y of U)for(let ie of Object.values(e.facilities))if(!N.has(`facility:${ie.id}`)&&d(Y.rect,l(ie),12))return!0;for(let Y=0;YYe.entry&&!ae.has(`facility:${Ye.id}`)&&Ye.entry.col===xe.col&&Ye.entry.row===xe.row))return r("Cell occupied");we.add(St)}}if(Ct(N,U,ae))return r("Facility overlap");ge();let Ee=e.groupDrag.origins.filter(B=>B.type==="conveyor");for(let B of Ee){let Re=B.currentKey||B.oldKey;e.conveyorTiles.delete(Re),e.conveyorMeta.delete(Re)}for(let B of Ee){let Re=P(B.col+Y,B.row+ie);e.conveyorTiles.add(Re),e.conveyorMeta.set(Re,B.meta||{price:Oe("conveyor"),builtSession:null,uses:0}),B.currentKey=Re}for(let B of e.groupDrag.origins)if(B.type==="eggFarm")B.obj.col=B.col+Y,B.obj.row=B.row+ie;else if(B.type==="scanner")B.obj.col=B.col+Y,B.obj.row=B.row+ie;else if(B.type==="facility")if(B.maintenanceRoom)B.obj.col=B.col+Y,B.obj.row=B.row+ie,L(B.obj);else{let Re={x:B.center.x+N,y:B.center.y+U},{entry:ze,side:je}=Jn(Re);Zn(B.obj,ze,je)}e.groupDrag.selections=e.groupDrag.origins.map(B=>B.type==="conveyor"?{type:"conveyor",id:B.currentKey||B.oldKey}:{type:B.type,id:B.id}),e.multiSelected=[...e.groupDrag.selections],e.selected=e.multiSelected[0]||null,be(e),n()}function G(){e.groupDrag&&(be(e),e.groupDrag=null,n())}return{selectionFromHit:V,startSelectionBox:_,updateSelectionBox:W,finishSelectionBox:fe,startGroupDrag:he,updateGroupDrag:R,finishGroupDrag:G}}function As({game:e,ui:t,canvas:n,canvasPoint:o,onUpdatePanels:r}){function s(){r&&r()}let l=null,d=!1,v=null,M=!1,g=["random","up","right","down","left"],A={random:"RND",up:"UP",right:"RT",down:"DN",left:"LF"},L={w:40,h:40,y:0};function V(){return Dn(e)}function ee(){e.gridCols=y.cols,e.gridRows=y.rows,e.gridOwnedCells=Nr(e)}function q(i){if(i){y.y-=i*y.cell,y.rows+=i,e.gridChunkOriginY=(e.gridChunkOriginY||0)-Math.floor(i/10),e.ownedCells=In(e.ownedCells,i),e.blockedCells=In(e.blockedCells,i),e.conveyorTiles=In(e.conveyorTiles,i),e.conveyorMeta=Kr(e.conveyorMeta,i);for(let c of e.eggFarms||[])c.row+=i;for(let c of e.scanners||[])c.row+=i;for(let c of Object.values(e.facilities||{}))ge(c)&&(c.row+=i,De(c)),c.entry&&(c.entry.row+=i)}}function le(i){e.ownedCells=e.ownedCells||new Set;for(let c of Ur(i))e.ownedCells.add(P(c.col,c.row))}function me(i){return e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending?null:Rt(e).find(c=>c&&Gr(c,i))||null}function ne(i="right"){if(i&&typeof i=="object"&&Number.isFinite(i.chunkX)&&Number.isFinite(i.chunkY))return i;let c=["right","up"].includes(i)?i:"right";return Wr(e,c)}function I(i="right"){let c=ne(i);if(e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending)return{ok:!1,reason:"Cannot expand now."};if(!c)return{ok:!1,reason:"Invalid expansion lot."};let h=c.direction||"right",m=V();if(e.cashy.cols&&(y.cols=E.colStart+E.cols),E.rowStart+E.rows>y.rows&&(y.rows=E.rowStart+E.rows),le(E),_r(e,E.chunkX,E.chunkY),e.gridExpansionPurchases=(e.gridExpansionPurchases||0)+1,e.gridExpansionPurchasesByDirection[h]=(e.gridExpansionPurchasesByDirection[h]||0)+1,ee();let z=$r({seed:`${e.runSeed}:grid-expansion:${E.chunkX},${E.chunkY}:${e.gridExpansionPurchases}`,colStart:E.colStart,colEnd:E.colStart+E.cols,rowStart:E.rowStart,rowEnd:E.rowStart+E.rows,targetRatio:Z.map.blockedRatio});for(let Ie of z)e.blockedCells.add(Ie);be(e);let oe=E.cols*E.rows,de=Pt(E);return K(e,de.x+de.w/2,de.y+de.h/2,`${h.toUpperCase()} +${oe} CELLS -${T(m)}`,f.green),s(),{ok:!0,direction:h,cost:m,addedCells:oe,blocked:z.size,chunkX:E.chunkX,chunkY:E.chunkY}}function _(i,c){return i>=0&&i=0&&c!c.has(m.id)&&Ce(h,fe(m),12))}function he(i){return i?.baseId||i?.id||""}function ge(i){return he(i)==="maintenanceRoom"}function De(i){i&&(i.cols=i.cols||2,i.rows=i.rows||2,i.x=y.x+i.col*y.cell,i.y=y.y+i.row*y.cell,i.w=i.cols*y.cell,i.h=i.rows*y.cell)}function Ct(i,c,h=2,m=2){let b=[];for(let E=0;E{if(!ge(m)||h.has(m.id))return!1;let b=m.cols||2,E=m.rows||2;return i>=m.col&&i=m.row&&cm.entry&&!h.has(m.id)&&m.entry.col===i&&m.entry.row===c)||null}function x(i,c,h=null){if(!_(i,c))return!1;if(W(i,c))return!0;let m=P(i,c),b=ut(e,i,c);if(b&&!(h?.type==="eggFarm"&&h.ref?.id===b.id))return!0;let E=dt(e,i,c);if(E&&!(h?.type==="scanner"&&h.ref?.id===E.id)||e.conveyorTiles.has(m)&&!(h?.type==="conveyor"&&h.oldKey===m))return!0;let z=h?.type==="facility"&&h.ref?.id?new Set([h.ref.id]):new Set;return!!R(i,c,z)}function C(i,c,h=null){let m={col:i,row:c};for(let b of Dt(m)){if(!_(b.col,b.row)||W(b.col,b.row))return!0;let E=P(b.col,b.row);if(ut(e,b.col,b.row))return!0;let oe=dt(e,b.col,b.row);if(oe&&!(h?.type==="scanner"&&h.ref?.id===oe.id)||e.conveyorTiles.has(E)||R(b.col,b.row)||G(b.col,b.row))return!0}return!1}function N(){return e.selected?e.selected.type==="eggFarm"?e.eggFarms.find(i=>i.id===e.selected.id)||null:e.selected.type==="scanner"?e.scanners.find(i=>i.id===e.selected.id)||null:e.selected.type==="conveyor"?e.conveyorTiles.has(e.selected.id)?{type:"conveyor",id:e.selected.id}:null:e.selected.type==="facility"&&e.facilities[e.selected.id]||null:null}function U(i){return i?i.type==="eggFarm"?`${Et("eggFarm")} #${i.id}`:i.type==="scanner"?`${i.kind==="auto"?Et("autoScanner"):Et("manualScanner")} #${i.id}`:i.type==="conveyor"?e.conveyorMeta.get(i.id)?.kind==="boostConveyor"?Et("boostConveyor"):Et("conveyor"):i.type==="facility"?i.name:"Equipment":"Equipment"}function Y(i){return i?.type==="conveyor"?e.conveyorMeta?.get(i.oldKey||i.ref?.id)?.price||Nn(i):Nn(i)}function ie(){return!!e.cardEffects?.usedMachineActive}function ae(){return ie()?{usedMachine:!0,noRefund:!0,durabilityBaseMultiplier:.6}:{usedMachine:!1,noRefund:!1,durabilityBaseMultiplier:1}}function we(i){let c=ae();return Object.assign(i,c),i}function Ee(i){let c=i?.ref||i||{};if(i?.type==="eggFarm"||c.type==="eggFarm")return e.eggFarms.length<=1?"Cannot sell the last EGG FARM.":"";if((i?.type==="facility"||c.type==="facility")&&["mixer","trash","truck"].includes(c.baseId||c.id)){let h=c.baseId||c.id;if(Object.values(e.facilities).filter(b=>(b.baseId||b.id)===h).length<=1)return`Cannot sell the last ${c.shortName||c.name||c.id}.`}return""}function B(i){t.buildStatus.textContent=i,K(e,n.width/2-e.view.x,86-e.view.y,i.toUpperCase(),f.danger)}function Re(i){if(!i)return null;let c=P(i.col,i.row);return e.conveyorMeta.get(c)||null}function ze(i){return i?(Array.isArray(i.outDirs)||(i.outDirs=[]),i.outDirs=[...new Set(i.outDirs.filter(c=>["right","left","down","up"].includes(c)))],g.includes(i.branchMode)||(i.branchMode="random"),i.kind||(i.kind="conveyor"),i.speedMultiplier||(i.speedMultiplier=i.kind==="boostConveyor"?2:1),i.outDirs):[]}function je(i){return i?ue.filter(c=>e.conveyorTiles.has(P(i.col+c.dc,i.row+c.dr))).map(c=>c.name):[]}function xe(i){return je(i).length>=3}function St(i,c){let h=ue.find(oe=>oe.name===c);if(!i||!h)return!1;let m=P(i.col+h.dc,i.row+h.dr),b=e.conveyorMeta.get(m);if(!b)return!1;let E=h.opposite;return[...new Set([...Array.isArray(b.outDirs)?b.outDirs:[],b.dir].filter(Boolean))].includes(E)}function Ye(i){if(!e.conveyorTiles.has(i))return[];let c=j(i),h=e.conveyorMeta.get(i),m=ze(h),b=je(c),E=[...new Set([...m,h?.dir].filter(Boolean))].filter(de=>b.includes(de)),z=b,oe=E.length?E:z;return h&&(h.outDirs=m.filter(de=>b.includes(de)),h.dir&&!b.includes(h.dir)&&(h.dir=h.outDirs[0]||null),h.branchMode!=="random"&&!b.includes(h.branchMode)&&(h.branchMode="random")),oe}function vn(i){if(!e.conveyorTiles.has(i))return[];let c=j(i),h=je(c);return xe(c)||h.length?h:Ye(i)}function oo(i){let c=j(i),h=vn(i);if(h.length<3)return[];let m=new Set(h.filter(E=>St(c,E))),b=h.filter(E=>!m.has(E));return b.length>=2?b:h}function kn(i,c){let h=tt(i,c);if(!h)return!1;let m=P(i.col,i.row),b=P(c.col,c.row),E=e.conveyorMeta.get(m),z=e.conveyorMeta.get(b);if(!E||!z)return!1;let oe=ze(E),de=!oe.includes(h)||E.dir!==h||!z.dir;return oe.includes(h)||oe.push(h),E.dir=h,z.dir||(z.dir=h),de}function Mt(i){if(Gt(i).length<=1)return Cn(i);let h=tn(i);return h.length<2?Cn(i):(Be(e),nn(h),be(e),s(),!0)}function Cn(i){if(!i)return!1;let c=P(i.col,i.row),h=e.conveyorMeta.get(c);if(!h||xe(i))return!1;let m=ue.map(z=>z.name),b=h.dir&&m.includes(h.dir)?h.dir:null,E=m[(Math.max(-1,m.indexOf(b))+1)%m.length];return Be(e),h.dir=E,h.outDirs=[E],h.branchMode="random",be(e),s(),!0}function Gt(i){return i?ue.map(c=>({col:i.col+c.dc,row:i.row+c.dr})).filter(c=>e.conveyorTiles.has(P(c.col,c.row))):[]}function tn(i){if(!i||!e.conveyorTiles.has(P(i.col,i.row))||xe(i))return[];let c=Gt(i);if(!c.length||c.length>2)return[];let h=E=>{let z=[],oe=new Set([P(i.col,i.row)]),de=i,Ie=E,mr=0;for(;Ie&&mr<200;){mr+=1;let wr=P(Ie.col,Ie.row);if(oe.has(wr)||(oe.add(wr),z.push(Ie),xe(Ie)))break;let br=Gt(Ie).filter(xr=>!(xr.col===de.col&&xr.row===de.row));if(br.length!==1)break;de=Ie,Ie=br[0]}return z},m=c[0]?h(c[0]).reverse():[],b=c[1]?h(c[1]):[];return[...m,i,...b]}function Sn(i,c){return!!i&&[...new Set([...Array.isArray(i.outDirs)?i.outDirs:[],i.dir].filter(Boolean))].includes(c)}function nn(i){let c=0,h=0;for(let b=0;bh?[...i].reverse():i;for(let b=0;b=2){let b=m[m.length-1],E=m[m.length-2],z=e.conveyorMeta.get(P(b.col,b.row)),oe=tt(E,b);z&&oe&&!xe(b)&&(z.dir=oe,z.outDirs=[oe],z.branchMode="random")}}function ro(){return Z.facilities[e.buildTool==="boostConveyor"?"boostConveyor":"conveyor"]}function u(i,c=null){if(!i)return{ok:!1,reason:re.fail.outOfGrid};let{col:h,row:m}=i,b=P(h,m);if(e.conveyorTiles.has(b))return{ok:!1,exists:!0};if(W(h,m))return{ok:!1,reason:"Cannot build on blocked ground."};if(ut(e,h,m)||dt(e,h,m)||R(h,m))return{ok:!1,reason:re.fail.cellOccupied};let E=ro(),z=E.id||"conveyor",oe=Oe(z,e);if(e.cash=Math.abs(m)?{col:i.col+Math.sign(h),row:i.row}:{col:i.col,row:i.row+Math.sign(m)}}function S(i){d=!1,v=i?{col:i.col,row:i.row}:null,M=!1,l=null,p(i)}function k(i){if(!i)return;if(!l)return p(i);let c=l,h=0;for(;(c.col!==i.col||c.row!==i.row)&&h<64;){let m=w(c,i);if(p(m),c=l||m,h+=1,!l)break}}function D(){!M&&v&&e.conveyorTiles.has(P(v.col,v.row))&&Mt(v),v=null,M=!1,l=null,d=!1}function F(i,c,h=null){let m=h?.type==="facility"&&h.ref?.id?new Set([h.ref.id]):new Set;for(let b of Ct(i,c))if(!_(b.col,b.row)||W(b.col,b.row)||ut(e,b.col,b.row)||dt(e,b.col,b.row)||e.conveyorTiles.has(P(b.col,b.row))||R(b.col,b.row,m)||G(b.col,b.row,m))return!0;return!1}function J(i,c){if(F(i,c))return B(re.fail.cellOccupied);let h=Oe("maintenanceRoom",e);if(e.cashE.kind==="manual").length>=8)return B("Manual Scanner limit reached (8 max).");let m=h==="auto"?Oe("autoScanner",e):Oe("manualScanner",e);if(e.cashb.id!==c.ref.id),mt(e,m.x,m.y),be(e)}if(c.type==="scanner"){let m=pe(c.ref);Mn(c,m.x,m.y),e.scanners=e.scanners.filter(b=>b.id!==c.ref.id),mt(e,m.x,m.y),be(e)}if(c.type==="facility"){let m=c.ref;Mn(c,m.x+m.w/2,m.y+m.h/2),delete e.facilities[m.id],mt(e,m.x+m.w/2,m.y+m.h/2),be(e)}e.selected=null,e.multiSelected=[],s()}function io(i){let c=Object.values(e.facilities).find(z=>{if(i.x>=z.x&&i.x<=z.x+z.w&&i.y>=z.y&&i.y<=z.y+z.h)return!0;if(!z.entry)return!1;let de=$(z.entry.col,z.entry.row);return Math.abs(i.x-de.x)<=y.cell*.46&&Math.abs(i.y-de.y)<=y.cell*.46});if(c)return{type:"facility",ref:c};let h=Ge(i.x,i.y);if(!h)return null;let m=ut(e,h.col,h.row);if(m)return{type:"eggFarm",ref:m};let b=dt(e,h.col,h.row);if(b)return{type:"scanner",ref:b};let E=P(h.col,h.row);return e.conveyorTiles.has(E)?{type:"conveyor",oldKey:E,ref:{type:"conveyor",id:E}}:null}let Ut=Ps({game:e,canvasPoint:o,updatePanels:s,equipmentAtPoint:io,fail:B,pointInGrid:_,rectOfFacility:fe,rectsOverlap:Ce});function Qs(i){return null}function cr(){let i=N();i&&(i.type==="conveyor"?on($(...Object.values(j(i.id)))):i.type==="eggFarm"?on($(i.col,i.row)):i.type==="scanner"?on(pe(i)):i.type==="facility"&&on({x:i.x+i.w/2,y:i.y+i.h/2}))}function Xe(i,c,h="primary-button"){let m=document.createElement("button");return m.type="button",m.className=h,m.textContent=i,m.addEventListener("click",c),m}function rn(){e.cardTargetPick?.mode==="autoScannerMenu"&&(e.cardTargetPick=null),t.modal.classList.remove("visible","equipment-popover"),t.modal.style.removeProperty("--popover-x"),t.modal.style.removeProperty("--popover-y")}function so(i){t.modalTitle.textContent="Auto Scanner",e.cardTargetPick={pending:!0,mode:"autoScannerMenu",cardId:null,targetKeys:[`scanner:${i.id}`],remaining:1},t.modalBody.innerHTML=`

Left / right output routing for this AUTO scanner.

Current: ${i.role===0?"Male left / others right":"Poop left / others right"}

-

Cooldown: ${Qe(i).toFixed(1)}s.

-
`,t.modalActions.innerHTML="";let c=R("Male Left / Others Right",()=>{Oe(e),i.role=0,V(),s()},i.role===0?"facility-action warn":"facility-action"),h=R("Poop Left / Others Right",()=>{Oe(e),i.role=1,V(),s()},i.role===1?"facility-action warn":"facility-action");t.modalActions.append(c,h,R("Close",V,"facility-action")),Jn(i),t.modal.classList.add("visible","equipment-popover"),s()}function ge(i){return i?.label||i?.key||"?"}function fn(i){let c=i.key===" "?"Space":i.key.length===1?i.key.toUpperCase():i.key.replace(/^Arrow/,"");return{key:i.key.toLowerCase(),code:i.code,label:c}}function Qn(i,c,h){for(let m of e.scanners.filter(T=>T.kind==="manual"))for(let T of["left","right"]){if(m.id===i.id&&T===c)continue;let A=m.keys?.[T];if(A&&(A.code===h.code||A.key===h.key))return!0}return!1}function Qt(i,c=""){if(!i||i.kind!=="manual")return;t.modalTitle.textContent=`Manual Scanner #${i.id}`;let h=ge(i.keys?.left),m=ge(i.keys?.right),T=Ss(i);t.modalBody.innerHTML=` +

Cooldown: ${it(i).toFixed(1)}s.

+ `,t.modalActions.innerHTML="";let c=Xe("Male Left / Others Right",()=>{Be(e),i.role=0,rn(),s()},i.role===0?"facility-action warn":"facility-action"),h=Xe("Poop Left / Others Right",()=>{Be(e),i.role=1,rn(),s()},i.role===1?"facility-action warn":"facility-action");t.modalActions.append(c,h,Xe("Close",rn,"facility-action")),lo(i),t.modal.classList.add("visible","equipment-popover"),s()}function ur(i){return i?.label||i?.key||"?"}function Js(i){let c=i.key===" "?"Space":i.key.length===1?i.key.toUpperCase():i.key.replace(/^Arrow/,"");return{key:i.key.toLowerCase(),code:i.code,label:c}}function Zs(i,c,h){for(let m of e.scanners.filter(b=>b.kind==="manual"))for(let b of["left","right"]){if(m.id===i.id&&b===c)continue;let E=m.keys?.[b];if(E&&(E.code===h.code||E.key===h.key))return!0}return!1}function sn(i,c=""){if(!i||i.kind!=="manual")return;t.modalTitle.textContent=`Manual Scanner #${i.id}`;let h=ur(i.keys?.left),m=ur(i.keys?.right),b=js(i);t.modalBody.innerHTML=`

Left route key: ${h}

Right route key: ${m}

${c?`

${c}

`:""} - ${T?`

${T}

`:""} -
`,t.modalActions.innerHTML="",t.modalActions.appendChild(R("Set Left Key",()=>Jo(i,"left"),"facility-action warn")),t.modalActions.appendChild(R("Set Right Key",()=>Jo(i,"right"),"facility-action warn")),t.modalActions.appendChild(R("Close",V,"facility-action")),Jn(i),t.modal.classList.add("visible","equipment-popover")}function Ss(i){let c=[{side:"left",binding:i.keys?.left},{side:"right",binding:i.keys?.right}];if(c[0].binding&&c[1].binding&&(c[0].binding.code===c[1].binding.code||c[0].binding.key===c[1].binding.key))return"Left and right keys conflict.";for(let h of c)if(h.binding)for(let m of e.scanners.filter(T=>T.kind==="manual"&&T.id!==i.id)){if(m.keys?.left&&(m.keys.left.code===h.binding.code||m.keys.left.key===h.binding.key))return`Key conflicts with Manual Scanner #${m.id} left.`;if(m.keys?.right&&(m.keys.right.code===h.binding.code||m.keys.right.key===h.binding.key))return`Key conflicts with Manual Scanner #${m.id} right.`}return""}function Jo(i,c){t.modalBody.innerHTML=`

Press a key for ${c.toUpperCase()} route.

Esc cancels.

`,t.modalActions.innerHTML="";let h=m=>{if(m.preventDefault(),m.stopPropagation(),window.removeEventListener("keydown",h,!0),m.key==="Escape")return Qt(i);let T=fn(m),A=Qn(i,c,T);Oe(e),i.keys=i.keys||{left:null,right:null},i.keys[c]=T,s(),Qt(i,A?`${T.label} assigned, but another scanner already uses it.`:`${c.toUpperCase()} set to ${T.label}.`)};window.addEventListener("keydown",h,!0)}function Ms(){let i=w();if(!(!i||i.type!=="scanner"))return i.kind==="auto"?Z(i):Qt(i)}function Ts(i){if(!i)return{x:80,y:80};if(i.type==="conveyor"){let c=J(i.id);return $(c.col,c.row)}return i.type==="eggFarm"?$(i.col,i.row):i.type==="scanner"?de(i):i.type==="facility"?{x:i.x+i.w/2,y:i.y+10}:{x:80,y:80}}function Jn(i){let c=n.getBoundingClientRect(),h=Ts(i),m=e.view?.scale||1,T=c.left+(h.x*m+(e.view?.x||0))/n.width*c.width+18,A=c.top+(h.y*m+(e.view?.y||0))/n.height*c.height-12,ne=Math.max(12,window.innerWidth-274),ue=Math.max(12,window.innerHeight-220);t.modal.style.setProperty("--popover-x",`${Math.max(12,Math.min(ne,T))}px`),t.modal.style.setProperty("--popover-y",`${Math.max(12,Math.min(ue,A))}px`)}function Zo(){let i=w();if(!i)return;let c=er(i);t.modalTitle.textContent=C(i),t.modalBody.innerHTML=` -
${c.map(T=>`

${T}

`).join("")}
- ${["mixer","truck"].includes(i.baseId||i.id)?'

Income/Fine = ceil(base x 1.10^upgrades)

':""}`,t.modalActions.innerHTML="",i.type==="scanner"&&i.kind==="auto"&&t.modalActions.appendChild(R("Auto Menu",()=>Z(i),"facility-action warn")),i.type==="scanner"&&i.kind==="manual"&&t.modalActions.appendChild(R("Set Keys",()=>Qt(i),"facility-action warn")),i.type==="conveyor"&&Zn(i.id)&&t.modalActions.appendChild(R(`Branch: ${pn(e.conveyorMeta.get(i.id)?.branchMode||"random")}`,()=>{jn(i.id),Zo()},"facility-action warn"));let h=i.type==="conveyor"?{type:"conveyor",oldKey:i.id,ref:i}:{type:i.type,ref:i},m=ve(h);if(m){let T=R(m,()=>{},"facility-action danger disabled");T.disabled=!0,t.modalActions.appendChild(T)}else{let T=gt(h,e),A=T.sameBuild?`Remove / Refund ${S(T.amount)}`:`Sell ${S(T.amount)}`;t.modalActions.appendChild(R(A,()=>{P(),V()},"facility-action danger"))}Jn(i),t.modal.classList.add("visible","equipment-popover")}function gs(i){e.phase==="build"&&(dn(),e.buildTool=e.buildTool===i?null:i,e.selected=null,e.multiSelected=[],e.selectionBox=null,s())}function Es(){let i=[];for(let[c,h]of Object.entries(e.facilities))if(i.push({id:`facility:${c}`,price:O({type:"facility",ref:h}),x:h.x,y:h.y,w:h.w,h:h.h}),h.entry){let m=$(h.entry.col,h.entry.row);i.push({id:`facilityPort:${c}`,price:O({type:"facility",ref:h}),x:m.x-18,y:m.y-18,w:36,h:36})}for(let c of e.eggFarms){let h=$(c.col,c.row);i.push({id:`eggFarm:${c.id}`,price:O({type:"eggFarm",ref:c}),x:h.x-24,y:h.y-24,w:48,h:48})}for(let c of e.scanners){let h=de(c);i.push({id:`scanner:${c.id}`,price:O({type:"scanner",ref:c}),x:h.x-50,y:h.y-73,w:100,h:146})}for(let c of e.conveyorTiles){let h=J(c),m=$(h.col,h.row),T=e.conveyorMeta.get(c)||{};i.push({id:`conveyor:${c}`,price:T.price||Be(T.kind==="boostConveyor"?"boostConveyor":"conveyor",e),x:m.x-22,y:m.y-22,w:44,h:44})}return i}function pn(i){return L[i]||L.random}function nc(i){return qe(i)}function Zn(i){return e.conveyorTiles.has(i)?xe(J(i)).length>=3:!1}function jo(i){if(e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending)return null;for(let c of e.conveyorTiles){if(!Zn(c))continue;let h=J(c),m=$(h.col,h.row),T=m.x-F.w/2,A=m.y+F.y-F.h/2;if(i.x>=T&&i.x<=T+F.w&&i.y>=A&&i.y<=A+F.h)return c}return null}function Ps(i){let c=jo(i);return c?jn(c):!1}function jn(i){let c=e.conveyorMeta.get(i);if(!c)return!1;let h=qe(i);if(h.length<2)return!1;Oe(e);let m=D.includes(c.branchMode)?c.branchMode:"random",T=["random",...h.length>=4?["pass"]:[],...pe.map(ne=>ne.name).filter(ne=>h.includes(ne))];c.branchMode=T[(Math.max(0,T.indexOf(m))+1)%T.length],e.selected={type:"conveyor",id:i};let A=$(...Object.values(J(i)));return N(e,A.x,A.y-22,pn(c.branchMode),f.ink),s(),!0}function er(i){ye(e);let c=[],h=i?.type==="facility"?i.baseId||i.id:"";if(i.type==="eggFarm"){let[m,T]=en(i);c.push(`Price: ${S(O(i))}`);{let A=ve({type:i.type,ref:i});c.push(A?`Sale: blocked (${A})`:`Sale value: ${S(gt({type:i.type,ref:i},e).amount)}`)}c.push(`Level: ${i.level}/4`),c.push(`Random interval: ${m.toFixed(2)}-${T.toFixed(2)}s`),c.push(`Durability: ${ln(i)}% | Spawn delay x${Ln(i).toFixed(2)}`)}else if(i.type==="scanner")c.push(`Price: ${S(O(i))}`),c.push(`Sale value: ${S(gt({type:i.type,ref:i},e).amount)}`),c.push(`Type: ${i.kind.toUpperCase()} / Standard`),i.kind==="auto"&&c.push(`Role: ${i.role===0?"Male left / Others right":"Poop left / Others right"}`),c.push(`Queue: ${i.queue.length}`),i.kind==="auto"&&c.push(`Cooldown: ${i.cooldown.toFixed(1)}s / ${Qe(i,e).toFixed(1)}s`),i.kind==="auto"&&c.push(`Durability: ${ln(i)}% | Delay x${Bn(i).toFixed(2)}`),i.kind==="manual"&&c.push("Durability: none | Always normal");else if(i.type==="conveyor"){let m=e.conveyorMeta.get(i.id),T=e.componentLookup?.get(i.id),A=T?e.congestion.get(T):null;c.push(`Price: ${S(O({type:"conveyor",oldKey:i.id,ref:i}))}`),c.push(`Cell: ${i.id}`),c.push(`Type: ${m?.kind==="boostConveyor"?"BOOST BELT":"Normal Belt"} | Speed x${Number(m?.speedMultiplier||1).toFixed(1)}`),c.push(`Congestion: ${A?Math.floor(A.ratio*100):0}%`),c.push(`Durability: ${ln({meta:m})}% | Speed x${Fn({meta:m}).toFixed(2)}`),Zn(i.id)?(c.push(`Branch mode: ${pn(m?.branchMode||"random")}`),c.push("Use the Branch button below or the on-belt branch badge.")):c.push("Branch mode: needs 3+ conveyor exits."),c.push(`Sale value: ${S(gt({type:"conveyor",oldKey:i.id,ref:i},e).amount)}`)}else if(i.type==="facility"){c.push(`Price: ${S(O(i))}`);{let m=ve({type:i.type,ref:i});c.push(m?`Sale: blocked (${m})`:`Sale value: ${S(gt({type:i.type,ref:i},e).amount)}`)}if(c.push(["mixer","truck","trash"].includes(i.id)?`Level: ${i.level} / no cap`:`Level: ${i.level}`),h==="truck"&&c.push("Durability: none | Always normal"),["mixer","trash"].includes(h)&&c.push(`Durability: ${ln(i)}% | Extra delay ${Bo(e,i).toFixed(2)}s`),h==="mixer"&&(c.push(`Income: ${S(_e(e))} per chick | upgrade x${nt(e,"mixer").toFixed(3)}`),c.push(`Poop fine: -${S(nn(e))}`)),h==="truck"&&(c.push(`Income: ${S(Le(e))} per target cargo | upgrade x${nt(e,"truck").toFixed(3)}`),c.push(`Poop shipment fine: -${S(on(e))}`)),h==="trash"){let m=Tt(e),T=ot(m);c.push(`Bonus cards: ${m}/${Gt()}`),c.push(`Bonus chance: ${(T*100).toFixed(2)}% for ${S(1)} per processed item.`),c.push(`Expected value: ${T.toFixed(3)} JPY per item.`)}i.entry&&c.push(`Receiver: edge cell ${i.entry.col},${i.entry.row} (${i.side})`)}return c}function Rs(i){if(!i||e.phase!=="build")return"";let c=gn(e).find(h=>h.type===i.type&&h.id===i.id);return c?c.message||"Not connected to conveyor route":""}function Ds(i){return i?i.type==="conveyor"&&e.conveyorMeta.get(i.id)?.kind==="boostConveyor"?"Moves chicks twice as fast. Wears down like a normal belt.":i.type==="conveyor"?"A stubborn belt tile. It only cares about the next cell.":i.type==="eggFarm"?"A tiny gatehouse producing questionable eggs on schedule.":i.type==="scanner"&&i.kind==="auto"?"Slower than hands without upgrades.":i.type==="scanner"?"A manual checkpoint. The operator is the algorithm.":i.type==="facility"&&(i.baseId||i.id)==="mixer"?"Male chicks become revenue here. Do not feed it poop.":i.type==="facility"&&(i.baseId||i.id)==="trash"?"Poop goes in. Sometimes coins come out, for reasons best left unaudited.":i.type==="facility"&&(i.baseId||i.id)==="truck"?"The shipping endpoint. Correct cargo pays; wrong cargo complains.":"Factory equipment.":""}return{buildAtCell:Vt,buildAtPoint:Xt,eraseAtPoint:p,equipmentAtPoint:b,startSelectionBox:x.startSelectionBox,updateSelectionBox:x.updateSelectionBox,finishSelectionBox:x.finishSelectionBox,startGroupDrag:x.startGroupDrag,updateGroupDrag:x.updateGroupDrag,finishGroupDrag:x.finishGroupDrag,selectedObject:w,selectedTitle:C,equipmentPrice:O,selectedUpgradeCost:v,selectedInfoLines:er,flavorText:Ds,disconnectedWarningFor:Rs,gridExpansionCost:z,buyGridExpansion:g,expansionOfferAtPoint:he,beginConveyorDrag:zn,continueConveyorDrag:$t,endConveyorDrag:dn,cycleSingleConveyorDirection:cn,branchSwitcherAtPoint:jo,cycleBranchModeAtPoint:Ps,cycleBranchModeForKey:jn,removeSelected:P,switchScannerRole:Ms,showManualScannerMenu:Qt,showAutoScannerMenu:Z,showSelectedMenu:Zo,setBuildTool:gs,fail:ce,isEquipmentCell:E,equipmentHitBoxes:Es,routeFromFarmToScanner:Mt}}function ss({game:e,ui:t,build:n,startGame:o,beginCardDraft:r,activeQueuedChick:s}){function l(E){return String(E).replace(/[&<>"']/g,G=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[G])}function d(){return Math.round(Gn(e))}function k(){if(e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending)return!1;let E=!1;for(let G of no)if(E=!0,e.cash>=Be(G,e))return!1;return E}function M(){D(),L(),oe(),z(),F()}function D(){for(let[G,w]of Object.entries(t.buttons))w?.classList?.contains("tool-button")&&w.classList.toggle("active",e.buildTool===G);for(let G of no){let w=t.buttons[G];if(!w)continue;let C=Be(G,e),O=w.querySelector("span");O&&(O.textContent=G==="conveyor"||G==="boostConveyor"?`${S(C)} / tile`:S(C));let Y=G==="manualScanner"&&e.scanners.filter(se=>se.kind==="manual").length>=8,te=e.cashMt(e,ve)?.scannerId).length,G=tn(e),w=Math.max(0,Math.floor(e.lastResult?.profit??e.stats?.profit??0)),C=Pn(w,e),O=e.phase==="running"?Math.max(1,(e.totals.turnsCompleted||0)+1):Math.max(1,Number(e.turn)||1),Y=So(e,O),te=e.lastBuildFees?.turn===O?e.lastBuildFees.fairiesTribute||0:null,se=e.lastBuildFees?.turn===O&&e.lastBuildFees.reduction||0;t.turnSummary.innerHTML=[`Seed: ${l(e.runSeed||"none")} | Grid box: ${e.gridRows||10}x${e.gridCols||20} | Owned: ${e.gridOwnedCells||e.ownedCells?.size||(e.gridRows||10)*(e.gridCols||20)} cells | Blocked: ${e.blockedCells?.size||0} cells`,`Truck target: ${ze(e).toUpperCase()} | Male fine: -${S(Co(e))}`,`Poop fine: Mixer -${S(nn(e))} / Shipment -${S(on(e))}`,`ZUNDA TAX on Next Day: -${S(C.tax)} | basis net ${S(w)} | taxable ${S(C.taxable)} x${C.ratePercent}% | exemption ${S(C.exemption)} | 95% at ${S(C.maxRateCash)}`,te!=null?`Fairies tribute paid before Build: -${S(te)}${se?` | Flattery -${S(se)}`:""}`:`Fairies tribute before Build: -${S(Y.amount)}${Y.reduction?` | Flattery -${S(Y.reduction)}`:""}`,`Maintenance: worst ${No(e).worst.percent}% dirty (${No(e).worst.label}) | Repairman: ${e.repairman?.hiredForNextDay?"hired next day":e.repairman?.active?"working":"none"}`,`Manual combo: ${e.manualCombo?.count||0} | Card draft: ${e.cardDraft?.pending?Math.max(1,e.cardDraft.picksRemaining||1)+" pick(s) left":"ready after each day"} | Belt: ${d()}px/s | Farms with scanner route: ${E}/${e.eggFarms.length}`,e.cardEffects?.usedMachineActive?'Procurement: USED MACHINE MODE / no refunds / 60% durability':"",G.length?`Blocked: ${G[0]}`:`${re.status.allPortsConnected}`].join("
")}function oe(){if(!t.contractPanel)return;let E=e.contractOffer;if(e.phase!=="build"){t.contractPanel.innerHTML=re.status.eventPanelRunning;return}if(!E){t.contractPanel.className="contract-card empty",e.turn+1Event lock active.No irregular events during Days 1-7. First possible forced event: Day ${ft}.`:t.contractPanel.innerHTML="No event.Random forced events can appear later.";return}t.contractPanel.className="contract-card accepted",t.contractPanel.innerHTML=` + ${b?`

${b}

`:""} + `,t.modalActions.innerHTML="",t.modalActions.appendChild(Xe("Set Left Key",()=>dr(i,"left"),"facility-action warn")),t.modalActions.appendChild(Xe("Set Right Key",()=>dr(i,"right"),"facility-action warn")),t.modalActions.appendChild(Xe("Close",rn,"facility-action")),lo(i),t.modal.classList.add("visible","equipment-popover")}function js(i){let c=[{side:"left",binding:i.keys?.left},{side:"right",binding:i.keys?.right}];if(c[0].binding&&c[1].binding&&(c[0].binding.code===c[1].binding.code||c[0].binding.key===c[1].binding.key))return"Left and right keys conflict.";for(let h of c)if(h.binding)for(let m of e.scanners.filter(b=>b.kind==="manual"&&b.id!==i.id)){if(m.keys?.left&&(m.keys.left.code===h.binding.code||m.keys.left.key===h.binding.key))return`Key conflicts with Manual Scanner #${m.id} left.`;if(m.keys?.right&&(m.keys.right.code===h.binding.code||m.keys.right.key===h.binding.key))return`Key conflicts with Manual Scanner #${m.id} right.`}return""}function dr(i,c){t.modalBody.innerHTML=`

Press a key for ${c.toUpperCase()} route.

Esc cancels.

`,t.modalActions.innerHTML="";let h=m=>{if(m.preventDefault(),m.stopPropagation(),window.removeEventListener("keydown",h,!0),m.key==="Escape")return sn(i);let b=Js(m),E=Zs(i,c,b);Be(e),i.keys=i.keys||{left:null,right:null},i.keys[c]=b,s(),sn(i,E?`${b.label} assigned, but another scanner already uses it.`:`${c.toUpperCase()} set to ${b.label}.`)};window.addEventListener("keydown",h,!0)}function el(){let i=N();if(!(!i||i.type!=="scanner"))return i.kind==="auto"?so(i):sn(i)}function tl(i){if(!i)return{x:80,y:80};if(i.type==="conveyor"){let c=j(i.id);return $(c.col,c.row)}return i.type==="eggFarm"?$(i.col,i.row):i.type==="scanner"?pe(i):i.type==="facility"?{x:i.x+i.w/2,y:i.y+10}:{x:80,y:80}}function lo(i){let c=n.getBoundingClientRect(),h=tl(i),m=e.view?.scale||1,b=c.left+(h.x*m+(e.view?.x||0))/n.width*c.width+18,E=c.top+(h.y*m+(e.view?.y||0))/n.height*c.height-12,z=Math.max(12,window.innerWidth-274),oe=Math.max(12,window.innerHeight-220);t.modal.style.setProperty("--popover-x",`${Math.max(12,Math.min(z,b))}px`),t.modal.style.setProperty("--popover-y",`${Math.max(12,Math.min(oe,E))}px`)}function nl(){let i=N();if(!i)return;let c=hr(i);t.modalTitle.textContent=U(i),t.modalBody.innerHTML=` +
${c.map(b=>`

${b}

`).join("")}
+ ${["mixer","truck"].includes(i.baseId||i.id)?'

Income/Fine = ceil(base x 1.10^upgrades)

':""}`,t.modalActions.innerHTML="",i.type==="scanner"&&i.kind==="auto"&&t.modalActions.appendChild(Xe("Auto Menu",()=>so(i),"facility-action warn")),i.type==="scanner"&&i.kind==="manual"&&t.modalActions.appendChild(Xe("Set Keys",()=>sn(i),"facility-action warn"));let h=i.type==="conveyor"?{type:"conveyor",oldKey:i.id,ref:i}:{type:i.type,ref:i},m=Ee(h);if(m){let b=Xe(m,()=>{},"facility-action danger disabled");b.disabled=!0,t.modalActions.appendChild(b)}else{let b=Bt(h,e),E=b.sameBuild?`Remove / Refund ${T(b.amount)}`:`Sell ${T(b.amount)}`;t.modalActions.appendChild(Xe(E,()=>{cr(),rn()},"facility-action danger"))}lo(i),t.modal.classList.add("visible","equipment-popover")}function ol(i){e.phase==="build"&&(D(),e.buildTool=e.buildTool===i?null:i,e.selected=null,e.multiSelected=[],e.selectionBox=null,s())}function rl(){let i=[];for(let[c,h]of Object.entries(e.facilities))if(i.push({id:`facility:${c}`,price:Y({type:"facility",ref:h}),x:h.x,y:h.y,w:h.w,h:h.h}),h.entry){let m=$(h.entry.col,h.entry.row);i.push({id:`facilityPort:${c}`,price:Y({type:"facility",ref:h}),x:m.x-18,y:m.y-18,w:36,h:36})}for(let c of e.eggFarms){let h=$(c.col,c.row);i.push({id:`eggFarm:${c.id}`,price:Y({type:"eggFarm",ref:c}),x:h.x-24,y:h.y-24,w:48,h:48})}for(let c of e.scanners){let h=pe(c);i.push({id:`scanner:${c.id}`,price:Y({type:"scanner",ref:c}),x:h.x-50,y:h.y-73,w:100,h:146})}for(let c of e.conveyorTiles){let h=j(c),m=$(h.col,h.row),b=e.conveyorMeta.get(c)||{};i.push({id:`conveyor:${c}`,price:b.price||Oe(b.kind==="boostConveyor"?"boostConveyor":"conveyor",e),x:m.x-22,y:m.y-22,w:44,h:44})}return i}function il(i){return A[i]||A.random}function Lc(i){return vn(i)}function fr(i){return e.conveyorTiles.has(i)?xe(j(i)):!1}function pr(i){if(e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending)return null;for(let c of e.conveyorTiles){if(!fr(c))continue;let h=j(c),m=$(h.col,h.row),b=m.x-L.w/2,E=m.y+L.y-L.h/2;if(i.x>=b&&i.x<=b+L.w&&i.y>=E&&i.y<=E+L.h)return c}return null}function sl(i){let c=pr(i);return c?(yr(c),!0):!1}function yr(i){let c=e.conveyorMeta.get(i);if(!c)return!1;let h=oo(i);if(h.length<2)return!1;Be(e);let m=g.includes(c.branchMode)?c.branchMode:"random",b=["random",...ue.map(E=>E.name).filter(E=>h.includes(E))];return c.branchMode=b[(Math.max(0,b.indexOf(m))+1)%b.length],e.selected=null,e.multiSelected=[],s(),!0}function hr(i){ke(e);let c=[],h=i?.type==="facility"?i.baseId||i.id:"";if(i.type==="eggFarm"){let[m,b]=un(i);c.push(`Price: ${T(Y(i))}`);{let E=Ee({type:i.type,ref:i});c.push(E?`Sale: blocked (${E})`:`Sale value: ${T(Bt({type:i.type,ref:i},e).amount)}`)}c.push(`Level: ${i.level}/4`),c.push(`Random interval: ${m.toFixed(2)}-${b.toFixed(2)}s`),c.push(`Durability: ${bn(i)}% | Spawn delay x${Yn(i).toFixed(2)}`)}else if(i.type==="scanner")c.push(`Price: ${T(Y(i))}`),c.push(`Sale value: ${T(Bt({type:i.type,ref:i},e).amount)}`),c.push(`Type: ${i.kind.toUpperCase()} / Standard`),i.kind==="auto"&&c.push(`Role: ${i.role===0?"Male left / Others right":"Poop left / Others right"}`),c.push(`Queue: ${i.queue.length}`),i.kind==="auto"&&c.push(`Cooldown: ${i.cooldown.toFixed(1)}s / ${it(i,e).toFixed(1)}s`),i.kind==="auto"&&c.push(`Durability: ${bn(i)}% | Delay x${qn(i).toFixed(2)}`),i.kind==="manual"&&c.push("Durability: none | Always normal");else if(i.type==="conveyor"){let m=e.conveyorMeta.get(i.id),b=e.componentLookup?.get(i.id),E=b?e.congestion.get(b):null;c.push(`Price: ${T(Y({type:"conveyor",oldKey:i.id,ref:i}))}`),c.push(`Cell: ${i.id}`),c.push(`Type: ${m?.kind==="boostConveyor"?"BOOST BELT":"Normal Belt"} | Speed x${Number(m?.speedMultiplier||1).toFixed(1)}`),c.push(`Congestion: ${E?Math.floor(E.ratio*100):0}%`),c.push(`Durability: ${bn({meta:m})}% | Speed x${Hn({meta:m}).toFixed(2)}`),fr(i.id)?(c.push(`Branch mode: ${il(m?.branchMode||"random")}`),c.push("Use the Branch button below or the on-belt branch badge.")):c.push("Branch mode: needs 3+ conveyor connections."),c.push(`Sale value: ${T(Bt({type:"conveyor",oldKey:i.id,ref:i},e).amount)}`)}else if(i.type==="facility"){c.push(`Price: ${T(Y(i))}`);{let m=Ee({type:i.type,ref:i});c.push(m?`Sale: blocked (${m})`:`Sale value: ${T(Bt({type:i.type,ref:i},e).amount)}`)}if(h==="maintenanceRoom"){let m=Math.max(1,Math.floor(i.level||1));return c.push(`Workers: ${m}`),c.push(`Wage: ${T(wt(e))} / worker / day`),c.push(`Daily cost: ${T(m*wt(e))}`),c.push(`Footprint: ${i.cols||2}x${i.rows||2} cells`),c}if(c.push(["mixer","truck","trash"].includes(h)?`Level: ${i.level} / no cap`:`Level: ${i.level}`),h==="truck"&&c.push("Durability: none | Always normal"),["mixer","trash"].includes(h)&&c.push(`Durability: ${bn(i)}% | Extra delay ${Yo(e,i).toFixed(2)}s`),h==="mixer"&&(c.push(`Income: ${T(Qe(e))} per chick | upgrade x${ft(e,"mixer").toFixed(3)}`),c.push(`Poop fine: -${T(fn(e))}`)),h==="truck"&&(c.push(`Income: ${T(Ke(e))} per target cargo | upgrade x${ft(e,"truck").toFixed(3)}`),c.push(`Poop shipment fine: -${T(pn(e))}`)),h==="trash"){let m=Ft(e),b=pt(m);c.push(`Bonus cards: ${m}/${Vt()}`),c.push(`Bonus chance: ${(b*100).toFixed(2)}% for ${T(1)} per processed item.`),c.push(`Expected value: ${b.toFixed(3)} JPY per item.`)}i.entry&&c.push(`Receiver: edge cell ${i.entry.col},${i.entry.row} (${i.side})`)}return c}function ll(i){if(!i||e.phase!=="build")return"";let c=On(e).find(h=>h.type===i.type&&h.id===i.id);return c?c.message||"Not connected to conveyor route":""}function al(i){return i?i.type==="conveyor"&&e.conveyorMeta.get(i.id)?.kind==="boostConveyor"?"Moves chicks twice as fast. Wears down like a normal belt.":i.type==="conveyor"?"A stubborn belt tile. It only cares about the next cell.":i.type==="eggFarm"?"A tiny gatehouse producing questionable eggs on schedule.":i.type==="scanner"&&i.kind==="auto"?"Slower than hands without upgrades.":i.type==="scanner"?"A manual checkpoint. The operator is the algorithm.":i.type==="facility"&&(i.baseId||i.id)==="maintenanceRoom"?"Spawns repair workers at day start. Upgrade cards add workers.":i.type==="facility"&&(i.baseId||i.id)==="mixer"?"Male chicks become revenue here. Do not feed it poop.":i.type==="facility"&&(i.baseId||i.id)==="trash"?"Poop goes in. Sometimes coins come out, for reasons best left unaudited.":i.type==="facility"&&(i.baseId||i.id)==="truck"?"The shipping endpoint. Correct cargo pays; wrong cargo complains.":"Factory equipment.":""}return{buildAtCell:se,buildAtPoint:et,eraseAtPoint:on,equipmentAtPoint:io,startSelectionBox:Ut.startSelectionBox,updateSelectionBox:Ut.updateSelectionBox,finishSelectionBox:Ut.finishSelectionBox,startGroupDrag:Ut.startGroupDrag,updateGroupDrag:Ut.updateGroupDrag,finishGroupDrag:Ut.finishGroupDrag,selectedObject:N,selectedTitle:U,equipmentPrice:Y,selectedUpgradeCost:Qs,selectedInfoLines:hr,flavorText:al,disconnectedWarningFor:ll,gridExpansionCost:V,buyGridExpansion:I,expansionOfferAtPoint:me,beginConveyorDrag:S,continueConveyorDrag:k,endConveyorDrag:D,cycleSingleConveyorDirection:Mt,branchSwitcherAtPoint:pr,cycleBranchModeAtPoint:sl,cycleBranchModeForKey:yr,removeSelected:cr,switchScannerRole:el,showManualScannerMenu:sn,showAutoScannerMenu:so,showSelectedMenu:nl,setBuildTool:ol,fail:B,isEquipmentCell:x,equipmentHitBoxes:rl,routeFromFarmToScanner:$t}}function Ds({game:e,ui:t,build:n,startGame:o,beginCardDraft:r,activeQueuedChick:s}){function l(R){return String(R).replace(/[&<>"']/g,G=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[G])}function d(){return Math.round(Xn(e))}function v(){if(e.phase!=="build"||e.cardDraft?.pending||e.cardTargetPick?.pending)return!1;let R=!1;for(let G of uo)if(R=!0,e.cash>=Oe(G,e))return!1;return R}function M(){g(),A(),ne(),me(),q(),le()}function g(){for(let[G,x]of Object.entries(t.buttons))x?.classList?.contains("tool-button")&&x.classList.toggle("active",e.buildTool===G);for(let G of uo){let x=t.buttons[G];if(!x)continue;let C=Oe(G,e),N=x.querySelector("span");N&&(N.textContent=G==="conveyor"||G==="boostConveyor"?`${T(C)} / tile`:T(C));let U=G==="manualScanner"&&e.scanners.filter(ie=>ie.kind==="manual").length>=8,Y=e.cashL.has(R.id)).map(R=>({card:R,count:V(R)})).filter(R=>R.count>0)}function q(){let R=t.cardInventory;if(!R)return;let G=ee(),x=G.reduce((U,Y)=>U+Y.count,0),C=R.querySelector("summary"),N=R.querySelector(".acquired-cards-list");if(C&&(C.innerHTML=`CARDS ${x}${G.length?"passive":"none"}`),!!N){if(!G.length){N.innerHTML="

No passive acquired cards yet. Consumed cards and equipment improvements are hidden.

",R.classList.add("empty");return}R.classList.remove("empty"),N.innerHTML=G.map(({card:U,count:Y})=>{let ie=(U.tags||[]).filter(ae=>!["ACTIVE","ONE-SHOT"].includes(ae)).slice(0,2);return`
${l(U.title)}x${Y}${ie.length?`${ie.map(l).join(" / ")}`:""}
`}).join("")}}function le(){t.buttons.undo.disabled=e.phase!=="build"||e.undoStack.length===0,t.buttons.redo.disabled=e.phase!=="build"||e.redoStack.length===0}function me(){let R=e.eggFarms.filter(Ee=>$t(e,Ee)?.scannerId).length,G=dn(e),x=Math.max(0,Math.floor(e.lastResult?.profit??e.stats?.profit??0)),C=Wn(x,e),N=e.phase==="running"?Math.max(1,(e.totals.turnsCompleted||0)+1):Math.max(1,Number(e.turn)||1),U=Do(e,N),Y=e.lastBuildFees?.turn===N?e.lastBuildFees.fairiesTribute||0:null,ie=e.lastBuildFees?.turn===N&&e.lastBuildFees.reduction||0,ae=ns(e),we=Array.isArray(e.repairmen)?e.repairmen.filter(Ee=>Ee.active).length:e.repairman?.active?1:0;t.turnSummary.innerHTML=[`Seed: ${l(e.runSeed||"none")} | Grid box: ${e.gridRows||10}x${e.gridCols||20} | Owned: ${e.gridOwnedCells||e.ownedCells?.size||(e.gridRows||10)*(e.gridCols||20)} cells | Blocked: ${e.blockedCells?.size||0} cells`,`Truck target: ${nt(e).toUpperCase()} | Male fine: -${T(Ao(e))}`,`Poop fine: Mixer -${T(fn(e))} / Shipment -${T(pn(e))}`,`ZUNDA TAX on Next Day: -${T(C.tax)} | basis net ${T(x)} | taxable ${T(C.taxable)} x${C.ratePercent}% | exemption ${T(C.exemption)} | 95% at ${T(C.maxRateCash)}`,Y!=null?`Fairies tribute paid before Build: -${T(Y)}${ie?` | Flattery -${T(ie)}`:""}`:`Fairies tribute before Build: -${T(U.amount)}${U.reduction?` | Flattery -${T(U.reduction)}`:""}`,`Maintenance: worst ${ae.worst.percent}% dirty (${ae.worst.label}) | Repair workers: ${we||ae.workers} | Daily wages: -${T(ae.dailyWage||0)}`,`Manual combo: ${e.manualCombo?.count||0} | Card draft: ${e.cardDraft?.pending?Math.max(1,e.cardDraft.picksRemaining||1)+" pick(s) left":"ready after each day"} | Belt: ${d()}px/s | Farms with scanner route: ${R}/${e.eggFarms.length}`,e.cardEffects?.usedMachineActive?'Procurement: USED MACHINE MODE / no refunds / 60% durability':"",G.length?`Blocked: ${G[0]}`:`${re.status.allPortsConnected}`].join("
")}function ne(){if(!t.contractPanel)return;let R=e.contractOffer;if(e.phase!=="build"){t.contractPanel.innerHTML=re.status.eventPanelRunning;return}if(!R){t.contractPanel.className="contract-card empty",e.turn+1Event lock active.No irregular events during Days 1-7. First possible forced event: Day ${Tt}.`:t.contractPanel.innerHTML="No event.Random forced events can appear later.";return}t.contractPanel.className="contract-card accepted",t.contractPanel.innerHTML=`
FORCED NEXT DAY
-

${E.title}

+

${R.title}

- Route: ${E.route} - Target: ${E.targetAmount} truck exports - Reward: ${S(E.reward)} - Output x${E.productionMultiplier} / Poop x${E.poopMultiplier} -
`}function X(){D(),t.shell.classList.toggle("phase-running",e.phase==="running"),t.shell.classList.toggle("phase-build",e.phase==="build"),t.shell.classList.toggle("phase-gameover",e.phase==="gameover"),t.shell.classList.toggle("phase-title",e.phase==="title"),t.shell.classList.toggle("phase-loanDecision",e.phase==="loanDecision"),t.money.textContent=S(e.cash),t.money.classList.toggle("cash-negative",e.cash<0),t.money.classList.toggle("cash-positive",e.cash>Jt),t.turn.textContent=e.turn,t.timeLeft.textContent=e.phase==="running"&&e.timeLeft<=0&&e.shutdownTimeLeft>0?`+${e.shutdownTimeLeft.toFixed(1)}s`:`${Math.max(0,e.timeLeft).toFixed(1)}s`,t.timeLeft.closest?.(".hud-card")?.classList.toggle("time-critical",e.phase==="running"&&(e.timeLeft<=10||e.timeLeft<=0&&e.shutdownTimeLeft>0)),t.phase.textContent=he(),t.comboCount&&(t.comboCount.textContent=e.manualCombo?.count||0),ae(),t.turnProfit.textContent=S(e.stats.profit),t.turnProfit.classList.toggle("cash-negative",e.stats.profit<0),t.turnProfit.classList.toggle("cash-positive",e.stats.profit>0),t.mixerCount.textContent=e.stats.mixerCount,t.truckFemaleCount.textContent=e.stats.truckFemale,t.truckMaleCount.textContent=e.stats.truckMale,t.poopCount.textContent=e.stats.poopSpawned,t.buttons.s1Left.disabled=e.phase!=="running"||!s(0),t.buttons.s1Right.disabled=e.phase!=="running"||!s(0),t.buttons.s2Left.disabled=e.phase!=="running"||!s(1),t.buttons.s2Right.disabled=e.phase!=="running"||!s(1);let E=e.scanners.find(C=>C.kind==="manual"&&C.slot===0),G=e.scanners.find(C=>C.kind==="manual"&&C.slot===1);E&&(t.buttons.s1Left.textContent=`S1 ${E.keys?.left?.label||"A"} -> Mixer`,t.buttons.s1Right.textContent=`S1 ${E.keys?.right?.label||"D"} -> S2`),G&&(t.buttons.s2Left.textContent=`S2 ${G.keys?.left?.label||"Left"} -> Waste`,t.buttons.s2Right.textContent=`S2 ${G.keys?.right?.label||"Right"} -> Truck`);let w=Pn(Math.max(0,Math.floor(e.lastResult?.profit??e.stats?.profit??0)),e).tax;t.buttons.nextTurn.textContent=e.phase==="build"&&w>0?`Next Day - ZUNDA ${S(w)}`:re.actions.nextDay,t.buttons.nextTurn.disabled=e.phase!=="build"||!!e.cardDraft?.pending||!!e.cardTargetPick?.pending||!Wr(e)}function ae(){if(!t.eventBrief||!t.targetBrief||!t.speedBrief)return;let E=ze(e).toUpperCase();t.targetBrief.textContent=`TRUCK TARGET: ${E}`,t.speedBrief.textContent=`BELT: ${d()} px/s | COMBO: ${e.manualCombo?.count||0}`,t.speedBrief.title=`Conveyor speed does not increase by day. Degraded belts reduce speed. Cap: ${Lt}px/s.`;let G=e.contractActive,w=e.contractOffer;G?(t.eventBrief.textContent=`ACTIVE EVENT: ${G.route} | Output x${G.productionMultiplier} | Poop x${G.poopMultiplier}`,t.eventBrief.className="event-brief active"):e.phase==="build"&&w?(t.eventBrief.textContent=`NEXT DAY EVENT: ${w.route} | Target ${w.targetAmount} | Reward ${S(w.reward)}`,t.eventBrief.className="event-brief next"):e.phase==="build"&&e.turn+1
\u2642Male->MixerS1: A
\u2640Female->TruckS1: D, then S2: right
\u{1F4A9}Poop->ShredderS1: D, then S2: left

Build phase: connect at least one EGG through a scanner to Mixer, Shredder, or Truck before Next Day. One day is one run.

',t.modalActions.innerHTML="",t.modalActions.appendChild(le(re.actions.startGame,o,"primary-button")),t.modal.classList.remove("equipment-popover","gameover-modal"),t.modal.classList.add("visible")}function g(E){let G=Y=>`${Y>0?"+":""}${S(Y)}`,w=Y=>`${Y>0?"-":""}${S(Y)}`,C=E.profit>=0?"cash-positive":"cash-negative",O=(E.mixerPoopFine||0)+(E.truckPoopFine||0)+(E.maleTruckFine||0)+(E.explosionDamage||0)+(E.cardRerollCost||0);t.modalTitle.textContent=`Day ${E.turn} Settlement`,t.modalBody.innerHTML=` -
Net result${S(E.profit)}
+ Route: ${R.route} + Target: ${R.targetAmount} truck exports + Reward: ${T(R.reward)} + Output x${R.productionMultiplier} / Poop x${R.poopMultiplier} + `}function I(){g(),t.shell.classList.toggle("phase-running",e.phase==="running"),t.shell.classList.toggle("phase-build",e.phase==="build"),t.shell.classList.toggle("phase-gameover",e.phase==="gameover"),t.shell.classList.toggle("phase-title",e.phase==="title"),t.shell.classList.toggle("phase-loanDecision",e.phase==="loanDecision"),t.money.textContent=T(e.cash),t.money.classList.toggle("cash-negative",e.cash<0),t.money.classList.toggle("cash-positive",e.cash>ln),t.turn.textContent=e.turn,t.timeLeft.textContent=e.phase==="running"&&e.timeLeft<=0&&e.shutdownTimeLeft>0?`+${e.shutdownTimeLeft.toFixed(1)}s`:`${Math.max(0,e.timeLeft).toFixed(1)}s`,t.timeLeft.closest?.(".hud-card")?.classList.toggle("time-critical",e.phase==="running"&&(e.timeLeft<=10||e.timeLeft<=0&&e.shutdownTimeLeft>0)),t.phase.textContent=W(),t.comboCount&&(t.comboCount.textContent=e.manualCombo?.count||0),_(),q(),t.turnProfit.textContent=T(e.stats.profit),t.turnProfit.classList.toggle("cash-negative",e.stats.profit<0),t.turnProfit.classList.toggle("cash-positive",e.stats.profit>0),t.mixerCount.textContent=e.stats.mixerCount,t.truckFemaleCount.textContent=e.stats.truckFemale,t.truckMaleCount.textContent=e.stats.truckMale,t.poopCount.textContent=e.stats.poopSpawned,t.buttons.s1Left.disabled=e.phase!=="running"||!s(0),t.buttons.s1Right.disabled=e.phase!=="running"||!s(0),t.buttons.s2Left.disabled=e.phase!=="running"||!s(1),t.buttons.s2Right.disabled=e.phase!=="running"||!s(1);let R=e.scanners.find(C=>C.kind==="manual"&&C.slot===0),G=e.scanners.find(C=>C.kind==="manual"&&C.slot===1);R&&(t.buttons.s1Left.textContent=`S1 ${R.keys?.left?.label||"A"} -> Mixer`,t.buttons.s1Right.textContent=`S1 ${R.keys?.right?.label||"D"} -> S2`),G&&(t.buttons.s2Left.textContent=`S2 ${G.keys?.left?.label||"Left"} -> Waste`,t.buttons.s2Right.textContent=`S2 ${G.keys?.right?.label||"Right"} -> Truck`);let x=Wn(Math.max(0,Math.floor(e.lastResult?.profit??e.stats?.profit??0)),e).tax;t.buttons.nextTurn.textContent=e.phase==="build"&&x>0?`Next Day - ZUNDA ${T(x)}`:re.actions.nextDay,t.buttons.nextTurn.disabled=e.phase!=="build"||!!e.cardDraft?.pending||!!e.cardTargetPick?.pending||!ii(e)}function _(){if(!t.eventBrief||!t.targetBrief||!t.speedBrief)return;let R=nt(e).toUpperCase();t.targetBrief.textContent=`TRUCK TARGET: ${R}`,t.speedBrief.textContent=`BELT: ${d()} px/s | COMBO: ${e.manualCombo?.count||0}`,t.speedBrief.title=`Conveyor speed does not increase by day. Degraded belts reduce speed. Cap: ${_t}px/s.`;let G=e.contractActive,x=e.contractOffer;G?(t.eventBrief.textContent=`ACTIVE EVENT: ${G.route} | Output x${G.productionMultiplier} | Poop x${G.poopMultiplier}`,t.eventBrief.className="event-brief active"):e.phase==="build"&&x?(t.eventBrief.textContent=`NEXT DAY EVENT: ${x.route} | Target ${x.targetAmount} | Reward ${T(x.reward)}`,t.eventBrief.className="event-brief next"):e.phase==="build"&&e.turn+1
\u2642Male->MixerS1: A
\u2640Female->TruckS1: D, then S2: right
\u{1F4A9}Poop->ShredderS1: D, then S2: left

Build phase: connect at least one EGG through a scanner to Mixer, Shredder, or Truck before Next Day. One day is one run.

',t.modalActions.innerHTML="",t.modalActions.appendChild(ge(re.actions.startGame,o,"primary-button")),t.modal.classList.remove("equipment-popover","gameover-modal"),t.modal.classList.add("visible")}function Ce(R){let G=U=>`${U>0?"+":""}${T(U)}`,x=U=>`${U>0?"-":""}${T(U)}`,C=R.profit>=0?"cash-positive":"cash-negative",N=(R.mixerPoopFine||0)+(R.truckPoopFine||0)+(R.maleTruckFine||0)+(R.explosionDamage||0)+(R.cardRerollCost||0);t.modalTitle.textContent=`Day ${R.turn} Settlement`,t.modalBody.innerHTML=` +
Net result${T(R.profit)}
-
Chick shipment income${G(E.chickShipmentIncome||0)}
-
Poop shipment income${G(E.poopShipmentIncome||0)}
-
Mixer income${G(E.mixerRevenue||0)}
-
Composter income${G(E.composterIncome||0)}
-
DUD refund income${G(E.dudRefundIncome||0)}
-
Manual combo bonus${G(E.manualComboBonus||0)}
-
Chemical subsidy${G(E.chemicalWeaponSubsidy||0)}
-
Contract bonus${G(E.contractBonus||0)}
-
Penalties${w(O)}
-
Fairies fee${w(E.fairiesTribute?.amount||E.fairiesTribute||0)}
-
Repair worker wages${w(E.repairWorkerWages||0)}
-
Loan repayments${w(E.loanRepayment||0)}
+
Chick shipment income${G(R.chickShipmentIncome||0)}
+
Poop shipment income${G(R.poopShipmentIncome||0)}
+
Mixer income${G(R.mixerRevenue||0)}
+
Composter income${G(R.composterIncome||0)}
+
DUD refund income${G(R.dudRefundIncome||0)}
+
Manual combo bonus${G(R.manualComboBonus||0)}
+
Chemical subsidy${G(R.chemicalWeaponSubsidy||0)}
+
Contract bonus${G(R.contractBonus||0)}
+
Penalties${x(N)}
+
Fairies fee${x(R.fairiesTribute?.amount||R.fairiesTribute||0)}
+
Repair worker wages${x(R.repairWorkerWages||0)}
+
Loan repayments${x(R.loanRepayment||0)}
-

Income ${S(E.revenue)} - losses ${S(E.penalty)} = ${S(E.profit)}

`,t.modalActions.innerHTML="",t.modalActions.appendChild(le("Choose Upgrade Card",r||me,"primary-button")),t.modal.classList.remove("equipment-popover","gameover-modal"),t.modal.classList.add("visible")}function _(E,G){let w=Math.max(0,e.cardEffects?.rescueLoanCharges||0);t.modalTitle.textContent="Emergency Loan Available",t.modalBody.innerHTML=` -

End-of-day cash is below ${S(0)}.

+

Income ${T(R.revenue)} - losses ${T(R.penalty)} = ${T(R.profit)}

`,t.modalActions.innerHTML="",t.modalActions.appendChild(ge("Choose Upgrade Card",r||De,"primary-button")),t.modal.classList.remove("equipment-popover","gameover-modal"),t.modal.classList.add("visible")}function ye(R,G){let x=Math.max(0,e.cardEffects?.rescueLoanCharges||0);t.modalTitle.textContent="Emergency Loan Available",t.modalBody.innerHTML=` +

End-of-day cash is below ${T(0)}.

-
Current money${S(e.cash)}
-
Emergency Loan+${S(500)}
-
Cards remaining${w}
+
Current money${T(e.cash)}
+
Emergency Loan+${T(500)}
+
Cards remaining${x}
-

Use one Emergency Loan to gain ${S(500)} immediately. If cash is still negative, another decision will be shown if another card remains.

`,t.modalActions.innerHTML="",t.modalActions.appendChild(le("Accept Game Over",G,"facility-action danger")),t.modalActions.appendChild(le("Use Emergency Loan",E,"primary-button")),t.modal.classList.remove("equipment-popover","gameover-modal"),t.modal.classList.add("visible")}function U(){let E=e.totals.processed,G=E?Math.round(e.totals.correct/E*100):0,w=di(e);t.modalTitle.textContent=re.phases.gameover,t.modalBody.innerHTML=`

Your cash went negative.

-
Final Score${w.score}
Survival${e.totals.turnsCompleted} days
Final Cash${S(e.cash)}
Accuracy${G}%
+

Use one Emergency Loan to gain ${T(500)} immediately. If cash is still negative, another decision will be shown if another card remains.

`,t.modalActions.innerHTML="",t.modalActions.appendChild(ge("Accept Game Over",G,"facility-action danger")),t.modalActions.appendChild(ge("Use Emergency Loan",R,"primary-button")),t.modal.classList.remove("equipment-popover","gameover-modal"),t.modal.classList.add("visible")}function he(){let R=e.totals.processed,G=R?Math.round(e.totals.correct/R*100):0,x=Pi(e);t.modalTitle.textContent=re.phases.gameover,t.modalBody.innerHTML=`

Your cash went negative.

+
Final Score${x.score}
Survival${e.totals.turnsCompleted} days
Final Cash${T(e.cash)}
Accuracy${G}%
Score breakdown -
Formula
Base = Cash + Total revenue + Factory value \u7E5D\uFF7B\u7E5D\uFF7B\u7E5D\uFF7B0.5 + Contract bonus \u7E5D\uFF7B\u7E5D\uFF7B\u7E5D\uFF7B0.5 = ${S(w.base)}
Daily earned = ceil(Base \u7E5D\uFF7B\u7E5D\uFF7B\u30FB\uFF7D\u30FB\uFF77 Days) = ${S(w.dailyEarned)}
Score = max(0, floor(Base + Daily earned + Correct\u7E5D\uFF7B\u7E5D\uFF7B\u7E5D\uFF7B - Total outflow - Day penalty)) = ${w.score}
-
Factory Value${S(w.factoryValue)}
Total Revenue${S(e.totals.revenue)}
Daily Earned${S(w.dailyEarned)} / day
Total Outflow${S(e.totals.penalty)}
ZUNDA TAX${S(e.totals.zundaTax)}
Fairies Tribute${S(e.totals.fairiesTribute||0)}
Correct Sorts${e.totals.correct}
Auto Sorted${e.totals.autoSorted}
Poop Control${e.totals.poopTrash} trashed / ${e.totals.poopTruck} shipped / ${e.totals.poopMixer} mixer
Explosion Penalty${S(e.totals.explosionDamage)}
Contract Bonus${S(e.totals.contractBonus)}
Day Penalty-${w.dayPenalty}
Contracts${e.totals.contractSuccess} success / ${e.totals.contractFailed} failed
-
`,t.modalActions.innerHTML="",t.modalActions.appendChild(le(re.actions.restart||"Restart",o,"primary-button restart-button")),t.modal.classList.remove("equipment-popover"),t.modal.classList.add("visible","gameover-modal")}function le(E,G,w="primary-button"){let C=document.createElement("button");return C.type="button",C.className=w,C.textContent=E,C.addEventListener("click",G),C}function me(){t.modal.classList.remove("visible","equipment-popover","gameover-modal"),t.modal.style.removeProperty("--popover-x"),t.modal.style.removeProperty("--popover-y")}function fe(){}return{updatePanels:M,updateUI:X,showTitle:j,showTurnResult:g,showEmergencyLoanDecision:_,showGameOver:U,hideModal:me,checkGameOver:fe}}function Qo(e){e.totals.processed+=1}function Ht(e){e.stats.correct+=1,e.totals.correct+=1}function lt(e){e.stats.mistakes+=1,e.totals.mistakes+=1}function ls(e){e.stats.autoSorted+=1,e.totals.autoSorted+=1}function as(e){e.stats.poopSpawned+=1}function cs(e){e.stats.mixerCount+=1,Qo(e)}function us(e){e.stats.trashCount+=1,Qo(e)}function ds(e,t){t==="female"?e.stats.truckFemale+=1:t==="male"&&(e.stats.truckMale+=1),Qo(e)}function _n(e,t){let n=t==="truck"?"poopTruck":t==="trash"?"poopTrash":t==="mixer"?"poopMixer":null;n&&(e.stats[n]+=1,e.totals[n]+=1)}function fs({game:e,currentConveyorSpeed:t,onGameOverCheck:n}){function o(u){return wt(u)*Ln(u)*Wi(e)/Yr(e)}function r(u){let p=$(u.col,u.row),b=1+_i(e,u),x=l(u,b);if(!x.length)return s(u,p),!0;let v=0,P=0;for(let R of x){let V=$(R.col,R.row),Z=d(V);if(Z){Z.stoppedTimer=.85,P+=1;continue}let ge=os(e,{stage:"belt",scannerId:null,route:[V]});ge.sex==="poop"&&as(e),e.chicks.push(ge),Mi(e,u),v+=1}return v>0?(pi(e,p.x,p.y),v>1&&N(e,p.x,p.y-28,`x${v}`,f.green),!0):P===0}function s(u,p){(u.noRouteNoticeCooldown||0)<=0&&(N(e,p.x,p.y-24,"NO BELT: PAUSED",f.warn),u.noRouteNoticeCooldown=2.5)}function l(u,p){let x=pe.map(v=>({col:u.col+v.dc,row:u.row+v.dr})).filter(v=>Pe(v.col,v.row)&&e.conveyorTiles.has(I(v.col,v.row))).map(v=>({p:v,blocked:F(null,$(v.col,v.row))?1:0,roll:Math.random()}));return x.sort((v,P)=>v.blocked-P.blocked||v.roll-P.roll),x.slice(0,Math.max(1,p||1)).map(v=>v.p)}function d(u){return e.chicks.find(p=>p.stage!=="flying"&&Math.hypot(p.x-u.x,p.y-u.y)0;if(e.timeLeft=Math.max(0,e.timeLeft-u),x&&e.timeLeft<=0&&p(),e.timeLeft<=0){e.shutdownTimeLeft>0&&(e.shutdownTimeLeft=Math.max(0,e.shutdownTimeLeft-u));for(let v of e.eggFarms)v.shutterProgress=Math.min(1,(v.shutterProgress||0)+u*2.8)}if(x&&e.timeLeft>0){for(let v of e.eggFarms)if(v.noRouteNoticeCooldown>0&&(v.noRouteNoticeCooldown=Math.max(0,v.noRouteNoticeCooldown-u)),v.nextSpawn-=u,v.nextSpawn<=0){let P=r(v);v.nextSpawn=P?o(v):.15,v.lastInterval=v.nextSpawn}}for(let v=e.chicks.length-1;v>=0;v-=1){let P=e.chicks[v];P.bob+=u*7,P.stoppedTimer>0&&(P.stoppedTimer=Math.max(0,P.stoppedTimer-u)),P.stage!=="queued"&&D(P,v,u)}ce(u),Xt(),Vn(),e.timeLeft<=0&&(e.chicks.length===0?b():e.shutdownTimeLeft<=0&&M())}function M(){if(e.cleanupBlown)return;e.cleanupBlown=!0,e.cleanupTimer=1;let u=[...e.chicks];for(let p of u)go(e,p.sex,p.x,p.y,-Math.PI/2,!0);e.chicks=[];for(let p of e.scanners)p.queue=[];if(Xt(),u.length){let p=u.reduce((x,v)=>x+v.x,0)/u.length,b=u.reduce((x,v)=>x+v.y,0)/u.length;Xe(e,p,b,f.warn,58),N(e,p,b-24,"CLEANUP",f.warn)}}function D(u,p,b){if(L(u)){u.stoppedTimer=.35;return}if(Y(u,b),!(u.route&&u.targetIndex=u.route.length)return!1;let p=u.route[u.targetIndex];return F(u,p)}function F(u,p){if(!p)return!1;for(let b of e.chicks)if(!(u&&b.id===u.id)&&b.stage!=="flying"&&Math.hypot(b.x-p.x,b.y-p.y)1)&&u.push({x:p.x,y:p.y})}function oe(u){let p=e.conveyorMeta?.get?.(u)||{},b=[];Array.isArray(p.outDirs)&&b.push(...p.outDirs),p.dir&&b.push(p.dir);let x=[...new Set(b)].filter(Z=>pe.some(ge=>ge.name===Z)),v=J(u),P=pe.filter(Z=>U(v,Z.name)).map(Z=>Z.name),R=x.filter(Z=>P.includes(Z)),V=P;return R.length?R:V}function X(u){return pe.find(p=>p.name===u)||null}function ae(u,p){return e.scanners.find(b=>b.col<=u&&u<=b.col+1&&b.row-1<=p&&p<=b.row+1)||null}function he(u,p){let b=X(p);if(!b)return null;let x=ae(u.col+b.dc,u.row+b.dr);if(!x)return null;let v=Me(x,"inputA");return v&&v.col===u.col&&v.row===u.row?x:null}function j(u){return Object.entries(e.facilities||{}).find(([,p])=>p?.entry&&p.entry.col===u.col&&p.entry.row===u.row)||null}function g(u){return u?.baseId||u?.id||""}function _(u,p){return u.side==="left"&&p==="left"||u.side==="right"&&p==="right"||u.side==="top"&&p==="up"||u.side==="bottom"&&p==="down"}function U(u,p){let b=X(p);if(!b)return null;let x={col:u.col+b.dc,row:u.row+b.dr};if(Pe(x.col,x.row)&&e.conveyorTiles.has(I(x.col,x.row)))return{type:"conveyor",dirName:p,target:$(x.col,x.row),nextCell:x};let v=he(u,p);if(v)return{type:"scanner",dirName:p,target:de(v),scanner:v};let P=j(u);return P&&_(P[1],p)?{type:"facility",dirName:p,facilityId:P[0],facility:P[1],target:$(u.col,u.row)}:null}function le(u,p){let b=X(p);if(!b)return!1;let x=e.conveyorMeta?.get?.(I(u.col+b.dc,u.row+b.dr));return x?[...new Set([...Array.isArray(x.outDirs)?x.outDirs:[],x.dir].filter(Boolean))].includes(b.opposite):!1}function me(u,p){return p&&(p.type==="facility"||!F(u,p.target))}function fe(u,p,b,x="random"){let v=b.map(Z=>U(p,Z)).filter(Boolean);if(!v.length)return null;if(x==="pass"){let Z=u.prevConveyorCell?J(u.prevConveyorCell):null,ge=Z?yt(Z,p):null,fn=ge?v.find(Qn=>Qn.dirName===ge):null;if(me(u,fn))return fn}if(x&&x!=="random"){let Z=v.find(ge=>ge.dirName===x);if(me(u,Z))return Z}let R=v.filter(Z=>Z.type!=="facility").filter(Z=>!F(u,Z.target)),V=R.length?R:v.filter(Z=>Z.type==="facility");return V.length?V[Math.floor(Math.random()*V.length)]:null}function E(u,p){let b=[];z(b,{x:u.x,y:u.y}),z(b,p),u.route=b.length?b:[{x:u.x,y:u.y}],u.targetIndex=u.route.length>1?1:u.route.length}function G(u,p){let b=Ge(u.x,u.y);if(!b)return $t(p,"OFF GRID");let x=I(b.col,b.row);if(!e.conveyorTiles.has(x))return $t(p,"OFF BELT");let v=e.conveyorMeta?.get(x)||{},P=oe(x);if(!P.length){u.stoppedTimer=.25;return}let R=fe(u,b,P,v.branchMode||"random");if(!R){u.stoppedTimer=.25;return}if(R.type==="facility"){let V=g(R.facility);V==="mixer"?zt(p,R.facility):V==="truck"?Yn(p,R.facility):V==="trash"?zn(p,R.facility):$t(p,"DONE");return}R.type==="scanner"&&(u.pendingScannerId=R.scanner.id),u.prevConveyorCell=x,E(u,R.target)}function w(u){let p=Ge(u.x,u.y);if(!p)return null;let b=I(p.col,p.row);return e.conveyorTiles.has(b)?b:null}function C(u){let p=w(u)||w(u.route?.[u.targetIndex]||u);return t(p)}function O(u,p){let b=w(p);!b||u.lastWearKey===b||(u.lastWearKey=b,Si(e,b))}function Y(u,p){let b=C(u)*p;for(;b>0&&u.targetIndex=R?(u.x=x.x,u.y=x.y,O(u,x),u.targetIndex+=1,b-=R):(u.x+=v/R*b,u.y+=P/R*b,b=0)}}function te(u,p,b){u.stage="queued",u.route=null,u.queueRoute=b?b.map(x=>({x:x.x,y:x.y})):[de(p)],u.scannerId=p.id,p.queue.includes(u.id)||p.queue.push(u.id),se(p)}function se(u){u.queue=u.queue.filter(b=>e.chicks.some(x=>x.id===b&&x.stage==="queued"));let p=y.cell*.86*Ki(e);u.queue.forEach((b,x)=>{let v=e.chicks.find(P=>P.id===b);v&&(ve(v,v.queueRoute||[de(u)],x*p),v.queueIndex=x)})}function ve(u,p,b){if(!p||!p.length)return;if(p.length===1||b<=0){let P=p[p.length-1];u.x=P.x,u.y=P.y;return}let x=b;for(let P=p.length-1;P>0;P-=1){let R=p[P-1],V=p[P],Z=Math.hypot(V.x-R.x,V.y-R.y);if(x<=Z){let ge=1-x/Z;u.x=R.x+(V.x-R.x)*ge,u.y=R.y+(V.y-R.y)*ge;return}x-=Z}let v=p[0];u.x=v.x,u.y=v.y}function ce(u){for(let p of e.scanners){if(p.cooldown>0&&(p.cooldown=Math.max(0,p.cooldown-u)),p.keyPressTime>0&&(p.keyPressTime=Math.max(0,p.keyPressTime-u)),se(p),p.kind!=="auto"||p.cooldown>0||!p.queue.length)continue;let b=p.queue[0],x=e.chicks.findIndex(v=>v.id===b);if(x<0){p.queue.shift();continue}un(x,mo(p,e.chicks[x]),!0)&&(Ti(e,p),p.cooldown=Qe(p,e),ls(e))}}function je(u){let p=fo(e,u);return!p||!p.queue.length?null:e.chicks.find(b=>b.id===p.queue[0])||null}function Q(u,p){if(e.phase!=="running")return;let b=fo(e,u);xe(b,p)}function xe(u,p){if(e.phase!=="running"||!u||u.kind!=="manual"||(u.keyPressTime=.18,u.keyPressSide=p,!u.queue.length))return;let b=e.chicks.findIndex(x=>x.id===u.queue[0]);b>=0&&un(b,p,!1)}function ut(u,p,b){return u.kind==="manual"&&mo(u,p)===b}function qe(u){let p=e.manualCombo?.count||0;if(e.manualCombo||(e.manualCombo={count:0,lastBonus:0}),e.manualCombo.count=0,e.manualCombo.lastBonus=0,e.stats.manualComboFailure=(e.stats.manualComboFailure||0)+1,e.totals.manualComboFailure=(e.totals.manualComboFailure||0)+1,p>=10){let b=de(u);Et(e,10,.25),N(e,b.x,b.y-36,"COMBO RESET",f.danger)}}function Fe(u){e.manualCombo||(e.manualCombo={count:0,lastBonus:0}),e.manualCombo.count=Math.max(0,e.manualCombo.count||0)+1,e.stats.manualComboSuccess=(e.stats.manualComboSuccess||0)+1,e.totals.manualComboSuccess=(e.totals.manualComboSuccess||0)+1;let p=e.manualCombo.count;if(e.manualCombo.lastBonus=0,p<10)return 0;let b=Math.max(1,Math.floor(p/10)),x=e.scanners.filter(V=>V.kind==="manual").length,v=Math.min(1,(20+x*10)/100),P=0;for(let V=0;V0){Ae(e,R),e.stats.manualComboBonus=(e.stats.manualComboBonus||0)+R,e.totals.manualComboBonus=(e.totals.manualComboBonus||0)+R,e.manualCombo.lastBonus=R;let V=de(u);N(e,V.x,V.y-42,`COMBO +${S(R)}`,f.green)}return R}function At(u,p){let b=Me(u,p);if(!b)return null;let x=I(b.col,b.row);if(e.conveyorTiles.has(x))return{...b};let P=(De(e).scannerPorts.get(u.id)?.[p]?.keys||[]).filter(Boolean);if(!P.length)return null;let R=P.map(J);return R.find(Z=>Z.col===b.col&&Z.row===b.row)||R[0]||null}function cn(u,p,b,x){let v=[];z(v,{x:u.x,y:u.y});let P=Me(p,b);return P&&z(v,$(P.col,P.row)),z(v,$(x.col,x.row)),v}function un(u,p,b){let x=e.chicks[u];if(!x||x.stage!=="queued")return!1;let v=St(e,x.scannerId);if(!v)return!1;let P=At(v,p);if(!P)return!b&&v.kind==="manual"&&qe(v),N(e,x.x,x.y-16,p==="left"?"NO LEFT BELT":"NO RIGHT BELT",f.danger),!1;!b&&v.kind==="manual"&&(ut(v,x,p)?Fe(v):qe(v)),v.queue=v.queue.filter(V=>V!==x.id),x.stage="belt",x.route=cn(x,v,p,P),x.targetIndex=x.route.length>1?1:x.route.length,x.scannerId=null,x.nextScannerId=null,x.pendingScannerId=null,x.queueRoute=null,x.queueIndex=-1;let R=b?f.green:p==="left"?f.mixerBlue:f.truckPink;return yi(e,de(v).x,de(v).y,R),N(e,x.x,x.y-20,b?"AUTO":p.toUpperCase(),R),!0}function dt(u){let p=e.chicks[u];return p?(e.chicks.splice(u,1),p):null}function zt(u,p=e.facilities.mixer){if(p?.processingCooldown>0){let R=e.chicks[u];R&&(R.stoppedTimer=.25);return}let b=dt(u);if(!b)return;let{x,y:v}=b;if(cs(e),Lo(e,p),Oo(e,p),b.sex==="poop"){let R=ni(e);_n(e,"mixer"),lt(e),mi(e,x,v),N(e,x,v-18,`-${S(R)}`,f.danger);return}let P=ti(e);b.sex==="male"?Ht(e):lt(e),hi(e,x,v),N(e,x,v-18,`+${S(P)}`,f.green)}function Yn(u,p=e.facilities.truck){let b=dt(u);if(!b)return;let{x,y:v}=b;dn(b.sex,p),wi(e,x,v,b.sex),ds(e,b.sex);let P=ze(e),R=Vr(e,b.sex);if(b.sex==="poop"){let V=oi(e);_n(e,"truck"),R?Ht(e):lt(e),N(e,x,v-18,`-${S(V)}`,f.danger);return}if(b.sex==="female"){R?(Ht(e),N(e,x,v-18,`+${S(Le(e))}`,f.green)):(lt(e),N(e,x,v-18,P==="male"?"REJECT":"NO SALE",f.warn));return}if(R){Ht(e),N(e,x,v-18,`+${S(Le(e))}`,f.green);return}if(Xr(e)){let V=ri(e);lt(e),N(e,x,v-18,`-${S(V)}`,f.danger)}else lt(e),N(e,x,v-18,"REJECT",f.warn)}function zn(u,p=e.facilities.trash){if(p?.processingCooldown>0){let V=e.chicks[u];V&&(V.stoppedTimer=.25);return}let b=dt(u);if(!b)return;let{x,y:v}=b;bi(e,x,v,b.sex),us(e),Lo(e,p),Oo(e,p);let P=Zr(e),R=P.amount>0?` +${S(P.amount)}`:"";if(b.sex==="poop"){_n(e,"trash"),Ht(e);let V=Math.max(0,Number(e.cardEffects?.composter)||0)*3;if(V>0){let Z=Ae(e,V);e.stats.composterIncome=(e.stats.composterIncome||0)+Z,e.totals.composterIncome=(e.totals.composterIncome||0)+Z,N(e,x,v-36,`COMPOST +${S(Z)}`,f.green)}N(e,x,v-18,`CLEAN${R}`,P.amount>0?f.green:f.green)}else lt(e),N(e,x,v-18,P.amount>0?`WASTE${R}`:"WASTE",P.amount>0?f.green:f.ink)}function $t(u,p){let b=dt(u);b&&N(e,b.x,b.y-12,p,f.muted)}function dn(u,p=e.facilities.truck){let b=p||e.facilities.truck;b&&(e.truckCargo.push({sex:u,x:W(22,b.w-22),y:W(66,b.h-24)}),e.truckCargo.length>45&&e.truckCargo.shift())}function Vt(u){let p=Ge(u.x,u.y);if(p){let P=I(p.col,p.row);if(e.conveyorTiles.has(P))return P}let b=Or(e,u.x,u.y);if(!b)return null;let x=J(b),v=$(x.col,x.row);return Math.hypot(u.x-v.x,u.y-v.y)<=y.cell*.55?b:null}function Xt(){let{components:u,cellToComponent:p}=Lr(e);for(let b of e.chicks){if(b.stage==="flying")continue;let x=Vt(b);if(!x)continue;let v=p.get(x),P=u.get(v);P&&(P.count+=1)}for(let b of u.values())b.ratio=b.count/b.capacity;e.congestion=u,e.componentLookup=p}function Vn(){for(let u of e.congestion.values()){if(u.ratio>.8&&u.ratio<1)for(let p of It(u.id).slice(0,3))Math.random()<.06&&vi(e,p.x,p.y-p.radius-8);if(u.ratio>=1){let p=It(u.id);if(!p.length||p.some(v=>v.stage!=="queued"&&v.stoppedTimer<=.2))continue;let b=e.lastExplodedComponent.get(u.id)||0,x=performance.now();x-b>900&&(e.lastExplodedComponent.set(u.id,x),Xn(u))}}}function It(u){return e.chicks.filter(p=>{let b=Vt(p);return b&&e.componentLookup?.get(b)===u})}function Xn(u){let p=It(u.id);if(!p.length)return;for(let v of p)go(e,v.sex,v.x,v.y);e.chicks=e.chicks.filter(v=>!p.some(P=>P.id===v.id));for(let v of e.scanners)v.queue=v.queue.filter(P=>e.chicks.some(R=>R.id===P));for(let v of u.cells){let P=J(v),R=$(P.col,P.row);Xe(e,R.x,R.y,f.danger),it(e,R.x,R.y,6),Rn(e,R.x,R.y,4)}Et(e,18,.75);let b=J(u.cells[0]),x=$(b.col,b.row);N(e,x.x,x.y-22,"100% JAM EXPLOSION",f.danger)}return{activeQueuedChick:je,currentSpawnDelay:o,sortSlot:Q,updateCongestion:Xt,sortScanner:xe,updateRunning:k}}var ie=document.getElementById("gameCanvas"),Da=ie.getContext("2d"),B={shell:document.getElementById("gameShell"),money:document.getElementById("money"),turn:document.getElementById("turn"),timeLeft:document.getElementById("timeLeft"),phase:document.getElementById("phaseLabel"),comboCount:document.getElementById("comboCount"),eventBrief:document.getElementById("eventBrief"),targetBrief:document.getElementById("targetBrief"),speedBrief:document.getElementById("speedBrief"),turnProfit:document.getElementById("turnProfit"),mixerCount:document.getElementById("mixerCount"),truckFemaleCount:document.getElementById("truckFemaleCount"),truckMaleCount:document.getElementById("truckMaleCount"),poopCount:document.getElementById("poopCount"),buildStatus:document.getElementById("buildStatus"),turnSummary:document.getElementById("turnSummary"),contractPanel:document.getElementById("contractPanel"),modal:document.getElementById("modal"),modalTitle:document.getElementById("modalTitle"),modalBody:document.getElementById("modalBody"),modalActions:document.getElementById("modalActions"),hoverTooltip:document.getElementById("hoverTooltip"),manualScannerMonitor:document.getElementById("manualScannerMonitor"),hireRepairmanButton:document.getElementById("hireRepairmanButton"),debug:{panel:document.getElementById("debugPanel"),infiniteCash:document.getElementById("debugInfiniteCash"),dayInput:document.getElementById("debugDayInput"),setDay:document.getElementById("debugSetDayButton"),zeroTimer:document.getElementById("debugZeroTimerButton"),cardSelect:document.getElementById("debugCardSelect"),grantCard:document.getElementById("debugGrantCardButton"),grantAllCards:document.getElementById("debugGrantAllCardsButton"),readout:document.getElementById("debugReadout")},buttons:{s1Left:document.getElementById("scanner1MixerButton"),s1Right:document.getElementById("scanner1TruckButton"),s2Left:document.getElementById("scanner2MixerButton"),s2Right:document.getElementById("scanner2TruckButton"),nextTurn:document.getElementById("nextTurnButton"),conveyor:document.getElementById("buildConveyorButton"),boostConveyor:document.getElementById("buildBoostConveyorButton"),eggFarm:document.getElementById("buildEggFarmButton"),autoScanner:document.getElementById("buildAutoScannerButton"),manualScanner:document.getElementById("buildManualScannerButton"),mixer:document.getElementById("buildMixerButton"),trash:document.getElementById("buildTrashButton"),truck:document.getElementById("buildTruckButton"),erase:document.getElementById("eraseButton"),undo:document.getElementById("undoButton"),redo:document.getElementById("redoButton")}};function Aa(){let e={conveyor:"conveyor",boostConveyor:"boostConveyor",eggFarm:"eggFarm",autoScanner:"autoScanner",manualScanner:"manualScanner",mixer:"mixer",trash:"trash",truck:"truck"};for(let[t,n]of Object.entries(e))B.buttons[t]&&(B.buttons[t].innerHTML=cr(n))}function $a(){B.debug?.panel&&B.debug.cardSelect&&(B.debug.cardSelect.innerHTML=Kt.map(e=>``).join(""))}var a=co(),H,K,Te,Yt;function Ia(e=null){let t=Gn(a,e);return e?t*Ao(a,e):t}function Fa(){Object.assign(a,co()),a.phase="running",a.view={x:0,y:0,scale:1},uo(a),xs(),ye(a),K.hideModal(),Te.updateCongestion(),K.updatePanels(),K.updateUI(),Dt()}function Ba(){let e=Math.max(1,Math.floor(Number(B.debug.dayInput?.value)||1));a.turn=e,B.debug.dayInput&&(B.debug.dayInput.value=String(e)),N(a,ie.width/2-a.view.x,86-a.view.y,`DAY ${e}`,f.green),K.updatePanels(),K.updateUI(),Dt(`Day set to ${e}.`)}function La(){a.timeLeft=0,a.phase==="running"&&ws(),N(a,ie.width/2-a.view.x,86-a.view.y,"TIMER 0",f.warn),K.updatePanels(),K.updateUI(),Dt("Timer set to 0.")}function Oa(){let e=B.debug.cardSelect?.value,t=Wo(a,e);B.debug.grantCard&&(B.debug.grantCard.textContent=t.ok?"Granted":"Grant failed",window.setTimeout(()=>{B.debug.grantCard&&(B.debug.grantCard.textContent="Grant Card")},900)),K.updatePanels(),K.updateUI(),Dt(t.reason)}function Na(){let t=Kt.map(n=>Wo(a,n)).filter(n=>n.ok).length;K.updatePanels(),K.updateUI(),Dt(`Granted ${t}/${Kt.length} cards.`)}function Ga(){B.debug?.infiniteCash?.checked&&a.cash<999999&&(a.cash=999999,a.totals.maxCash=Math.max(a.totals.maxCash,a.cash))}function Dt(e=""){B.debug?.readout&&(B.debug.readout.textContent=[e,`D${a.turn}`,S(a.cash),`Combo ${a.manualCombo?.count||0}`,`Rescue ${a.cardEffects?.rescueLoanCharges||0}`,`Chem ${a.cardEffects?.chemicalWeaponSubsidy||0}`].filter(Boolean).join(" | "))}function Ua(){if(a.phase!=="build")return;if(a.cardDraft?.pending||a.cardTargetPick?.pending){H.fail(a.cardTargetPick?.pending?"Choose upgrade target first.":"Choose an upgrade card first."),K.updatePanels();return}let e=tn(a);if(e.length){H.fail(`Cannot start: ${e[0]}`),K.updatePanels();return}ye(a),Fr(a),Ai(a);let t=Math.max(0,Math.floor(a.lastResult?.profit??a.stats?.profit??0)),n=li(a,t),o=ui(a);a.lastStartFees={zundaTax:n.tax||0,loanRepayment:o.amount||0,fairiesTribute:0},n.tax>0&&N(a,ie.width/2-a.view.x,116-a.view.y,`ZUNDA TAX -${S(n.tax)}`,f.danger),o.amount>0&&N(a,ie.width/2-a.view.x,146-a.view.y,`LOAN -${S(o.amount)}`,f.danger),Hr(a),a.phase="running",a.turn+=1,a.timeLeft=Bt,a.shutdownTimeLeft=0,a.cleanupTimer=null,a.cleanupBlown=!1,a.chicks=[],a.effects=[],a.floatingTexts=[],a.shake={time:0,strength:0},a.truckCargo=[],xs(),a.stats=ao(),a.stats.zundaTax=0,a.stats.loanRepayment=a.lastStartFees?.loanRepayment||0,a.stats.repairWorkerWages=a.pendingRepairWorkerWages||0;let r=a.stats.loanRepayment+a.stats.repairWorkerWages;r>0&&(a.stats.penalty+=r,a.stats.profit-=r),a.pendingRepairWorkerWages=0;for(let s of a.scanners)s.queue=[],s.cooldown=0;for(let s of a.eggFarms)s.nextSpawn=Te.currentSpawnDelay(s),s.lastInterval=s.nextSpawn,s.shutterProgress=0,s.shutterSparked=!1;a.buildTool=null,a.selected=null,a.groupDrag=null,a.selectionBox=null,a.pan=null,Te.updateCongestion(),K.updatePanels(),K.updateUI()}function Wa(){ye(a);let e=Di(a);e.ok?N(a,ie.width/2-a.view.x,96-a.view.y,`Repairman hired -${S(e.cost)}`,f.green):(N(a,ie.width/2-a.view.x,96-a.view.y,e.reason||"Cannot hire repairman.",f.danger),H?.fail?.(e.reason||"Cannot hire repairman.")),K.updatePanels(),K.updateUI()}function _a(){if(a.phase!=="running")return;let e=si(a),t=Qr(a),n=ci(a);if(e.adjusted>0){let s=a.facilities.truck,l=s?s.x+s.w/2:ie.width/2-a.view.x,d=s?s.y+26:96-a.view.y;N(a,l,d,`${e.target==="poop"?"POOP SHIP":"SHIP"} +${S(e.adjusted)}`,f.green)}t?.bonus>0&&N(a,ie.width/2-a.view.x,96-a.view.y,`CONTRACT +${S(t.bonus)}`,f.green),n.amount>0&&N(a,ie.width/2-a.view.x,116-a.view.y,`CHEM SUBSIDY +${S(n.amount)}`,f.green),qr(a);let o=Math.max(1,(a.totals.turnsCompleted||0)+1),r=ai(a,o);if(a.lastBuildFees={fairiesTribute:r.amount||0,reduction:r.reduction||0,turn:o},r.amount>0&&N(a,ie.width/2-a.view.x,146-a.view.y,`FAIRIES -${S(r.amount)}`,f.danger),a.totals.turnsCompleted+=1,a.lastResult={...a.stats,ship:e,contract:t,chemical:n,rescue:{amount:0,used:0,remaining:a.cardEffects?.rescueLoanCharges||0},fairiesTribute:r,turn:a.turn,cash:a.cash},a.cash<0){if((a.cardEffects?.rescueLoanCharges||0)>0){a.phase="loanDecision",K.showEmergencyLoanDecision(bs,Kn);return}a.phase="gameover",K.showGameOver();return}ms()}function ms(){a.phase="build",a.truckCargo=[],a.chicks=[],a.shutdownTimeLeft=0,a.cleanupTimer=null,a.cleanupBlown=!1,a.buildSession+=1,a.undoStack=[],a.redoStack=[],a.timeLeft=0;for(let e of a.scanners)e.queue=[];for(let e of a.eggFarms)e.shutterProgress=0,e.shutterSparked=!1;$i(a),a.contractOffer=Kr(a),Yt&&Yt.prepareDraft(),a.cardTargetPick=null,a.buildTool=null,a.groupDrag=null,a.selectionBox=null,a.pan=null,K.updatePanels(),K.showTurnResult(a.lastResult)}function bs(){if((a.cardEffects?.rescueLoanCharges||0)<=0)return Kn();a.cardEffects.rescueLoanCharges-=1,a.stats.rescueLoan=(a.stats.rescueLoan||0)+500,a.totals.rescueLoan=(a.totals.rescueLoan||0)+500;let e=Ae(a,500);a.lastResult={...a.lastResult||{},...a.stats,rescue:{amount:(a.lastResult?.rescue?.amount||0)+e,used:(a.lastResult?.rescue?.used||0)+1,remaining:a.cardEffects.rescueLoanCharges},cash:a.cash},N(a,ie.width/2-a.view.x,176-a.view.y,`RESCUE +${S(e)}`,f.green),a.cash>=0?ms():(a.cardEffects?.rescueLoanCharges||0)>0?(a.phase="loanDecision",K.showEmergencyLoanDecision(bs,Kn)):Kn()}function Kn(){a.phase="gameover",K.showGameOver()}function ws(){a.shutdownTimeLeft<=0&&(a.shutdownTimeLeft=a.shutdownGraceSeconds||10);for(let e of a.eggFarms){if(e.shutterSparked)continue;e.shutterProgress=.02,e.shutterSparked=!0;let t=$(e.col,e.row);Xe(a,t.x,t.y,f.ink),it(a,t.x,t.y+10,10),N(a,t.x,t.y-34,"SHUT +10s",f.ink)}}function vs(e){a.lastTimestamp||(a.lastTimestamp=e);let t=Math.min((e-a.lastTimestamp)/1e3,.05);a.lastTimestamp=e,Ga(),a.phase==="running"&&Te.updateRunning(t,{closeFarmShutters:ws,completeTurn:_a}),xi(a,t,ie,H.equipmentHitBoxes),Te.updateCongestion(),Xi(Da,ie,a,{activeQueuedChick:Te.activeQueuedChick,selectedObject:H.selectedObject,selectedTitle:H.selectedTitle}),K.updateUI(),Cs(),Dt(),requestAnimationFrame(vs)}function Hn(){return a.view?.scale||1}function an(){a.view||(a.view={x:0,y:0,scale:1}),a.view.scale==null&&(a.view.scale=1)}function qn(e){let t=ie.getBoundingClientRect();return{x:(e.clientX-t.left)*ie.width/t.width,y:(e.clientY-t.top)*ie.height/t.height}}function at(e){let t=qn(e),n=Hn();return{x:(t.x-(a.view?.x||0))/n,y:(t.y-(a.view?.y||0))/n}}function Ka(e){let t=Hn();return{x:e.x*t+(a.view?.x||0),y:e.y*t+(a.view?.y||0)}}function Ha(){let e={left:y.x-90,top:y.y-130,right:y.x+y.cols*y.cell+260,bottom:y.y+y.rows*y.cell+220};if(a.phase==="build")for(let t of mt(a)){let n=bt(t);e.left=Math.min(e.left,n.x-60),e.top=Math.min(e.top,n.y-80),e.right=Math.max(e.right,n.x+n.w+80),e.bottom=Math.max(e.bottom,n.y+n.h+80)}return e}function ps(e,t){if(e<=t)return[e,t];let n=(e+t)/2;return[n,n]}function ct(){an();let e=Hn(),t=Ha(),[n,o]=ps(ie.width-160-t.right*e,110-t.left*e),[r,s]=ps(ie.height-150-t.bottom*e,150-t.top*e);a.view.x=jt(a.view.x,n,o),a.view.y=jt(a.view.y,r,s)}function xs(){an(),a.view.scale=1;let e=a.eggFarms?.[0],t=e?$(e.col,e.row):{x:y.x,y:y.y};a.view.x=280-t.x,a.view.y=190-t.y,ct()}function qa(e){let t=qn(e);an(),a.pan={start:t,viewX:a.view.x,viewY:a.view.y,moved:!1}}function Ya(e){if(!a.pan)return;let t=qn(e);Math.hypot(t.x-a.pan.start.x,t.y-a.pan.start.y)>5&&(a.pan.moved=!0),an(),a.view.x=a.pan.viewX+t.x-a.pan.start.x,a.view.y=a.pan.viewY+t.y-a.pan.start.y,ct()}function za(e){e.preventDefault(),an();let t=qn(e),n=at(e),o=Math.exp(-e.deltaY*.0012),r=jt(Hn()*o,.55,1.8);a.view.scale=r,a.view.x=t.x-n.x*r,a.view.y=t.y-n.y*r,ct(),Ze()}function Va(e){return e?e.type==="conveyor"?{type:"conveyor",id:e.oldKey}:{type:e.type,id:e.ref.id}:null}function Xa(e){return e?e.type==="conveyor"?{type:"conveyor",id:e.oldKey}:e.ref||null:null}function Ze(){B.hoverTooltip&&(B.hoverTooltip.classList.remove("visible"),B.hoverTooltip.setAttribute("aria-hidden","true"),a.hover=null)}function Qa(e,t){if(!B.hoverTooltip||a.phase!=="build"||a.cardTargetPick?.pending||a.groupDrag||a.selectionBox||a.pan)return Ze();let n=Xa(t);if(!n)return Ze();let o=H.selectedTitle(n),r=H.flavorText?H.flavorText(n):"",s=H.disconnectedWarningFor?H.disconnectedWarningFor(n):"",l=H.selectedInfoLines(n).slice(0,7);B.hoverTooltip.innerHTML=`${o}${s?`${s}`:r?`${r}`:""}${l.map(M=>`${M}`).join("")}`;let d=Math.min(window.innerWidth-330,e.clientX+18),k=Math.min(window.innerHeight-220,e.clientY+18);B.hoverTooltip.style.left=`${Math.max(8,d)}px`,B.hoverTooltip.style.top=`${Math.max(8,k)}px`,B.hoverTooltip.classList.add("visible"),B.hoverTooltip.setAttribute("aria-hidden","false"),a.hover={type:t.type,id:t.oldKey||t.ref?.id||null}}function Ja(e){if(a.phase!=="build")return Ze();Qa(e,H.equipmentAtPoint(at(e)))}function ks(e){Ze();let t=at(e),n=H.equipmentAtPoint(t),o=Va(n);if(!o){a.selected=null,a.multiSelected=[],K.updatePanels();return}a.selected=o,a.multiSelected=[o],K.updatePanels();let r=H.selectedObject();r?.type==="scanner"&&r.kind==="manual"&&H.showManualScannerMenu(r),r?.type==="scanner"&&r.kind==="auto"&&H.showAutoScannerMenu(r)}function qt(){a.buildTool=null,a.selected=null,a.multiSelected=[],a.selectionBox=null,a.cardTargetPick?.mode==="autoScannerMenu"&&(a.cardTargetPick=null),K.hideModal(),Ze(),K.updatePanels(),K.updateUI()}function ys(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function Za(e){let t=e?.queue?.[0];return t==null?null:a.chicks.find(n=>n.id===t)||null}function ja(e){if(!e)return'WAIT';let t=e.sex==="poop"?"\u{1F4A9} POOP":e.sex==="male"?"\u2642 MALE":"\u2640 FEMALE";return`${t}`}function ec(e,t){let n=new Set(a.eggFarms.map(D=>I(D.col,D.row))),o=new Map(a.scanners.map(D=>[I(D.col,D.row),D])),r=Me(e,"inputA"),s=r?I(r.col,r.row):null,l=new Set(Object.values(a.facilities||{}).map(D=>D.entry?I(D.entry.col,D.entry.row):null).filter(Boolean)),d=t?.sex||"",k=d==="poop"?"\u{1F4A9}":d==="male"?"\u2642":d==="female"?"\u2640":"",M=[];for(let D=e.row-2;D<=e.row+2;D+=1)for(let L=e.col-2;L<=e.col+2;L+=1){let F=I(L,D),z=["mini-cell"],oe="";L===e.col&&D===e.row?(z.push("scanner"),d&&z.push(d),oe=k||"S"):F===s?(z.push("input"),oe="IN"):n.has(F)?(z.push("egg"),oe="E"):o.has(F)?(z.push("scanner-other"),oe="S"):l.has(F)?(z.push("port"),oe="P"):a.conveyorTiles.has(F)?(z.push("belt"),oe="\u2022"):a.blockedCells?.has?.(F)&&(z.push("blocked"),oe="\xD7"),M.push(`${oe}`)}return`
${M.join("")}
`}function tc(e){let t=Ka($(e.col,e.row)),n=76;return t.x<-n||t.y<-n||t.x>ie.width+n||t.y>ie.height+n}function Cs(){if(!B.manualScannerMonitor)return;if(a.phase!=="running"){B.manualScannerMonitor.classList.remove("visible"),B.manualScannerMonitor.innerHTML="";return}let e=a.scanners.filter(t=>t.kind==="manual"&&tc(t));if(!e.length){B.manualScannerMonitor.classList.remove("visible"),B.manualScannerMonitor.innerHTML="";return}B.manualScannerMonitor.innerHTML=e.map(t=>{let n=Za(t),o=t.keys?.left?.label||(t.slot===0?"A":"Left"),r=t.keys?.right?.label||(t.slot===0?"D":"Right"),s=t.role===0?"MIXER":"WASTE",l=t.role===0?"S2":"TRUCK",d=n?"":" disabled";return` +
Formula
Base = Cash + Total revenue + Factory value \u7E5D\uFF7B\u7E5D\uFF7B\u7E5D\uFF7B0.5 + Contract bonus \u7E5D\uFF7B\u7E5D\uFF7B\u7E5D\uFF7B0.5 = ${T(x.base)}
Daily earned = ceil(Base \u7E5D\uFF7B\u7E5D\uFF7B\u30FB\uFF7D\u30FB\uFF77 Days) = ${T(x.dailyEarned)}
Score = max(0, floor(Base + Daily earned + Correct\u7E5D\uFF7B\u7E5D\uFF7B\u7E5D\uFF7B - Total outflow - Day penalty)) = ${x.score}
+
Factory Value${T(x.factoryValue)}
Total Revenue${T(e.totals.revenue)}
Daily Earned${T(x.dailyEarned)} / day
Total Outflow${T(e.totals.penalty)}
ZUNDA TAX${T(e.totals.zundaTax)}
Fairies Tribute${T(e.totals.fairiesTribute||0)}
Correct Sorts${e.totals.correct}
Auto Sorted${e.totals.autoSorted}
Poop Control${e.totals.poopTrash} trashed / ${e.totals.poopTruck} shipped / ${e.totals.poopMixer} mixer
Explosion Penalty${T(e.totals.explosionDamage)}
Contract Bonus${T(e.totals.contractBonus)}
Day Penalty-${x.dayPenalty}
Contracts${e.totals.contractSuccess} success / ${e.totals.contractFailed} failed
+ `,t.modalActions.innerHTML="",t.modalActions.appendChild(ge(re.actions.restart||"Restart",o,"primary-button restart-button")),t.modal.classList.remove("equipment-popover"),t.modal.classList.add("visible","gameover-modal")}function ge(R,G,x="primary-button"){let C=document.createElement("button");return C.type="button",C.className=x,C.textContent=R,C.addEventListener("click",G),C}function De(){t.modal.classList.remove("visible","equipment-popover","gameover-modal"),t.modal.style.removeProperty("--popover-x"),t.modal.style.removeProperty("--popover-y")}function Ct(){}return{updatePanels:M,updateUI:I,showTitle:fe,showTurnResult:Ce,showEmergencyLoanDecision:ye,showGameOver:he,hideModal:De,checkGameOver:Ct}}function lr(e){e.totals.processed+=1}function Zt(e){e.stats.correct+=1,e.totals.correct+=1}function xt(e){e.stats.mistakes+=1,e.totals.mistakes+=1}function Is(e){e.stats.autoSorted+=1,e.totals.autoSorted+=1}function $s(e){e.stats.poopSpawned+=1}function Fs(e){e.stats.mixerCount+=1,lr(e)}function Bs(e){e.stats.trashCount+=1,lr(e)}function Ls(e,t){t==="female"?e.stats.truckFemale+=1:t==="male"&&(e.stats.truckMale+=1),lr(e)}function jn(e,t){let n=t==="truck"?"poopTruck":t==="trash"?"poopTrash":t==="mixer"?"poopMixer":null;n&&(e.stats[n]+=1,e.totals[n]+=1)}function Os({game:e,currentConveyorSpeed:t,onGameOverCheck:n}){function o(u){return At(u)*Yn(u)*us(e)/ui(e)}function r(u){let p=$(u.col,u.row),w=1+ds(e,u),S=l(u,w);if(!S.length)return s(u,p),!0;let k=0,D=0;for(let F of S){let J=$(F.col,F.row),se=d(J);if(se){se.stoppedTimer=.85,D+=1;continue}let et=Rs(e,{stage:"belt",scannerId:null,route:[J]});et.sex==="poop"&&$s(e),e.chicks.push(et),Yi(e,u),k+=1}return k>0?(Di(e,p.x,p.y),k>1&&K(e,p.x,p.y-28,`x${k}`,f.green),!0):D===0}function s(u,p){(u.noRouteNoticeCooldown||0)<=0&&(K(e,p.x,p.y-24,"NO BELT: PAUSED",f.warn),u.noRouteNoticeCooldown=2.5)}function l(u,p){let S=ue.map(k=>({col:u.col+k.dc,row:u.row+k.dr})).filter(k=>Te(k.col,k.row)&&e.conveyorTiles.has(P(k.col,k.row))).map(k=>({p:k,blocked:L(null,$(k.col,k.row))?1:0,roll:Math.random()}));return S.sort((k,D)=>k.blocked-D.blocked||k.roll-D.roll),S.slice(0,Math.max(1,p||1)).map(k=>k.p)}function d(u){return e.chicks.find(p=>p.stage!=="flying"&&Math.hypot(p.x-u.x,p.y-u.y)0;if(e.timeLeft=Math.max(0,e.timeLeft-u),S&&e.timeLeft<=0&&p(),e.timeLeft<=0){e.shutdownTimeLeft>0&&(e.shutdownTimeLeft=Math.max(0,e.shutdownTimeLeft-u));for(let k of e.eggFarms)k.shutterProgress=Math.min(1,(k.shutterProgress||0)+u*2.8)}if(S&&e.timeLeft>0){for(let k of e.eggFarms)if(k.noRouteNoticeCooldown>0&&(k.noRouteNoticeCooldown=Math.max(0,k.noRouteNoticeCooldown-u)),k.nextSpawn-=u,k.nextSpawn<=0){let D=r(k);k.nextSpawn=D?o(k):.15,k.lastInterval=k.nextSpawn}}for(let k=e.chicks.length-1;k>=0;k-=1){let D=e.chicks[k];D.bob+=u*7,D.stoppedTimer>0&&(D.stoppedTimer=Math.max(0,D.stoppedTimer-u)),D.stage!=="queued"&&g(D,k,u)}ie(u),tn(),Sn(),e.timeLeft<=0&&(e.chicks.length===0?w():e.shutdownTimeLeft<=0&&M())}function M(){if(e.cleanupBlown)return;e.cleanupBlown=!0,e.cleanupTimer=1;let u=[...e.chicks];for(let p of u)Fo(e,p.sex,p.x,p.y,-Math.PI/2,!0);e.chicks=[];for(let p of e.scanners)p.queue=[];if(tn(),u.length){let p=u.reduce((S,k)=>S+k.x,0)/u.length,w=u.reduce((S,k)=>S+k.y,0)/u.length;rt(e,p,w,f.warn,58),K(e,p,w-24,"CLEANUP",f.warn)}}function g(u,p,w){if(A(u)){u.stoppedTimer=.35;return}if(C(u,w),!(u.route&&u.targetIndex=u.route.length)return!1;let p=u.route[u.targetIndex];return L(u,p)}function L(u,p){if(!p)return!1;for(let w of e.chicks)if(!(u&&w.id===u.id)&&w.stage!=="flying"&&Math.hypot(w.x-p.x,w.y-p.y)1)&&u.push({x:p.x,y:p.y})}function ee(u){let p=e.conveyorMeta?.get?.(u)||{},w=[];Array.isArray(p.outDirs)&&w.push(...p.outDirs),p.dir&&w.push(p.dir);let S=[...new Set(w)].filter(se=>ue.some(et=>et.name===se)),k=j(u),D=ue.filter(se=>W(k,se.name)).map(se=>se.name),F=S.filter(se=>D.includes(se)),J=D;return F.length?F:J}function q(u){return ue.find(p=>p.name===u)||null}function le(u,p){return e.scanners.find(w=>w.col<=u&&u<=w.col+1&&w.row-1<=p&&p<=w.row+1)||null}function me(u,p){let w=q(p);if(!w)return null;let S=le(u.col+w.dc,u.row+w.dr);if(!S)return null;let k=Pe(S,"inputA");return k&&k.col===u.col&&k.row===u.row?S:null}function ne(u){return Object.entries(e.facilities||{}).find(([,p])=>p?.entry&&p.entry.col===u.col&&p.entry.row===u.row)||null}function I(u){return u?.baseId||u?.id||""}function _(u,p){return u.side==="left"&&p==="left"||u.side==="right"&&p==="right"||u.side==="top"&&p==="up"||u.side==="bottom"&&p==="down"}function W(u,p){let w=q(p);if(!w)return null;let S={col:u.col+w.dc,row:u.row+w.dr};if(Te(S.col,S.row)&&e.conveyorTiles.has(P(S.col,S.row)))return{type:"conveyor",dirName:p,target:$(S.col,S.row),nextCell:S};let k=me(u,p);if(k)return{type:"scanner",dirName:p,target:pe(k),scanner:k};let D=ne(u);return D&&_(D[1],p)?{type:"facility",dirName:p,facilityId:D[0],facility:D[1],target:$(u.col,u.row)}:null}function fe(u,p){let w=q(p);if(!w)return!1;let S=e.conveyorMeta?.get?.(P(u.col+w.dc,u.row+w.dr));return S?[...new Set([...Array.isArray(S.outDirs)?S.outDirs:[],S.dir].filter(Boolean))].includes(w.opposite):!1}function Ce(u){return ue.filter(p=>Te(u.col+p.dc,u.row+p.dr)&&e.conveyorTiles.has(P(u.col+p.dc,u.row+p.dr))).map(p=>p.name)}function ye(u,p,w="random"){let S=Ce(u);if(S.length<3)return p;if(w&&w!=="random"&&w!=="pass"&&S.includes(w))return[w];let k=new Set(S.filter(J=>fe(u,J))),D=S.filter(J=>!k.has(J));if(D.length)return D;let F=p.filter(J=>!k.has(J));return F.length?F:p}function he(u,p){return p&&(p.type==="facility"||!L(u,p.target))}function ge(u,p,w,S="random"){let k=w.map(se=>W(p,se)).filter(Boolean);if(!k.length)return null;let F=k.filter(se=>se.type!=="facility").filter(se=>!L(u,se.target)),J=F.length?F:k.filter(se=>se.type==="facility");return J.length?J[Math.floor(Math.random()*J.length)]:null}function De(u,p){let w=[];V(w,{x:u.x,y:u.y}),V(w,p),u.route=w.length?w:[{x:u.x,y:u.y}],u.targetIndex=u.route.length>1?1:u.route.length}function Ct(u,p){let w=Ge(u.x,u.y);if(!w)return Mt(p,"OFF GRID");let S=P(w.col,w.row);if(!e.conveyorTiles.has(S))return Mt(p,"OFF BELT");let k=e.conveyorMeta?.get(S)||{},D=ye(w,ee(S),k.branchMode||"random");if(!D.length){u.stoppedTimer=.25;return}let F=ge(u,w,D,k.branchMode||"random");if(!F){u.stoppedTimer=.25;return}if(F.type==="facility"){let J=I(F.facility);J==="mixer"?vn(p,F.facility):J==="truck"?oo(p,F.facility):J==="trash"?kn(p,F.facility):Mt(p,"DONE");return}F.type==="scanner"&&(u.pendingScannerId=F.scanner.id),u.prevConveyorCell=S,De(u,F.target)}function R(u){let p=Ge(u.x,u.y);if(!p)return null;let w=P(p.col,p.row);return e.conveyorTiles.has(w)?w:null}function G(u){let p=R(u)||R(u.route?.[u.targetIndex]||u);return t(p)}function x(u,p){let w=R(p);!w||u.lastWearKey===w||(u.lastWearKey=w,qi(e,w))}function C(u,p){let w=G(u)*p;for(;w>0&&u.targetIndex=F?(u.x=S.x,u.y=S.y,x(u,S),u.targetIndex+=1,w-=F):(u.x+=k/F*w,u.y+=D/F*w,w=0)}}function N(u,p,w){u.stage="queued",u.route=null,u.queueRoute=w?w.map(S=>({x:S.x,y:S.y})):[pe(p)],u.scannerId=p.id,p.queue.includes(u.id)||p.queue.push(u.id),U(p)}function U(u){u.queue=u.queue.filter(w=>e.chicks.some(S=>S.id===w&&S.stage==="queued"));let p=y.cell*.86*fs(e);u.queue.forEach((w,S)=>{let k=e.chicks.find(D=>D.id===w);k&&(Y(k,k.queueRoute||[pe(u)],S*p),k.queueIndex=S)})}function Y(u,p,w){if(!p||!p.length)return;if(p.length===1||w<=0){let D=p[p.length-1];u.x=D.x,u.y=D.y;return}let S=w;for(let D=p.length-1;D>0;D-=1){let F=p[D-1],J=p[D],se=Math.hypot(J.x-F.x,J.y-F.y);if(S<=se){let et=1-S/se;u.x=F.x+(J.x-F.x)*et,u.y=F.y+(J.y-F.y)*et;return}S-=se}let k=p[0];u.x=k.x,u.y=k.y}function ie(u){for(let p of e.scanners){if(p.cooldown>0&&(p.cooldown=Math.max(0,p.cooldown-u)),p.keyPressTime>0&&(p.keyPressTime=Math.max(0,p.keyPressTime-u)),U(p),p.kind!=="auto"||p.cooldown>0||!p.queue.length)continue;let w=p.queue[0],S=e.chicks.findIndex(k=>k.id===w);if(S<0){p.queue.shift();continue}St(S,Mo(p,e.chicks[S]),!0)&&(Vi(e,p),p.cooldown=it(p,e),Is(e))}}function ae(u){let p=vo(e,u);return!p||!p.queue.length?null:e.chicks.find(w=>w.id===p.queue[0])||null}function we(u,p){if(e.phase!=="running")return;let w=vo(e,u);Ee(w,p)}function Ee(u,p){if(e.phase!=="running"||!u||u.kind!=="manual"||(u.keyPressTime=.18,u.keyPressSide=p,!u.queue.length))return;let w=e.chicks.findIndex(S=>S.id===u.queue[0]);w>=0&&St(w,p,!1)}function B(u,p,w){return u.kind==="manual"&&Mo(u,p)===w}function Re(u){let p=e.manualCombo?.count||0;if(e.manualCombo||(e.manualCombo={count:0,lastBonus:0}),e.manualCombo.count=0,e.manualCombo.lastBonus=0,e.stats.manualComboFailure=(e.stats.manualComboFailure||0)+1,e.totals.manualComboFailure=(e.totals.manualComboFailure||0)+1,p>=10){let w=pe(u);Lt(e,10,.25),K(e,w.x,w.y-36,"COMBO RESET",f.danger)}}function ze(u){e.manualCombo||(e.manualCombo={count:0,lastBonus:0}),e.manualCombo.count=Math.max(0,e.manualCombo.count||0)+1,e.stats.manualComboSuccess=(e.stats.manualComboSuccess||0)+1,e.totals.manualComboSuccess=(e.totals.manualComboSuccess||0)+1;let p=e.manualCombo.count;if(e.manualCombo.lastBonus=0,p<10)return 0;let w=Math.max(1,Math.floor(p/10)),S=e.scanners.filter(J=>J.kind==="manual").length,k=Math.min(1,(20+S*10)/100),D=0;for(let J=0;J0){Le(e,F),e.stats.manualComboBonus=(e.stats.manualComboBonus||0)+F,e.totals.manualComboBonus=(e.totals.manualComboBonus||0)+F,e.manualCombo.lastBonus=F;let J=pe(u);K(e,J.x,J.y-42,`COMBO +${T(F)}`,f.green)}return F}function je(u,p){let w=Pe(u,p);if(!w)return null;let S=P(w.col,w.row);if(e.conveyorTiles.has(S))return{...w};let D=(Ue(e).scannerPorts.get(u.id)?.[p]?.keys||[]).filter(Boolean);if(!D.length)return null;let F=D.map(j);return F.find(se=>se.col===w.col&&se.row===w.row)||F[0]||null}function xe(u,p,w,S){let k=[];V(k,{x:u.x,y:u.y});let D=Pe(p,w);return D&&V(k,$(D.col,D.row)),V(k,$(S.col,S.row)),k}function St(u,p,w){let S=e.chicks[u];if(!S||S.stage!=="queued")return!1;let k=It(e,S.scannerId);if(!k)return!1;let D=je(k,p);if(!D)return!w&&k.kind==="manual"&&Re(k),K(e,S.x,S.y-16,p==="left"?"NO LEFT BELT":"NO RIGHT BELT",f.danger),!1;!w&&k.kind==="manual"&&(B(k,S,p)?ze(k):Re(k)),k.queue=k.queue.filter(J=>J!==S.id),S.stage="belt",S.route=xe(S,k,p,D),S.targetIndex=S.route.length>1?1:S.route.length,S.scannerId=null,S.nextScannerId=null,S.pendingScannerId=null,S.queueRoute=null,S.queueIndex=-1;let F=w?f.green:p==="left"?f.mixerBlue:f.truckPink;return Ii(e,pe(k).x,pe(k).y,F),K(e,S.x,S.y-20,w?"AUTO":p.toUpperCase(),F),!0}function Ye(u){let p=e.chicks[u];return p?(e.chicks.splice(u,1),p):null}function vn(u,p=e.facilities.mixer){if(p?.processingCooldown>0){let F=e.chicks[u];F&&(F.stoppedTimer=.25);return}let w=Ye(u);if(!w)return;let{x:S,y:k}=w;if(Fs(e),Vo(e,p),zo(e,p),w.sex==="poop"){let F=vi(e);jn(e,"mixer"),xt(e),Fi(e,S,k),K(e,S,k-18,`-${T(F)}`,f.danger);return}let D=xi(e);w.sex==="male"?Zt(e):xt(e),$i(e,S,k),K(e,S,k-18,`+${T(D)}`,f.green)}function oo(u,p=e.facilities.truck){let w=Ye(u);if(!w)return;let{x:S,y:k}=w;Cn(w.sex,p),Li(e,S,k,w.sex),Ls(e,w.sex);let D=nt(e),F=fi(e,w.sex);if(w.sex==="poop"){let J=ki(e);jn(e,"truck"),F?Zt(e):xt(e),K(e,S,k-18,`-${T(J)}`,f.danger);return}if(w.sex==="female"){F?(Zt(e),K(e,S,k-18,`+${T(Ke(e))}`,f.green)):(xt(e),K(e,S,k-18,D==="male"?"REJECT":"NO SALE",f.warn));return}if(F){Zt(e),K(e,S,k-18,`+${T(Ke(e))}`,f.green);return}if(pi(e)){let J=Ci(e);xt(e),K(e,S,k-18,`-${T(J)}`,f.danger)}else xt(e),K(e,S,k-18,"REJECT",f.warn)}function kn(u,p=e.facilities.trash){if(p?.processingCooldown>0){let J=e.chicks[u];J&&(J.stoppedTimer=.25);return}let w=Ye(u);if(!w)return;let{x:S,y:k}=w;Bi(e,S,k,w.sex),Bs(e),Vo(e,p),zo(e,p);let D=mi(e),F=D.amount>0?` +${T(D.amount)}`:"";if(w.sex==="poop"){jn(e,"trash"),Zt(e);let J=Math.max(0,Number(e.cardEffects?.composter)||0)*3;if(J>0){let se=Le(e,J);e.stats.composterIncome=(e.stats.composterIncome||0)+se,e.totals.composterIncome=(e.totals.composterIncome||0)+se,K(e,S,k-36,`COMPOST +${T(se)}`,f.green)}K(e,S,k-18,`CLEAN${F}`,D.amount>0?f.green:f.green)}else xt(e),K(e,S,k-18,D.amount>0?`WASTE${F}`:"WASTE",D.amount>0?f.green:f.ink)}function Mt(u,p){let w=Ye(u);w&&K(e,w.x,w.y-12,p,f.muted)}function Cn(u,p=e.facilities.truck){let w=p||e.facilities.truck;w&&(e.truckCargo.push({sex:u,x:H(22,w.w-22),y:H(66,w.h-24)}),e.truckCargo.length>45&&e.truckCargo.shift())}function Gt(u){let p=Ge(u.x,u.y);if(p){let D=P(p.col,p.row);if(e.conveyorTiles.has(D))return D}let w=ti(e,u.x,u.y);if(!w)return null;let S=j(w),k=$(S.col,S.row);return Math.hypot(u.x-k.x,u.y-k.y)<=y.cell*.55?w:null}function tn(){let{components:u,cellToComponent:p}=ei(e);for(let w of e.chicks){if(w.stage==="flying")continue;let S=Gt(w);if(!S)continue;let k=p.get(S),D=u.get(k);D&&(D.count+=1)}for(let w of u.values())w.ratio=w.count/w.capacity;e.congestion=u,e.componentLookup=p}function Sn(){for(let u of e.congestion.values()){if(u.ratio>.8&&u.ratio<1)for(let p of nn(u.id).slice(0,3))Math.random()<.06&&Oi(e,p.x,p.y-p.radius-8);if(u.ratio>=1){let p=nn(u.id);if(!p.length||p.some(k=>k.stage!=="queued"&&k.stoppedTimer<=.2))continue;let w=e.lastExplodedComponent.get(u.id)||0,S=performance.now();S-w>900&&(e.lastExplodedComponent.set(u.id,S),ro(u))}}}function nn(u){return e.chicks.filter(p=>{let w=Gt(p);return w&&e.componentLookup?.get(w)===u})}function ro(u){let p=nn(u.id);if(!p.length)return;for(let k of p)Fo(e,k.sex,k.x,k.y);e.chicks=e.chicks.filter(k=>!p.some(D=>D.id===k.id));for(let k of e.scanners)k.queue=k.queue.filter(D=>e.chicks.some(F=>F.id===D));for(let k of u.cells){let D=j(k),F=$(D.col,D.row);rt(e,F.x,F.y,f.danger),ht(e,F.x,F.y,6),Gn(e,F.x,F.y,4)}Lt(e,18,.75);let w=j(u.cells[0]),S=$(w.col,w.row);K(e,S.x,S.y-22,"100% JAM EXPLOSION",f.danger)}return{activeQueuedChick:ae,currentSpawnDelay:o,sortSlot:we,updateCongestion:tn,sortScanner:Ee,updateRunning:v}}var ce=document.getElementById("gameCanvas"),dc=ce.getContext("2d"),O={shell:document.getElementById("gameShell"),money:document.getElementById("money"),turn:document.getElementById("turn"),timeLeft:document.getElementById("timeLeft"),phase:document.getElementById("phaseLabel"),comboCount:document.getElementById("comboCount"),eventBrief:document.getElementById("eventBrief"),targetBrief:document.getElementById("targetBrief"),speedBrief:document.getElementById("speedBrief"),turnProfit:document.getElementById("turnProfit"),mixerCount:document.getElementById("mixerCount"),truckFemaleCount:document.getElementById("truckFemaleCount"),truckMaleCount:document.getElementById("truckMaleCount"),poopCount:document.getElementById("poopCount"),buildStatus:document.getElementById("buildStatus"),turnSummary:document.getElementById("turnSummary"),contractPanel:document.getElementById("contractPanel"),modal:document.getElementById("modal"),modalTitle:document.getElementById("modalTitle"),modalBody:document.getElementById("modalBody"),modalActions:document.getElementById("modalActions"),hoverTooltip:document.getElementById("hoverTooltip"),manualScannerMonitor:document.getElementById("manualScannerMonitor"),cardInventory:document.getElementById("cardInventory"),debug:{panel:document.getElementById("debugPanel"),infiniteCash:document.getElementById("debugInfiniteCash"),dayInput:document.getElementById("debugDayInput"),setDay:document.getElementById("debugSetDayButton"),zeroTimer:document.getElementById("debugZeroTimerButton"),cardSelect:document.getElementById("debugCardSelect"),grantCard:document.getElementById("debugGrantCardButton"),grantAllCards:document.getElementById("debugGrantAllCardsButton"),readout:document.getElementById("debugReadout")},buttons:{s1Left:document.getElementById("scanner1MixerButton"),s1Right:document.getElementById("scanner1TruckButton"),s2Left:document.getElementById("scanner2MixerButton"),s2Right:document.getElementById("scanner2TruckButton"),nextTurn:document.getElementById("nextTurnButton"),conveyor:document.getElementById("buildConveyorButton"),boostConveyor:document.getElementById("buildBoostConveyorButton"),eggFarm:document.getElementById("buildEggFarmButton"),autoScanner:document.getElementById("buildAutoScannerButton"),manualScanner:document.getElementById("buildManualScannerButton"),maintenanceRoom:document.getElementById("buildMaintenanceRoomButton"),mixer:document.getElementById("buildMixerButton"),trash:document.getElementById("buildTrashButton"),truck:document.getElementById("buildTruckButton"),erase:document.getElementById("eraseButton"),undo:document.getElementById("undoButton"),redo:document.getElementById("redoButton")}};function fc(){let e={conveyor:"conveyor",boostConveyor:"boostConveyor",eggFarm:"eggFarm",autoScanner:"autoScanner",manualScanner:"manualScanner",maintenanceRoom:"maintenanceRoom",mixer:"mixer",trash:"trash",truck:"truck"};for(let[t,n]of Object.entries(e))O.buttons[t]&&(O.buttons[t].innerHTML=Rr(n))}function pc(){O.debug?.panel&&O.debug.cardSelect&&(O.debug.cardSelect.innerHTML=bt.map(e=>``).join(""))}var a=bo(),X,Q,Ae,en;function yc(e=null){let t=Xn(a,e);return e?t*Ko(a,e):t}function hc(){Object.assign(a,bo()),a.phase="running",a.view={x:0,y:0,scale:1},xo(a),qs(),ke(a),Q.hideModal(),Ae.updateCongestion(),Q.updatePanels(),Q.updateUI(),Wt()}function mc(){let e=Math.max(1,Math.floor(Number(O.debug.dayInput?.value)||1));a.turn=e,O.debug.dayInput&&(O.debug.dayInput.value=String(e)),K(a,ce.width/2-a.view.x,86-a.view.y,`DAY ${e}`,f.green),Q.updatePanels(),Q.updateUI(),Wt(`Day set to ${e}.`)}function wc(){a.timeLeft=0,a.phase==="running"&&_s(),K(a,ce.width/2-a.view.x,86-a.view.y,"TIMER 0",f.warn),Q.updatePanels(),Q.updateUI(),Wt("Timer set to 0.")}function bc(){let e=O.debug.cardSelect?.value,t=Jo(a,e);O.debug.grantCard&&(O.debug.grantCard.textContent=t.ok?"Granted":"Grant failed",window.setTimeout(()=>{O.debug.grantCard&&(O.debug.grantCard.textContent="Grant Card")},900)),Q.updatePanels(),Q.updateUI(),Wt(t.reason)}function xc(){let t=bt.map(n=>Jo(a,n)).filter(n=>n.ok).length;Q.updatePanels(),Q.updateUI(),Wt(`Granted ${t}/${bt.length} cards.`)}function vc(){O.debug?.infiniteCash?.checked&&a.cash<999999&&(a.cash=999999,a.totals.maxCash=Math.max(a.totals.maxCash,a.cash))}function Wt(e=""){O.debug?.readout&&(O.debug.readout.textContent=[e,`D${a.turn}`,T(a.cash),`Combo ${a.manualCombo?.count||0}`,`Rescue ${a.cardEffects?.rescueLoanCharges||0}`,`Chem ${a.cardEffects?.chemicalWeaponSubsidy||0}`].filter(Boolean).join(" | "))}function kc(){if(a.phase!=="build")return;if(a.cardDraft?.pending||a.cardTargetPick?.pending){X.fail(a.cardTargetPick?.pending?"Choose upgrade target first.":"Choose an upgrade card first."),Q.updatePanels();return}let e=dn(a);if(e.length){X.fail(`Cannot start: ${e[0]}`),Q.updatePanels();return}ke(a),jr(a);let t=Ji(a),n=Math.max(0,Math.floor(a.lastResult?.profit??a.stats?.profit??0)),o=Ti(a,n),r=Ri(a);a.lastStartFees={zundaTax:o.tax||0,loanRepayment:r.amount||0,fairiesTribute:0},o.tax>0&&K(a,ce.width/2-a.view.x,116-a.view.y,`ZUNDA TAX -${T(o.tax)}`,f.danger),r.amount>0&&K(a,ce.width/2-a.view.x,146-a.view.y,`LOAN -${T(r.amount)}`,f.danger),ai(a),a.phase="running",a.turn+=1,a.timeLeft=Kt,a.shutdownTimeLeft=0,a.cleanupTimer=null,a.cleanupBlown=!1,a.chicks=[],a.effects=[],a.floatingTexts=[],a.shake={time:0,strength:0},a.truckCargo=[],qs(),a.stats=wo(),a.stats.zundaTax=0,a.stats.loanRepayment=a.lastStartFees?.loanRepayment||0,a.stats.repairWorkerWages=t.wages||0;let s=a.stats.loanRepayment;s>0&&(a.stats.penalty+=s,a.stats.profit-=s),a.stats.repairWorkerWages>0&&(a.cash-=a.stats.repairWorkerWages,a.stats.penalty+=a.stats.repairWorkerWages,a.stats.profit-=a.stats.repairWorkerWages,a.totals.repairWorkerWages=(a.totals.repairWorkerWages||0)+a.stats.repairWorkerWages,a.totals.penalty=(a.totals.penalty||0)+a.stats.repairWorkerWages,a.totals.profit=(a.totals.profit||0)-a.stats.repairWorkerWages,K(a,ce.width/2-a.view.x,176-a.view.y,`REPAIR WAGES -${T(a.stats.repairWorkerWages)}`,f.danger)),a.pendingRepairWorkerWages=0;for(let l of a.scanners)l.queue=[],l.cooldown=0;for(let l of a.eggFarms)l.nextSpawn=Ae.currentSpawnDelay(l),l.lastInterval=l.nextSpawn,l.shutterProgress=0,l.shutterSparked=!1;a.buildTool=null,a.selected=null,a.groupDrag=null,a.selectionBox=null,a.pan=null,Ae.updateCongestion(),Q.updatePanels(),Q.updateUI()}function Cc(){if(a.phase!=="running")return;let e=Mi(a),t=yi(a),n=Ei(a);if(e.adjusted>0){let s=a.facilities.truck,l=s?s.x+s.w/2:ce.width/2-a.view.x,d=s?s.y+26:96-a.view.y;K(a,l,d,`${e.target==="poop"?"POOP SHIP":"SHIP"} +${T(e.adjusted)}`,f.green)}t?.bonus>0&&K(a,ce.width/2-a.view.x,96-a.view.y,`CONTRACT +${T(t.bonus)}`,f.green),n.amount>0&&K(a,ce.width/2-a.view.x,116-a.view.y,`CHEM SUBSIDY +${T(n.amount)}`,f.green),ci(a);let o=Math.max(1,(a.totals.turnsCompleted||0)+1),r=gi(a,o);if(a.lastBuildFees={fairiesTribute:r.amount||0,reduction:r.reduction||0,turn:o},r.amount>0&&K(a,ce.width/2-a.view.x,146-a.view.y,`FAIRIES -${T(r.amount)}`,f.danger),a.totals.turnsCompleted+=1,a.lastResult={...a.stats,ship:e,contract:t,chemical:n,rescue:{amount:0,used:0,remaining:a.cardEffects?.rescueLoanCharges||0},fairiesTribute:r,turn:a.turn,cash:a.cash},a.cash<0){if((a.cardEffects?.rescueLoanCharges||0)>0){a.phase="loanDecision",Q.showEmergencyLoanDecision(Ks,eo);return}a.phase="gameover",Q.showGameOver();return}Us()}function Us(){a.phase="build",a.truckCargo=[],a.chicks=[],a.shutdownTimeLeft=0,a.cleanupTimer=null,a.cleanupBlown=!1,a.buildSession+=1,a.undoStack=[],a.redoStack=[],a.timeLeft=0;for(let e of a.scanners)e.queue=[];for(let e of a.eggFarms)e.shutterProgress=0,e.shutterSparked=!1;Zi(a),a.contractOffer=li(a),en&&en.prepareDraft(),a.cardTargetPick=null,a.buildTool=null,a.groupDrag=null,a.selectionBox=null,a.pan=null,Q.updatePanels(),Q.showTurnResult(a.lastResult)}function Ks(){if((a.cardEffects?.rescueLoanCharges||0)<=0)return eo();a.cardEffects.rescueLoanCharges-=1,a.stats.rescueLoan=(a.stats.rescueLoan||0)+500,a.totals.rescueLoan=(a.totals.rescueLoan||0)+500;let e=Le(a,500);a.lastResult={...a.lastResult||{},...a.stats,rescue:{amount:(a.lastResult?.rescue?.amount||0)+e,used:(a.lastResult?.rescue?.used||0)+1,remaining:a.cardEffects.rescueLoanCharges},cash:a.cash},K(a,ce.width/2-a.view.x,176-a.view.y,`RESCUE +${T(e)}`,f.green),a.cash>=0?Us():(a.cardEffects?.rescueLoanCharges||0)>0?(a.phase="loanDecision",Q.showEmergencyLoanDecision(Ks,eo)):eo()}function eo(){a.phase="gameover",Q.showGameOver()}function _s(){a.shutdownTimeLeft<=0&&(a.shutdownTimeLeft=a.shutdownGraceSeconds||10);for(let e of a.eggFarms){if(e.shutterSparked)continue;e.shutterProgress=.02,e.shutterSparked=!0;let t=$(e.col,e.row);rt(a,t.x,t.y,f.ink),ht(a,t.x,t.y+10,10),K(a,t.x,t.y-34,"SHUT +10s",f.ink)}}function Hs(e){a.lastTimestamp||(a.lastTimestamp=e);let t=Math.min((e-a.lastTimestamp)/1e3,.05);a.lastTimestamp=e,vc(),a.phase==="running"&&Ae.updateRunning(t,{closeFarmShutters:_s,completeTurn:Cc}),Ni(a,t,ce,X.equipmentHitBoxes),Ae.updateCongestion(),bs(dc,ce,a,{activeQueuedChick:Ae.activeQueuedChick,selectedObject:X.selectedObject,selectedTitle:X.selectedTitle}),Q.updateUI(),Vs(),Wt(),requestAnimationFrame(Hs)}function to(){return a.view?.scale||1}function xn(){a.view||(a.view={x:0,y:0,scale:1}),a.view.scale==null&&(a.view.scale=1)}function no(e){let t=ce.getBoundingClientRect();return{x:(e.clientX-t.left)*ce.width/t.width,y:(e.clientY-t.top)*ce.height/t.height}}function vt(e){let t=no(e),n=to();return{x:(t.x-(a.view?.x||0))/n,y:(t.y-(a.view?.y||0))/n}}function Sc(e){let t=to();return{x:e.x*t+(a.view?.x||0),y:e.y*t+(a.view?.y||0)}}function Mc(){let e={left:y.x-90,top:y.y-130,right:y.x+y.cols*y.cell+260,bottom:y.y+y.rows*y.cell+220};if(a.phase==="build")for(let t of Rt(a)){let n=Pt(t);e.left=Math.min(e.left,n.x-60),e.top=Math.min(e.top,n.y-80),e.right=Math.max(e.right,n.x+n.w+80),e.bottom=Math.max(e.bottom,n.y+n.h+80)}return e}function Ns(e,t){if(e<=t)return[e,t];let n=(e+t)/2;return[n,n]}function kt(){xn();let e=to(),t=Mc(),[n,o]=Ns(ce.width-160-t.right*e,110-t.left*e),[r,s]=Ns(ce.height-150-t.bottom*e,150-t.top*e);a.view.x=cn(a.view.x,n,o),a.view.y=cn(a.view.y,r,s)}function qs(){xn(),a.view.scale=1;let e=a.eggFarms?.[0],t=e?$(e.col,e.row):{x:y.x,y:y.y};a.view.x=280-t.x,a.view.y=190-t.y,kt()}function Tc(e){let t=no(e);xn(),a.pan={start:t,viewX:a.view.x,viewY:a.view.y,moved:!1}}function gc(e){if(!a.pan)return;let t=no(e);Math.hypot(t.x-a.pan.start.x,t.y-a.pan.start.y)>5&&(a.pan.moved=!0),xn(),a.view.x=a.pan.viewX+t.x-a.pan.start.x,a.view.y=a.pan.viewY+t.y-a.pan.start.y,kt()}function Ec(e){e.preventDefault(),xn();let t=no(e),n=vt(e),o=Math.exp(-e.deltaY*.0012),r=cn(to()*o,.55,1.8);a.view.scale=r,a.view.x=t.x-n.x*r,a.view.y=t.y-n.y*r,kt(),lt()}function Rc(e){return e?e.type==="conveyor"?{type:"conveyor",id:e.oldKey}:{type:e.type,id:e.ref.id}:null}function Pc(e){return e?e.type==="conveyor"?{type:"conveyor",id:e.oldKey}:e.ref||null:null}function lt(){O.hoverTooltip&&(O.hoverTooltip.classList.remove("visible"),O.hoverTooltip.setAttribute("aria-hidden","true"),a.hover=null)}function Ac(e,t){if(!O.hoverTooltip||a.phase!=="build"||a.cardTargetPick?.pending||a.groupDrag||a.selectionBox||a.pan)return lt();let n=Pc(t);if(!n)return lt();let o=X.selectedTitle(n),r=X.flavorText?X.flavorText(n):"",s=X.disconnectedWarningFor?X.disconnectedWarningFor(n):"",l=X.selectedInfoLines(n).slice(0,7);O.hoverTooltip.innerHTML=`${o}${s?`${s}`:r?`${r}`:""}${l.map(M=>`${M}`).join("")}`;let d=Math.min(window.innerWidth-330,e.clientX+18),v=Math.min(window.innerHeight-220,e.clientY+18);O.hoverTooltip.style.left=`${Math.max(8,d)}px`,O.hoverTooltip.style.top=`${Math.max(8,v)}px`,O.hoverTooltip.classList.add("visible"),O.hoverTooltip.setAttribute("aria-hidden","false"),a.hover={type:t.type,id:t.oldKey||t.ref?.id||null}}function Dc(e){if(a.phase!=="build")return lt();Ac(e,X.equipmentAtPoint(vt(e)))}function Ys(e){lt();let t=vt(e),n=X.equipmentAtPoint(t),o=Rc(n);if(!o){a.selected=null,a.multiSelected=[],Q.updatePanels();return}a.selected=o,a.multiSelected=[o],Q.updatePanels();let r=X.selectedObject();r?.type==="scanner"&&r.kind==="manual"&&X.showManualScannerMenu(r),r?.type==="scanner"&&r.kind==="auto"&&X.showAutoScannerMenu(r)}function jt(){a.buildTool=null,a.selected=null,a.multiSelected=[],a.selectionBox=null,a.cardTargetPick?.mode==="autoScannerMenu"&&(a.cardTargetPick=null),Q.hideModal(),lt(),Q.updatePanels(),Q.updateUI()}function Ws(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function Ic(e){let t=e?.queue?.[0];return t==null?null:a.chicks.find(n=>n.id===t)||null}function $c(e){if(!e)return'WAIT';let t=e.sex==="poop"?"\u{1F4A9} POOP":e.sex==="male"?"\u2642 MALE":"\u2640 FEMALE";return`${t}`}function Fc(e,t){let n=new Set(a.eggFarms.map(g=>P(g.col,g.row))),o=new Map(a.scanners.map(g=>[P(g.col,g.row),g])),r=Pe(e,"inputA"),s=r?P(r.col,r.row):null,l=new Set(Object.values(a.facilities||{}).map(g=>g.entry?P(g.entry.col,g.entry.row):null).filter(Boolean)),d=t?.sex||"",v=d==="poop"?"\u{1F4A9}":d==="male"?"\u2642":d==="female"?"\u2640":"",M=[];for(let g=e.row-2;g<=e.row+2;g+=1)for(let A=e.col-2;A<=e.col+2;A+=1){let L=P(A,g),V=["mini-cell"],ee="";A===e.col&&g===e.row?(V.push("scanner"),d&&V.push(d),ee=v||"S"):L===s?(V.push("input"),ee="IN"):n.has(L)?(V.push("egg"),ee="E"):o.has(L)?(V.push("scanner-other"),ee="S"):l.has(L)?(V.push("port"),ee="P"):a.conveyorTiles.has(L)?(V.push("belt"),ee="\u2022"):a.blockedCells?.has?.(L)&&(V.push("blocked"),ee="\xD7"),M.push(`${ee}`)}return`
${M.join("")}
`}function Bc(e){let t=Sc($(e.col,e.row)),n=76;return t.x<-n||t.y<-n||t.x>ce.width+n||t.y>ce.height+n}function Vs(){if(!O.manualScannerMonitor)return;if(a.phase!=="running"){O.manualScannerMonitor.classList.remove("visible"),O.manualScannerMonitor.innerHTML="";return}let e=a.scanners.filter(t=>t.kind==="manual"&&Bc(t));if(!e.length){O.manualScannerMonitor.classList.remove("visible"),O.manualScannerMonitor.innerHTML="";return}O.manualScannerMonitor.innerHTML=e.map(t=>{let n=Ic(t),o=t.keys?.left?.label||(t.slot===0?"A":"Left"),r=t.keys?.right?.label||(t.slot===0?"D":"Right"),s=t.role===0?"MIXER":"WASTE",l=t.role===0?"S2":"TRUCK",d=n?"":" disabled";return`
S${(t.slot??0)+1}OFFSCREEN / Q:${t.queue.length}
- IN${ja(n)}SCANNER + IN${$c(n)}SCANNER
- ${ec(t,n)} + ${Fc(t,n)}
- - + +
-
`}).join(""),B.manualScannerMonitor.classList.add("visible")}B.manualScannerMonitor?.addEventListener("click",e=>{let t=e.target.closest("button[data-scanner-id]");if(!t||t.disabled)return;let n=a.scanners.find(o=>o.id===Number(t.dataset.scannerId));n&&(Te.sortScanner(n,t.dataset.side),Cs())});ie.addEventListener("contextmenu",e=>e.preventDefault());ie.addEventListener("wheel",za,{passive:!1});ie.addEventListener("pointerdown",e=>{if(a.phase!=="build")return;ie.setPointerCapture(e.pointerId);let t=at(e);if(e.button===2){qa(e);return}if(e.button!==0)return;if(a.cardTargetPick?.mode==="autoScannerMenu"){qt();return}if(a.cardTargetPick?.pending){Yt.chooseTargetAtPoint(t);return}let n=H.expansionOfferAtPoint?.(t);if(n){H.buyGridExpansion(n),ct(),K.updatePanels(),K.updateUI();return}if(H.cycleBranchModeAtPoint?.(t)){K.updatePanels(),K.updateUI();return}let o=Ge(t.x,t.y),r=H.equipmentAtPoint(t);if(!a.buildTool&&r?.type==="conveyor"&&o&&H.cycleSingleConveyorDirection?.(o)){K.updatePanels(),K.updateUI(),H.showSelectedMenu?.();return}if(a.buildTool==="erase"){if(!H.equipmentAtPoint(t)){qt();return}H.eraseAtPoint(t);return}if(a.buildTool){let s=Ge(t.x,t.y);if(!s&&!["mixer","trash","truck"].includes(a.buildTool)){qt();return}if(a.buildTool==="conveyor"||a.buildTool==="boostConveyor"){H.beginConveyorDrag(s);return}if(H.equipmentAtPoint(t)){a.buildTool=null,ks(e);return}H.buildAtPoint(t);return}H.startGroupDrag(e)||H.startSelectionBox(e)});ie.addEventListener("pointermove",e=>{if(a.phase!=="build"){Ze();return}Ja(e),a.pan&&e.buttons&2&&Ya(e),a.groupDrag&&e.buttons&1&&H.updateGroupDrag(e),a.selectionBox&&e.buttons&1&&H.updateSelectionBox(e),(a.buildTool==="conveyor"||a.buildTool==="boostConveyor")&&e.buttons&1&&H.continueConveyorDrag(Ge(at(e).x,at(e).y)),a.buildTool==="erase"&&e.buttons&1&&H.eraseAtPoint(at(e))});ie.addEventListener("pointerup",e=>{a.selectionBox?H.finishSelectionBox():a.groupDrag&&(a.groupDrag.committed||ks(e),H.finishGroupDrag()),e.button===2&&a.pan&&!a.pan.moved&&a.buildTool&&qt(),(a.buildTool==="conveyor"||a.buildTool==="boostConveyor")&&H.endConveyorDrag?.(),a.groupDrag=null,a.pan=null;try{ie.releasePointerCapture(e.pointerId)}catch{}});ie.addEventListener("pointerleave",Ze);ie.addEventListener("pointercancel",()=>{H?.endConveyorDrag?.(),a.groupDrag=null,a.selectionBox=null,a.pan=null,Ze()});document.addEventListener("pointerdown",e=>{!B.modal.classList.contains("visible")||!B.modal.classList.contains("equipment-popover")||e.target.closest("#modal .modal-card")||qt()},!0);B.buttons.s1Left.addEventListener("click",()=>Te.sortSlot(0,"left"));B.buttons.s1Right.addEventListener("click",()=>Te.sortSlot(0,"right"));B.buttons.s2Left.addEventListener("click",()=>Te.sortSlot(1,"left"));B.buttons.s2Right.addEventListener("click",()=>Te.sortSlot(1,"right"));B.buttons.nextTurn.addEventListener("click",Ua);B.buttons.conveyor.addEventListener("click",()=>H.setBuildTool("conveyor"));B.buttons.boostConveyor.addEventListener("click",()=>H.setBuildTool("boostConveyor"));B.buttons.eggFarm.addEventListener("click",()=>H.setBuildTool("eggFarm"));B.buttons.autoScanner.addEventListener("click",()=>H.setBuildTool("autoScanner"));B.buttons.manualScanner.addEventListener("click",()=>H.setBuildTool("manualScanner"));B.buttons.mixer.addEventListener("click",()=>H.setBuildTool("mixer"));B.buttons.trash.addEventListener("click",()=>H.setBuildTool("trash"));B.buttons.truck.addEventListener("click",()=>H.setBuildTool("truck"));B.buttons.erase.addEventListener("click",()=>H.setBuildTool("erase"));B.hireRepairmanButton?.addEventListener("click",Wa);B.debug.setDay?.addEventListener("click",Ba);B.debug.zeroTimer?.addEventListener("click",La);B.debug.grantCard?.addEventListener("click",Oa);B.debug.grantAllCards?.addEventListener("click",Na);B.buttons.undo.addEventListener("click",()=>{Mo(a)&&(ct(),K.updatePanels(),K.updateUI())});B.buttons.redo.addEventListener("click",()=>{To(a)&&(ct(),K.updatePanels(),K.updateUI())});function hs(e,t,n){let o=e.keys?.[t];return o?o.code&&n.code===o.code||o.key&&n.key.toLowerCase()===o.key:!1}window.addEventListener("keydown",e=>{if(e.repeat)return;let t=e.key.toLowerCase();if(a.phase==="running")for(let n of a.scanners.filter(o=>o.kind==="manual")){if(hs(n,"left",e)){e.preventDefault(),Te.sortScanner(n,"left");return}if(hs(n,"right",e)){e.preventDefault(),Te.sortScanner(n,"right");return}}a.phase==="build"&&(e.ctrlKey||e.metaKey)&&t==="z"&&(e.preventDefault(),Mo(a)&&(ct(),K.updatePanels(),K.updateUI())),a.phase==="build"&&(e.ctrlKey||e.metaKey)&&(t==="y"||e.shiftKey&&t==="z")&&(e.preventDefault(),To(a)&&(ct(),K.updatePanels(),K.updateUI())),e.key==="Escape"&&a.cardTargetPick?.pending&&(e.preventDefault(),a.cardTargetPick.mode==="autoScannerMenu"?qt():Yt.cancelTargetPick())});H=is({game:a,ui:B,canvas:ie,canvasPoint:at,onUpdatePanels:()=>K?.updatePanels()});Te=fs({game:a,currentConveyorSpeed:Ia,onGameOverCheck:()=>K?.checkGameOver()});Yt=Yi({game:a,ui:B,onUpdatePanels:()=>K?.updatePanels()});K=ss({game:a,ui:B,build:H,startGame:Fa,beginCardDraft:()=>Yt.showDraft(),activeQueuedChick:Te.activeQueuedChick});Aa();$a();uo(a);ye(a);Te.updateCongestion();K.updatePanels();K.updateUI();K.showTitle();Dt();requestAnimationFrame(vs); + `}).join(""),O.manualScannerMonitor.classList.add("visible")}O.manualScannerMonitor?.addEventListener("click",e=>{let t=e.target.closest("button[data-scanner-id]");if(!t||t.disabled)return;let n=a.scanners.find(o=>o.id===Number(t.dataset.scannerId));n&&(Ae.sortScanner(n,t.dataset.side),Vs())});ce.addEventListener("contextmenu",e=>e.preventDefault());ce.addEventListener("wheel",Ec,{passive:!1});ce.addEventListener("pointerdown",e=>{if(!["build","running"].includes(a.phase))return;ce.setPointerCapture(e.pointerId);let t=vt(e);if(e.button===2||e.button===1){Tc(e);return}if(a.phase!=="build"||e.button!==0)return;if(a.cardTargetPick?.mode==="autoScannerMenu"){jt();return}if(a.cardTargetPick?.pending){en.chooseTargetAtPoint(t);return}let n=X.expansionOfferAtPoint?.(t);if(n){X.buyGridExpansion(n),kt(),Q.updatePanels(),Q.updateUI();return}if(X.cycleBranchModeAtPoint?.(t)){Q.updatePanels(),Q.updateUI();return}let o=Ge(t.x,t.y),r=X.equipmentAtPoint(t);if(!a.buildTool&&r?.type==="conveyor"&&o&&X.cycleSingleConveyorDirection?.(o)){Q.updatePanels(),Q.updateUI();return}if(a.buildTool==="erase"){if(!X.equipmentAtPoint(t)){jt();return}X.eraseAtPoint(t);return}if(a.buildTool){let s=Ge(t.x,t.y);if(!s&&!["mixer","trash","truck"].includes(a.buildTool)){jt();return}if(a.buildTool==="conveyor"||a.buildTool==="boostConveyor"){X.beginConveyorDrag(s);return}if(X.equipmentAtPoint(t)){a.buildTool=null,Ys(e);return}X.buildAtPoint(t);return}X.startGroupDrag(e)||X.startSelectionBox(e)});ce.addEventListener("pointermove",e=>{if(a.pan&&(e.buttons&2||e.buttons&4)){gc(e);return}if(a.phase!=="build"){lt();return}Dc(e),a.groupDrag&&e.buttons&1&&X.updateGroupDrag(e),a.selectionBox&&e.buttons&1&&X.updateSelectionBox(e),(a.buildTool==="conveyor"||a.buildTool==="boostConveyor")&&e.buttons&1&&X.continueConveyorDrag(Ge(vt(e).x,vt(e).y)),a.buildTool==="erase"&&e.buttons&1&&X.eraseAtPoint(vt(e))});ce.addEventListener("pointerup",e=>{a.selectionBox?X.finishSelectionBox():a.groupDrag&&(a.groupDrag.committed||Ys(e),X.finishGroupDrag()),e.button===2&&a.pan&&!a.pan.moved&&a.buildTool&&jt(),(a.buildTool==="conveyor"||a.buildTool==="boostConveyor")&&X.endConveyorDrag?.(),a.groupDrag=null,a.pan=null;try{ce.releasePointerCapture(e.pointerId)}catch{}});ce.addEventListener("pointerleave",lt);ce.addEventListener("pointercancel",()=>{X?.endConveyorDrag?.(),a.groupDrag=null,a.selectionBox=null,a.pan=null,lt()});document.addEventListener("pointerdown",e=>{!O.modal.classList.contains("visible")||!O.modal.classList.contains("equipment-popover")||e.target.closest("#modal .modal-card")||jt()},!0);O.buttons.s1Left.addEventListener("click",()=>Ae.sortSlot(0,"left"));O.buttons.s1Right.addEventListener("click",()=>Ae.sortSlot(0,"right"));O.buttons.s2Left.addEventListener("click",()=>Ae.sortSlot(1,"left"));O.buttons.s2Right.addEventListener("click",()=>Ae.sortSlot(1,"right"));O.buttons.nextTurn.addEventListener("click",kc);O.buttons.conveyor.addEventListener("click",()=>X.setBuildTool("conveyor"));O.buttons.boostConveyor.addEventListener("click",()=>X.setBuildTool("boostConveyor"));O.buttons.eggFarm.addEventListener("click",()=>X.setBuildTool("eggFarm"));O.buttons.autoScanner.addEventListener("click",()=>X.setBuildTool("autoScanner"));O.buttons.manualScanner.addEventListener("click",()=>X.setBuildTool("manualScanner"));O.buttons.maintenanceRoom?.addEventListener("click",()=>X.setBuildTool("maintenanceRoom"));O.buttons.mixer.addEventListener("click",()=>X.setBuildTool("mixer"));O.buttons.trash.addEventListener("click",()=>X.setBuildTool("trash"));O.buttons.truck.addEventListener("click",()=>X.setBuildTool("truck"));O.buttons.erase.addEventListener("click",()=>X.setBuildTool("erase"));O.debug.setDay?.addEventListener("click",mc);O.debug.zeroTimer?.addEventListener("click",wc);O.debug.grantCard?.addEventListener("click",bc);O.debug.grantAllCards?.addEventListener("click",xc);O.buttons.undo.addEventListener("click",()=>{Io(a)&&(kt(),Q.updatePanels(),Q.updateUI())});O.buttons.redo.addEventListener("click",()=>{$o(a)&&(kt(),Q.updatePanels(),Q.updateUI())});function Gs(e,t,n){let o=e.keys?.[t];return o?o.code&&n.code===o.code||o.key&&n.key.toLowerCase()===o.key:!1}window.addEventListener("keydown",e=>{if(e.repeat)return;let t=e.key.toLowerCase();if(a.phase==="running")for(let n of a.scanners.filter(o=>o.kind==="manual")){if(Gs(n,"left",e)){e.preventDefault(),Ae.sortScanner(n,"left");return}if(Gs(n,"right",e)){e.preventDefault(),Ae.sortScanner(n,"right");return}}a.phase==="build"&&(e.ctrlKey||e.metaKey)&&t==="z"&&(e.preventDefault(),Io(a)&&(kt(),Q.updatePanels(),Q.updateUI())),a.phase==="build"&&(e.ctrlKey||e.metaKey)&&(t==="y"||e.shiftKey&&t==="z")&&(e.preventDefault(),$o(a)&&(kt(),Q.updatePanels(),Q.updateUI())),e.key==="Escape"&&a.cardTargetPick?.pending&&(e.preventDefault(),a.cardTargetPick.mode==="autoScannerMenu"?jt():en.cancelTargetPick())});X=As({game:a,ui:O,canvas:ce,canvasPoint:vt,onUpdatePanels:()=>Q?.updatePanels()});Ae=Os({game:a,currentConveyorSpeed:yc,onGameOverCheck:()=>Q?.checkGameOver()});en=hs({game:a,ui:O,onUpdatePanels:()=>Q?.updatePanels()});Q=Ds({game:a,ui:O,build:X,startGame:hc,beginCardDraft:()=>en.showDraft(),activeQueuedChick:Ae.activeQueuedChick});fc();pc();xo(a);ke(a);Ae.updateCongestion();Q.updatePanels();Q.updateUI();Q.showTitle();Wt();requestAnimationFrame(Hs); diff --git a/index.html b/index.html index a17a4a9..aa0adcb 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - Chick Sorter v27.6 + Chick Sorter v27.8 @@ -36,7 +36,7 @@
-

CHICK SORTER v27.6

+

CHICK SORTER v27.8

Build, expand, connect at least one valid EGG route, then start the next day.

@@ -54,8 +54,8 @@ + -
Build tools unlock after each day.
@@ -67,6 +67,12 @@
+ +
+ CARDS 0acquired +
No passive cards yet.
+
+
diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e6d625a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,496 @@ +{ + "name": "zunda-shiwake", + "version": "27.8.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zunda-shiwake", + "version": "27.8.0", + "devDependencies": { + "esbuild": "^0.25.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + } + } +} diff --git a/package.json b/package.json index 6e13396..1df3932 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zunda-shiwake", - "version": "27.6.0", + "version": "27.8.0", "private": true, "type": "module", "scripts": { diff --git a/src/core/balance.js b/src/core/balance.js index 083b2d2..bf336ce 100644 --- a/src/core/balance.js +++ b/src/core/balance.js @@ -1,7 +1,7 @@ // Central balance sheet for tuning gameplay. // Edit this file first when adjusting prices, income, penalties, timing, card rates, or caps. export const BALANCE = { - version: 'v27.6 direct conveyor chick movement', + version: 'v27.8 repair routing and acquired-card UI', time: { daySeconds: 60, farmShutdownGraceSeconds: 10 @@ -88,6 +88,15 @@ export const BALANCE = { description: 'Choose one AUTO SCANNER on the map and raise it by 1 level.', tags: ['SCANNER', 'ACTIVE'] }, + { + id: 'upgradeMaintenanceRoom', + title: 'MAINTENANCE ROOM Improvement', + rarity: 'common', + type: 'equipmentUpgrade', + target: 'maintenanceRoom', + description: 'Choose one MAINTENANCE ROOM and add +1 repair worker. Each worker costs JPY 80 per day.', + tags: ['MAINTENANCE', 'ACTIVE'] + }, { id: 'upgradeMixer', title: 'MIXER Improvement', @@ -367,7 +376,7 @@ export const BALANCE = { trash: 1.00 }, repairman: { - dailyCost: 100, + dailyCost: 80, secondsPerOnePercent: 0.5, walkSpeed: 90 } @@ -393,6 +402,10 @@ export const BALANCE = { id: 'autoScanner', type: 'scanner', kind: 'auto', name: 'Auto Scanner', shortName: 'Auto Scanner', price: 360, buildable: true, upgradeable: false }, + maintenanceRoom: { + id: 'maintenanceRoom', type: 'maintenanceRoom', name: 'Maintenance Room', shortName: 'MAINT ROOM', price: 400, + buildable: true, upgradeable: true, footprint: { cols: 2, rows: 2 } + }, mixer: { id: 'mixer', type: 'facility', name: 'Mixer', shortName: 'MIXER', price: 450, buildable: true, upgradeable: true, incomeKey: 'mixer', body: { w: 164, h: 126 } diff --git a/src/core/config.js b/src/core/config.js index 020a362..ccbeaa3 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -24,7 +24,7 @@ export const EGG_SPAWN_RANGES = BALANCE.production.eggSpawnRanges; export const GRID = BALANCE.grid; export const FACILITY_DEFS = BALANCE.facilities; -export const BUILD_TOOL_IDS = ['conveyor', 'boostConveyor', 'eggFarm', 'autoScanner', 'manualScanner', 'mixer', 'trash', 'truck']; +export const BUILD_TOOL_IDS = ['conveyor', 'boostConveyor', 'eggFarm', 'autoScanner', 'manualScanner', 'maintenanceRoom', 'mixer', 'trash', 'truck']; export const MACHINE_FACILITY_IDS = ['mixer', 'trash', 'truck']; export const INCOME_FACILITY_IDS = ['mixer', 'truck']; diff --git a/src/core/entities.js b/src/core/entities.js index 9aa24fe..2566e57 100644 --- a/src/core/entities.js +++ b/src/core/entities.js @@ -49,6 +49,27 @@ export function createScanner(game, col, row, kind) { }; } +export function createMaintenanceRoom(game, col, row, price) { + const def = FACILITY_DEFS.maintenanceRoom || {}; + const cols = def.footprint?.cols || 2; + const rows = def.footprint?.rows || 2; + return { + type: 'facility', + id: 'maintenanceRoom', + baseId: 'maintenanceRoom', + name: def.name || 'Maintenance Room', + shortName: def.shortName || 'MAINT ROOM', + col, row, cols, rows, + x: GRID.x + col * GRID.cell, + y: GRID.y + row * GRID.cell, + w: cols * GRID.cell, + h: rows * GRID.cell, + level: 1, + price: price || def.price || 400, + builtSession: game.buildSession + }; +} + export function nearestGridEdge(point) { const colFloat = (point.x - GRID.x) / GRID.cell; const rowFloat = (point.y - GRID.y) / GRID.cell; diff --git a/src/core/state.js b/src/core/state.js index 97ad388..31dbcfa 100644 --- a/src/core/state.js +++ b/src/core/state.js @@ -147,6 +147,7 @@ export function createGame() { manualCombo: { count: 0, lastBonus: 0 }, pendingRepairWorkerWages: 0, repairman: { hiredForNextDay: false, active: false, x: GRID.x + GRID.cell * 0.5, y: GRID.y + BALANCE.grid.rows * GRID.cell + 84, target: null, repairedToday: 0 }, + repairmen: [], stats: newTurnStats(), lastResult: null, totals: newTotalStats() @@ -322,6 +323,7 @@ export function resetLayout(game) { game.repairman.active = false; game.repairman.hiredForNextDay = false; } + game.repairmen = []; game.conveyorTiles.clear(); game.conveyorMeta.clear(); game.blockedCells = new Set(); diff --git a/src/core/text.js b/src/core/text.js index 065f9a3..06e1d99 100644 --- a/src/core/text.js +++ b/src/core/text.js @@ -24,8 +24,8 @@ export const TEXT = { autoMenu: 'Auto Menu' }, status: { - build: tool => `BUILD: ${(tool === 'erase' ? 'SELL' : (tool || 'SELECT')).toUpperCase()} | Right-drag pan | Hover equipment for info`, - sorting: 'Sorting: use scanner keys. Build after clearing the day.', + build: tool => `BUILD: ${(tool === 'erase' ? 'SELL' : (tool || 'SELECT')).toUpperCase()} | Right/middle-drag pan | Hover equipment for info`, + sorting: 'Sorting: use scanner keys. Right/middle-drag to pan. Build after clearing the day.', allPortsConnected: 'Start condition met: one EGG route reaches an exit.', equipmentPanelEmpty: 'Hover equipment in Build phase.', eventPanelRunning: 'Forced events appear in Build phase.' @@ -69,6 +69,7 @@ export function buildToolFlavorText(id) { eggFarm: 'A tiny gatehouse producing questionable eggs on schedule.', autoScanner: 'Slower than hands without upgrades.', manualScanner: 'A manual checkpoint. The operator is the algorithm.', + maintenanceRoom: '2x2 repair base. Spawns repair workers during sorting.', mixer: 'Male chicks become revenue here. Do not feed it poop.', trash: 'A polite shredder for poop and other regrets.', truck: 'Ships the target cargo. Wrong cargo still leaves a paper trail.' diff --git a/src/game.js b/src/game.js index f40fe54..2a931df 100644 --- a/src/game.js +++ b/src/game.js @@ -13,7 +13,7 @@ import { createChickSystem } from './systems/chickSystem.js'; import { CARD_DEFS, conveyorSpeedForGame, createCardSystem, debugGrantCard } from './systems/cards.js'; import { floating, shockwave, sparkBurst, updateEffects } from './systems/effects.js'; import { rollContractOffer, activateAcceptedContract, clearActiveContract, resolveContract } from './systems/contracts.js'; -import { ensureMaintenanceState, hireRepairmanForNextDay, activateRepairmanForDay, deactivateRepairman, conveyorSpeedFactorForKey } from './systems/maintenance.js'; +import { ensureMaintenanceState, activateRepairmanForDay, deactivateRepairman, conveyorSpeedFactorForKey } from './systems/maintenance.js'; const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); @@ -27,7 +27,7 @@ const ui = { modal: document.getElementById('modal'), modalTitle: document.getElementById('modalTitle'), modalBody: document.getElementById('modalBody'), modalActions: document.getElementById('modalActions'), hoverTooltip: document.getElementById('hoverTooltip'), manualScannerMonitor: document.getElementById('manualScannerMonitor'), - hireRepairmanButton: document.getElementById('hireRepairmanButton'), + cardInventory: document.getElementById('cardInventory'), debug: { panel: document.getElementById('debugPanel'), infiniteCash: document.getElementById('debugInfiniteCash'), @@ -41,7 +41,7 @@ const ui = { }, buttons: { s1Left: document.getElementById('scanner1MixerButton'), s1Right: document.getElementById('scanner1TruckButton'), s2Left: document.getElementById('scanner2MixerButton'), s2Right: document.getElementById('scanner2TruckButton'), - nextTurn: document.getElementById('nextTurnButton'), conveyor: document.getElementById('buildConveyorButton'), boostConveyor: document.getElementById('buildBoostConveyorButton'), eggFarm: document.getElementById('buildEggFarmButton'), autoScanner: document.getElementById('buildAutoScannerButton'), manualScanner: document.getElementById('buildManualScannerButton'), mixer: document.getElementById('buildMixerButton'), trash: document.getElementById('buildTrashButton'), truck: document.getElementById('buildTruckButton'), erase: document.getElementById('eraseButton'), undo: document.getElementById('undoButton'), redo: document.getElementById('redoButton') + nextTurn: document.getElementById('nextTurnButton'), conveyor: document.getElementById('buildConveyorButton'), boostConveyor: document.getElementById('buildBoostConveyorButton'), eggFarm: document.getElementById('buildEggFarmButton'), autoScanner: document.getElementById('buildAutoScannerButton'), manualScanner: document.getElementById('buildManualScannerButton'), maintenanceRoom: document.getElementById('buildMaintenanceRoomButton'), mixer: document.getElementById('buildMixerButton'), trash: document.getElementById('buildTrashButton'), truck: document.getElementById('buildTruckButton'), erase: document.getElementById('eraseButton'), undo: document.getElementById('undoButton'), redo: document.getElementById('redoButton') } }; @@ -53,6 +53,7 @@ function initializeStaticText() { eggFarm: 'eggFarm', autoScanner: 'autoScanner', manualScanner: 'manualScanner', + maintenanceRoom: 'maintenanceRoom', mixer: 'mixer', trash: 'trash', truck: 'truck' @@ -169,7 +170,7 @@ function startNextTurn() { } ensureMaintenanceState(game); commitFactoryGraphForDay(game); - activateRepairmanForDay(game); + const repairActivation = activateRepairmanForDay(game); const zundaBasisProfit = Math.max(0, Math.floor(game.lastResult?.profit ?? game.stats?.profit ?? 0)); const zunda = collectZundaTax(game, zundaBasisProfit); const loanRepayment = collectLoanRepayments(game); @@ -193,12 +194,21 @@ function startNextTurn() { game.stats = newTurnStats(); game.stats.zundaTax = 0; game.stats.loanRepayment = game.lastStartFees?.loanRepayment || 0; - game.stats.repairWorkerWages = game.pendingRepairWorkerWages || 0; - const carriedStartCosts = game.stats.loanRepayment + game.stats.repairWorkerWages; + game.stats.repairWorkerWages = repairActivation.wages || 0; + const carriedStartCosts = game.stats.loanRepayment; if (carriedStartCosts > 0) { game.stats.penalty += carriedStartCosts; game.stats.profit -= carriedStartCosts; } + if (game.stats.repairWorkerWages > 0) { + game.cash -= game.stats.repairWorkerWages; + game.stats.penalty += game.stats.repairWorkerWages; + game.stats.profit -= game.stats.repairWorkerWages; + game.totals.repairWorkerWages = (game.totals.repairWorkerWages || 0) + game.stats.repairWorkerWages; + game.totals.penalty = (game.totals.penalty || 0) + game.stats.repairWorkerWages; + game.totals.profit = (game.totals.profit || 0) - game.stats.repairWorkerWages; + floating(game, canvas.width / 2 - game.view.x, 176 - game.view.y, `REPAIR WAGES -${yen(game.stats.repairWorkerWages)}`, THEME.danger); + } game.pendingRepairWorkerWages = 0; for (const scanner of game.scanners) { scanner.queue = []; scanner.cooldown = 0; } for (const farm of game.eggFarms) { farm.nextSpawn = chicks.currentSpawnDelay(farm); farm.lastInterval = farm.nextSpawn; farm.shutterProgress = 0; farm.shutterSparked = false; } @@ -213,18 +223,6 @@ function startNextTurn() { } -function hireRepairman() { - ensureMaintenanceState(game); - const result = hireRepairmanForNextDay(game); - if (!result.ok) { - floating(game, canvas.width / 2 - game.view.x, 96 - game.view.y, result.reason || 'Cannot hire repairman.', THEME.danger); - build?.fail?.(result.reason || 'Cannot hire repairman.'); - } else { - floating(game, canvas.width / 2 - game.view.x, 96 - game.view.y, `Repairman hired -${yen(result.cost)}`, THEME.green); - } - uiSystem.updatePanels(); - uiSystem.updateUI(); -} function completeTurn() { if (game.phase !== 'running') return; @@ -582,10 +580,11 @@ ui.manualScannerMonitor?.addEventListener('click', event => { canvas.addEventListener('contextmenu', event => event.preventDefault()); canvas.addEventListener('wheel', zoomAt, { passive: false }); canvas.addEventListener('pointerdown', event => { - if (game.phase !== 'build') return; + if (!['build', 'running'].includes(game.phase)) return; canvas.setPointerCapture(event.pointerId); const world = canvasPoint(event); - if (event.button === 2) { startPan(event); return; } + if (event.button === 2 || event.button === 1) { startPan(event); return; } + if (game.phase !== 'build') return; if (event.button !== 0) return; if (game.cardTargetPick?.mode === 'autoScannerMenu') { cancelBuildAction(); return; } if (game.cardTargetPick?.pending) { cardSystem.chooseTargetAtPoint(world); return; } @@ -597,7 +596,6 @@ canvas.addEventListener('pointerdown', event => { if (!game.buildTool && clickedHit?.type === 'conveyor' && clickedCell && build.cycleSingleConveyorDirection?.(clickedCell)) { uiSystem.updatePanels(); uiSystem.updateUI(); - build.showSelectedMenu?.(); return; } if (game.buildTool === 'erase') { @@ -621,9 +619,9 @@ canvas.addEventListener('pointerdown', event => { build.startSelectionBox(event); }); canvas.addEventListener('pointermove', event => { + if (game.pan && ((event.buttons & 2) || (event.buttons & 4))) { updatePan(event); return; } if (game.phase !== 'build') { hideHoverTooltip(); return; } updateHover(event); - if (game.pan && (event.buttons & 2)) updatePan(event); if (game.groupDrag && (event.buttons & 1)) build.updateGroupDrag(event); if (game.selectionBox && (event.buttons & 1)) build.updateSelectionBox(event); if ((game.buildTool === 'conveyor' || game.buildTool === 'boostConveyor') && (event.buttons & 1)) build.continueConveyorDrag(pointToCell(canvasPoint(event).x, canvasPoint(event).y)); @@ -649,8 +647,7 @@ document.addEventListener('pointerdown', event => { }, true); ui.buttons.s1Left.addEventListener('click', () => chicks.sortSlot(0, 'left')); ui.buttons.s1Right.addEventListener('click', () => chicks.sortSlot(0, 'right')); ui.buttons.s2Left.addEventListener('click', () => chicks.sortSlot(1, 'left')); ui.buttons.s2Right.addEventListener('click', () => chicks.sortSlot(1, 'right')); -ui.buttons.nextTurn.addEventListener('click', startNextTurn); ui.buttons.conveyor.addEventListener('click', () => build.setBuildTool('conveyor')); ui.buttons.boostConveyor.addEventListener('click', () => build.setBuildTool('boostConveyor')); ui.buttons.eggFarm.addEventListener('click', () => build.setBuildTool('eggFarm')); ui.buttons.autoScanner.addEventListener('click', () => build.setBuildTool('autoScanner')); ui.buttons.manualScanner.addEventListener('click', () => build.setBuildTool('manualScanner')); ui.buttons.mixer.addEventListener('click', () => build.setBuildTool('mixer')); ui.buttons.trash.addEventListener('click', () => build.setBuildTool('trash')); ui.buttons.truck.addEventListener('click', () => build.setBuildTool('truck')); ui.buttons.erase.addEventListener('click', () => build.setBuildTool('erase')); -ui.hireRepairmanButton?.addEventListener('click', hireRepairman); +ui.buttons.nextTurn.addEventListener('click', startNextTurn); ui.buttons.conveyor.addEventListener('click', () => build.setBuildTool('conveyor')); ui.buttons.boostConveyor.addEventListener('click', () => build.setBuildTool('boostConveyor')); ui.buttons.eggFarm.addEventListener('click', () => build.setBuildTool('eggFarm')); ui.buttons.autoScanner.addEventListener('click', () => build.setBuildTool('autoScanner')); ui.buttons.manualScanner.addEventListener('click', () => build.setBuildTool('manualScanner')); ui.buttons.maintenanceRoom?.addEventListener('click', () => build.setBuildTool('maintenanceRoom')); ui.buttons.mixer.addEventListener('click', () => build.setBuildTool('mixer')); ui.buttons.trash.addEventListener('click', () => build.setBuildTool('trash')); ui.buttons.truck.addEventListener('click', () => build.setBuildTool('truck')); ui.buttons.erase.addEventListener('click', () => build.setBuildTool('erase')); ui.debug.setDay?.addEventListener('click', debugSetDay); ui.debug.zeroTimer?.addEventListener('click', debugZeroTimer); ui.debug.grantCard?.addEventListener('click', debugGrantSelectedCard); diff --git a/src/render/draw.js b/src/render/draw.js index 5312cb0..2e7d479 100644 --- a/src/render/draw.js +++ b/src/render/draw.js @@ -1,7 +1,7 @@ import { DIRS, GRID, THEME, EFFECT_PRIORITY, VERSION } from '../core/config.js'; import { expansionCost, expansionLots, lotBounds, expansionButtonBounds, isOwnedCell } from '../core/gridExpansion.js'; import { key, parseKey, cellCenter, mixHex, randomBetween, yen } from '../core/utils.js'; -import { getConveyorNeighbors, scannerCenter, scannerConnector, scannerOutputs, destinationLabel, destinationColor, scannerPortHasConveyor, disconnectedBuildWarnings } from '../systems/routing.js'; +import { scannerCenter, scannerConnector, scannerOutputs, destinationLabel, destinationColor, scannerPortHasConveyor, disconnectedBuildWarnings } from '../systems/routing.js'; import { upgradedMixerPrice, upgradedTruckPrice } from '../systems/economy.js'; import { cardTargetBounds } from '../systems/cards.js'; import { wearRatio } from '../systems/maintenance.js'; @@ -225,8 +225,10 @@ function drawConveyors(ctx, game) { const seen = new Set(); for (const k of game.conveyorTiles) { const { col, row } = parseKey(k); - for (const n of getConveyorNeighbors(game, col, row)) { + for (const d of DIRS) { + const n = { col: col + d.dc, row: row + d.dr }; const nk = key(n.col, n.row); + if (!game.conveyorTiles.has(nk)) continue; const e = [k, nk].sort().join('|'); if (seen.has(e)) continue; seen.add(e); @@ -270,25 +272,13 @@ function drawConveyors(ctx, game) { drawDirtOverlay(ctx, c.x - 14, c.y - 14, 28, 28, { meta }); drawSelection(ctx, game, 'conveyor', k, c.x, c.y, 38, 38); drawConveyorDirectionMarkers(ctx, c, directionMarkers.get(k) || []); - drawBranchModeLabel(ctx, c, meta, reachableConveyorDirs(game, k, meta), connectedConveyorDirs(game, k)); + const connectedDirs = connectedConveyorDirs(game, k); + drawBranchModeIcon(ctx, c, meta, branchSelectableEntranceDirs(game, k, connectedDirs), connectedDirs); if (ratio > 0.5) label(ctx, c.x, c.y - 15, `${Math.floor(ratio * 100)}%`, THEME.danger); } ctx.restore(); } -function branchModeText(mode) { - return { random: 'RND', pass: 'PASS', up: 'UP', right: 'RT', down: 'DN', left: 'LF' }[mode || 'random'] || 'RND'; -} - -function neighborPointsIntoCell(game, p, dirName) { - const d = DIRS.find(item => item.name === dirName); - if (!d) return false; - const meta = game.conveyorMeta?.get?.(key(p.col + d.dc, p.row + d.dr)); - if (!meta) return false; - const names = [...new Set([...(Array.isArray(meta.outDirs) ? meta.outDirs : []), meta.dir].filter(Boolean))]; - return names.includes(d.opposite); -} - function reachableConveyorDirs(game, k, meta) { const p = parseKey(k); const neighborNames = DIRS @@ -307,6 +297,34 @@ function connectedConveyorDirs(game, k) { .map(d => d.name); } +function neighborPointsIntoCell(game, p, dirName) { + const d = DIRS.find(item => item.name === dirName); + if (!d) return false; + const meta = game.conveyorMeta?.get?.(key(p.col + d.dc, p.row + d.dr)); + if (!meta) return false; + const names = [...new Set([...(Array.isArray(meta.outDirs) ? meta.outDirs : []), meta.dir].filter(Boolean))]; + return names.includes(d.opposite); +} + +function branchExitDirs(game, k, dirs, connectedDirs = connectedConveyorDirs(game, k), branchMode = 'random') { + if (connectedDirs.length < 3) return dirs; + if (branchMode && branchMode !== 'random' && connectedDirs.includes(branchMode)) return [branchMode]; + const p = parseKey(k); + const entrances = new Set(connectedDirs.filter(dir => neighborPointsIntoCell(game, p, dir))); + const exits = connectedDirs.filter(dir => !entrances.has(dir)); + if (exits.length) return exits; + const fallback = dirs.filter(dir => !entrances.has(dir)); + return fallback.length ? fallback : dirs; +} + +function branchSelectableEntranceDirs(game, k, connectedDirs = connectedConveyorDirs(game, k)) { + if (connectedDirs.length < 3) return []; + const p = parseKey(k); + const physicalEntrances = new Set(connectedDirs.filter(dir => neighborPointsIntoCell(game, p, dir))); + const exits = connectedDirs.filter(dir => !physicalEntrances.has(dir)); + return exits.length >= 2 ? exits : connectedDirs; +} + function effectiveConveyorDirs(game, k, meta) { const p = parseKey(k); const neighborNames = DIRS @@ -318,73 +336,70 @@ function effectiveConveyorDirs(game, k, meta) { return explicit.length ? explicit : inferred; } -function drawBranchModeLabel(ctx, center, meta, dirs, connectedDirs = dirs) { +function drawBranchModeIcon(ctx, center, meta, dirs, connectedDirs = dirs) { if (connectedDirs.length < 3 || dirs.length < 2) return; ctx.save(); - const mode = meta.branchMode === 'pass' && dirs.length >= 4 ? 'pass' : (dirs.includes(meta.branchMode) ? meta.branchMode : 'random'); + const mode = dirs.includes(meta.branchMode) ? meta.branchMode : 'random'; const isFixed = mode !== 'random'; - const text = branchModeText(mode); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; - ctx.lineWidth = 4; + ctx.lineWidth = 2; ctx.fillStyle = 'rgba(255,255,255,.9)'; ctx.strokeStyle = isFixed ? THEME.warn : THEME.green; - rect(ctx, center.x - 28, center.y - 19, 56, 38, true, true); + rect(ctx, center.x - 17, center.y - 17, 34, 34, true, true); ctx.strokeStyle = THEME.ink; ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(center.x - 18, center.y); - ctx.lineTo(center.x + 18, center.y); - ctx.moveTo(center.x, center.y - 13); - ctx.lineTo(center.x, center.y + 13); - ctx.stroke(); - ctx.fillStyle = isFixed ? THEME.warn : THEME.green; - ctx.beginPath(); - ctx.arc(center.x, center.y, 12, 0, Math.PI * 2); - ctx.fill(); - ctx.strokeStyle = THEME.ink; - ctx.lineWidth = 2; - ctx.stroke(); - ctx.font = '900 9px ui-monospace, monospace'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; ctx.fillStyle = THEME.ink; - ctx.fillText(text, center.x, center.y + 0.5); + if (mode !== 'random') { + const d = DIRS.find(item => item.name === mode); + drawDirectionTriangle(ctx, center.x, center.y, d?.angle || 0, 9, 6); + } else { + ctx.beginPath(); + ctx.moveTo(center.x - 10, center.y); + ctx.lineTo(center.x - 2, center.y); + ctx.lineTo(center.x + 8, center.y - 8); + ctx.moveTo(center.x - 2, center.y); + ctx.lineTo(center.x + 8, center.y + 8); + ctx.stroke(); + drawDirectionTriangle(ctx, center.x + 9, center.y - 9, -Math.PI / 4, 4, 3); + drawDirectionTriangle(ctx, center.x + 9, center.y + 9, Math.PI / 4, 4, 3); + } ctx.restore(); } -function explicitConveyorAngles(game, k, meta) { +function explicitConveyorMarkerDirs(game, k, meta) { if (!meta) return []; const p = parseKey(k); const neighborNames = DIRS .filter(d => game.conveyorTiles.has(key(p.col + d.dc, p.row + d.dr))) .map(d => d.name); - const explicit = [...new Set([...(Array.isArray(meta.outDirs) ? meta.outDirs : []), meta.dir].filter(Boolean))] - .filter(name => neighborNames.includes(name)); - const dirs = explicit.length ? explicit : effectiveConveyorDirs(game, k, meta).slice(0, 1); + const rawExplicit = [...new Set([...(Array.isArray(meta.outDirs) ? meta.outDirs : []), meta.dir].filter(Boolean))] + .filter(name => DIRS.some(d => d.name === name)); + const explicitReachable = rawExplicit.filter(name => neighborNames.includes(name)); + const terminalVisual = meta.dir && rawExplicit.includes(meta.dir) ? [meta.dir] : []; + const visualSource = explicitReachable.length ? explicitReachable : (terminalVisual.length ? terminalVisual : effectiveConveyorDirs(game, k, meta)); + const dirs = branchExitDirs(game, k, visualSource, neighborNames, meta.branchMode || 'random'); return dirs - .map(name => DIRS.find(d => d.name === name)?.angle) - .filter(angle => Number.isFinite(angle)); + .map(name => DIRS.find(d => d.name === name)) + .filter(Boolean); } function collectConveyorDirectionMarkers(game) { const markers = new Map(); for (const [k, meta] of game.conveyorMeta || []) { - const angles = explicitConveyorAngles(game, k, meta); - if (angles.length) markers.set(k, angles); + const dirs = explicitConveyorMarkerDirs(game, k, meta); + if (dirs.length) markers.set(k, dirs); } return markers; } -function drawConveyorDirectionMarkers(ctx, center, angles) { - const limited = angles.slice(0, 3); +function drawConveyorDirectionMarkers(ctx, center, dirs) { + const limited = dirs.slice(0, 4); if (!limited.length) return; ctx.save(); ctx.fillStyle = THEME.ink; ctx.globalAlpha = 0.86; - limited.forEach((angle, index) => { - const offset = (index - (limited.length - 1) / 2) * 4; - const nx = Math.cos(angle + Math.PI / 2), ny = Math.sin(angle + Math.PI / 2); - drawDirectionTriangle(ctx, center.x + nx * offset, center.y + ny * offset, angle, 6, 5); + limited.forEach(d => { + drawDirectionTriangle(ctx, center.x + d.dc * 12, center.y + d.dr * 12, d.angle, 6, 5); }); ctx.restore(); } @@ -599,9 +614,28 @@ function drawFacilities(ctx, game) { if (kind === 'mixer') drawMixer(ctx, game, f); else if (kind === 'trash') drawTrash(ctx, game, f); else if (kind === 'truck') drawTruck(ctx, game, f); + else if (kind === 'maintenanceRoom') drawMaintenanceRoom(ctx, game, f); } for (const f of Object.values(game.facilities || {})) drawFacilityReceiver(ctx, game, f); } +function drawMaintenanceRoom(ctx, game, room) { + ctx.save(); + ctx.fillStyle = '#f7fff5'; + ctx.strokeStyle = THEME.ink; + ctx.lineWidth = 4; + rect(ctx, room.x, room.y, room.w, room.h, true, true); + ctx.fillStyle = THEME.greenSoft; + rect(ctx, room.x + 8, room.y + 8, room.w - 16, room.h - 16, true, false); + ctx.fillStyle = THEME.ink; + ctx.font = '900 14px ui-monospace, monospace'; + ctx.textAlign = 'center'; + ctx.fillText('MAINT', room.x + room.w / 2, room.y + 31); + ctx.font = '900 11px ui-monospace, monospace'; + ctx.fillText(`WORKERS ${Math.max(1, Math.floor(room.level || 1))}`, room.x + room.w / 2, room.y + room.h - 18); + drawSelection(ctx, game, 'facility', room.id, room.x + room.w / 2, room.y + room.h / 2, room.w + 10, room.h + 10); + ctx.restore(); +} + function drawExternalDuct(ctx, f) { if (!f?.entry) return; const c = cellCenter(f.entry.col, f.entry.row); @@ -816,37 +850,39 @@ function drawFloatingTexts(ctx, game) { } function drawRepairman(ctx, game) { - const r = game.repairman; - const visible = !!r?.active || (game.phase === 'build' && !!r?.hiredForNextDay); - if (!visible) return; - ctx.save(); - ctx.lineWidth = 3; - ctx.fillStyle = '#fff0d1'; - ctx.strokeStyle = THEME.ink; - ctx.beginPath(); ctx.arc(r.x, r.y, 17, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); - ctx.fillStyle = '#ffd6e7'; - ctx.beginPath(); ctx.arc(r.x - 6, r.y - 2, 3.2, 0, Math.PI * 2); ctx.fill(); - ctx.beginPath(); ctx.arc(r.x + 6, r.y - 2, 3.2, 0, Math.PI * 2); ctx.fill(); + const workers = Array.isArray(game.repairmen) && game.repairmen.length ? game.repairmen : (game.repairman ? [game.repairman] : []); + const visibleWorkers = workers.filter(r => r?.active || (game.phase === 'build' && !!r?.hiredForNextDay)); + if (!visibleWorkers.length) return; const exploited = !!game.cardEffects?.laborExploitation; - ctx.fillStyle = exploited ? THEME.danger : THEME.ink; - ctx.beginPath(); ctx.arc(r.x - 5, r.y - 5, exploited ? 2.8 : 2.2, 0, Math.PI * 2); ctx.fill(); - ctx.beginPath(); ctx.arc(r.x + 5, r.y - 5, exploited ? 2.8 : 2.2, 0, Math.PI * 2); ctx.fill(); - ctx.strokeStyle = THEME.ink; - ctx.lineWidth = exploited ? 3 : 2; - ctx.beginPath(); ctx.arc(r.x, r.y + (exploited ? 0 : 1), exploited ? 8 : 7, 0.10 * Math.PI, 0.90 * Math.PI); ctx.stroke(); - ctx.fillStyle = THEME.green; - rect(ctx, r.x - 9, r.y + 14, 18, 8, true, true); - ctx.strokeStyle = THEME.ink; - ctx.lineWidth = 3; - ctx.beginPath(); ctx.moveTo(r.x - 11, r.y + 18); ctx.lineTo(r.x - 20, r.y + 24); ctx.stroke(); - ctx.beginPath(); ctx.moveTo(r.x + 11, r.y + 18); ctx.lineTo(r.x + 20, r.y + 24); ctx.stroke(); - ctx.fillStyle = THEME.ink; - ctx.font = '900 11px ui-monospace, monospace'; - ctx.textAlign = 'center'; - ctx.fillText(r.active ? 'FIX' : 'HIRED', r.x, r.y + 33); - if (r.target?.label) label(ctx, r.x, r.y - 24, r.target.label, THEME.green); - else if (!r.active && r.hiredForNextDay) label(ctx, r.x, r.y - 24, 'NEXT DAY', THEME.green); - ctx.restore(); + for (const r of visibleWorkers) { + ctx.save(); + ctx.lineWidth = 3; + ctx.fillStyle = '#fff0d1'; + ctx.strokeStyle = THEME.ink; + ctx.beginPath(); ctx.arc(r.x, r.y, 17, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); + ctx.fillStyle = '#ffd6e7'; + ctx.beginPath(); ctx.arc(r.x - 6, r.y - 2, 3.2, 0, Math.PI * 2); ctx.fill(); + ctx.beginPath(); ctx.arc(r.x + 6, r.y - 2, 3.2, 0, Math.PI * 2); ctx.fill(); + ctx.fillStyle = exploited ? THEME.danger : THEME.ink; + ctx.beginPath(); ctx.arc(r.x - 5, r.y - 5, exploited ? 2.8 : 2.2, 0, Math.PI * 2); ctx.fill(); + ctx.beginPath(); ctx.arc(r.x + 5, r.y - 5, exploited ? 2.8 : 2.2, 0, Math.PI * 2); ctx.fill(); + ctx.strokeStyle = THEME.ink; + ctx.lineWidth = exploited ? 3 : 2; + ctx.beginPath(); ctx.arc(r.x, r.y + (exploited ? 0 : 1), exploited ? 8 : 7, 0.10 * Math.PI, 0.90 * Math.PI); ctx.stroke(); + ctx.fillStyle = THEME.green; + rect(ctx, r.x - 9, r.y + 14, 18, 8, true, true); + ctx.strokeStyle = THEME.ink; + ctx.lineWidth = 3; + ctx.beginPath(); ctx.moveTo(r.x - 11, r.y + 18); ctx.lineTo(r.x - 20, r.y + 24); ctx.stroke(); + ctx.beginPath(); ctx.moveTo(r.x + 11, r.y + 18); ctx.lineTo(r.x + 20, r.y + 24); ctx.stroke(); + ctx.fillStyle = THEME.ink; + ctx.font = '900 11px ui-monospace, monospace'; + ctx.textAlign = 'center'; + ctx.fillText(r.active ? 'FIX' : 'HIRED', r.x, r.y + 33); + if (r.target?.label) label(ctx, r.x, r.y - 24, r.target.label, THEME.green); + else if (!r.active && r.hiredForNextDay) label(ctx, r.x, r.y - 24, 'NEXT DAY', THEME.green); + ctx.restore(); + } } function drawCanvasHints(ctx, game, canvas) { diff --git a/src/systems/buildSystem.js b/src/systems/buildSystem.js index 0f436d6..b827ad1 100644 --- a/src/systems/buildSystem.js +++ b/src/systems/buildSystem.js @@ -1,7 +1,7 @@ import { BALANCE } from '../core/balance.js'; import { DIRS, MACHINE_FACILITY_IDS, GRID, THEME } from '../core/config.js'; import { getSpawnRange } from '../core/state.js'; -import { createEggFarm, createScanner, createFacility } from '../core/entities.js'; +import { createEggFarm, createScanner, createFacility, createMaintenanceRoom } from '../core/entities.js'; import { generateBlockedCellsInRect } from '../core/mapGen.js'; import { expansionCost, expansionLot, expansionLots, lotFromChunk, lotBounds, lotContainsPoint, cellsInLot, isOwnedCell, countOwnedCells, shiftRowIndexedSet, shiftRowIndexedMap, ownChunk } from '../core/gridExpansion.js'; import { key, parseKey, pointToCell, cellCenter, yen, directionNameBetweenCells } from '../core/utils.js'; @@ -12,7 +12,7 @@ import { autoScannerCooldownSeconds } from './cards.js'; import { record } from './history.js'; import { floating, eraseEffect } from './effects.js'; import { createSelectionSystem } from './selectionSystem.js'; -import { ensureMaintenanceState, remainingPercent, performanceFactor, autoScannerDelayMultiplier, eggSpawnDelayMultiplier, facilityProcessingDelay, durabilityCapFor } from './maintenance.js'; +import { ensureMaintenanceState, remainingPercent, performanceFactor, autoScannerDelayMultiplier, eggSpawnDelayMultiplier, facilityProcessingDelay, durabilityCapFor, repairmanDailyCost } from './maintenance.js'; export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanels }) { function updatePanels() { if (onUpdatePanels) onUpdatePanels(); } @@ -21,9 +21,9 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel let conveyorDragRecordedDirectionEdit = false; let conveyorDragStartCell = null; let conveyorDragMoved = false; - const BRANCH_MODES = ['random', 'pass', 'up', 'right', 'down', 'left']; - const BRANCH_LABELS = { random: 'RND', pass: 'PASS', up: 'UP', right: 'RT', down: 'DN', left: 'LF' }; - const BRANCH_MARKER = { w: 56, h: 38, y: 0 }; + const BRANCH_MODES = ['random', 'up', 'right', 'down', 'left']; + const BRANCH_LABELS = { random: 'RND', up: 'UP', right: 'RT', down: 'DN', left: 'LF' }; + const BRANCH_MARKER = { w: 40, h: 40, y: 0 }; function gridExpansionCost() { return expansionCost(game); } @@ -45,6 +45,10 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel for (const farm of game.eggFarms || []) farm.row += rowDelta; for (const scanner of game.scanners || []) scanner.row += rowDelta; for (const facility of Object.values(game.facilities || {})) { + if (isMaintenanceRoom(facility)) { + facility.row += rowDelta; + syncMaintenanceRoomRect(facility); + } if (facility.entry) facility.entry.row += rowDelta; } } @@ -121,6 +125,33 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel return Object.values(game.facilities).some(f => !ignoreIds.has(f.id) && rectsOverlap(rect, rectOfFacility(f), 12)); } + function facilityBaseId(f) { return f?.baseId || f?.id || ''; } + function isMaintenanceRoom(f) { return facilityBaseId(f) === 'maintenanceRoom'; } + function syncMaintenanceRoomRect(room) { + if (!room) return; + room.cols = room.cols || 2; + room.rows = room.rows || 2; + room.x = GRID.x + room.col * GRID.cell; + room.y = GRID.y + room.row * GRID.cell; + room.w = room.cols * GRID.cell; + room.h = room.rows * GRID.cell; + } + function maintenanceRoomCells(col, row, cols = 2, rows = 2) { + const cells = []; + for (let r = 0; r < rows; r += 1) for (let c = 0; c < cols; c += 1) cells.push({ col: col + c, row: row + r }); + return cells; + } + function maintenanceRoomAtCell(col, row, ignoreIds = new Set()) { + return Object.values(game.facilities || {}).find(f => { + if (!isMaintenanceRoom(f) || ignoreIds.has(f.id)) return false; + const cols = f.cols || 2, rows = f.rows || 2; + return col >= f.col && col < f.col + cols && row >= f.row && row < f.row + rows; + }) || null; + } + function facilityEntryAtCell(col, row, ignoreIds = new Set()) { + return Object.values(game.facilities || {}).find(f => f.entry && !ignoreIds.has(f.id) && f.entry.col === col && f.entry.row === row) || null; + } + function isEquipmentCell(col, row, moving = null) { if (!pointInGrid(col, row)) return false; if (isBlockedCell(col, row)) return true; @@ -130,6 +161,8 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel const existingScanner = scannerAt(game, col, row); if (existingScanner && !(moving?.type === 'scanner' && moving.ref?.id === existingScanner.id)) return true; if (game.conveyorTiles.has(k) && !(moving?.type === 'conveyor' && moving.oldKey === k)) return true; + const ignore = moving?.type === 'facility' && moving.ref?.id ? new Set([moving.ref.id]) : new Set(); + if (maintenanceRoomAtCell(col, row, ignore)) return true; return false; } @@ -143,6 +176,7 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel const existingScanner = scannerAt(game, cell.col, cell.row); if (existingScanner && !(moving?.type === 'scanner' && moving.ref?.id === existingScanner.id)) return true; if (game.conveyorTiles.has(k)) return true; + if (maintenanceRoomAtCell(cell.col, cell.row) || facilityEntryAtCell(cell.col, cell.row)) return true; } return false; } @@ -225,6 +259,10 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel return DIRS.filter(d => game.conveyorTiles.has(key(cell.col + d.dc, cell.row + d.dr))).map(d => d.name); } + function isBranchCell(cell) { + return conveyorNeighborOutDirs(cell).length >= 3; + } + function neighborPointsIntoCell(cell, dirName) { const d = DIRS.find(item => item.name === dirName); if (!cell || !d) return false; @@ -249,12 +287,28 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel if (meta) { meta.outDirs = explicit.filter(dir => neighbors.includes(dir)); if (meta.dir && !neighbors.includes(meta.dir)) meta.dir = meta.outDirs[0] || null; - if (meta.branchMode !== 'random' && meta.branchMode !== 'pass' && !reachable.includes(meta.branchMode)) meta.branchMode = 'random'; - if (meta.branchMode === 'pass' && reachable.length < 4) meta.branchMode = 'random'; + if (meta.branchMode !== 'random' && !neighbors.includes(meta.branchMode)) meta.branchMode = 'random'; } return reachable; } + function branchExitDirsForKey(k) { + if (!game.conveyorTiles.has(k)) return []; + const cell = parseKey(k); + const connected = conveyorNeighborOutDirs(cell); + if (!isBranchCell(cell)) return connected.length ? connected : reachableOutDirsForKey(k); + return connected; + } + + function branchSelectableEntranceDirsForKey(k) { + const cell = parseKey(k); + const connected = branchExitDirsForKey(k); + if (connected.length < 3) return []; + const physicalEntrances = new Set(connected.filter(dir => neighborPointsIntoCell(cell, dir))); + const exits = connected.filter(dir => !physicalEntrances.has(dir)); + return exits.length >= 2 ? exits : connected; + } + function markConveyorDirection(from, to) { const dir = directionNameBetweenCells(from, to); if (!dir) return false; @@ -271,35 +325,109 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel return changed; } - function availableConveyorOutDirs(cell) { - if (!cell) return []; - const neighbors = conveyorNeighborOutDirs(cell); - return neighbors.length ? neighbors : DIRS.map(d => d.name); + function cycleSingleConveyorDirection(cell) { + const neighbors = conveyorNeighborCells(cell); + if (neighbors.length <= 1) return cycleIsolatedConveyorDirection(cell); + const path = conveyorRunPathFrom(cell); + if (path.length < 2) return cycleIsolatedConveyorDirection(cell); + record(game); + applyRunDirection(path); + refreshRoutingAfterEdit(game); + updatePanels(); + return true; } - function cycleSingleConveyorDirection(cell) { + function cycleIsolatedConveyorDirection(cell) { if (!cell) return false; const k = key(cell.col, cell.row); const meta = game.conveyorMeta.get(k); - if (!meta) return false; - const choices = availableConveyorOutDirs(cell); - if (!choices.length) return false; - const explicit = normalizeOutDirs(meta); - const current = meta.dir && choices.includes(meta.dir) ? meta.dir : explicit.find(dir => choices.includes(dir)); + if (!meta || isBranchCell(cell)) return false; + const choices = DIRS.map(d => d.name); + const current = meta.dir && choices.includes(meta.dir) ? meta.dir : null; const next = choices[(Math.max(-1, choices.indexOf(current)) + 1) % choices.length]; - if (!next) return false; record(game); meta.dir = next; meta.outDirs = [next]; meta.branchMode = 'random'; - game.selected = { type: 'conveyor', id: k }; - const c = cellCenter(cell.col, cell.row); - floating(game, c.x, c.y - 22, branchModeLabel(next), THEME.ink); refreshRoutingAfterEdit(game); updatePanels(); return true; } + function conveyorNeighborCells(cell) { + if (!cell) return []; + return DIRS + .map(d => ({ col: cell.col + d.dc, row: cell.row + d.dr })) + .filter(p => game.conveyorTiles.has(key(p.col, p.row))); + } + + function conveyorRunPathFrom(cell) { + if (!cell || !game.conveyorTiles.has(key(cell.col, cell.row)) || isBranchCell(cell)) return []; + const neighbors = conveyorNeighborCells(cell); + if (!neighbors.length || neighbors.length > 2) return []; + const walk = (first) => { + const path = []; + const visited = new Set([key(cell.col, cell.row)]); + let prev = cell; + let cur = first; + let guard = 0; + while (cur && guard < 200) { + guard += 1; + const curKey = key(cur.col, cur.row); + if (visited.has(curKey)) break; + visited.add(curKey); + path.push(cur); + if (isBranchCell(cur)) break; + const nexts = conveyorNeighborCells(cur).filter(p => !(p.col === prev.col && p.row === prev.row)); + if (nexts.length !== 1) break; + prev = cur; + cur = nexts[0]; + } + return path; + }; + const before = neighbors[0] ? walk(neighbors[0]).reverse() : []; + const after = neighbors[1] ? walk(neighbors[1]) : []; + return [...before, cell, ...after]; + } + + function directionInMeta(meta, dir) { + return !!meta && [...new Set([...(Array.isArray(meta.outDirs) ? meta.outDirs : []), meta.dir].filter(Boolean))].includes(dir); + } + + function applyRunDirection(path) { + let forward = 0, backward = 0; + for (let i = 0; i < path.length - 1; i += 1) { + const a = path[i], b = path[i + 1]; + const fwd = directionNameBetweenCells(a, b); + const back = directionNameBetweenCells(b, a); + if (directionInMeta(game.conveyorMeta.get(key(a.col, a.row)), fwd)) forward += 1; + if (directionInMeta(game.conveyorMeta.get(key(b.col, b.row)), back)) backward += 1; + } + const target = forward > backward ? [...path].reverse() : path; + for (let i = 0; i < target.length - 1; i += 1) { + const from = target[i]; + if (isBranchCell(from)) continue; + const to = target[i + 1]; + const meta = game.conveyorMeta.get(key(from.col, from.row)); + const dir = directionNameBetweenCells(from, to); + if (!meta || !dir) continue; + meta.dir = dir; + meta.outDirs = [dir]; + meta.branchMode = 'random'; + } + if (target.length >= 2) { + const last = target[target.length - 1]; + const prev = target[target.length - 2]; + const meta = game.conveyorMeta.get(key(last.col, last.row)); + const dir = directionNameBetweenCells(prev, last); + if (meta && dir && !isBranchCell(last)) { + meta.dir = dir; + meta.outDirs = [dir]; + meta.branchMode = 'random'; + } + } + } + function conveyorToolDef() { return BALANCE.facilities[game.buildTool === 'boostConveyor' ? 'boostConveyor' : 'conveyor']; } @@ -310,7 +438,7 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel const k = key(col, row); if (game.conveyorTiles.has(k)) return { ok: false, exists: true }; if (isBlockedCell(col, row)) return { ok: false, reason: 'Cannot build on blocked ground.' }; - if (farmAt(game, col, row) || scannerAt(game, col, row)) return { ok: false, reason: TEXT.fail.cellOccupied }; + if (farmAt(game, col, row) || scannerAt(game, col, row) || maintenanceRoomAtCell(col, row)) return { ok: false, reason: TEXT.fail.cellOccupied }; const toolDef = conveyorToolDef(); const kind = toolDef.id || 'conveyor'; const cost = buildPrice(kind, game); @@ -408,6 +536,34 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel conveyorDragRecordedDirectionEdit = false; } + function maintenanceRoomPlacementBlocked(col, row, moving = null) { + const ignoreIds = moving?.type === 'facility' && moving.ref?.id ? new Set([moving.ref.id]) : new Set(); + for (const cell of maintenanceRoomCells(col, row)) { + if (!pointInGrid(cell.col, cell.row) || isBlockedCell(cell.col, cell.row)) return true; + if (farmAt(game, cell.col, cell.row) || scannerAt(game, cell.col, cell.row)) return true; + if (game.conveyorTiles.has(key(cell.col, cell.row))) return true; + if (maintenanceRoomAtCell(cell.col, cell.row, ignoreIds)) return true; + if (facilityEntryAtCell(cell.col, cell.row, ignoreIds)) return true; + } + return false; + } + + function buildMaintenanceRoom(col, row) { + if (maintenanceRoomPlacementBlocked(col, row)) return fail(TEXT.fail.cellOccupied); + const cost = buildPrice('maintenanceRoom', game); + if (game.cash < cost) return fail(TEXT.fail.notEnoughCash); + record(game); + spendCash(game, cost); + const room = applyBuildQuality(createMaintenanceRoom(game, col, row, cost)); + const id = game.facilities.maintenanceRoom ? `maintenanceRoom:${game.nextId++}` : 'maintenanceRoom'; + room.id = id; + room.baseId = 'maintenanceRoom'; + game.facilities[id] = room; + game.selected = { type: 'facility', id }; + floating(game, room.x + room.w / 2, room.y + room.h / 2, `-${yen(cost)}`, THEME.ink); + updatePanels(); + } + function buildAtCell(cell) { if (!cell) return fail(TEXT.fail.outOfGrid); const { col, row } = cell; @@ -417,6 +573,7 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel refreshRoutingAfterEdit(game); return; } + if (game.buildTool === 'maintenanceRoom') return buildMaintenanceRoom(col, row); if (isBlockedCell(col, row)) return fail('Cannot build on blocked ground.'); if ((game.buildTool === 'manualScanner' || game.buildTool === 'autoScanner') ? scannerPlacementBlocked(col, row) : isEquipmentCell(col, row)) return fail(TEXT.fail.cellOccupied); if (game.buildTool === 'eggFarm') buildFarm(col, row); @@ -426,7 +583,7 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel function buildAtPoint(p) { const cell = pointToCell(p.x, p.y); - if (['conveyor', 'boostConveyor', 'eggFarm', 'manualScanner', 'autoScanner'].includes(game.buildTool)) return buildAtCell(cell); + if (['conveyor', 'boostConveyor', 'eggFarm', 'manualScanner', 'autoScanner', 'maintenanceRoom'].includes(game.buildTool)) return buildAtCell(cell); if (MACHINE_FACILITY_IDS.includes(game.buildTool)) return buildFacility(p, game.buildTool); } @@ -714,9 +871,6 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel ui.modalActions.innerHTML = ''; if (obj.type === 'scanner' && obj.kind === 'auto') ui.modalActions.appendChild(modalButton('Auto Menu', () => showAutoScannerMenu(obj), 'facility-action warn')); if (obj.type === 'scanner' && obj.kind === 'manual') ui.modalActions.appendChild(modalButton('Set Keys', () => showManualScannerMenu(obj), 'facility-action warn')); - if (obj.type === 'conveyor' && isBranchKey(obj.id)) { - ui.modalActions.appendChild(modalButton(`Branch: ${branchModeLabel(game.conveyorMeta.get(obj.id)?.branchMode || 'random')}`, () => { cycleBranchModeForKey(obj.id); showSelectedMenu(); }, 'facility-action warn')); - } const hit = obj.type === 'conveyor' ? { type: 'conveyor', oldKey: obj.id, ref: obj } : { type: obj.type, ref: obj }; const saleReason = lastProtectedSaleReason(hit); if (saleReason) { @@ -764,12 +918,12 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel } function branchOutDirsForKey(k) { - return reachableOutDirsForKey(k); + return branchExitDirsForKey(k); } function isBranchKey(k) { if (!game.conveyorTiles.has(k)) return false; - return conveyorNeighborOutDirs(parseKey(k)).length >= 3; + return isBranchCell(parseKey(k)); } function branchSwitcherAtPoint(p) { @@ -788,21 +942,21 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel function cycleBranchModeAtPoint(p) { const k = branchSwitcherAtPoint(p); if (!k) return false; - return cycleBranchModeForKey(k); + cycleBranchModeForKey(k); + return true; } function cycleBranchModeForKey(k) { const meta = game.conveyorMeta.get(k); if (!meta) return false; - const reachable = reachableOutDirsForKey(k); + const reachable = branchSelectableEntranceDirsForKey(k); if (reachable.length < 2) return false; record(game); const current = BRANCH_MODES.includes(meta.branchMode) ? meta.branchMode : 'random'; - const modes = ['random', ...(reachable.length >= 4 ? ['pass'] : []), ...DIRS.map(d => d.name).filter(mode => reachable.includes(mode))]; + const modes = ['random', ...DIRS.map(d => d.name).filter(mode => reachable.includes(mode))]; meta.branchMode = modes[(Math.max(0, modes.indexOf(current)) + 1) % modes.length]; - game.selected = { type: 'conveyor', id: k }; - const c = cellCenter(...Object.values(parseKey(k))); - floating(game, c.x, c.y - 22, branchModeLabel(meta.branchMode), THEME.ink); + game.selected = null; + game.multiSelected = []; updatePanels(); return true; } @@ -841,13 +995,21 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel lines.push(`Branch mode: ${branchModeLabel(meta?.branchMode || 'random')}`); lines.push('Use the Branch button below or the on-belt branch badge.'); } else { - lines.push('Branch mode: needs 3+ conveyor exits.'); + lines.push('Branch mode: needs 3+ conveyor connections.'); } lines.push(`Sale value: ${yen(resaleValueFor({ type: 'conveyor', oldKey: obj.id, ref: obj }, game).amount)}`); } else if (obj.type === 'facility') { lines.push(`Price: ${yen(equipmentPrice(obj))}`); { const reason = lastProtectedSaleReason({ type: obj.type, ref: obj }); lines.push(reason ? `Sale: blocked (${reason})` : `Sale value: ${yen(resaleValueFor({ type: obj.type, ref: obj }, game).amount)}`); } - lines.push(['mixer', 'truck', 'trash'].includes(obj.id) ? `Level: ${obj.level} / no cap` : `Level: ${obj.level}`); + if (facilityKind === 'maintenanceRoom') { + const workers = Math.max(1, Math.floor(obj.level || 1)); + lines.push(`Workers: ${workers}`); + lines.push(`Wage: ${yen(repairmanDailyCost(game))} / worker / day`); + lines.push(`Daily cost: ${yen(workers * repairmanDailyCost(game))}`); + lines.push(`Footprint: ${obj.cols || 2}x${obj.rows || 2} cells`); + return lines; + } + lines.push(['mixer', 'truck', 'trash'].includes(facilityKind) ? `Level: ${obj.level} / no cap` : `Level: ${obj.level}`); if (facilityKind === 'truck') lines.push('Durability: none | Always normal'); if (['mixer', 'trash'].includes(facilityKind)) lines.push(`Durability: ${remainingPercent(obj)}% | Extra delay ${facilityProcessingDelay(game, obj).toFixed(2)}s`); if (facilityKind === 'mixer') { @@ -885,6 +1047,7 @@ export function createBuildSystem({ game, ui, canvas, canvasPoint, onUpdatePanel if (obj.type === 'eggFarm') return 'A tiny gatehouse producing questionable eggs on schedule.'; if (obj.type === 'scanner' && obj.kind === 'auto') return 'Slower than hands without upgrades.'; if (obj.type === 'scanner') return 'A manual checkpoint. The operator is the algorithm.'; + if (obj.type === 'facility' && (obj.baseId || obj.id) === 'maintenanceRoom') return 'Spawns repair workers at day start. Upgrade cards add workers.'; if (obj.type === 'facility' && (obj.baseId || obj.id) === 'mixer') return 'Male chicks become revenue here. Do not feed it poop.'; if (obj.type === 'facility' && (obj.baseId || obj.id) === 'trash') return 'Poop goes in. Sometimes coins come out, for reasons best left unaudited.'; if (obj.type === 'facility' && (obj.baseId || obj.id) === 'truck') return 'The shipping endpoint. Correct cargo pays; wrong cargo complains.'; diff --git a/src/systems/cards.js b/src/systems/cards.js index fc2f474..c37b417 100644 --- a/src/systems/cards.js +++ b/src/systems/cards.js @@ -3,7 +3,7 @@ import { nextSpawnDelay } from '../core/state.js'; import { cellCenter, key, yen } from '../core/utils.js'; import { applyPenalty, applyRevenue, upgradedMixerPrice, upgradedTruckPrice, shredderUpgradeCount, rawShredderUpgradeCount, shredderBonusChance, shredderBonusMaxCards } from './economy.js'; import { floating, shake, eraseEffect, shockwave, sparkBurst, smokeBurst } from './effects.js'; -import { averageConveyorPerformance, autoScannerDelayMultiplier, ensureMaintenanceState, repairAllEquipment } from './maintenance.js'; +import { averageConveyorPerformance, autoScannerDelayMultiplier, ensureMaintenanceState, maintenanceRoomWorkerCount, repairAllEquipment, repairmanDailyCost } from './maintenance.js'; import { scannerCenter, refreshRoutingAfterEdit } from './routing.js'; const COMMON_WEIGHT = CARD_BALANCE.commonWeight; @@ -118,6 +118,10 @@ function targetLabel(game, target) { if (target.type === 'scanner') { return `AUTO #${target.id} L${target.level} -> L${target.level + 1} | CD ${autoScannerCooldownSeconds(target, game).toFixed(1)}s -> ${autoScannerCooldownSeconds({ ...target, level: target.level + 1 }, game).toFixed(1)}s`; } + if (target.type === 'facility' && (target.baseId || target.id) === 'maintenanceRoom') { + const before = maintenanceRoomWorkerCount(target); + return `MAINT ROOM workers ${before} -> ${before + 1} | wage ${yen(repairmanDailyCost(game))}/worker/day`; + } if (target.type === 'facility' && (target.baseId || target.id) === 'mixer') { const beforeLevel = target.level; const before = upgradedMixerPrice(game); @@ -161,6 +165,7 @@ export function targetsForCard(game, cardOrId) { .filter(f => Math.max(0, Number(f.extraEggOutlet) || 0) < 3) .map(f => ({ type: 'eggOutlet', id: f.id, col: f.col, row: f.row, farm: f })); if (card.target === 'autoScanner') return game.scanners.filter(s => s.kind === 'auto'); + if (card.target === 'maintenanceRoom') return Object.values(game.facilities || {}).filter(f => (f.baseId || f.id) === 'maintenanceRoom'); if (card.target === 'mixer') return Object.values(game.facilities || {}).filter(f => (f.baseId || f.id) === 'mixer'); if (card.target === 'truck') return Object.values(game.facilities || {}).filter(f => (f.baseId || f.id) === 'truck'); if (card.target === 'trash') return rawShredderUpgradeCount(game) < shredderBonusMaxCards() @@ -186,7 +191,8 @@ function cardAtCap(game, card) { function repairmanCardVisible(game) { const r = game.repairman || {}; - return !!(r.active || r.hiredForNextDay || game.pendingRepairWorkerWages > 0); + const hasMaintenanceRoom = Object.values(game.facilities || {}).some(f => (f.baseId || f.id) === 'maintenanceRoom'); + return !!(hasMaintenanceRoom || r.active || r.hiredForNextDay || game.pendingRepairWorkerWages > 0); } function availableCards(game) { @@ -430,6 +436,11 @@ function cardDescription(game, card) { const after = autoScannerCooldownSeconds({ ...scanner, level: (scanner.level || 1) + 1 }, game); return `Choose one AUTO SCANNER. Level +1. Cooldown ${before.toFixed(2)}s -> ${after.toFixed(2)}s.`; } + if (card.id === 'upgradeMaintenanceRoom') { + const room = targetsForCard(game, card)[0] || { level: 1 }; + const workers = maintenanceRoomWorkerCount(room); + return `Choose one MAINTENANCE ROOM. Repair workers ${workers} -> ${workers + 1}. Wage ${yen(repairmanDailyCost(game))} per worker per day.`; + } if (card.id === 'upgradeMixer') { const preview = incomeUpgradePreview(game, 'mixer', ECONOMY.income.mixer); return `Choose one MIXER. Earning +10%. Male-chick income ${yen(preview.before)} -> ${yen(preview.after)}.`; diff --git a/src/systems/chickSystem.js b/src/systems/chickSystem.js index 165e195..7593e66 100644 --- a/src/systems/chickSystem.js +++ b/src/systems/chickSystem.js @@ -1,7 +1,7 @@ import { DIRS, GRID, THEME } from '../core/config.js'; import { createChick } from '../core/entities.js'; import { nextSpawnDelay } from '../core/state.js'; -import { key, parseKey, pointToCell, cellCenter, randomBetween, yen, inGrid, directionNameBetweenCells } from '../core/utils.js'; +import { key, parseKey, pointToCell, cellCenter, randomBetween, yen, inGrid } from '../core/utils.js'; import { scannerById, scannerBySlot, scannerCenter, scannerConnector, nearestConveyorKey, buildConveyorComponents, autoSideFor, ensureFactoryGraph } from './routing.js'; import { applyMixerIncome, applyMixerPoopFine, applyTruckPoopFine, applyWrongTruckFine, upgradedTruckPrice, applyShredderBonus, applyRevenue } from './economy.js'; import { autoScannerCooldownSeconds, eggProductionDelayMultiplier, extraEggOutletCount, scannerQueueSpacingMultiplier } from './cards.js'; @@ -239,6 +239,23 @@ export function createChickSystem({ game, currentConveyorSpeed, onGameOverCheck return names.includes(d.opposite); } + function conveyorNeighborDirNames(cell) { + return DIRS + .filter(d => inGrid(cell.col + d.dc, cell.row + d.dr) && game.conveyorTiles.has(key(cell.col + d.dc, cell.row + d.dr))) + .map(d => d.name); + } + + function branchExitDirNames(cell, dirs, branchMode = 'random') { + const connected = conveyorNeighborDirNames(cell); + if (connected.length < 3) return dirs; + if (branchMode && branchMode !== 'random' && branchMode !== 'pass' && connected.includes(branchMode)) return [branchMode]; + const entrances = new Set(connected.filter(dir => neighborPointsIntoCell(cell, dir))); + const exits = connected.filter(dir => !entrances.has(dir)); + if (exits.length) return exits; + const fallback = dirs.filter(dir => !entrances.has(dir)); + return fallback.length ? fallback : dirs; + } + function optionAvailableForBranch(chick, opt) { return opt && (opt.type === 'facility' || !targetBlockedByChick(chick, opt.target)); } @@ -246,16 +263,6 @@ export function createChickSystem({ game, currentConveyorSpeed, onGameOverCheck function pickMovementOption(chick, cell, dirs, branchMode = 'random') { const candidates = dirs.map(dir => movementOptionFromDir(cell, dir)).filter(Boolean); if (!candidates.length) return null; - if (branchMode === 'pass') { - const previous = chick.prevConveyorCell ? parseKey(chick.prevConveyorCell) : null; - const forward = previous ? directionNameBetweenCells(previous, cell) : null; - const pass = forward ? candidates.find(opt => opt.dirName === forward) : null; - if (optionAvailableForBranch(chick, pass)) return pass; - } - if (branchMode && branchMode !== 'random') { - const preferred = candidates.find(opt => opt.dirName === branchMode); - if (optionAvailableForBranch(chick, preferred)) return preferred; - } const moving = candidates.filter(opt => opt.type !== 'facility'); const empty = moving.filter(opt => !targetBlockedByChick(chick, opt.target)); const pool = empty.length ? empty : candidates.filter(opt => opt.type === 'facility'); @@ -277,7 +284,7 @@ export function createChickSystem({ game, currentConveyorSpeed, onGameOverCheck const currentKey = key(cell.col, cell.row); if (!game.conveyorTiles.has(currentKey)) return removeChick(index, 'OFF BELT'); const meta = game.conveyorMeta?.get(currentKey) || {}; - const dirs = conveyorOutDirNames(currentKey); + const dirs = branchExitDirNames(cell, conveyorOutDirNames(currentKey), meta.branchMode || 'random'); if (!dirs.length) { chick.stoppedTimer = 0.25; return; diff --git a/src/systems/economy.js b/src/systems/economy.js index e295355..82dca09 100644 --- a/src/systems/economy.js +++ b/src/systems/economy.js @@ -24,7 +24,7 @@ export function equipmentBasePrice(objOrHit) { if (type === 'conveyor') return obj.price || FACILITY_DEFS[obj.kind]?.price || FACILITY_DEFS.conveyor.price; if (type === 'eggFarm') return obj.price || FACILITY_DEFS.eggFarm.price; if (type === 'scanner') return obj.price || (obj.kind === 'auto' ? FACILITY_DEFS.autoScanner.price : FACILITY_DEFS.manualScanner.price); - if (type === 'facility') return obj.price || FACILITY_DEFS[obj.id]?.price || 0; + if (type === 'facility') return obj.price || FACILITY_DEFS[obj.baseId || obj.id]?.price || 0; return FACILITY_DEFS[obj.id]?.price || 0; } diff --git a/src/systems/maintenance.js b/src/systems/maintenance.js index b17815d..cf7bcee 100644 --- a/src/systems/maintenance.js +++ b/src/systems/maintenance.js @@ -1,10 +1,12 @@ import { BALANCE } from '../core/balance.js'; import { GRID, THEME } from '../core/config.js'; -import { cellCenter, parseKey, key, yen } from '../core/utils.js'; +import { cellCenter, parseKey, key, yen, pointToCell, inGrid } from '../core/utils.js'; +import { isOwnedCell } from '../core/gridExpansion.js'; import { spendCash } from './economy.js'; import { floating } from './effects.js'; const RULES = BALANCE.maintenance; +const REPAIR_INTEREST_WEAR_THRESHOLD = 0.20; function effectCount(game, id) { return Math.max(0, Number(game?.cardEffects?.[id]) || 0); @@ -30,6 +32,44 @@ export function repairmanDailyCost(game) { return game?.cardEffects?.laborExploitation ? Math.ceil(base * 0.7) : base; } +function facilityKind(target) { + return target?.baseId || target?.id || target; +} + +export function maintenanceRooms(game) { + return Object.values(game?.facilities || {}).filter(f => facilityKind(f) === 'maintenanceRoom'); +} + +export function maintenanceRoomWorkerCount(room) { + return Math.max(1, Math.floor(Number(room?.level) || 1)); +} + +export function maintenanceWorkerCount(game) { + return maintenanceRooms(game).reduce((sum, room) => sum + maintenanceRoomWorkerCount(room), 0); +} + +export function repairWorkerWageForDay(game) { + return maintenanceWorkerCount(game) * repairmanDailyCost(game); +} + +function roomSpawnPoint(room, index = 0, total = 1) { + const cols = Math.max(1, Math.ceil(Math.sqrt(total))); + const row = Math.floor(index / cols); + const col = index % cols; + const spacing = 12; + return { + x: room.x + room.w / 2 + (col - (cols - 1) / 2) * spacing, + y: room.y + room.h / 2 + (row - Math.floor((total - 1) / cols) / 2) * spacing + }; +} + +function defaultRepairmanPosition() { + return { + x: GRID.x + GRID.cell * 0.5, + y: GRID.y + GRID.rows * GRID.cell + 84 + }; +} + function qualityMultiplierOf(objOrMeta) { return Math.max(0.1, Number(objOrMeta?.durabilityBaseMultiplier) || 1); } @@ -39,12 +79,12 @@ export function ensureMaintenanceState(game) { game.repairman = { hiredForNextDay: false, active: false, - x: GRID.x + GRID.cell * 0.5, - y: GRID.y + GRID.rows * GRID.cell + 84, + ...defaultRepairmanPosition(), target: null, repairedToday: 0 }; } + if (!Array.isArray(game.repairmen)) game.repairmen = []; for (const [k, meta] of game.conveyorMeta || []) { if (meta.uses == null) meta.uses = 0; if (meta.maintenanceType == null) meta.maintenanceType = 'conveyor'; @@ -59,8 +99,9 @@ export function ensureMaintenanceState(game) { if (scanner.kind === 'auto') attachMaintenance(scanner, 'autoScanner', game); } for (const f of Object.values(game.facilities || {})) { - if (f.id === 'mixer') attachMaintenance(f, 'mixer', game); - if (f.id === 'trash') attachMaintenance(f, 'trash', game); + const id = facilityKind(f); + if (id === 'mixer') attachMaintenance(f, 'mixer', game); + if (id === 'trash') attachMaintenance(f, 'trash', game); } } @@ -141,10 +182,6 @@ export function eggSpawnDelayMultiplier(farm) { return delayMultiplier(farm); } -function facilityKind(target) { - return target?.baseId || target?.id || target; -} - function facilityFromTarget(game, target) { if (!target) return null; if (typeof target === 'object') return target; @@ -213,14 +250,16 @@ export function equipmentMaintenanceTargets(game) { for (const [k, meta] of game.conveyorMeta || []) targets.push({ type: 'conveyor', key: k, meta, label: `${meta.kind === 'boostConveyor' ? 'Boost Belt' : 'Belt'} ${k}`, center: cellCenter(parseKey(k).col, parseKey(k).row) }); for (const farm of game.eggFarms || []) targets.push({ type: 'eggFarm', ref: farm, label: `EGG #${farm.id}`, center: cellCenter(farm.col, farm.row) }); for (const scanner of (game.scanners || []).filter(s => s.kind === 'auto')) targets.push({ type: 'autoScanner', ref: scanner, label: `AUTO #${scanner.id}`, center: cellCenter(scanner.col, scanner.row) }); - for (const id of ['mixer', 'trash']) { - const f = game.facilities?.[id]; - if (f) targets.push({ type: id, ref: f, label: f.shortName || f.name || id, center: { x: f.x + f.w / 2, y: f.y + f.h / 2 } }); + for (const f of Object.values(game.facilities || {})) { + const id = facilityKind(f); + if (!['mixer', 'trash'].includes(id)) continue; + targets.push({ type: id, ref: f, label: f.shortName || f.name || id, center: { x: f.x + f.w / 2, y: f.y + f.h / 2 } }); } return targets; } function targetWear(t) { return t.meta ? wearRatio(t) : wearRatio(t.ref); } +function targetNeedsRepair(t) { return targetWear(t) > REPAIR_INTEREST_WEAR_THRESHOLD; } function targetUses(t) { return t.meta ? usesOf(t) : usesOf(t.ref); } function targetDurability(t) { return t.meta ? durabilityOf(t) : durabilityOf(t.ref); } function reduceTargetUses(t, amount) { setUses(t.meta ? t : t.ref, Math.max(0, targetUses(t) - amount)); } @@ -252,14 +291,36 @@ export function hireRepairmanForNextDay(game) { export function activateRepairmanForDay(game) { ensureMaintenanceState(game); - game.repairman.active = !!game.repairman.hiredForNextDay; - game.repairman.hiredForNextDay = false; - game.repairman.target = null; - game.repairman.repairedToday = 0; + const rooms = maintenanceRooms(game); + const workers = []; + for (const room of rooms) { + const count = maintenanceRoomWorkerCount(room); + for (let i = 0; i < count; i += 1) { + const p = roomSpawnPoint(room, i, count); + workers.push({ + id: `${room.id}:worker:${i}`, + homeRoomId: room.id, + hiredForNextDay: false, + active: true, + x: p.x, + y: p.y, + target: null, + repairedToday: 0 + }); + } + } + game.repairmen = workers; + const legacy = game.repairman || { ...defaultRepairmanPosition() }; + game.repairman = workers[0] || { ...legacy, active: false, hiredForNextDay: false, target: null, repairedToday: 0 }; + return { count: workers.length, wages: workers.length * repairmanDailyCost(game) }; } export function deactivateRepairman(game) { ensureMaintenanceState(game); + for (const r of game.repairmen || []) { + r.active = false; + r.target = null; + } game.repairman.active = false; game.repairman.target = null; } @@ -270,29 +331,114 @@ function resolveStoredTarget(game, stored) { return targets.find(t => targetKey(t) === stored.key) || null; } -function chooseRepairTarget(game) { +function repairWalkableCell(game, col, row) { + return inGrid(col, row) && isOwnedCell(game, col, row) && !game.blockedCells?.has?.(key(col, row)); +} + +function nearestRepairWalkableCell(game, point) { + const direct = pointToCell(point.x, point.y); + if (direct && repairWalkableCell(game, direct.col, direct.row)) return direct; + let best = null; + let bestDist = Infinity; + for (let row = 0; row < GRID.rows; row += 1) { + for (let col = 0; col < GRID.cols; col += 1) { + if (!repairWalkableCell(game, col, row)) continue; + const c = cellCenter(col, row); + const dist = Math.hypot(c.x - point.x, c.y - point.y); + if (dist < bestDist) { + bestDist = dist; + best = { col, row }; + } + } + } + return best; +} + +function repairApproachPoint(_game, target) { + const ref = target?.ref || null; + if (ref?.entry && Number.isFinite(ref.entry.col) && Number.isFinite(ref.entry.row)) return cellCenter(ref.entry.col, ref.entry.row); + return target?.center || null; +} + +function repairCellPath(game, fromPoint, toPoint) { + const start = nearestRepairWalkableCell(game, fromPoint); + const goal = nearestRepairWalkableCell(game, toPoint); + if (!start || !goal) return null; + const startKey = key(start.col, start.row); + const goalKey = key(goal.col, goal.row); + const queue = [start]; + const cameFrom = new Map([[startKey, null]]); + for (let qi = 0; qi < queue.length; qi += 1) { + const cur = queue[qi]; + const ck = key(cur.col, cur.row); + if (ck === goalKey) break; + for (const d of [{ dc: 1, dr: 0 }, { dc: -1, dr: 0 }, { dc: 0, dr: 1 }, { dc: 0, dr: -1 }]) { + const next = { col: cur.col + d.dc, row: cur.row + d.dr }; + if (!repairWalkableCell(game, next.col, next.row)) continue; + const nk = key(next.col, next.row); + if (cameFrom.has(nk)) continue; + cameFrom.set(nk, ck); + queue.push(next); + } + } + if (!cameFrom.has(goalKey)) return null; + const cells = []; + let curKey = goalKey; + while (curKey) { + const p = parseKey(curKey); + cells.push(p); + curKey = cameFrom.get(curKey); + } + cells.reverse(); + return cells; +} + +function repairWaypoints(game, r, target) { + const approach = repairApproachPoint(game, target); + if (!approach) return null; + const cellPath = repairCellPath(game, { x: r.x, y: r.y }, approach); + if (!cellPath) return null; + const waypoints = cellPath.slice(1).map(c => cellCenter(c.col, c.row)); + const last = waypoints[waypoints.length - 1] || cellCenter(cellPath[0].col, cellPath[0].row); + if (Math.hypot(last.x - approach.x, last.y - approach.y) > 2) waypoints.push(approach); + if (!waypoints.length) waypoints.push(approach); + return waypoints; +} + +function canRepairmanReachTarget(game, r, target) { + return !!repairWaypoints(game, r || defaultRepairmanPosition(), target); +} + +function chooseRepairTarget(game, r) { let best = null; for (const t of equipmentMaintenanceTargets(game)) { const w = targetWear(t); - if (w <= 0.001) continue; + if (!targetNeedsRepair(t)) continue; + if (r && !canRepairmanReachTarget(game, r, t)) continue; if (!best || w > targetWear(best)) best = t; } return best; } -export function updateRepairman(game, dt) { - ensureMaintenanceState(game); - const r = game.repairman; +function updateSingleRepairman(game, r, dt) { if (!r.active || game.phase !== 'running') return; - let target = resolveStoredTarget(game, r.target) || chooseRepairTarget(game); + let target = resolveStoredTarget(game, r.target); + if (target && (!targetNeedsRepair(target) || !canRepairmanReachTarget(game, r, target))) target = null; + target = target || chooseRepairTarget(game, r); if (!target) { r.target = null; return; } r.target = { key: targetKey(target), label: target.label }; + const waypoints = repairWaypoints(game, r, target); + if (!waypoints?.length) { + r.target = null; + return; + } + const waypoint = waypoints[0]; const speed = RULES.repairman.walkSpeed; - const dx = target.center.x - r.x; - const dy = target.center.y - r.y; + const dx = waypoint.x - r.x; + const dy = waypoint.y - r.y; const dist = Math.hypot(dx, dy); if (dist > 8) { const step = Math.min(dist, speed * dt); @@ -300,14 +446,23 @@ export function updateRepairman(game, dt) { r.y += dy / dist * step; return; } + if (waypoints.length > 1 || Math.hypot((repairApproachPoint(game, target)?.x || waypoint.x) - r.x, (repairApproachPoint(game, target)?.y || waypoint.y) - r.y) > 10) return; const repairWear = dt / (RULES.repairman.secondsPerOnePercent * 100); const usesBefore = targetUses(target); + const maxAllowedUses = targetDurability(target) * REPAIR_INTEREST_WEAR_THRESHOLD; const usesDelta = repairWear * targetDurability(target); - reduceTargetUses(target, usesDelta); + setUses(target.meta ? target : target.ref, Math.max(maxAllowedUses, targetUses(target) - usesDelta)); const repairedPct = Math.max(0, (usesBefore - targetUses(target)) / targetDurability(target) * 100); r.repairedToday = (r.repairedToday || 0) + repairedPct; if (repairedPct > 0 && Math.random() < 0.02) floating(game, r.x, r.y - 28, 'REPAIR', THEME.green); - if (targetWear(target) <= 0.001) r.target = null; + if (!targetNeedsRepair(target)) r.target = null; +} + +export function updateRepairman(game, dt) { + ensureMaintenanceState(game); + const workers = Array.isArray(game.repairmen) && game.repairmen.length ? game.repairmen : (game.repairman ? [game.repairman] : []); + for (const r of workers) updateSingleRepairman(game, r, dt); + if (workers.length) game.repairman = workers[0]; } function worstDegradation(game) { @@ -323,5 +478,12 @@ function worstDegradation(game) { export function maintenanceSummary(game) { const worst = worstDegradation(game); const avgBeltPerformance = averageConveyorPerformance(game); - return { worst, avgBeltPerformance, repairman: game.repairman || null }; + return { + worst, + avgBeltPerformance, + repairman: game.repairman || null, + repairmen: Array.isArray(game.repairmen) ? game.repairmen : [], + workers: maintenanceWorkerCount(game), + dailyWage: repairWorkerWageForDay(game) + }; } diff --git a/src/systems/routing.js b/src/systems/routing.js index 92585cb..426ca5e 100644 --- a/src/systems/routing.js +++ b/src/systems/routing.js @@ -133,12 +133,20 @@ function neighborPointsIntoCell(game, p, dirName) { function inferredConveyorOutDirNames(game, cellKey) { const p = graphCell(cellKey); + const meta = game.conveyorMeta?.get?.(cellKey) || {}; const neighbors = DIRS .filter(d => game.conveyorTiles.has(graphNodeKey(p.col + d.dc, p.row + d.dr))) .map(d => d.name); const explicit = conveyorOutDirNames(game, cellKey).filter(dir => neighbors.includes(dir)); const inferred = neighbors; - return explicit.length ? explicit : inferred; + const dirs = explicit.length ? explicit : inferred; + if (neighbors.length < 3) return dirs; + if (meta.branchMode && meta.branchMode !== 'random' && neighbors.includes(meta.branchMode)) return [meta.branchMode]; + const entrances = new Set(neighbors.filter(dir => neighborPointsIntoCell(game, p, dir))); + const exits = neighbors.filter(dir => !entrances.has(dir)); + if (exits.length) return exits; + const fallback = dirs.filter(dir => !entrances.has(dir)); + return fallback.length ? fallback : dirs; } function emptyFactoryGraph(game) { @@ -306,13 +314,6 @@ function dirBetween(a, b) { return DIRS.find(d => d.dc === dc && d.dr === dr)?.name || null; } -function isPassCross(game, graph, cellKey) { - if (game.conveyorMeta?.get?.(cellKey)?.branchMode !== 'pass') return false; - const ns = graph.adjacency.get(cellKey) || []; - const names = new Set(ns.map(n => n.dir)); - return ns.length === 4 && names.has('left') && names.has('right') && names.has('up') && names.has('down'); -} - export function bfsAllRoutes(game, start, isGoal) { const graph = ensureFactoryGraph(game); const startK = graphNodeKey(start.col, start.row); @@ -343,7 +344,6 @@ export function bfsAllRoutes(game, start, isGoal) { for (const n of graph.adjacency.get(cur.key) || []) { const next = graphCell(n.key); const outDir = dirBetween(cur, next); - if (cur.incoming !== 'none' && isPassCross(game, graph, cur.key) && outDir !== cur.incoming) continue; const nextDepth = cur.depth + 1; if (nextDepth > bestGoalDepth) continue; const nextStateK = `${n.key}|${outDir}`; diff --git a/src/systems/selectionSystem.js b/src/systems/selectionSystem.js index 505a729..6ed6011 100644 --- a/src/systems/selectionSystem.js +++ b/src/systems/selectionSystem.js @@ -13,6 +13,25 @@ export function createSelectionSystem({ game, canvasPoint, updatePanels, equipme return `${item.type}:${item.id || item.ref?.id}`; } + function facilityKind(f) { return f?.baseId || f?.id || ''; } + function isMaintenanceRoom(f) { return facilityKind(f) === 'maintenanceRoom'; } + function maintenanceRoomFootprint(room, col = room?.col, row = room?.row) { + const cells = []; + const cols = room?.cols || 2; + const rows = room?.rows || 2; + for (let r = 0; r < rows; r += 1) for (let c = 0; c < cols; c += 1) cells.push({ col: col + c, row: row + r }); + return cells; + } + function syncMaintenanceRoomRect(room) { + if (!room) return; + room.cols = room.cols || 2; + room.rows = room.rows || 2; + room.x = GRID.x + room.col * GRID.cell; + room.y = GRID.y + room.row * GRID.cell; + room.w = room.cols * GRID.cell; + room.h = room.rows * GRID.cell; + } + function selectionFromHit(hit) { if (!hit) return null; if (hit.type === 'conveyor') return { type: 'conveyor', id: hit.oldKey }; @@ -118,7 +137,10 @@ export function createSelectionSystem({ game, canvasPoint, updatePanels, equipme return { ...sel, obj, oldKey: sel.id, currentKey: sel.id, meta: game.conveyorMeta.get(sel.id) || { price: buildPrice('conveyor'), builtSession: null }, col: cell.col, row: cell.row }; } if (sel.type === 'eggFarm' || sel.type === 'scanner') return { ...sel, obj, col: obj.col, row: obj.row }; - if (sel.type === 'facility') return { ...sel, obj, x: obj.x, y: obj.y, w: obj.w, h: obj.h, entry: obj.entry ? { ...obj.entry } : null, side: obj.side, center: { x: obj.x + obj.w / 2, y: obj.y + obj.h / 2 } }; + if (sel.type === 'facility') { + if (isMaintenanceRoom(obj)) return { ...sel, obj, col: obj.col, row: obj.row, cols: obj.cols || 2, rows: obj.rows || 2, maintenanceRoom: true, center: { x: obj.x + obj.w / 2, y: obj.y + obj.h / 2 } }; + return { ...sel, obj, x: obj.x, y: obj.y, w: obj.w, h: obj.h, entry: obj.entry ? { ...obj.entry } : null, side: obj.side, center: { x: obj.x + obj.w / 2, y: obj.y + obj.h / 2 } }; + } return null; } @@ -150,13 +172,17 @@ export function createSelectionSystem({ game, canvasPoint, updatePanels, equipme const scanner = scannerAt(game, col, row); if (scanner && !selectedTokens.has(`scanner:${scanner.id}`)) return true; if (game.conveyorTiles.has(k) && !selectedTokens.has(`conveyor:${k}`)) return true; + for (const f of Object.values(game.facilities || {})) { + if (!isMaintenanceRoom(f) || selectedTokens.has(`facility:${f.id}`)) continue; + if (maintenanceRoomFootprint(f).some(cell => cell.col === col && cell.row === row)) return true; + } return false; } function facilityDragWouldOverlap(dx, dy, selectedTokens) { const candidateRects = []; for (const origin of game.groupDrag.origins) { - if (origin.type !== 'facility') continue; + if (origin.type !== 'facility' || origin.maintenanceRoom) continue; const nextCenter = { x: origin.center.x + dx, y: origin.center.y + dy }; const { entry, side } = nearestGridEdge(nextCenter); if (!pointInGrid(entry.col, entry.row)) return true; @@ -190,15 +216,16 @@ export function createSelectionSystem({ game, canvasPoint, updatePanels, equipme const selectedTokens = new Set(game.groupDrag.selections.map(selectionToken)); const targetCells = new Set(); for (const origin of game.groupDrag.origins) { - if (origin.type === 'facility') continue; + if (origin.type === 'facility' && !origin.maintenanceRoom) continue; const col = origin.col + dcol, row = origin.row + drow; - const cells = origin.type === 'scanner' ? scannerFootprintCells({ col, row }) : [{ col, row }]; + const cells = origin.maintenanceRoom ? maintenanceRoomFootprint(origin.obj, col, row) : (origin.type === 'scanner' ? scannerFootprintCells({ col, row }) : [{ col, row }]); for (const cell of cells) { if (!pointInGrid(cell.col, cell.row)) return fail('Selection outside grid'); if (game.blockedCells?.has?.(key(cell.col, cell.row))) return fail('Selection hits blocked ground'); const tk = key(cell.col, cell.row); if (targetCells.has(tk)) return fail('Selection overlap'); if (cellOccupiedByNonSelected(cell.col, cell.row, selectedTokens)) return fail('Cell occupied'); + if (origin.maintenanceRoom && Object.values(game.facilities || {}).some(f => f.entry && !selectedTokens.has(`facility:${f.id}`) && f.entry.col === cell.col && f.entry.row === cell.row)) return fail('Cell occupied'); targetCells.add(tk); } } @@ -225,9 +252,15 @@ export function createSelectionSystem({ game, canvasPoint, updatePanels, equipme origin.obj.col = origin.col + dcol; origin.obj.row = origin.row + drow; } else if (origin.type === 'facility') { - const nextCenter = { x: origin.center.x + dx, y: origin.center.y + dy }; - const { entry, side } = nearestGridEdge(nextCenter); - layoutFacilityOnEdge(origin.obj, entry, side); + if (origin.maintenanceRoom) { + origin.obj.col = origin.col + dcol; + origin.obj.row = origin.row + drow; + syncMaintenanceRoomRect(origin.obj); + } else { + const nextCenter = { x: origin.center.x + dx, y: origin.center.y + dy }; + const { entry, side } = nearestGridEdge(nextCenter); + layoutFacilityOnEdge(origin.obj, entry, side); + } } } game.groupDrag.selections = game.groupDrag.origins.map(o => o.type === 'conveyor' ? { type: 'conveyor', id: o.currentKey || o.oldKey } : { type: o.type, id: o.id }); diff --git a/src/systems/uiSystem.js b/src/systems/uiSystem.js index fc1a9df..bd5385d 100644 --- a/src/systems/uiSystem.js +++ b/src/systems/uiSystem.js @@ -4,8 +4,8 @@ import { TEXT } from '../core/text.js'; import { routeFromFarmToScanner, factoryReady, facilityConnectionIssues } from './routing.js'; import { buildPrice, fairiesTributeInfo, finalScore, maleTruckPenalty, mixerPoopPenalty, truckPoopPenalty, zundaTaxInfo } from './economy.js'; import { truckTarget } from './contracts.js'; -import { conveyorSpeedForGame } from './cards.js'; -import { maintenanceSummary, repairmanDailyCost } from './maintenance.js'; +import { CARD_DEFS, conveyorSpeedForGame } from './cards.js'; +import { maintenanceSummary } from './maintenance.js'; export function createUISystem({ game, ui, build, startGame, beginCardDraft, activeQueuedChick }) { function escapeHtml(value) { @@ -29,6 +29,7 @@ export function createUISystem({ game, ui, build, startGame, beginCardDraft, act updateBuildStatus(); updateContractPanel(); updateTurnSummary(); + updateAcquiredCardsPanel(); updateHistoryButtons(); } @@ -56,15 +57,6 @@ export function createUISystem({ game, ui, build, startGame, beginCardDraft, act : ''; } if (ui.buttons.erase) ui.buttons.erase.disabled = game.phase !== 'build' || !!game.cardDraft?.pending || !!game.cardTargetPick?.pending; - if (ui.hireRepairmanButton) { - const hired = !!game.repairman?.hiredForNextDay; - const cost = repairmanDailyCost(game); - const blocked = game.phase !== 'build' || !!game.cardDraft?.pending || !!game.cardTargetPick?.pending || hired || game.cash < cost; - ui.hireRepairmanButton.disabled = blocked; - ui.hireRepairmanButton.classList.toggle('active', hired); - ui.hireRepairmanButton.classList.toggle('unaffordable', game.cash < cost); - ui.hireRepairmanButton.title = hired ? 'Repairman is hired for the next production phase.' : `Hire for ${yen(cost)} for one day. Repairs 2% durability per second.`; - } const cashout = noAffordableBuildTools(); ui.buttons.nextTurn?.classList.toggle('cashout-emphasis', cashout); ui.buttons.erase?.classList.toggle('cashout-emphasis', cashout); @@ -81,6 +73,50 @@ export function createUISystem({ game, ui, build, startGame, beginCardDraft, act : TEXT.status.sorting; } + + const ACQUIRED_CARD_IDS = new Set([ + 'bearing', 'legalWork', 'crowdedFarming', 'hatchingFeed', 'safetyCover', + 'preventiveMaintenance', 'dudFilter', 'durabilityCoating', 'dudRefund', + 'freeReroll', 'composter', 'scannerQueueSpacing', 'recyclingSubsidy', + 'chemicalWeaponSubsidy', 'laborExploitation', 'usedMachine' + ]); + + function acquiredCardCount(card) { + const effects = game.cardEffects || {}; + if (!ACQUIRED_CARD_IDS.has(card.id)) return 0; + if (card.id === 'usedMachine') return effects.usedMachineActive ? 1 : 0; + if (card.id === 'laborExploitation') return effects.laborExploitation ? 1 : 0; + return Math.max(0, Number(effects[card.id]) || 0); + } + + function acquiredCardEntries() { + return CARD_DEFS + .filter(card => ACQUIRED_CARD_IDS.has(card.id)) + .map(card => ({ card, count: acquiredCardCount(card) })) + .filter(item => item.count > 0); + } + + function updateAcquiredCardsPanel() { + const panel = ui.cardInventory; + if (!panel) return; + const entries = acquiredCardEntries(); + const total = entries.reduce((sum, item) => sum + item.count, 0); + const summary = panel.querySelector('summary'); + const list = panel.querySelector('.acquired-cards-list'); + if (summary) summary.innerHTML = `CARDS ${total}${entries.length ? 'passive' : 'none'}`; + if (!list) return; + if (!entries.length) { + list.innerHTML = '

No passive acquired cards yet. Consumed cards and equipment improvements are hidden.

'; + panel.classList.add('empty'); + return; + } + panel.classList.remove('empty'); + list.innerHTML = entries.map(({ card, count }) => { + const tags = (card.tags || []).filter(tag => !['ACTIVE', 'ONE-SHOT'].includes(tag)).slice(0, 2); + return `
${escapeHtml(card.title)}x${count}${tags.length ? `${tags.map(escapeHtml).join(' / ')}` : ''}
`; + }).join(''); + } + function updateHistoryButtons() { ui.buttons.undo.disabled = game.phase !== 'build' || game.undoStack.length === 0; ui.buttons.redo.disabled = game.phase !== 'build' || game.redoStack.length === 0; @@ -95,6 +131,8 @@ export function createUISystem({ game, ui, build, startGame, beginCardDraft, act const tribute = fairiesTributeInfo(game, tributeDay); const lastTribute = game.lastBuildFees?.turn === tributeDay ? game.lastBuildFees.fairiesTribute || 0 : null; const lastTributeReduction = game.lastBuildFees?.turn === tributeDay ? game.lastBuildFees.reduction || 0 : 0; + const maint = maintenanceSummary(game); + const activeWorkers = Array.isArray(game.repairmen) ? game.repairmen.filter(r => r.active).length : (game.repairman?.active ? 1 : 0); ui.turnSummary.innerHTML = [ `Seed: ${escapeHtml(game.runSeed || 'none')} | Grid box: ${game.gridRows || 10}x${game.gridCols || 20} | Owned: ${game.gridOwnedCells || game.ownedCells?.size || ((game.gridRows || 10) * (game.gridCols || 20))} cells | Blocked: ${game.blockedCells?.size || 0} cells`, `Truck target: ${truckTarget(game).toUpperCase()} | Male fine: -${yen(maleTruckPenalty(game))}`, @@ -103,7 +141,7 @@ export function createUISystem({ game, ui, build, startGame, beginCardDraft, act lastTribute != null ? `Fairies tribute paid before Build: -${yen(lastTribute)}${lastTributeReduction ? ` | Flattery -${yen(lastTributeReduction)}` : ''}` : `Fairies tribute before Build: -${yen(tribute.amount)}${tribute.reduction ? ` | Flattery -${yen(tribute.reduction)}` : ''}`, - `Maintenance: worst ${maintenanceSummary(game).worst.percent}% dirty (${maintenanceSummary(game).worst.label}) | Repairman: ${game.repairman?.hiredForNextDay ? 'hired next day' : game.repairman?.active ? 'working' : 'none'}`, + `Maintenance: worst ${maint.worst.percent}% dirty (${maint.worst.label}) | Repair workers: ${activeWorkers || maint.workers} | Daily wages: -${yen(maint.dailyWage || 0)}`, `Manual combo: ${game.manualCombo?.count || 0} | Card draft: ${game.cardDraft?.pending ? Math.max(1, game.cardDraft.picksRemaining || 1) + ' pick(s) left' : 'ready after each day'} | Belt: ${displaySpeed()}px/s | Farms with scanner route: ${connected}/${game.eggFarms.length}`, game.cardEffects?.usedMachineActive ? 'Procurement: USED MACHINE MODE / no refunds / 60% durability' : '', issues.length ? `Blocked: ${issues[0]}` : `${TEXT.status.allPortsConnected}` @@ -155,6 +193,7 @@ export function createUISystem({ game, ui, build, startGame, beginCardDraft, act ui.phase.textContent = phaseLabel(); if (ui.comboCount) ui.comboCount.textContent = game.manualCombo?.count || 0; updatePriorityStrip(); + updateAcquiredCardsPanel(); ui.turnProfit.textContent = yen(game.stats.profit); ui.turnProfit.classList.toggle('cash-negative', game.stats.profit < 0); ui.turnProfit.classList.toggle('cash-positive', game.stats.profit > 0); diff --git a/styles.css b/styles.css index 796ac90..045756f 100644 --- a/styles.css +++ b/styles.css @@ -748,3 +748,111 @@ body { font-size: 20px; } @media (max-width: 1180px) { .scanner-monitor { bottom: 132px; width: min(520px, calc(100% - 16px)); left: 8px; } } + +/* v27.8 acquired passive-card panel */ +.acquired-cards-panel { + position: absolute; + z-index: 7; + left: 12px; + top: 124px; + width: min(360px, calc(100% - 450px)); + max-height: 42px; + overflow: hidden; + border: 3px solid var(--line); + background: rgba(255,255,255,.94); + box-shadow: 5px 5px 0 rgba(16,32,21,.16); + font-weight: 900; + pointer-events: auto; + transition: max-height .14s ease, background .14s ease; +} +.acquired-cards-panel[open], +.acquired-cards-panel:hover, +.acquired-cards-panel:focus-within { + max-height: 360px; + background: rgba(255,255,255,.98); +} +.acquired-cards-panel summary { + min-height: 36px; + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + cursor: pointer; + list-style: none; + text-transform: uppercase; + letter-spacing: .05em; + font-size: 15px; +} +.acquired-cards-panel summary::-webkit-details-marker { display: none; } +.acquired-cards-panel summary::after { + content: 'show'; + margin-left: auto; + color: var(--muted); + font-size: 11px; +} +.acquired-cards-panel[open] summary::after, +.acquired-cards-panel:hover summary::after { content: 'hide'; } +.acquired-cards-panel summary strong { + display: inline-grid; + place-items: center; + min-width: 28px; + height: 24px; + padding: 0 6px; + border: 2px solid var(--line); + background: var(--green-soft); + color: var(--green-dark); +} +.acquired-cards-panel summary span { + color: var(--muted); + font-size: 11px; +} +.acquired-cards-list { + display: grid; + grid-template-columns: 1fr; + gap: 6px; + max-height: 306px; + overflow: auto; + padding: 8px; + border-top: 3px solid var(--line); +} +.acquired-cards-list p { + margin: 0; + color: var(--muted); + font-size: 12px; + line-height: 1.35; +} +.acquired-card-chip { + display: grid; + grid-template-columns: 1fr auto; + gap: 2px 8px; + align-items: center; + border: 2px solid var(--line); + background: #f7fff5; + padding: 7px 8px; + font-size: 12px; + line-height: 1.2; +} +.acquired-card-chip.rare { background: #fff0d1; } +.acquired-card-chip.ultraRare { background: #ffe6f0; } +.acquired-card-chip b { + text-transform: uppercase; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.acquired-card-chip strong { + color: var(--green); + font-size: 13px; +} +.acquired-card-chip small { + grid-column: 1 / -1; + color: var(--muted); + font-size: 10px; + text-transform: uppercase; +} +.acquired-cards-panel.empty { opacity: .78; } +.game-shell.phase-title .acquired-cards-panel { display: none; } +@media (max-width: 1180px) { + .acquired-cards-panel { top: 252px; left: 8px; width: min(420px, calc(100% - 16px)); } +}