1(function(){ 2 var R=document.body; 3 4 var $=function(id){return document.getElementById(id);}; 5 6 /* ---------- FM-52 CARD DATA ---------- */ 7 var C={ 8 c:{ 9 2:['Surplus Growth','E',1,'The body has produced more of itself than requested.'], 10 3:['Rigid Patch','E',1,'A region of you has stopped agreeing to bend.'], 11 4:['Miscount','E',1,"Your count of yourself is no longer the body's count."], 12 5:['Load-Bearing','E',1,'The flesh is volunteering for structural work.'], 13 6:['Structural Recruitment','E',1,'Construction is proceeding inside the host. The host is the foundation.'], 14 7:['Directed Remains','H',2,'Tissue that is finished living, still under instruction.'], 15 8:['Conversion Event','E',2,'The body is being finished as something else. The finishing is the problem.'], 16 9:['Feeding Pattern','E',2,'An appetite with offspring. It teaches as it eats.'], 17 10:['Compositor','H',2,'It joins living material into composites. Reports agree that it is proud of the work.'], 18 11:['Wall Figure','E',3,'A face where a surface should be. The surface answers for it now.'], 19 12:['Flayed Walker','H',3,'It walks wrong and speaks right. The voice will be borrowed from your dead.'], 20 13:['Artisan Class','H',3,'A craftsman of the body. His work opens doors. Treat the door as the threat.'], 21 14:['Total Graft Event','C',2,'The flesh of a place remembers it was always meant to be one body.'] 22 }, 23 d:{ 24 2:['Extended Room','E',1,'The space is longer at the end than it was at the start.'], 25 3:['Surplus Room','E',1,'A familiar space has quietly grown one too many of something.'], 26 4:['Perforation','E',1,'Openings, in quantity, and each one is in use from the other side.'], 27 5:['Regression Room','E',1,'The space runs its occupants backward.'], 28 6:['Resident','H',1,"The room's prior occupant. The lease never listed it. It was here first."], 29 7:['Stasis Field','E',2,'Time has stopped locally and is offering terms.'], 30 8:['Grain Closure','E',2,'The room is thickening. Less room, more wall, no motion in it.'], 31 9:['Rising Water','E',2,'The level is coming up, patiently. What it keeps, it keeps standing.'], 32 10:['Retaining Structure','E',2,'The structure has learned to keep its occupants. It calls this care.'], 33 11:['Herding Presence','H',3,'Never seen head-on. It moves you along and closes the way behind.'], 34 12:['Tidal Figure','H',3,'A grief with a current. It collects what was never said.'], 35 13:['Sovereign Water','H',3,'The deep, given office. It keeps its own, and it is owed.'], 36 14:['Closure Event','C',2,'The room finishes closing. The door is no longer anywhere.'] 37 }, 38 s:{ 39 2:['Consensus Drift','E',1,'A wrong past, gaining signatures.'], 40 3:['Unsupported Record','E',1,'It happened. Nothing will agree with you that it did.'], 41 4:['Hostile Language','E',1,'A language that speaks its speakers. Writing it feeds it.'], 42 5:['Still Figure','H',1,"It holds still so you don't have to. It is waiting for you to stop."], 43 6:['Duplicate','H',1,'Your reflection, running late on purpose. Only one of you is needed.'], 44 7:['Foreign Memory','E',2,'A memory has been installed. It fits perfectly. That is the tell.'], 45 8:['Visible Seam','E',2,'The stitching that holds the day together has become visible. Do not follow it.'], 46 9:['Occupant','H',2,'Not entry. Erasure with tenancy. The laugh arrives a half-second late.'], 47 10:['Subtraction','E',2,'Something is being removed from the record while you watch. Possibly this.'], 48 11:['Following Figure','H',3,'Behind you, at pace, in the place your eye just left. It collects silences.'], 49 12:['Observer Group','H',3,'They match their number to yours. They only watch, until they finish.'], 50 13:['Named Edit','E',3,'The revision itself, given a designation so it can be spoken of.'], 51 14:['Revision Event','C',2,'A place is revised out of having been.'] 52 }, 53 h:{ 54 2:['Improved Offer','E',1,'A better body, honestly offered. The honesty is the problem.'], 55 3:['Guide Light','H',1,'A small helpful light, three steps off the safe path, then four.'], 56 4:['Elevated Invitation','E',1,'An invitation to leap upward. There is no upward.'], 57 5:['Preserving Cold','E',1,'It does not want you dead. It wants you kept.'], 58 6:['Voice Mimic','H',1,'It wears the voice of your dead. It will know the nickname.'], 59 7:['Emptying Doctrine','E',2,'A teaching that hollows its students into doorways.'], 60 8:['Recruiting Signal','E',2,'A song with a vacancy shaped like you.'], 61 9:['Living Seal','E',2,'A person made into a lock, and told it was protection.'], 62 10:['Rooted Gift','E',2,'Growth, offered as salvation. It only wants you to take root.'], 63 11:['Homecoming Mist','H',3,'It makes vanishing feel like arriving. The comfort is the mechanism.'], 64 12:['Walking Figure','H',3,'Tall, slow, certain, on a road only it can see.'], 65 13:['Grounded Lure','E',3,'Everything it offers is real. All of it is bait.'], 66 14:['False Relief Event','C',2,'Everything is suddenly, terribly fine. That feeling is the trap closing.'] 67 } 68 }; 69 var GLYPH={c:'\u2663',d:'\u2666',s:'\u2660',h:'\u2665'}; 70 var SUITNAME={c:'Clubs \u00b7 the body',d:'Diamonds \u00b7 the room',s:'Spades \u00b7 the record',h:'Hearts \u00b7 the help'}; 71 72 /* [lead, punch] â punch renders on its own italic line so the block never reflows */ 73 var AMBIENT=[ 74 ["Grab your friends! Scrounge whatever junk the room has lying around \u2014 that's your pool. Deal yourself three lives, and see if you can make it to the end of the deck.", 75 "All you have to do is not lose."], 76 ["You're doing great. Keep flipping. The booklet was left where you found it on purpose.",
77 "Somebody wanted you to have it."], 78 ["You can stop whenever you like.", 79 "You can. You can stop whenever you like."], 80 ["It looks like a joke because a joke gets carried home, left in gloveboxes, read on toilets, remembered drunk.", 81 "A warning gets filed."], 82 ["the room you are in right now may already be one. no alarm sounded. nobody is coming.", 83 "from the outside, nothing is wrong."], 84 ["THE MANUAL WAS NEVER THE JOKE.", 85 "You are holding the safety measure."] 86 ]; 87 var HUBNOTE=["Fresh delights, added all the time. Come back often. You'll want to.", 88 "Come back often. You'll want to. You won't be able to help it.", 89 "you keep coming back. we have noticed. we are pleased."]; 90 91 function sour(t){ 92 t=Math.max(0,Math.min(1,t)); 93 var imgs=R.querySelectorAll('.keyart img'); 94 for(var i=0;i<imgs.length;i++){ 95 imgs[i].style.filter='saturate('+(1-0.62*t).toFixed(2)+') contrast('+(1+0.2*t).toFixed(2)+') brightness('+(1-0.36*t).toFixed(2)+')'; 96 } 97 $('veilDark').style.opacity=(t*0.92).toFixed(2); 98 $('veilPink').style.opacity=(t*0.7).toFixed(2); 99 if(t>0.55)R.classList.add('deep');else R.classList.remove('deep'); 100 var am=AMBIENT[Math.min(AMBIENT.length-1,Math.floor(t*AMBIENT.length))]; 101 $('ambient').innerHTML='<span class="lead">'+am[0]+'</span><span class="punch">'+am[1]+'</span>'; 102 $('hubNote').textContent=HUBNOTE[Math.min(2,Math.floor(t*3))]; 103 if(t>0.45)$('subtitle').textContent="Personnel are encouraged to play it drunk at training. Personnel do."; 104 if(t>0.72){ 105 $('badge').textContent="CATALOGUED INTERNALLY AS FM-52"; 106 $('title').textContent="THE CORRUPTING DECK"; 107 $('alias').innerHTML='it was never a party game<span class="alias-name">you are holding the safety measure</span>'; 108 } 109 } 110 111 var pile,lives,pool,seals,aces,over,current,pastLine,pages,lever,drawn,livesLost; 112 113 function build(){ 114 pile=[]; 115 var suits=['c','d','s','h']; 116 for(var k=0;k<suits.length;k++){ 117 for(var r=2;r<=14;r++)pile.push({su:suits[k],r:r}); 118 } 119 pile.push({joker:'lever'});pile.push({joker:'bill'}); 120 for(var i=pile.length-1;i>0;i--){var j=Math.floor(Math.random()*(i+1));var t=pile[i];pile[i]=pile[j];pile[j]=t;} 121 } 122 function rankName(r){return r===14?'A':r===13?'K':r===12?'Q':r===11?'J':String(r);} 123 function info(c){return C[c.su][c.r];} 124 function patron(){return lives.length?lives[0].su:null;} 125 function costOf(c){var b=info(c)[2];return Math.max(0,b-(c.su===patron()?1:0));} 126 function priceOf(c){return info(c)[2]+1;} 127 function dread(){ 128 var d=(drawn/54)*0.5 + (livesLost*0.14) + (aces*0.16) + (pastLine?0.25:0); 129 return Math.min(1,d); 130 } 131 132 function reset(){ 133 build(); 134 lives=[]; 135 for(var k=0;k<3;k++){ 136 var c; 137 do{c=pile.pop();}while(c.joker||c.r===14); 138 lives.push(c); 139 } 140 pool=10;seals=0;aces=0;over=false;current=null;pastLine=false;pages=0;lever=false;drawn=0;livesLost=0; 141 R.classList.remove('deep');sour(0); 142 $('game').className='game on'; 143 $('ending').className='ending'; 144 $('hub').style.display=''; 145 $('badge').textContent="\u2665 An Ellium Leisure publication \u2665"; 146 $('title').textContent="The Corrupting Deck"; 147 $('alias').innerHTML='distributed as<span class="alias-name">52-Card Pickup</span><span class="alias-ed">Survival Edition</span>'; 148 $('subtitle').textContent="The party game you already own. Any deck. Any room. Any number of players, including none."; 149 $('sealK').textContent='Seals'; 150 $('cardface').className='cardface empty'; 151 $('cardface').innerHTML='<div class="rank">♣ ♦ ♠ ♥</div><div class="hint">the pile is face-down<br>flip the top card</div>'; 152 $('flavor').textContent='Scrounge once \u2014 ten objects, your pool. Deal three lives. Brace yourself against the one on top.'; 153 $('log').innerHTML='';$('routes').innerHTML=''; 154 render(); 155 $('prompt').textContent='Flip the first threat.'; 156 setActions([['Flip a card','primary',flip]]); 157 } 158 159 function render(){ 160 $('pool').textContent=pool; 161 $('sealK').textContent=pastLine?'Pages':'Seals'; 162 $('seals').textContent=pastLine?String(pages):(seals+' / 3'); 163 $('aces').textContent=aces+' / 2'; 164 $('pile').textContent=pile.length; 165 var row='';
166 for(var i=0;i<lives.length;i++){ 167 var c=lives[i]; 168 var red=(c.su==='h'||c.su==='d'); 169 row+='<span class="lifecard'+(i===0?' patron':'')+'" style="'+(red?'color:var(--accent-br)':'')+'">' 170 +'<span>'+rankName(c.r)+'</span><span class="sm">'+GLYPH[c.su]+'</span></span>'; 171 } 172 if(!lives.length)row='<span class="lifelbl">none left</span>'; 173 $('lifeRow').innerHTML=row; 174 sour(dread()); 175 } 176 function logMsg(m){var l=$('log');var d=document.createElement('div');d.textContent=m;l.insertBefore(d,l.firstChild);while(l.children.length>3)l.removeChild(l.lastChild);} 177 function setActions(list){ 178 var a=$('actions');a.innerHTML=''; 179 for(var i=0;i<list.length;i++){ 180 (function(it){ 181 var b=document.createElement('button'); 182 b.className='act'+(it[1]==='primary'?' primary':(it[1]==='ghost'?' ghost':'')); 183 b.textContent=it[0]; 184 b.onclick=it[2]; 185 a.appendChild(b); 186 })(list[i]); 187 } 188 } 189 function showCard(c){ 190 var i=info(c),red=(c.su==='h'||c.su==='d'); 191 $('cardface').className='cardface'+(red?' red':''); 192 $('cardface').innerHTML= 193 '<div class="rank">'+rankName(c.r)+'</div><div class="suit">'+GLYPH[c.su]+'</div>' 194 +'<div class="fileno">'+SUITNAME[c.su]+'</div>' 195 +'<div class="desig">filed as '+i[0]+'</div>' 196 +'<div class="cls">'+(i[1]==='H'?'Hunter':(i[1]==='C'?'Convergence':'Environment'))+' \u00b7 cost '+i[2]+'</div>'; 197 } 198 function showJoker(kind){ 199 $('cardface').className='cardface empty'; 200 $('cardface').innerHTML='<div class="rank">'+(kind==='lever'?'I':'II')+'</div>' 201 +'<div class="hint">the unsuited<br>'+(kind==='lever'?'the lever':'the bill')+'</div>'; 202 } 203 204 function flip(){ 205 if(over)return; 206 if(!pile.length)return endRun('outlast'); 207 $('routes').innerHTML=''; 208 var c; 209 if(pool<=0 && pile.length>1){ 210 var a=pile.pop(),b=pile.pop(); 211 var ca=a.joker?9:info(a)[2], cb=b.joker?9:info(b)[2]; 212 if(cb>ca){c=b;pile.unshift(a);}else{c=a;pile.unshift(b);} 213 logMsg('Zero pool. Your reads fail. Two cards flipped; the worse one stands.'); 214 } else { c=pile.pop(); } 215 drawn++; 216 if(c.joker)return resolveJoker(c.joker); 217 current=c;showCard(c); 218 var i=info(c); 219 $('flavor').textContent=i[3]; 220 var cost=costOf(c),match=(c.su===patron()); 221 222 if(i[1]==='C'){ 223 render(); 224 $('prompt').textContent='Convergence \u00b7 toll 2'+(match?' \u00b7 your patron shields you; it will not count':' \u00b7 it counts toward the limit'); 225 setActions([ 226 ['Pay the toll (2)','primary',function(){payToll(match);}], 227 ['Take the hit',null,function(){takeHit(!match,true);}] 228 ]); 229 return; 230 } 231 render(); 232 var isH=(i[1]==='H'); 233 $('prompt').textContent=(isH?'Hunter':'Environment')+' \u00b7 survive for '+cost+(match?' (patron holds, \u22121)':'')+' \u00b7 or take the hit'; 234 var acts=[['Survive ('+cost+')','primary',function(){survive(cost,isH);}], 235 ['Take the hit',null,function(){takeHit(false,false);}]]; 236 if(isH){ 237 acts.push(['Seal by price ('+priceOf(c)+')','ghost',function(){seal(priceOf(c));}]); 238 $('routes').innerHTML='The booklet lists three ways to seal a hunter: <b>by object</b>, <b>by act</b>, <b>by price</b>.<br>' 239 +'The first two need a real room and a voice out loud. This screen has neither.<br>' 240 +'<a href="/corrupting-deck.pdf">Take the booklet</a> and you get all three.'; 241 } 242 if(lever)acts.push(['Spend the Lever','ghost',spendLever]); 243 setActions(acts); 244 } 245 246 function survive(cost,isH){ 247 if(cost>pool){logMsg('Not enough in the pool. Take the hit, or seal it.');return;} 248 pool-=cost; 249 logMsg('Survived. Pool '+pool+'.'+(isH?' Shuffled back in. It will return.':' Discarded.')); 250 if(isH)pile.unshift(current); 251 render();after(); 252 } 253 function seal(price){ 254 if(price>pool){logMsg('The price is '+price+'. The pool has '+pool+'.');return;} 255 pool-=price;pool+=2; 256 if(pastLine){pages++;logMsg('Sealed past the line. You saw its face. Pages read: '+pages+'.');} 257 else{seals++;logMsg('Sealed. It leaves the game. The room returns 2.');} 258 render(); 259 if(!pastLine&&seals>=3)return offerLine(); 260 after(); 261 } 262 function payToll(match){ 263 pool=Math.max(0,pool-2); 264 if(!match)aces++; 265 logMsg(match?'Toll paid. Your patron shielded its own house. Once.':'Toll paid. The convergence counts. ('+aces+' of 2)'); 266 render(); 267 if(aces>=2)return endRun('converge'); 268 after(); 269 } 270 function takeHit(counts,isAce){ 271 if(!lives.length)return endRun('lives'); 272 var lost=lives.shift();livesLost++; 273 if(isAce&&counts)aces++; 274 logMsg('Hit taken. You lose the '+rankName(lost.r)+GLYPH[lost.su]+'.'+(lives.length?' Your patron is now '+GLYPH[patron()]+'.':' No lives left.')); 275 render(); 276 if(isAce&&aces>=2)return endRun('converge'); 277 if(!lives.length)return endRun('lives'); 278 after(); 279 } 280 function spendLever(){ 281 if(!lever)return; 282 lever=false; 283 pile.pop(); 284 logMsg('The Lever is spent. The next card was burned unseen. Do not look. Looking is how it counts.'); 285 render();after(); 286 } 287 function resolveJoker(kind){ 288 showJoker(kind); 289 if(kind==='lever'){ 290 lever=true; 291 $('flavor').textContent='Something intervened on your behalf. The files record interventions. The files do not record twice.'; 292 $('prompt').textContent='The Lever \u00b7 keep it \u00b7 spend it later to burn a card unseen'; 293 render();
294 setActions([['Flip a card','primary',flip]]); 295 } else { 296 pool=Math.max(0,pool-2); 297 $('flavor').textContent='No route. No conduct. Nothing is listed here because nothing is known.'; 298 $('prompt').textContent='The Bill \u00b7 the pool loses 2 \u00b7 you discard a life \u00b7 ten seconds of silence'; 299 if(!lives.length){render();return endRun('lives');} 300 var lost=lives.shift();livesLost++; 301 logMsg('The Bill. Pool \u22122. You lose the '+rankName(lost.r)+GLYPH[lost.su]+'.'); 302 render(); 303 if(!lives.length)return endRun('lives'); 304 setActions([['Flip a card','primary',flip]]); 305 } 306 } 307 function after(){ 308 if(!pile.length)return endRun('outlast'); 309 if(pastLine){ 310 $('prompt').textContent='Past the line \u00b7 pages read: '+pages+' \u00b7 the standing count is eleven'; 311 setActions([['Flip a card','primary',flip],['Walk away',null,function(){endRun('walk');}]]); 312 } else { 313 setActions([['Flip a card','primary',flip]]); 314 } 315 } 316 function offerLine(){ 317 $('flavor').textContent='Three seals. You have won. You may stop, and stopping banks it.'; 318 $('prompt').textContent='Or flip again, and spend the win on what the booklet left out'; 319 $('routes').innerHTML='The manual ships without the faces. There is a reason it ships without the faces.<br>The standing verified count is <b>eleven</b> pages past the line. Nobody has verified twelve.'; 320 setActions([ 321 ['Bank the win','primary',function(){endRun('bank');}], 322 ['Past the line',null,function(){ 323 pastLine=true;pages=0; 324 logMsg('You keep reading. The win is spent. You are playing for the count now.'); 325 render();$('routes').innerHTML='';after(); 326 }] 327 ]); 328 } 329 330 var PITCH_WIN='Fifty-two pages. Every threat, every seal route, the conduct, the jokers, and the parts this screen could not give you.'; 331 var PITCH_LOSE='The two routes you did not have were in the booklet. It is free. It was always free.'; 332 function endRun(type){ 333 over=true; 334 var e; 335 if(type==='bank')e=['Recovery complete','\u2014 you stopped \u2014','Three seals and you walked out clean. Most people do. Most people never learn what was on the pages they skipped.','',PITCH_WIN]; 336 else if(type==='walk')e=['You walked away','\u2014 past the line \u2014','You read '+pages+' page'+(pages===1?'':'s')+' past the line and stopped.', 337 pages>=11?'You have matched the standing count. Nobody has verified twelve. Something took note.':'The standing count is eleven. The deck holds seventeen.',PITCH_WIN]; 338 else if(type==='outlast')e=['The pile is empty','\u2014 you outlasted it \u2014','You reached the bottom of the deck with lives to spare. The files record it as a win, and the files do not sound pleased.','',PITCH_WIN]; 339 else if(type==='lives')e=['No lives left','\u2014 the hits landed \u2014','Every card you were braced behind is gone. There was nothing left to lose but the rest of you.','',PITCH_LOSE]; 340 else e=['Convergence','\u2014 the Ace limit \u2014','It remembered it was always meant to be one body, and the room agreed with it, and you were in the room.','',PITCH_LOSE]; 341 342 sour(1); 343 $('game').className='game'; 344 $('hub').style.display='none'; 345 $('etag').textContent=e[1];$('ehead').textContent=e[0];$('ebody').textContent=e[2];$('ebody2').textContent=e[3]; 346 $('epitch').textContent=e[4]; 347 $('ending').className='ending on'; 348 $('ending').scrollIntoView({behavior:'smooth'}); 349 } 350 351 $('playBtn').onclick=function(){reset();$('game').scrollIntoView({behavior:'smooth'});}; 352 $('again').onclick=function(){reset();$('game').scrollIntoView({behavior:'smooth'});}; 353 354 /* Leave menu: click toggles on every device; CSS hover is a bonus on desktop */ 355 (function(){ 356 var btn=document.getElementById('leaveBtn'), menu=document.getElementById('leaveMenu'); 357 if(!btn||!menu)return; 358 359 btn.addEventListener('click',function(e){ 360 e.preventDefault(); 361 e.stopPropagation(); 362 var open = menu.classList.toggle('open'); 363 btn.setAttribute('aria-expanded', open ? 'true' : 'false'); 364 }); 365 366 document.addEventListener('click',function(e){ 367 if(menu.classList.contains('open') && !menu.contains(e.target) && e.target!==btn){ 368 menu.classList.remove('open'); 369 btn.setAttribute('aria-expanded','false'); 370 } 371 }); 372 373 document.addEventListener('keydown',function(e){ 374 if(e.key==='Escape' && menu.classList.contains('open')){ 375 menu.classList.remove('open'); 376 btn.setAttribute('aria-expanded','false'); 377 } 378 }); 379 })(); 380})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.