Back to Wizard Duel
SOURCE / PINNED RELEASE

Made of little things.

Wizard Duel

Release
48b4ed933a32…
Author-recorded commit
3ed8fcbbf62c…
License
Check the original source
Author’s source reference
nostr://npub1ny7l44q0au5xkv3mjgt6zks5f0e2ydefslq4nhh2ve2xypgc2mqqat8qx2/wss%3A%2F%2Fgit.napplet.soy%2F/n-86005d75eb4

The hash-verified playable HTML, which may contain bundled or minified code. This view does not execute it.

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Wizard Duel</title>
    <script type="module" crossorigin>var Ft=Object.defineProperty;var Ut=(i,t,e)=>t in i?Ft(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var m=(i,t,e)=>Ut(i,typeof t!="symbol"?t+"":t,e);var Yt="config";function Kt(){const i=window;if(!i.napplet)throw new Error("window.napplet is unavailable -- runtime did not inject the napplet namespace");return i.napplet}function z(i){const e=Kt()[i];if(!e)throw new Error(`window.napplet.${String(i)} is unavailable -- runtime did not inject this domain`);return e}var ft={get(){return z("config").get()},subscribe(i){return z("config").subscribe(i)},openSettings(i){z("config").openSettings(i)},registerSchema(i,t){return z("config").registerSchema(i,t)},onSchemaError(i){return z("config").onSchemaError(i)},get schema(){return z("config").schema}};function jt(i,t){return!!(i!=null&&i[t])}function qt(i){return jt(window.napplet,i)}const zt="http://json-schema.org/draft-07/schema#",_t=1,Xt="object",Zt=!1,$t={sound:{type:"boolean",title:"Sound effects",description:"Play the zaps, boings, clucks and nose whistles.",default:!0,"x-napplet-section":"Game","x-napplet-order":1},screenShake:{type:"boolean",title:"Screen shake",description:"Shake the arena when big spells hit.",default:!0,"x-napplet-section":"Game","x-napplet-order":2}},Vt={$schema:zt,$version:_t,type:Xt,additionalProperties:Zt,properties:$t};function Qt(i){(async()=>{ft.schema||await ft.registerSchema(Vt);const t=ft.subscribe(e=>i(e));window.addEventListener("pagehide",()=>t.close(),{once:!0})})().catch(()=>{})}const mt={A:[".#.","#.#","###","#.#","#.#"],B:["##.","#.#","##.","#.#","##."],C:[".##","#..","#..","#..",".##"],D:["##.","#.#","#.#","#.#","##."],E:["###","#..","##.","#..","###"],F:["###","#..","##.","#..","#.."],G:[".##","#..","#.#","#.#",".##"],H:["#.#","#.#","###","#.#","#.#"],I:["###",".#.",".#.",".#.","###"],J:["..#","..#","..#","#.#",".#."],K:["#.#","#.#","##.","#.#","#.#"],L:["#..","#..","#..","#..","###"],M:["#.#","###","###","#.#","#.#"],N:["##.","#.#","#.#","#.#","#.#"],O:[".#.","#.#","#.#","#.#",".#."],P:["##.","#.#","##.","#..","#.."],Q:[".#.","#.#","#.#","##.",".##"],R:["##.","#.#","##.","#.#","#.#"],S:[".##","#..",".#.","..#","##."],T:["###",".#.",".#.",".#.",".#."],U:["#.#","#.#","#.#","#.#","###"],V:["#.#","#.#","#.#","#.#",".#."],W:["#.#","#.#","###","###","#.#"],X:["#.#","#.#",".#.","#.#","#.#"],Y:["#.#","#.#",".#.",".#.",".#."],Z:["###","..#",".#.","#..","###"],0:["###","#.#","#.#","#.#","###"],1:[".#.","##.",".#.",".#.","###"],2:["##.","..#",".#.","#..","###"],3:["##.","..#",".#.","..#","##."],4:["#.#","#.#","###","..#","..#"],5:["###","#..","##.","..#","##."],6:[".##","#..","###","#.#","###"],7:["###","..#",".#.",".#.",".#."],8:["###","#.#","###","#.#","###"],9:["###","#.#","###","..#","##."],"!":[".#.",".#.",".#.","...",".#."],"?":["##.","..#",".#.","...",".#."],".":["...","...","...","...",".#."],",":["...","...","...",".#.","#.."],"'":[".#.",".#.","...","...","..."],"-":["...","...","###","...","..."],":":["...",".#.","...",".#.","..."],"/":["..#","..#",".#.","#..","#.."],"+":["...",".#.","###",".#.","..."],"(":[".#.","#..","#..","#..",".#."],")":[".#.","..#","..#","..#",".#."]," ":["...","...","...","...","..."]};function F(i,t=1){return i.length===0?0:(i.length*4-1)*t}function S(i,t,e,a,s,n={}){const o=n.scale??1,f=t.toUpperCase();let c=Math.round(e);n.align==="center"&&(c=Math.round(e-F(f,o)/2)),n.align==="right"&&(c=Math.round(e-F(f,o)));const h=n.shadow===void 0?"#120a1f":n.shadow,l=h?[[h,o],[s,0]]:[[s,0]];for(const[u,d]of l){i.fillStyle=u;for(let y=0;y<f.length;y++){const R=mt[f[y]]??mt["?"];for(let b=0;b<5;b++)for(let g=0;g<3;g++)R[b][g]==="#"&&i.fillRect(c+(y*4+g)*o+d,Math.round(a)+b*o+d,o,o)}}}const J=["..#..",".###.","#.#.#","..#..","..#.."];function w(i){const t=[];for(let e=0;e<5;e++){let a="";for(let s=0;s<5;s++)i==="up"?a+=J[e][s]:i==="down"?a+=J[4-e][s]:i==="left"?a+=J[s][e]:a+=J[4-s][e];t.push(a)}return t}const Jt={up:w("up"),down:w("down"),left:w("left"),right:w("right")};function bt(i,t,e,a,s,n="#120a1f"){const o=Math.round(e),f=Math.round(a);n&&(i.fillStyle=n,i.fillRect(o-1,f-1,7,7)),i.fillStyle=s;const c=Jt[t];for(let h=0;h<5;h++)for(let l=0;l<5;l++)c[h][l]==="#"&&i.fillRect(o+l,f+h,1,1)}function E(i,t,e,a,s,n={}){var u;const o=n.scale??1,f=t.length,c=((u=t[0])==null?void 0:u.length)??0,h=Math.round(a),l=Math.round(s);for(let d=0;d<f;d++){const y=t[d];for(let R=0;R<c;R++){const b=e[y[R]];if(!b)continue;const g=n.flipX?c-1-R:R,D=n.flipY?f-1-d:d;i.fillStyle=b,i.fillRect(h+g*o,l+D*o,o,o)}}}const Ht=["....H.....","....HH....","...HHH....","...HHHh...","..HHHHh...","..BBBBB...",".HHHHHHHh.","...SSSE...","...SSSSN..","..WWWWW..."],wt=["..RWWWRr..",".RRRWRRrs.",".RRRRRRr..",".RRRRRRr..","RRRRRRRrr."],te=["..RWWWRrs.",".RRRWRRr..",".RRRRRRr..",".RRRRRRr..","RRRRRRRrr."],lt=[[".KK...KK.."],["..KK.KK..."],[".KK..KK..."]];function tt(i){const t=i==="cast"?te:wt,e=i==="walkA"?lt[1]:i==="walkB"?lt[2]:lt[0];return[...Ht,...t,...e]}const ee=["..y",".s.","s.."],yt=Ht.slice(0,7),gt=["...cc.....","..WWWW....","..WWEWY...","..WWWWYY..","..WWWW....","WWWWWWW...","WWWWWWWW..",".WWWWWW...","..Y..Y....",".YY.YY...."],xt=["...KK....KK...","....R....R....","..GGGGGGGGGG..",".GGgGGgGGgGGG.","GGgGGgGGgGGgGG","GGGGGGGGGGGGGG",".yyyyyyyyyyyy."],Mt=["..ww..ww..",".wEw..wEw.",".GGGGGGGG.","GGGGGGGGGG","GgGGGGGGgG","GGyyyyyyGG",".GGGGGGGG.","GG.G..G.GG"],Rt={w:"#ffffff",E:"#120a1f",G:"#4fbf4a",g:"#2d7a2a",y:"#c8e86b"},ae={c:"#e8433a",W:"#fbf5e6",E:"#120a1f",Y:"#f7b733"};function se(i,t="#f2c29b"){return{H:i.hat,h:i.hatDark,B:i.band,S:t,E:"#120a1f",N:"#e48a7a",W:"#eeeaf6",R:i.robe,r:i.robeDark,K:"#2b1c33",s:t,y:"#fff36b"}}const V={orb:["..aaa..",".abbba.","abbwbba","abwwwba","abbwbba",".abbba.","..aaa.."],snow:["...a...",".a.a.a.","..aba..","aabwbaa","..aba..",".a.a.a.","...a..."],banana:["......b",".....aa","....aa.","...aa..","..aa...","aaa....",".a....."],pie:[".......","..www..",".wwwww.","bbbbbbb","aaaaaaa",".aaaaa.","......."],note:["...aa..","...a.a.","...a..a","...a...",".aaa...","aaaa...",".aa...."],bolt:["...aa..","..aa...",".aa....",".aaaaa.","...aa..","..aa...",".a....."],heart:[".aa.aa.","awaaaaa","aaaaaaa","aaaaaaa",".aaaaa.","..aaa..","...a..."],cloud:[".......","..aaa..",".aaaaa.","aaaaaaa",".......",".b.b.b.","b.b.b.."],egg:["..aaa..",".aaaaa.","aaaaaaa","aawaaaa","aaaaaaa",".aaaaa.","..aaa.."],zzz:["aaaaa..","...a...","..a....",".a.....","aaaaa..",".......","......."],drop:["...a...","...a...","..aaa..",".aabaa.",".abaaa.",".aaaaa.","..aaa.."],swirl:[".aaaaa.","a.....a","a.bbb.a","a.b.b.a","a.b...a","a.bbbba","......."],shell:["..aaa..",".abbba.","ab.a.ba","ab.aaba","abbbba.",".aaaa..","......."],rubberchick:[".....a.","....aab",".aaaaa.","aaaaaa.",".aaaa..","..b.b..","......."],spark:[".......","...a...","..aba..",".abwba.","..aba..","...a...","......."],balloon:["..aaa..",".aawaa.",".aaaaa.",".aaaaa.","..aaa..","...b...","..b...."]};let B=null,Gt=!0;function ie(i){Gt=i}function rt(){try{B||(B=new AudioContext),B.state==="suspended"&&B.resume()}catch{B=null}}function Ct(){return!Gt||!B||B.state!=="running"?null:B}function x(i,t,e={}){const a=Ct();if(!a)return;const s=a.currentTime+(e.delay??0),n=a.createOscillator(),o=a.createGain();if(n.type=e.type??"square",n.frequency.setValueAtTime(i,s),e.to&&n.frequency.exponentialRampToValueAtTime(e.to,s+t),e.vibrato){const c=a.createOscillator(),h=a.createGain();c.frequency.value=9,h.gain.value=e.vibrato,c.connect(h).connect(n.frequency),c.start(s),c.stop(s+t)}const f=e.volume??.08;o.gain.setValueAtTime(f,s),o.gain.exponentialRampToValueAtTime(1e-4,s+t),n.connect(o).connect(a.destination),n.start(s),n.stop(s+t+.02)}function W(i,t=.1,e=1200,a=0){const s=Ct();if(!s)return;const n=s.currentTime+a,o=Math.max(1,Math.floor(s.sampleRate*i)),f=s.createBuffer(1,o,s.sampleRate),c=f.getChannelData(0);for(let d=0;d<o;d++)c[d]=Math.random()*2-1;const h=s.createBufferSource();h.buffer=f;const l=s.createBiquadFilter();l.type="lowpass",l.frequency.value=e;const u=s.createGain();u.gain.setValueAtTime(t,n),u.gain.exponentialRampToValueAtTime(1e-4,n+i),h.connect(l).connect(u).connect(s.destination),h.start(n)}const ne={up:660,right:587,down:494,left:523},r={rune(i,t){x(ne[i]*(1+t*.06),.07,{type:"square",volume:.05})},cast(){x(300,.25,{type:"sawtooth",to:900,volume:.06})},fizzle(){x(400,.3,{type:"square",to:80,volume:.05}),W(.2,.04,800)},hit(){W(.15,.12,900),x(160,.15,{type:"square",to:60,volume:.07})},clash(){x(1200,.2,{type:"triangle",to:300,volume:.07}),W(.2,.08,3e3)},fire(){W(.4,.12,600)},freeze(){x(1400,.4,{type:"triangle",to:2400,volume:.05}),x(1800,.3,{type:"triangle",to:2800,volume:.04,delay:.08})},zap(){W(.3,.15,5e3),x(90,.3,{type:"sawtooth",volume:.06})},boing(){x(180,.3,{type:"square",to:520,volume:.06,vibrato:30})},whistle(){x(1700,.45,{type:"sine",to:2300,volume:.07,vibrato:60}),x(2300,.35,{type:"sine",to:1500,volume:.06,vibrato:60,delay:.5})},hic(){x(500,.08,{type:"square",to:900,volume:.06})},cluck(){x(700,.06,{type:"square",to:500,volume:.05}),x(800,.08,{type:"square",to:450,volume:.05,delay:.09})},sneeze(){x(500,.25,{type:"sawtooth",to:900,volume:.04}),W(.3,.14,2500,.25)},pop(){W(.08,.16,4e3)},splat(){W(.2,.12,500),x(200,.12,{type:"square",to:90,volume:.05})},snore(){x(90,.5,{type:"sawtooth",to:70,volume:.04})},honk(){x(330,.14,{type:"sawtooth",to:290,volume:.07}),x(330,.14,{type:"sawtooth",to:290,volume:.07,delay:.2})},opera(){x(523,.5,{type:"sine",volume:.07,vibrato:18}),x(659,.5,{type:"sine",volume:.07,vibrato:18,delay:.45}),x(784,.7,{type:"sine",volume:.07,vibrato:25,delay:.9})},scratch(){[0,.1,.2].forEach(i=>W(.07,.08,3500,i))},bloop(){x(300,.2,{type:"sine",to:900,volume:.08})},buzz(){x(180,.5,{type:"sawtooth",volume:.04,vibrato:40})},croak(){x(140,.15,{type:"square",to:90,volume:.07}),x(160,.1,{type:"square",to:100,volume:.06,delay:.18})},boom(){W(.7,.22,700),x(120,.5,{type:"sawtooth",to:40,volume:.08})},squeak(){x(1200,.08,{type:"square",to:1800,volume:.04})},dance(){[196,247,294,247].forEach((i,t)=>x(i,.09,{type:"triangle",volume:.08,delay:t*.11}))},disco(){[523,659,784,1046].forEach((i,t)=>x(i,.1,{type:"square",volume:.04,delay:t*.1}))},laugh(){[700,600,700,600].forEach((i,t)=>x(i,.07,{type:"square",volume:.04,delay:t*.09}))},summon(){[784,988,1175,1568].forEach((i,t)=>x(i,.12,{type:"triangle",volume:.07,delay:t*.07}))},ko(){[523,392,330,262].forEach((i,t)=>x(i,.2,{type:"square",volume:.06,delay:t*.18}))},win(){[523,659,784,1046,784,1046].forEach((i,t)=>x(i,.14,{type:"square",volume:.06,delay:.8+t*.12}))}},oe=3,fe=12,et={fairy:{name:"Healing Fairy",hello:"TWINKLE!",sprite:["w.....w","ww.a.ww","wwaaaww",".waEaw.","..aaa..","...a...","..a.a.."],palette:{w:"#c8f4ff",a:"#ff9cf5",E:"#120a1f"},every:2},imp:{name:"Fire Imp",hello:"HEHEHE!",sprite:["r.....r","rr...rr",".rrrrr.",".ryryr.",".rrrrr.","..rrr..",".r...r."],palette:{r:"#e0402a",y:"#ffd23f"},every:3},owl:{name:"Guard Owl",hello:"HOOT!",sprite:["b.....b","bbbbbbb","bwwbwwb","bwEbEwb","bbbybbb",".bcccb.","..y.y.."],palette:{b:"#8a5a2b",w:"#fff4e0",E:"#120a1f",y:"#ffb000",c:"#d9a066"},every:0}},ct={id:"firespark",name:"Fire Spark",hitText:"OUCH!",len:0,colors:["#ff7a1a","#ffd23f"],icon:"spark",speed:150,damage:2,sound:r.fire},le={burning:"ON FIRE",frozen:"FROZEN",glue:"GLUED",revgrav:"UPSIDE",lowgrav:"FLOATY",sleepy:"SLEEPY",drunk:"WOBBLY",whistle:"WHISTLE",slippery:"SLIPPY",tiny:"TINY",bighead:"BIG HEAD",chicken:"CHICKEN",bouncy:"BOUNCY",hiccups:"HICCUPS",stun:"ZAPPED",rain:"SOGGY",pie:"PIE FACE",butter:"BUTTERY",sneeze:"SNEEZY",disco:"DISCO",hyper:"HYPER",balloon:"BALLOON",slimy:"SLIMY",dance:"DANCING",clown:"CLOWN",tripped:"OOF",opera:"OPERA",mustache:"MUSTACHE",invisible:"INVISIBLE",afro:"AFRO",squeaky:"SQUEAKY",pogo:"POGO",noodle:"NOODLY",fleas:"FLEAS",scratching:"SCRATCH",snail:"SNAIL",bubble:"BUBBLE",turtle:"TURTLE",bees:"BEES",pancake:"PANCAKE",boomerang:"CURSED",noodlearms:"NOODLE ARM",chickrain:"CHICKENS",toad:"TOAD",puppet:"PUPPET",potato:"POTATO"},K=[{id:"fireball",name:"Fireball",hitText:"TOASTY!",len:6,combo:["down","right","right","down","right","right"],colors:["#ff7a1a","#ffd23f"],icon:"orb",speed:170,damage:14,status:["burning",2],sound:r.fire,apply:i=>i.status.delete("frozen")},{id:"knockback",name:"Knockback",hitText:"BONK!",len:5,combo:["left","right","left","right","left"],colors:["#7fe0ff","#ffffff"],icon:"swirl",speed:190,damage:8,sound:r.boing,apply:(i,t)=>{const e=i.x>=t.x?1:-1;i.vx=e*330,i.vy=-170*i.gravitySign()}},{id:"glue",name:"Floor Glue",hitText:"STUCK!",len:5,colors:["#c8e86b","#8fb339"],icon:"drop",speed:150,damage:3,status:["glue",3.5],sound:r.splat},{id:"freeze",name:"Freeze",hitText:"BRRRR!",len:6,colors:["#9fe8ff","#ffffff"],icon:"snow",speed:150,damage:5,status:["frozen",2.5],sound:r.freeze,apply:i=>i.status.delete("burning")},{id:"hiccups",name:"Hiccup Hex",hitText:"HIC!",len:3,combo:["up","down","up"],colors:["#ff9fd0","#ffffff"],icon:"orb",speed:160,damage:2,status:["hiccups",6],sound:r.hic},{id:"pie",name:"Pie in the Face",hitText:"SPLAT!",len:5,colors:["#d9a066","#ff9fc8"],icon:"pie",speed:170,damage:6,status:["pie",4],sound:r.splat},{id:"sneeze",name:"Sneeze Storm",hitText:"ACHOO!",len:3,colors:["#b8f2a0","#ffffff"],icon:"orb",speed:150,damage:2,status:["sneeze",5],sound:r.sneeze},{id:"tiny",name:"Shrink Ray",hitText:"EEP!",len:3,colors:["#9be7ff","#2f7bff"],icon:"orb",speed:200,damage:3,status:["tiny",6],sound:r.boing},{id:"banana",name:"Banana Peel",hitText:"WHOOPS!",len:4,combo:["down","down","right","right"],colors:["#ffe14d","#5a4320"],icon:"banana",speed:150,damage:3,status:["slippery",4.5],sound:r.splat},{id:"drunk",name:"Drunken Wobble",hitText:"HIC... WHA?",len:4,colors:["#c070ff","#ffb3f0"],icon:"swirl",speed:150,damage:3,status:["drunk",5],sound:r.boing},{id:"sleepy",name:"Sleepy Time",hitText:"YAWN...",len:4,colors:["#a8b8ff","#ffffff"],icon:"zzz",speed:120,damage:3,status:["sleepy",5],sound:r.snore},{id:"bighead",name:"Big Head Mode",hitText:"WHOA!",len:3,colors:["#ffb36b","#ffffff"],icon:"orb",speed:160,damage:3,status:["bighead",6],sound:r.boing},{id:"bouncy",name:"Boing Boing",hitText:"BOING!",len:3,colors:["#ff5fa2","#ffd0e6"],icon:"orb",speed:170,damage:3,status:["bouncy",5],sound:r.boing},{id:"lowgrav",name:"Low Gravity",hitText:"FLOATY!",len:3,colors:["#c9b6ff","#ffffff"],icon:"orb",speed:140,damage:3,status:["lowgrav",6],sound:r.freeze},{id:"magnet",name:"Magnet Yank",hitText:"COME HERE!",len:5,colors:["#ff4a4a","#cccccc"],icon:"swirl",speed:200,damage:4,sound:r.boing,apply:(i,t)=>{i.vx=(t.x>=i.x?1:-1)*380,i.vy=-120*i.gravitySign()}},{id:"chicken",name:"Chickenify",hitText:"BAWK!",len:6,colors:["#fbf5e6","#f7b733"],icon:"egg",speed:160,damage:3,status:["chicken",4.5],sound:r.cluck},{id:"revgrav",name:"Reverse Gravity",hitText:"UP IS DOWN!",len:5,colors:["#5ad1c4","#ffffff"],icon:"swirl",speed:150,damage:3,status:["revgrav",4.5],sound:r.boing},{id:"whistle",name:"Nose Whistle of the North",hitText:"TWEEEE!",len:3,colors:["#e0f7ff","#8fd3ff"],icon:"note",speed:140,damage:2,status:["whistle",7],sound:r.whistle},{id:"rain",name:"Personal Rain Cloud",hitText:"SOGGY!",len:4,colors:["#9aa7c7","#4aa3ff"],icon:"cloud",speed:130,damage:2,status:["rain",5],sound:r.splat},{id:"disco",name:"Disco Fever",hitText:"GROOVY!",len:6,colors:["#ff4af0","#4affef"],icon:"orb",speed:160,damage:3,status:["disco",3],sound:r.disco},{id:"butter",name:"Butterfingers",hitText:"SLIPPY HANDS!",len:5,colors:["#ffe08a","#ffb000"],icon:"drop",speed:160,damage:3,status:["butter",5],sound:r.splat},{id:"hyper",name:"Hyper Speed",hitText:"ZOOOM!",len:4,colors:["#7dff6b","#ffffff"],icon:"bolt",speed:220,damage:3,status:["hyper",4],sound:r.zap},{id:"balloon",name:"Up Up and Away",hitText:"WHEEE!",len:4,colors:["#ff6b6b","#ffffff"],icon:"balloon",speed:130,damage:2,status:["balloon",4],sound:r.boing},{id:"lightning",name:"Lightning Bolt",hitText:"ZAP!",len:6,colors:["#fff36b","#ffffff"],icon:"bolt",speed:260,damage:18,status:["stun",.8],sound:r.zap},{id:"swap",name:"Switcheroo",hitText:"HUH?!",len:5,colors:["#ffffff","#b07bff"],icon:"swirl",speed:200,damage:2,sound:r.pop,apply:(i,t)=>{[i.x,t.x]=[t.x,i.x],[i.y,t.y]=[t.y,i.y]}},{id:"vampire",name:"Vampire Tickle",hitText:"HEHEHE!",len:6,colors:["#ff3a6b","#ffffff"],icon:"heart",speed:170,damage:10,sound:r.laugh,apply:(i,t,e)=>e.heal(t,10)},{id:"slime",name:"Slime Ball",hitText:"SQUELCH!",len:4,colors:["#5dff8a","#1f9e4a"],icon:"orb",speed:160,damage:3,status:["slimy",5],sound:r.splat},{id:"dance",name:"Dance Fever",hitText:"CANT STOP!",len:4,colors:["#ffd23f","#ff4af0"],icon:"note",speed:160,damage:3,status:["dance",5],sound:r.dance},{id:"clownshoes",name:"Clown Shoes",hitText:"HONK HONK!",len:3,colors:["#ff3a3a","#ffd23f"],icon:"orb",speed:160,damage:2,status:["clown",6],sound:r.honk},{id:"opera",name:"Opera Voice",hitText:"LA LA LAAA!",len:3,colors:["#ff9cf5","#ffffff"],icon:"note",speed:150,damage:2,status:["opera",6],sound:r.opera},{id:"mustache",name:"Magnificent Mustache",hitText:"FANCY!",len:3,colors:["#d9a066","#5a3a1a"],icon:"orb",speed:160,damage:2,status:["mustache",8],sound:r.boing},{id:"invisible",name:"Invisible Robe",hitText:"WHERE AM I?",len:3,colors:["#e8f0ff","#9aa7c7"],icon:"swirl",speed:160,damage:2,status:["invisible",6],sound:r.freeze},{id:"afro",name:"Bad Hair Day",hitText:"POOF!",len:3,colors:["#e0a060","#5a3a1a"],icon:"orb",speed:160,damage:2,status:["afro",7],sound:r.pop},{id:"squeaky",name:"Squeaky Steps",hitText:"SQUEAK!",len:3,colors:["#ffe14d","#ff9f1a"],icon:"orb",speed:170,damage:2,status:["squeaky",6],sound:r.squeak},{id:"pogo",name:"Pogo Stick",hitText:"BOINGY!",len:4,colors:["#7fe0ff","#ff5fa2"],icon:"swirl",speed:160,damage:3,status:["pogo",5],sound:r.boing},{id:"noodle",name:"Spaghetti Legs",hitText:"NOODLY!",len:4,colors:["#ffe08a","#ff5a3a"],icon:"orb",speed:160,damage:3,status:["noodle",5],sound:r.boing},{id:"fleas",name:"Itchy Fleas",hitText:"SO ITCHY!",len:4,colors:["#e0b080","#3b2414"],icon:"orb",speed:160,damage:3,status:["fleas",6],sound:r.scratch},{id:"snail",name:"Snail Pace",hitText:"SLOOOW...",len:4,colors:["#d9a066","#b8f2a0"],icon:"swirl",speed:140,damage:3,status:["snail",6],sound:r.snore},{id:"bubble",name:"Bubble Trap",hitText:"BLOOP!",len:4,colors:["#9fe8ff","#ffffff"],icon:"orb",speed:150,damage:3,status:["bubble",4],sound:r.bloop},{id:"turtle",name:"Turtle Shell",hitText:"FLIPPED!",len:4,colors:["#5fd06a","#e8d27a"],icon:"orb",speed:160,damage:3,status:["turtle",4],sound:r.pop},{id:"bees",name:"Bee Swarm",hitText:"BZZZZ!",len:5,colors:["#ffd23f","#120a1f"],icon:"orb",speed:170,damage:3,status:["bees",5],sound:r.buzz},{id:"pancake",name:"Pancake",hitText:"SQUISH!",len:5,colors:["#e8b060","#ffe08a"],icon:"orb",speed:160,damage:5,status:["pancake",5],sound:r.splat},{id:"boomerang",name:"Boomerang Curse",hitText:"UH OH...",len:5,colors:["#b07bff","#ffd23f"],icon:"swirl",speed:170,damage:3,status:["boomerang",8],sound:r.boing},{id:"noodlearms",name:"Wet Noodle Arms",hitText:"FLOPPY!",len:5,colors:["#ffe08a","#9fe8ff"],icon:"drop",speed:160,damage:3,status:["noodlearms",6],sound:r.splat},{id:"chickrain",name:"Rubber Chicken Rain",hitText:"INCOMING!",len:5,colors:["#ffe14d","#ff9f1a"],icon:"rubberchick",speed:150,damage:2,status:["chickrain",5],sound:r.squeak},{id:"toad",name:"Toad Form",hitText:"RIBBIT!",len:6,colors:["#4fbf4a","#c8e86b"],icon:"orb",speed:170,damage:4,status:["toad",5],sound:r.croak},{id:"puppet",name:"Puppet Strings",hitText:"DANCE, PUPPET!",len:6,colors:["#d9a066","#ffffff"],icon:"swirl",speed:180,damage:4,status:["puppet",4],sound:r.boing,apply:(i,t)=>{i.puppeteer=t}},{id:"potato",name:"Hot Potato",hitText:"HOT HOT HOT!",len:6,colors:["#c8894a","#ff5a1a"],icon:"orb",speed:160,damage:2,status:["potato",3],sound:r.fire},{id:"fairy",name:"Summon Healing Fairy",hitText:"TWINKLE!",len:5,colors:["#ff9cf5","#c8f4ff"],icon:"orb",speed:0,damage:0,familiar:"fairy",sound:r.summon},{id:"imp",name:"Summon Fire Imp",hitText:"HEHEHE!",len:5,colors:["#e0402a","#ffd23f"],icon:"orb",speed:0,damage:0,familiar:"imp",sound:r.summon},{id:"owl",name:"Summon Guard Owl",hitText:"HOOT!",len:5,colors:["#d9a066","#fff4e0"],icon:"orb",speed:0,damage:0,familiar:"owl",sound:r.summon}],re={id:"rubberchicken",name:"Rubber Chicken",hitText:"BONK!",len:0,colors:["#ffe14d","#ff9f1a"],icon:"rubberchick",speed:0,damage:2,sound:r.squeak};function ce(i){let t=i;return()=>{t=t+1831565813|0;let e=Math.imul(t^t>>>15,1|t);return e=e+Math.imul(e^e>>>7,61|e)^e,((e^e>>>14)>>>0)/4294967296}}const Lt=["up","down","left","right"];function he(){const i=new Set(K.filter(e=>e.combo).map(e=>e.combo.join(","))),t=ce(376337);for(const e of K)for(;!e.combo;){const a=Array.from({length:e.len},()=>Lt[Math.floor(t()*4)]),s=a.join(",");i.has(s)||(i.add(s),e.combo=a)}}he();const de=new Map(K.map(i=>[i.combo.join(","),i]));function ue(i){return de.get(i.join(","))}const pe=10,me={3:8,4:4,5:2,6:1};function vt(i){let t=2166136261;for(let e=0;e<i.length;e++)t^=i.charCodeAt(e),t=Math.imul(t,16777619);return t^=t>>>15,t=Math.imul(t,739982445),(t^t>>>12)>>>0}function be(i){const t=i.join(",");let e=K[0],a=-1/0;for(const s of K){if(s.len>i.length)continue;const n=(vt(`${t}|${s.id}`)+1)/4294967297,o=Math.log(n)/me[s.len];o>a&&(a=o,e=s)}return{spell:e,backfire:vt(`${t}|backfire`)%100<pe}}function ye(i){const t=K.find(e=>e.id===i);if(!t)throw new Error(`Unknown spell ${i}`);return t}const St=()=>Lt[Math.floor(Math.random()*4)],p=320,Y=180,O=162,N=22,st=6,Q=p-6,U=6,Pt=[{x:36,y:124,w:60},{x:224,y:124,w:60},{x:130,y:92,w:60}],ge=520,it=100,xe=3,at=6,It=[{name:"Zibble",colors:{hat:"#8a4bff",hatDark:"#4f2496",band:"#ffd23f",robe:"#7340d8",robeDark:"#45227f"},controls:{left:["KeyA","Pad1Left"],right:["KeyD","Pad1Right"],up:["KeyW","Pad1Up"],down:["KeyS","Pad1Down"],cast:["Space","ShiftLeft","KeyQ","Pad1Cast"]},startX:60},{name:"Fumblewick",colors:{hat:"#ff7a2f",hatDark:"#a53d10",band:"#5ad1c4",robe:"#e0582b",robeDark:"#8f2f14"},controls:{left:["ArrowLeft","Pad2Left"],right:["ArrowRight","Pad2Right"],up:["ArrowUp","Pad2Up"],down:["ArrowDown","Pad2Down"],cast:["Enter","NumpadEnter","ShiftRight","ControlRight","Numpad0","Pad2Cast"]},startX:260}],Wt=["Escape","KeyP"],Dt=new Set([...It.flatMap(i=>Object.values(i.controls).flat()),...Wt]);class Me{constructor(t,e,a,s,n){m(this,"x",0);m(this,"y",0);m(this,"vx",0);m(this,"vy",0);m(this,"facing",1);m(this,"hp",it);m(this,"wins",0);m(this,"onGround",!1);m(this,"dropTimer",0);m(this,"casting",!1);m(this,"castLocked",!1);m(this,"combo",[]);m(this,"cooldown",0);m(this,"anim",0);m(this,"flash",0);m(this,"jumpQueued",!1);m(this,"puppeteer",null);m(this,"danceMove",0);m(this,"status",new Map);m(this,"timers",new Map);m(this,"palette");this.index=t,this.name=e,this.colors=a,this.controls=s,this.startX=n,this.palette=se(a)}reset(){this.x=this.startX,this.y=O-8,this.vx=0,this.vy=0,this.facing=this.index===0?1:-1,this.hp=it,this.casting=!1,this.castLocked=!1,this.combo=[],this.cooldown=0,this.flash=0,this.status.clear(),this.timers.clear(),this.puppeteer=null}has(t){return(this.status.get(t)??0)>0}get halfW(){return this.has("pancake")?8:this.has("tiny")?3:4}get halfH(){return this.has("pancake")?3:this.has("tiny")?4:8}gravitySign(){let t=this.has("revgrav")?-1:1;return this.has("balloon")&&(t=t===1?-1:1),this.has("bubble")&&(t=t===1?-1:1),t}canMove(){return!this.has("frozen")&&!this.has("stun")&&!this.has("disco")&&!this.has("tripped")}canCast(){return this.canMove()&&!this.has("chicken")&&!this.has("toad")}}class Re{constructor(t){m(this,"wizards");m(this,"ctx");m(this,"background");m(this,"keys",new Set);m(this,"projectiles",[]);m(this,"particles",[]);m(this,"texts",[]);m(this,"familiars",[]);m(this,"phase","title");m(this,"phaseTime",0);m(this,"paused",!1);m(this,"time",0);m(this,"shakeTime",0);m(this,"winner",null);m(this,"lastFrame",0);m(this,"screenShake",!0);m(this,"touchOnly",!1);m(this,"padConnected",[!1,!1]);m(this,"beforeUpdate",null);const e=t.getContext("2d");if(!e)throw new Error("Canvas 2D is not available");this.ctx=e,t.width=p,t.height=Y,this.wizards=It.map((a,s)=>new Me(s,a.name,a.colors,a.controls,a.startX)),this.background=Te();for(const a of this.wizards)a.reset()}start(){const t=e=>{var s;const a=this.lastFrame?Math.min(.05,(e-this.lastFrame)/1e3):0;this.lastFrame=e,(s=this.beforeUpdate)==null||s.call(this),this.update(a),this.draw(),requestAnimationFrame(t)};requestAnimationFrame(t)}keyDown(t,e){if(rt(),!e){if(Wt.includes(t)){this.pausePressed();return}if(!this.paused){if(this.keys.add(t),this.phase==="title"){this.isAnyCastKey(t)&&this.startRound();return}if(this.phase==="ko"){this.isAnyCastKey(t)&&this.phaseTime>1.5&&this.startRound();return}for(const a of this.wizards){a.controls.cast.includes(t)&&this.canBeginCast(a)&&this.beginCast(a);const s=this.dirForKey(a,t);if(s&&this.holdingCast(a)&&!a.casting&&this.canBeginCast(a)&&this.beginCast(a),s&&a.casting)this.addRune(a,s);else if(!(s&&this.holdingCast(a)))if(s==="up"){a.jumpQueued=!0;for(const n of this.wizards)n!==a&&n.has("puppet")&&n.puppeteer===a&&(n.jumpQueued=!0)}else s==="down"&&a.onGround&&a.gravitySign()===1&&(a.dropTimer=.25)}}}}keyUp(t){if(this.keys.delete(t),this.phase==="fight")for(const e of this.wizards)e.controls.cast.includes(t)&&(this.holdingCast(e)||(e.castLocked=!1,e.casting&&this.releaseCast(e)))}releaseAll(){this.keys.clear();for(const t of this.wizards)t.casting=!1,t.combo=[],t.castLocked=!1}pointerStart(){rt(),(this.phase==="title"||this.phase==="ko"&&this.phaseTime>1.5)&&this.startRound()}pausePressed(){rt(),this.phase==="fight"&&(this.paused=!this.paused,this.paused&&this.releaseAll())}padUnplugged(t){const e=this.wizards[t];e&&(e.casting=!1,e.combo=[],this.phase==="fight"&&this.addText(e.x,e.y-30,"CONTROLLER UNPLUGGED","#ff9f5a"))}isAnyCastKey(t){return this.wizards.some(e=>e.controls.cast.includes(t))}holdingCast(t){return t.controls.cast.some(e=>this.keys.has(e))}held(t){return t.some(e=>this.keys.has(e))}dirForKey(t,e){const a=t.controls;return a.up.includes(e)?"up":a.down.includes(e)?"down":a.left.includes(e)?"left":a.right.includes(e)?"right":null}startRound(){this.paused=!1;for(const t of this.wizards)t.reset(),t.castLocked=this.holdingCast(t);this.projectiles=[],this.particles=[],this.texts=[],this.familiars=[],this.winner=null,this.phase="fight",this.phaseTime=0,this.addText(p/2,70,"DUEL!","#ffd23f",2),r.cast()}canBeginCast(t){return!t.casting&&!t.castLocked&&t.cooldown<=0&&t.canCast()}beginCast(t){t.casting=!0,t.combo=[]}addRune(t,e){let a=e;t.has("drunk")&&(a==="left"||a==="right")&&(a=a==="left"?"right":"left"),t.has("butter")&&(a=St()),!(t.combo.length>=at+2)&&(t.combo.push(a),r.rune(a,t.combo.length),this.burst(t.x,t.y-t.halfH-10,t.colors.band,3,30))}cancelCast(t,e){t.combo.length>0&&(this.addText(t.x,t.y-22,e,"#bbbbbb"),r.fizzle()),t.casting=!1,t.combo=[],t.castLocked=!0}releaseCast(t){const e=t.combo;if(t.casting=!1,t.combo=[],e.length===0)return;if(t.cooldown=t.has("sleepy")?1:.45,e.length<xe){this.addText(t.x,t.y-22,"FIZZLE...","#9a8fb0"),this.burst(t.x+t.facing*6,t.y-4,"#9a8fb0",8,40),r.fizzle();return}const a=e.length<=at?ue(e):void 0;if(a){this.shout(t,`${a.name}!`,a.colors[0]),a.familiar?this.summon(t,a.familiar):this.fire(t,a);return}const s=e.length>at,{spell:n,backfire:o}=be(e);n.familiar?(this.shout(t,o?"BACKFIRE! WRONG WIZARD!":`WILD MAGIC: ${n.name}!`,o?"#ff5a5a":"#ff9cf5"),this.summon(o?this.wizards[1-t.index]:t,n.familiar)):o?(this.shout(t,s?"OVERLOAD! BACKFIRE!":"BACKFIRE!","#ff5a5a"),this.addText(t.x,t.y-30,n.name,n.colors[0]),this.burst(t.x,t.y,n.colors[0],16,80),this.applySpell(n,t,t)):(this.shout(t,`WILD MAGIC: ${n.name}!`,"#ff9cf5"),this.fire(t,n))}fire(t,e){const a=this.wizards[1-t.index];t.facing=a.x>=t.x?1:-1;const s=t.x+t.facing*8,n=t.y-2;let o=Math.atan2(a.y-n,a.x-s);t.has("pie")&&(o+=(Math.random()-.5)*1.6);const f=t.has("noodlearms"),c=e.speed*(f?.4:1),h=t.has("boomerang");h&&(t.status.delete("boomerang"),this.addText(t.x,t.y-26,"BOOMERANG!","#b07bff")),this.projectiles.push({x:s,y:n,vx:Math.cos(o)*c,vy:Math.sin(o)*c,spell:e,owner:t,target:h?t:a,life:f?7:h?4:3,returnAfter:h?.45:void 0}),r.cast(),this.burst(s,n,e.colors[1],10,60)}summon(t,e){const a=this.familiars.filter(f=>f.owner===t);a.length>=oe&&this.dismiss(a[0]);const s=et[e],n=t.x-t.facing*10,o=t.y-12*t.gravitySign();this.familiars.push({kind:e,owner:t,x:n,y:o,life:fe,timer:s.every}),r.summon(),this.burst(n,o,s.palette[Object.keys(s.palette)[0]],16,70),this.addText(n,o-10,s.hello,"#ffffff")}dismiss(t){t.life=0,this.familiars=this.familiars.filter(e=>e!==t),this.burst(t.x,t.y,"#ffffff",10,50),r.pop()}familiarOf(t,e){return this.familiars.find(a=>a.owner===t&&a.kind===e)}heal(t,e){t.hp=Math.min(it,t.hp+e),this.addText(t.x,t.y-14,`+${e}`,"#6bff8f")}damage(t,e){e<=0||this.phase!=="fight"||(t.hp=Math.max(0,t.hp-e),t.flash=.2,this.addText(t.x+(Math.random()-.5)*8,t.y-12,`-${e}`,"#ff5a5a"),t.hp<=0&&this.knockOut(t))}applySpell(t,e,a){var n;const s=e.halfH;if(t.status){const[o,f]=t.status;e.status.set(o,Math.max(e.status.get(o)??0,f))}(n=t.apply)==null||n.call(t,e,a,this),e.y+=(s-e.halfH)*e.gravitySign(),e.casting&&!e.canCast()&&this.cancelCast(e,"INTERRUPTED!"),t.sound(),this.addText(e.x,e.y-26,t.hitText,t.colors[0]),this.shake(t.damage>=10?.3:.12),this.damage(e,t.damage)}explodePotato(t){r.boom(),this.burst(t.x,t.y,"#ff5a1a",30,140),this.burst(t.x,t.y,"#ffd23f",20,100),this.addText(t.x,t.y-26,"KABOOM!","#ff5a1a",2),this.shake(.5),t.vy=-220*t.gravitySign(),this.damage(t,15)}knockOut(t){this.phase="ko",this.phaseTime=0,this.winner=this.wizards[1-t.index],this.winner.wins+=1,this.projectiles=[];for(const e of this.familiars.filter(a=>a.owner===t))this.dismiss(e);for(const e of this.wizards)e.casting=!1,e.combo=[];this.burst(t.x,t.y,t.colors.hat,40,120),this.shake(.5),r.ko(),r.win()}update(t){if(this.time+=t,!this.paused){if(this.phaseTime+=t,this.shakeTime=Math.max(0,this.shakeTime-t),this.phase==="fight"){for(const e of this.wizards)this.updateWizard(e,t);this.updateFamiliars(t),this.updateProjectiles(t)}else this.phase==="ko"&&this.winner&&(this.winner.anim+=t,Math.random()<t*6&&this.burst(this.winner.x+(Math.random()-.5)*40,this.winner.y-20,Tt(),6,50));this.updateParticles(t)}}tickTimer(t,e,a,s){const n=(t.timers.get(e)??s())-a;return n<=0?(t.timers.set(e,s()),!0):(t.timers.set(e,n),!1)}updateWizard(t,e){t.anim+=e,t.flash=Math.max(0,t.flash-e),t.cooldown=Math.max(0,t.cooldown-e),t.dropTimer=Math.max(0,t.dropTimer-e);const a=t.halfH;for(const[b,g]of t.status){const D=g-e;if(D>0){t.status.set(b,D);continue}t.status.delete(b),t.timers.delete(b),b==="balloon"&&(r.pop(),this.burst(t.x,t.y-20,"#ff6b6b",10,60),this.addText(t.x,t.y-26,"POP!","#ff6b6b")),b==="potato"&&this.explodePotato(t),b==="bubble"&&(r.pop(),this.burst(t.x,t.y,"#9fe8ff",14,70),this.addText(t.x,t.y-26,"POP!","#9fe8ff")),b==="frozen"&&this.burst(t.x,t.y,"#9fe8ff",12,70)}t.y+=(a-t.halfH)*t.gravitySign(),this.canBeginCast(t)&&this.holdingCast(t)&&this.beginCast(t),t.casting&&!t.canCast()&&this.cancelCast(t,"INTERRUPTED!"),this.updateSillyEffects(t,e);const s=t.gravitySign();let n=0;this.held(t.controls.right)&&(n+=1),this.held(t.controls.left)&&(n-=1),t.has("drunk")&&(n=-n),(!t.canMove()||t.casting)&&(n=0),t.has("dance")&&t.canMove()&&(n=t.danceMove),(t.has("scratching")||t.has("bubble"))&&(n=0);const o=t.has("puppet")?t.puppeteer:null;o&&o!==t&&t.canMove()&&(n=o.casting?0:(this.held(o.controls.right)?1:0)-(this.held(o.controls.left)?1:0)),n!==0&&!t.has("disco")&&(t.facing=n>0?1:-1);let f=78;t.has("sleepy")&&(f*=.4),t.has("tiny")&&(f*=.75),t.has("rain")&&(f*=.8),t.has("mustache")&&(f*=.85),t.has("snail")&&(f*=.35),t.has("noodle")&&(f*=.75),t.has("turtle")&&(f*=.5),t.has("pancake")&&(f*=.6),t.has("hyper")&&(f*=2.6);let c=n*f;t.has("hyper")&&t.canMove()&&!t.casting&&n===0&&(c=t.facing*f),t.has("drunk")&&(c+=Math.sin(t.anim*3.1)*30),t.has("toad")&&(t.onGround&&n!==0&&t.vy*s>=0&&(t.vx=n*100,t.vy=-170*s,r.croak()),c=t.onGround?0:t.vx),t.has("glue")&&t.onGround&&(c=0,t.vx=0);let h=t.has("slippery")?110:t.onGround?900:450;const l=Math.abs(c)<Math.abs(t.vx)||Math.sign(c)!==Math.sign(t.vx);t.has("slimy")&&t.onGround&&l&&(h=60),t.has("turtle")&&(h=l?25:150),t.has("bubble")&&(h=60);const u=c-t.vx;if(t.vx+=Math.sign(u)*Math.min(Math.abs(u),h*e),t.jumpQueued&&(t.jumpQueued=!1,t.canMove()&&!t.casting)){if(t.has("chicken"))t.vy=-150*s,r.cluck();else if(t.onGround&&!t.has("glue")&&!t.has("turtle")&&!t.has("bubble")&&!t.has("pancake")){let b=235;t.has("tiny")&&(b*=.8),t.has("sleepy")&&(b*=.7),t.has("snail")&&(b*=.6),t.vy=-b*s}}let d=ge;t.has("lowgrav")&&(d*=.3),t.has("bighead")&&(d*=1.35),t.has("balloon")&&(d*=.2),t.has("bubble")&&(d*=.15),t.vy+=d*s*e,t.vy=Math.max(-420,Math.min(420,t.vy));const y=t.y-t.halfH,R=t.y+t.halfH;t.x+=t.vx*e,t.y+=t.vy*e,this.collide(t,y,R,s)}collide(t,e,a,s){const n=t.has("bouncy");t.x-t.halfW<st&&(t.x=st+t.halfW,t.vx=n||t.has("slippery")||t.has("turtle")?Math.abs(t.vx)*.8:0),t.x+t.halfW>Q&&(t.x=Q-t.halfW,t.vx=n||t.has("slippery")||t.has("turtle")?-Math.abs(t.vx)*.8:0),t.onGround=!1;const o=f=>{n&&Math.abs(t.vy)>70?(t.vy=-t.vy*.85,Math.abs(t.vy)>120&&r.boing()):t.vy=0,f&&(t.onGround=!0)};t.y+t.halfH>=O&&t.vy>=0&&(t.y=O-t.halfH,o(s===1)),t.y-t.halfH<=N&&t.vy<=0&&(t.y=N+t.halfH,o(s===-1));for(const f of Pt){if(t.x+t.halfW<f.x||t.x-t.halfW>f.x+f.w)continue;const c=t.y-t.halfH,h=t.y+t.halfH;s===1&&t.vy>=0&&t.dropTimer<=0&&a<=f.y+1&&h>=f.y?(t.y=f.y-t.halfH,o(!0)):s===-1&&t.vy<=0&&e>=f.y+U-1&&c<=f.y+U&&(t.y=f.y+U+t.halfH,o(!0))}}updateSillyEffects(t,e){const a=t.gravitySign();if(t.has("burning")&&(Math.random()<e*30&&this.spark(t.x+(Math.random()-.5)*8,t.y+4,Math.random()<.5?"#ff7a1a":"#ffd23f",-40),this.tickTimer(t,"burning",e,()=>.5)&&this.damage(t,1)),t.has("hiccups")&&this.tickTimer(t,"hiccups",e,()=>.6+Math.random()*.7)&&(t.vy=-150*a,r.hic(),this.addText(t.x,t.y-20,"HIC!","#ff9fd0")),t.has("sneeze")&&this.tickTimer(t,"sneeze",e,()=>1.1+Math.random()*.6)&&(t.vx=-t.facing*260,t.vy=-110*a,r.sneeze(),this.addText(t.x,t.y-20,"ACHOO!","#b8f2a0"),this.burst(t.x+t.facing*6,t.y-6,"#d8ffd0",10,90)),t.has("whistle")&&this.tickTimer(t,"whistle",e,()=>1.2)&&(r.whistle(),this.addText(t.x,t.y-20,"TWEEE!","#e0f7ff"),t.casting&&this.addRune(t,St())),t.has("whistle")&&Math.random()<e*4&&this.spark(t.x+t.facing*5,t.y-8,"#e0f7ff",-25,1.2),t.has("rain")&&(Math.random()<e*25&&this.spark(t.x+(Math.random()-.5)*12,t.y-t.halfH-16*a,"#4aa3ff",0,.6,140*a),this.tickTimer(t,"rain",e,()=>.7)&&this.damage(t,1)),t.has("sleepy")&&this.tickTimer(t,"sleepy",e,()=>1.4)&&(r.snore(),this.addText(t.x+6,t.y-18,"Z","#a8b8ff")),t.has("chicken")&&t.onGround&&this.tickTimer(t,"chicken",e,()=>.8+Math.random())&&(t.vy=-110*a,r.cluck()),t.has("disco")&&(this.tickTimer(t,"disco",e,()=>.25)&&(t.facing=t.facing===1?-1:1),this.tickTimer(t,"discoBeat",e,()=>1)&&r.disco(),Math.random()<e*20&&this.spark(t.x+(Math.random()-.5)*30,t.y-Math.random()*20,Tt(),0,.3,0)),t.has("hyper")&&Math.random()<e*30&&this.spark(t.x-t.facing*6,t.y+(Math.random()-.5)*12,"#7dff6b",0,.25,0),t.has("lowgrav")&&Math.random()<e*6&&this.spark(t.x+(Math.random()-.5)*12,t.y+8,"#c9b6ff",-15,1),t.has("dance")){if(this.tickTimer(t,"dance",e,()=>.35)){const n=Math.floor(Math.random()*4);t.danceMove=n===0?-1:n===1?1:0,n===2&&t.onGround&&(t.vy=-170*t.gravitySign()),n===3&&(t.facing=t.facing===1?-1:1)}this.tickTimer(t,"danceBeat",e,()=>.9)&&r.dance(),Math.random()<e*5&&this.spark(t.x+(Math.random()-.5)*14,t.y-10,Math.random()<.5?"#ffd23f":"#ff4af0",-30,.8)}t.has("slimy")&&(Math.random()<e*6&&this.spark(t.x+(Math.random()-.5)*8,t.y+2,"#5dff8a",0,.8,90),t.onGround&&Math.abs(t.vx)>20&&Math.random()<e*30&&this.spark(t.x,t.y+t.halfH*t.gravitySign()-(t.gravitySign()===1?1:0),"#1f9e4a",0,1.2,0));const s=t.onGround&&Math.abs(t.vx)>25;if(t.has("clown")&&s&&this.tickTimer(t,"clown",e,()=>.9+Math.random()*.8)&&(t.status.set("tripped",.6),t.vx*=.3,r.honk(),this.addText(t.x,t.y-20,"OOF!","#ff3a3a")),t.has("opera")){if(this.tickTimer(t,"opera",e,()=>1.8)){r.opera();const n=["LA LA LAAA!","FIGARO!","MI MI MIII!","OH SOLE MIO!"];this.addText(t.x,t.y-20,n[Math.floor(Math.random()*n.length)],"#ff9cf5")}Math.random()<e*6&&this.spark(t.x+t.facing*4,t.y-6,Math.random()<.5?"#ff9cf5":"#ffffff",-30,1)}if(t.has("squeaky")&&s&&this.tickTimer(t,"squeaky",e,()=>.28)&&r.squeak(),t.has("invisible")&&Math.random()<e*4&&this.spark(t.x+(Math.random()-.5)*10,t.y+(Math.random()-.5)*14,"#e8f0ff",0,.4,0),t.has("pogo")&&t.onGround&&!t.has("turtle")&&(t.vy=-210*a,r.boing()),t.has("noodle")&&s&&this.tickTimer(t,"noodle",e,()=>.6+Math.random()*.6)&&(t.vx=(Math.random()-.5)*220,t.vy=-80*a,this.addText(t.x,t.y-20,"WOBBLE!","#ffe08a")),t.has("fleas")&&(this.tickTimer(t,"fleas",e,()=>1+Math.random()*.7)&&(t.status.set("scratching",.7),r.scratch(),this.addText(t.x,t.y-20,"ITCHY!","#e0b080")),Math.random()<e*10&&this.spark(t.x+(Math.random()-.5)*12,t.y+(Math.random()-.5)*12,"#120a1f",-50,.4,250)),t.has("snail")&&s&&Math.random()<e*20&&this.spark(t.x,t.y+t.halfH*a-(a===1?1:0),"#b8f2a0",0,1,0),t.has("bubble")&&t.y-t.halfH<=N+1&&t.status.set("bubble",.001),t.has("bees")&&(this.tickTimer(t,"bees",e,()=>.5)&&this.damage(t,1),this.tickTimer(t,"beeBuzz",e,()=>1.2)&&(r.buzz(),Math.random()<.5&&this.addText(t.x,t.y-20,"OW!","#ffd23f"))),t.has("noodlearms")&&Math.random()<e*4&&this.spark(t.x+t.facing*5,t.y,"#ffe08a",0,.6,120),t.has("chickrain")&&this.tickTimer(t,"chickrain",e,()=>.55)&&this.projectiles.push({x:t.x+(Math.random()-.5)*30,y:N+3,vx:0,vy:20,spell:re,owner:this.wizards[1-t.index],target:t,life:3,gravity:260}),t.has("potato")){const n=t.status.get("potato")??0;t.timers.set("potatoHeld",(t.timers.get("potatoHeld")??0)+e),Math.random()<e*(12+(3-n)*15)&&this.spark(t.x+t.facing*6,t.y-2,Math.random()<.5?"#ff5a1a":"#ffd23f",-40,.5),this.tickTimer(t,"potatoCount",e,()=>1)&&this.addText(t.x,t.y-20,`${Math.max(1,Math.ceil(n))}...`,"#ff5a1a");const o=this.wizards[1-t.index];if(Math.abs(o.x-t.x)<12&&Math.abs(o.y-t.y)<14&&!o.has("potato")&&(t.timers.get("potatoHeld")??0)>.4){o.status.set("potato",Math.max(n,1)+.3),o.timers.set("potatoHeld",0),t.status.delete("potato"),t.timers.delete("potatoCount");const c=o.x>=t.x?1:-1;t.vx=-c*150,o.vx=c*150,r.pop(),this.addText(o.x,o.y-26,"PASSED!","#ffd23f")}}t.has("butter")&&Math.random()<e*5&&this.spark(t.x+t.facing*5,t.y+2,"#ffe08a",0,.6,120)}updateFamiliars(t){for(const e of this.wizards)this.familiars.filter(s=>s.owner===e).forEach((s,n)=>{const o=e.gravitySign(),f=Math.sin(this.time*4+n*1.7)*2,c=e.x-e.facing*(11+n*9),h=e.y-(10+f)*o,l=Math.min(1,t*(7-n*1.5));if(s.x+=(c-s.x)*l,s.y+=(h-s.y)*l,s.x=Math.max(st+3,Math.min(Q-3,s.x)),s.life-=t,s.life<=0){this.dismiss(s);return}et[s.kind].every<=0||(s.timer-=t,!(s.timer>0)&&(s.timer=et[s.kind].every,this.useFamiliarPower(s)))})}useFamiliarPower(t){const e=t.owner,a=this.wizards[1-e.index];if(t.kind==="fairy"){if(e.hp>=it)return;this.heal(e,2),this.burst(e.x,e.y-4,"#ff9cf5",6,30)}else if(t.kind==="imp"){const s=Math.atan2(a.y-t.y,a.x-t.x);this.projectiles.push({x:t.x,y:t.y,vx:Math.cos(s)*ct.speed,vy:Math.sin(s)*ct.speed,spell:ct,owner:e,target:a,life:3}),r.fire()}}updateProjectiles(t){for(const e of this.projectiles){if(e.gravity&&(e.vy+=e.gravity*t),e.returnAfter!==void 0&&(e.returnAfter-=t,e.returnAfter<=0)){const f=Math.hypot(e.vx,e.vy),c=Math.atan2(e.target.y-e.y,e.target.x-e.x);e.vx=Math.cos(c)*f,e.vy=Math.sin(c)*f}e.x+=e.vx*t,e.y+=e.vy*t,e.life-=t,Math.random()<t*40&&this.spark(e.x,e.y,e.spell.colors[Math.random()<.5?0:1],0,.35,0);const a=e.target,s=a.has("afro")?6:0,n=e.returnAfter!==void 0&&e.returnAfter>0,o=e.owner!==a?this.familiarOf(a,"owl"):void 0;if(o&&!n&&Math.abs(e.x-a.x)<=a.halfW+14&&Math.abs(e.y-a.y)<=a.halfH+14)e.life=-1,o.x=e.x,o.y=e.y,this.addText(e.x,e.y-10,"CAUGHT!","#d9a066"),this.burst(e.x,e.y,e.spell.colors[0],12,70),this.dismiss(o);else if(!n&&Math.abs(e.x-a.x)<=a.halfW+3+s&&Math.abs(e.y-a.y)<=a.halfH+3+s){if(e.life=-1,this.burst(e.x,e.y,e.spell.colors[0],14,90),this.applySpell(e.spell,a,e.owner),this.phase!=="fight")return}else(e.x<0||e.x>p||e.y<N-8||e.y>O+6)&&(e.life=-1,this.burst(e.x,e.y,e.spell.colors[0],6,40))}for(const e of this.projectiles)for(const a of this.projectiles)e===a||e.owner===a.owner||e.life<0||a.life<0||Math.abs(e.x-a.x)<7&&Math.abs(e.y-a.y)<7&&(e.life=-1,a.life=-1,this.burst((e.x+a.x)/2,(e.y+a.y)/2,"#ffffff",24,120),this.addText((e.x+a.x)/2,e.y-10,"CLASH!","#ffffff",2),this.shake(.25),r.clash());this.projectiles=this.projectiles.filter(e=>e.life>0)}updateParticles(t){for(const e of this.particles)e.vy+=e.gravity*t,e.x+=e.vx*t,e.y+=e.vy*t,e.life-=t;this.particles=this.particles.filter(e=>e.life>0);for(const e of this.texts)e.y-=14*t,e.life-=t;this.texts=this.texts.filter(e=>e.life>0)}spark(t,e,a,s=0,n=.5,o=0){this.particles.push({x:t,y:e,vx:(Math.random()-.5)*10,vy:s,life:n,maxLife:n,color:a,gravity:o,size:1})}burst(t,e,a,s,n){for(let o=0;o<s;o++){const f=Math.random()*Math.PI*2,c=n*(.3+Math.random()*.7),h=.3+Math.random()*.4;this.particles.push({x:t,y:e,vx:Math.cos(f)*c,vy:Math.sin(f)*c,life:h,maxLife:h,color:a,gravity:120,size:Math.random()<.3?2:1})}}addText(t,e,a,s,n=1){this.texts.push({x:t,y:e,text:a,color:s,life:1.1,scale:n})}shout(t,e,a){this.addText(Math.max(F(e)/2+2,Math.min(p-F(e)/2-2,t.x)),t.y-34,e,a)}shake(t){this.shakeTime=Math.max(this.shakeTime,t)}draw(){const t=this.ctx;t.save(),this.screenShake&&this.shakeTime>0&&t.translate(Math.round((Math.random()-.5)*4),Math.round((Math.random()-.5)*4)),t.drawImage(this.background,0,0),this.drawTorches();for(const e of this.wizards)this.drawWizard(e);for(const e of this.familiars)this.drawFamiliar(e);for(const e of this.projectiles)this.drawProjectile(e);for(const e of this.particles)t.globalAlpha=Math.min(1,e.life/e.maxLife+.2),t.fillStyle=e.color,t.fillRect(Math.round(e.x),Math.round(e.y),e.size,e.size);t.globalAlpha=1;for(const e of this.texts)e.life<.25&&Math.floor(e.life*20)%2===0||S(t,e.text,ve(e.x,e.text,e.scale),e.y,e.color,{align:"center",scale:e.scale});t.restore(),this.drawHud(),this.phase==="title"&&this.drawTitle(),this.phase==="ko"&&this.drawWinner(),this.paused&&this.drawPaused()}drawPaused(){const t=this.ctx;t.fillStyle="rgba(18,10,31,0.7)",t.fillRect(0,0,p,Y),S(t,"PAUSED",p/2,62,"#ffd23f",{align:"center",scale:3}),Math.floor(this.time*2)%2===0&&S(t,"PRESS START, ESC OR P TO KEEP DUELING",p/2,90,"#ffffff",{align:"center"})}drawTorches(){const t=this.ctx;for(const e of[14,p-17]){const a=Math.sin(this.time*13+e)>0?1:0;t.fillStyle="#ff7a1a",t.fillRect(e,88-a,3,3+a),t.fillStyle="#ffd23f",t.fillRect(e+1,89,1,2)}}drawWizard(t){const e=this.ctx,a=t.has("revgrav")?-1:1,s=a===-1;if(this.phase==="ko"&&t!==this.winner){E(e,yt,t.palette,t.x-5,O-yt.length);return}if(t.has("glue")){e.fillStyle="#8fb339";const l=a===1?t.y+t.halfH-1:t.y-t.halfH-1;e.fillRect(Math.round(t.x-8),Math.round(l),16,2),e.fillStyle="#c8e86b",e.fillRect(Math.round(t.x-6),Math.round(l),12,1)}if(t.casting){const l=11+Math.round(Math.sin(this.time*12));e.fillStyle=t.colors.band;for(let u=0;u<12;u++){const d=this.time*3+u/12*Math.PI*2;e.fillRect(Math.round(t.x+Math.cos(d)*l),Math.round(t.y+Math.sin(d)*l),1,1)}}if(t.has("balloon")){const l=a===1?t.y-t.halfH-22:t.y+t.halfH+8;E(e,V.balloon,{a:"#ff6b6b",b:"#ffffff",w:"#ffffff"},t.x-7,l,{scale:2,flipY:s})}const n=Math.abs(t.vx)>10&&t.onGround;let o="idle";t.casting?o="cast":t.has("scratching")?o=Math.floor(t.anim*12)%2===0?"cast":"idle":t.has("dance")?o=Math.floor(t.anim*6)%2===0?"cast":n?"walkA":"idle":n?o=Math.floor(t.anim*8)%2===0?"walkA":"walkB":t.has("disco")&&(o=Math.floor(t.anim*4)%2===0?"cast":"idle");const f=t.flash>0&&Math.floor(t.flash*30)%2===0,c=f||t.has("stun")&&Math.floor(this.time*20)%2===0?kt(t.palette):t.palette,h=t.facing===-1;if(t.has("toad")){const l=a===1?t.y+t.halfH-Mt.length:t.y-t.halfH;E(e,Mt,f?kt(Rt):Rt,t.x-5,l,{flipX:h,flipY:s})}else if(t.has("chicken")){const l=a===1?t.y+t.halfH-gt.length:t.y-t.halfH;E(e,gt,ae,t.x-5,l,{flipX:h,flipY:s})}else if(t.has("tiny")){const l=Se(tt(o)),u=a===1?t.y+t.halfH-l.length:t.y-t.halfH;E(e,l,c,t.x-2,u,{flipX:h,flipY:s})}else if(t.has("pancake")){const l=tt("idle").filter((d,y)=>y%3===0).map(d=>d.replace(/./g,y=>y+y)),u=a===1?t.y+t.halfH-l.length:t.y-t.halfH;E(e,l,c,t.x-10,u,{flipX:h,flipY:s})}else if(t.has("turtle")){const l=a===1?t.y+t.halfH-xt.length:t.y-t.halfH;E(e,xt,{...t.palette,G:"#3fae4a",g:"#1f6b2a",y:"#e8d27a"},t.x-7,l,{flipX:h,flipY:s})}else if(t.has("tripped"))e.save(),e.translate(Math.round(t.x),Math.round(a===1?t.y+t.halfH-5:t.y-t.halfH+5)),e.rotate((h?-1:1)*a*(Math.PI/2)),E(e,tt("idle"),c,-5,a===1?-16:0,{flipX:h,flipY:s}),e.restore(),t.has("clown")&&this.drawClownShoes(t,a,t.x-t.facing*7);else{const l=tt(o),u=o==="idle"&&Math.floor(t.anim*2)%2===0?1:0,d=l.slice(0,10),y=l.slice(10),R=a===1?t.y+t.halfH-y.length:t.y-t.halfH,b=t.has("bighead"),g=b?2:1,D=d.length*g,C=a===1?R-D+u:R+y.length-u,_=t.x-5-(b?5:0),M=(v,T,A,H,j)=>{const P=h?10-v-A:v,I=s?10-T-H:T;e.fillStyle=j,e.fillRect(Math.round(_+P*g),Math.round(C+I*g),A*g,H*g)};t.has("afro")&&[[2,6],[0,10],[-2,14],[-3,16],[-4,18],[-4,18],[-4,18],[-3,16],[-2,14],[0,10]].forEach(([T,A],H)=>M(T,H,A,1,H%3===0?"#5a3a1a":"#3b2414"));const k=t.has("invisible"),L=t.has("noodle")?Math.round(Math.sin(t.anim*14)):0;if(k||E(e,y,c,t.x-5+L,R,{flipX:h,flipY:s}),E(e,k?d.slice(0,7):d,c,_,s&&k?C+3*g:C,{flipX:h,flipY:s,scale:g}),t.has("mustache")&&!k){M(1,8,8,2,"#5a3a1a"),M(3,8,4,1,"#8a5a2b");const v=a===1?t.y+t.halfH:t.y-t.halfH,T=s?C:C+10*g,A=Math.abs(v-T);for(const H of[-1,1]){const j=t.x+H*4*g;for(let P=0;P<A;P++){const I=Math.round(Math.sqrt(P/A)*6)*H;e.fillStyle=P%4===0?"#8a5a2b":"#5a3a1a",e.fillRect(Math.round(j+I),Math.round(T+P*a),2,1)}}}if(t.has("clown")&&this.drawClownShoes(t,a,t.x),t.has("snail")&&!k&&E(e,V.shell,{a:"#8a5a2b",b:"#d9a066"},t.x-t.facing*5-3,t.y-3,{flipX:h,flipY:s}),t.has("pogo")){const v=a===1?t.y+t.halfH:t.y-t.halfH,T=Math.round(t.x+t.facing*4);e.fillStyle="#c0c0d0",e.fillRect(T,Math.round(Math.min(t.y-2*a,v+4*a)),1,12),e.fillStyle="#ff5fa2",e.fillRect(T-2,Math.round(t.y-2*a),5,1);for(let A=0;A<4;A++)e.fillRect(T-1+A%2,Math.round(v+A*a),2,1)}if(o==="cast"){const v=h?t.x-6-3:t.x+5,T=a===1?R-3:R+y.length;E(e,ee,c,v,T,{flipX:h,flipY:s})}if(t.has("pie")){const v=a===1?C+7*g:C+1*g;e.fillStyle="#fff4e0",e.fillRect(Math.round(t.x-3*g),Math.round(v),7*g,3*g),e.fillStyle="#ff9fc8",e.fillRect(Math.round(t.x),Math.round(v),g,g)}}if(t.has("puppet")){const l=Math.round(t.y-t.halfH),u=N+4;e.fillStyle="#d8d8e8";for(const d of[-3,3])e.fillRect(Math.round(t.x+d),u,1,Math.max(0,l-u));e.fillStyle="#8a5a2b",e.fillRect(Math.round(t.x-5),u-1,11,2),e.fillRect(Math.round(t.x),u-3,1,6)}if(t.has("potato")){const l=t.status.get("potato")??0,u=l<1&&Math.floor(this.time*12)%2===0;e.fillStyle=u?"#ffd23f":l>2?"#c8894a":l>1?"#e06a2a":"#ff3a1a";const d=Math.round(t.x+t.facing*6-2),y=Math.round(t.y-3);e.fillRect(d,y+1,5,2),e.fillRect(d+1,y,3,4)}if(t.has("bees"))for(let l=0;l<7;l++){const u=this.time*(4+l*.7)+l*2.1,d=Math.round(t.x+Math.cos(u)*(6+l%3*3)),y=Math.round(t.y-6+Math.sin(u*1.3)*5);e.fillStyle="#ffd23f",e.fillRect(d,y,1,1),e.fillStyle="#120a1f",e.fillRect(d+1,y,1,1)}if(t.has("bubble")){const l=12+Math.round(Math.sin(this.time*6)*.6);e.globalAlpha=.15,e.fillStyle="#9fe8ff",e.fillRect(Math.round(t.x-l+3),Math.round(t.y-l+3),l*2-6,l*2-6),e.globalAlpha=.9;for(let u=0;u<56;u++){const d=u/56*Math.PI*2;e.fillStyle=u%9===0?"#ff9cf5":"#c8f4ff",e.fillRect(Math.round(t.x+Math.cos(d)*l),Math.round(t.y+Math.sin(d)*l),1,1)}e.fillStyle="#ffffff",e.fillRect(Math.round(t.x-6),Math.round(t.y-7),2,1),e.fillRect(Math.round(t.x-7),Math.round(t.y-6),1,2),e.globalAlpha=1}if(t.has("frozen")&&(e.globalAlpha=.55,e.fillStyle="#9fe8ff",e.fillRect(Math.round(t.x-8),Math.round(t.y-t.halfH-4),16,t.halfH*2+5),e.globalAlpha=1,e.fillStyle="#ffffff",e.fillRect(Math.round(t.x-6),Math.round(t.y-t.halfH-2),1,6),e.fillRect(Math.round(t.x-5),Math.round(t.y-t.halfH-2),3,1)),t.has("rain")){const l=a===1?t.y-t.halfH-22:t.y+t.halfH+12;E(e,V.cloud.slice(0,4),{a:"#9aa7c7"},t.x-7,l,{scale:2,flipY:s})}if(t.has("drunk")||t.has("stun")){const l=a===1?t.y-t.halfH-6:t.y+t.halfH+5;for(let u=0;u<3;u++){const d=this.time*5+u/3*Math.PI*2;e.fillStyle="#fff36b",e.fillRect(Math.round(t.x+Math.cos(d)*7),Math.round(l+Math.sin(d)*2),1,1)}}if(t.casting&&t.combo.length>0){const l=t.combo.length>at,u=t.combo.length*8-1,d=a===1?t.y-t.halfH-(t.has("bighead")?34:24):t.y+t.halfH+18;let y=t.x-u/2;y=Math.max(2,Math.min(p-u-2,y)),t.combo.forEach((R,b)=>{bt(e,R,y+b*8+1,d,l?"#ff5a5a":t.colors.band)})}}drawClownShoes(t,e,a){const s=this.ctx,n=Math.round(e===1?t.y+t.halfH-3:t.y-t.halfH),o=t.facing;s.fillStyle="#ff3a3a",s.fillRect(Math.round(a-4+(o===1?0:-6)),n,14,3),s.fillStyle="#ffd23f",s.fillRect(Math.round(a+o*7),e===1?n:n+2,2,1)}drawFamiliar(t){if(t.life<2&&Math.floor(t.life*8)%2===0)return;const e=et[t.kind];E(this.ctx,e.sprite,e.palette,t.x-3,t.y-3,{flipX:t.owner.facing===-1})}drawProjectile(t){const e=V[t.spell.icon]??V.orb,a=Math.round(Math.sin(this.time*20)*.6);E(this.ctx,e,{a:t.spell.colors[0],b:t.spell.colors[1],w:"#ffffff"},t.x-3,t.y-3+a,{flipX:t.vx<0})}drawHud(){const t=this.ctx;t.fillStyle="#120a1f",t.fillRect(0,0,p,20),t.fillStyle="#2b1c45",t.fillRect(0,19,p,1),this.wizards.forEach((e,a)=>{const s=a===0,n=s?4:p-4-100;S(t,e.name,s?4:p-4,2,e.colors.hat,{align:s?"left":"right"});for(let h=0;h<e.wins;h++){const l=s?4+F(e.name)+4+h*5:p-4-F(e.name)-8-h*5;t.fillStyle="#ffd23f",t.fillRect(l+1,2,1,5),t.fillRect(l,3,3,3)}t.fillStyle="#3a2350",t.fillRect(n,9,100,4);const o=Math.round(e.hp);t.fillStyle=e.hp>30?e.colors.hat:"#ff3a3a",t.fillRect(s?n:n+100-o,9,o,4),t.fillStyle="rgba(255,255,255,0.35)",t.fillRect(s?n:n+100-o,9,o,1);const f=[...e.status.keys()].map(h=>le[h]),c=f.slice(0,3).join(" ")+(f.length>3?" +":"");S(t,c,s?4:p-4,14,"#d8c8ff",{align:s?"left":"right",shadow:null})})}drawTitle(){const t=this.ctx;t.fillStyle="rgba(18,10,31,0.86)",t.fillRect(0,0,p,Y);const e=Math.round(Math.sin(this.time*3)*1.5);S(t,"WIZARD DUEL",p/2,8+e,"#ffd23f",{align:"center",scale:3}),S(t,"CAST SILLY SPELLS AT YOUR FRIEND!",p/2,28,"#d8c8ff",{align:"center"});const[a,s]=this.wizards,n=(f,c,h)=>{S(t,f.name,c,40,f.colors.hat,{scale:2}),h.forEach((l,u)=>S(t,l,c,54+u*8,"#ffffff"))};n(a,14,["MOVE: A D   JUMP: W","HOLD SPACE, TAP W A S D","LET GO OF SPACE TO CAST!"]),n(s,170,["MOVE: ARROWS  JUMP: UP","HOLD ENTER, TAP ARROWS","LET GO OF ENTER TO CAST!"]),[a,s].forEach((f,c)=>{const h=this.padConnected[c];S(t,h?"PAD READY! HOLD A TRIGGER":"OR PLUG IN A CONTROLLER",c===0?14:170,77,h?"#7dff9a":"#9a8fb0")}),S(t,"SPELL SCROLL",p/2,84,"#ffd23f",{align:"center"}),["hiccups","banana","knockback","fireball"].map(ye).forEach((f,c)=>{const h=c%2===0?30:176,l=94+Math.floor(c/2)*10;S(t,f.name,h,l+1,f.colors[0]),f.combo.forEach((u,d)=>bt(t,u,h+62+d*8,l,"#ffffff","#3a2350"))}),S(t,`${K.length} SPELLS HIDE IN THE RUNES (3 TO 6 TAPS).`,p/2,120,"#d8c8ff",{align:"center"}),S(t,"SHORT COMBOS ARE SILLY. LONG COMBOS ARE STRONG!",p/2,129,"#ffd23f",{align:"center"}),S(t,"UNKNOWN RUNES MAKE WILD MAGIC - IT MIGHT BACKFIRE!",p/2,138,"#ff9cf5",{align:"center"}),this.touchOnly&&S(t,"NEEDS A KEYBOARD OR GAME CONTROLLERS",p/2,148,"#ff9f5a",{align:"center"}),Math.floor(this.time*2)%2===0&&S(t,"PRESS SPACE, ENTER OR A TO DUEL!",p/2,160,"#ffffff",{align:"center"})}drawWinner(){if(!this.winner)return;const t=this.ctx;t.fillStyle="rgba(18,10,31,0.55)",t.fillRect(0,48,p,44),S(t,`${this.winner.name} WINS!`,p/2,56,this.winner.colors.hat,{align:"center",scale:3}),this.phaseTime>1.5&&Math.floor(this.time*2)%2===0&&S(t,"PRESS SPACE, ENTER OR A FOR A REMATCH",p/2,80,"#ffffff",{align:"center"})}}function ve(i,t,e){const a=F(t.toUpperCase(),e)/2;return Math.max(a+2,Math.min(p-a-2,i))}function Tt(){const i=["#ffd23f","#ff9cf5","#7fe0ff","#7dff6b","#ffffff"];return i[Math.floor(Math.random()*i.length)]}function Se(i){return i.filter((t,e)=>e%2===0).map(t=>t.split("").filter((e,a)=>a%2===0).join(""))}const Et=new WeakMap;function kt(i){let t=Et.get(i);return t||(t=Object.fromEntries(Object.keys(i).map(e=>[e,"#ffffff"])),Et.set(i,t)),t}function Te(){const i=document.createElement("canvas");i.width=p,i.height=Y;const t=i.getContext("2d"),e=["#1a0f33","#21123f","#2a174d","#33205a","#3d2866"],a=Math.ceil(O/e.length);e.forEach((f,c)=>{t.fillStyle=f,t.fillRect(0,c*a,p,a)});let s=7;const n=()=>(s=s*16807%2147483647,s/2147483647);for(let f=0;f<60;f++)t.fillStyle=n()<.2?"#ffd23f":"#c8b8ff",t.fillRect(Math.floor(n()*p),20+Math.floor(n()*90),1,1);t.fillStyle="#fff4c8",t.fillRect(250,32,14,14),t.fillRect(248,34,18,10),t.fillRect(252,30,10,18),t.fillStyle="#e8d890",t.fillRect(254,36,3,3),t.fillRect(259,41,2,2),t.fillStyle="#170d2b";const o=[[20,70,24],[70,96,16],[110,60,20],[190,80,22],[280,66,18]];for(const[f,c,h]of o){t.fillRect(f,c,h,O-c);for(let l=0;l<h;l+=4)t.fillRect(f+l,c-3,2,3)}t.fillRect(0,120,p,O-120),t.fillStyle="#ffd23f",t.fillRect(116,74,2,3),t.fillRect(198,92,2,3),t.fillRect(286,80,2,3),t.fillStyle="#2b1c45",t.fillRect(0,20,st,O-20),t.fillRect(Q,20,p-Q,O-20),t.fillStyle="#5a4320",t.fillRect(14,91,3,4),t.fillRect(p-17,91,3,4),t.fillStyle="#4a3a63",t.fillRect(0,O,p,Y-O),t.fillStyle="#6b5a85",t.fillRect(0,O,p,1),t.fillStyle="#2f2342";for(let f=0;f<3;f++){const c=O+1+f*6;t.fillRect(0,c+5,p,1);for(let h=f%2*8;h<p;h+=16)t.fillRect(h,c,1,5)}t.fillStyle="#2b1c45",t.fillRect(0,20,p,N-20),t.fillStyle="#4a3a63",t.fillRect(0,N-1,p,1);for(const f of Pt){t.fillStyle="#5c4a78",t.fillRect(f.x,f.y,f.w,U),t.fillStyle="#8a76a8",t.fillRect(f.x,f.y,f.w,1),t.fillStyle="#2f2342",t.fillRect(f.x,f.y+U-1,f.w,1);for(let c=f.x+6;c<f.x+f.w;c+=10)t.fillRect(c,f.y+1,1,4);t.fillStyle="#7dff6b",t.fillRect(f.x+3,f.y+U,1,2),t.fillRect(f.x+f.w-8,f.y+U,1,3)}return i}const Ee={moveX:[{axis:0},{button:14,scale:-1},{button:15}],moveY:[{axis:1},{button:12,scale:-1},{button:13}],aimX:[{axis:2}],aimY:[{axis:3}],jump:[{button:0}],fire:[{button:7}],pause:[{button:9}]};function At(i){const t=Object.create(null);for(const[e,a]of Object.entries(i))t[e]=a.map(s=>{const n="button"in s?s.button:s.axis;if(!Number.isInteger(n)||n<0||s.scale!==void 0&&![1,-1].includes(s.scale))throw new Error("Controller bindings need nonnegative integer indices and a scale of 1 or -1.");return{...s}});return t}const Ot=(i,t=-1)=>Number.isFinite(i)?Math.max(t,Math.min(1,i)):0;function ke(i=Ee,t={}){const e=At(i),a=t.deadZone??.18,s=t.buttonThreshold??.5;if(!Number.isFinite(a)||a<0||a>=1||!Number.isFinite(s)||s<=0||s>1)throw new Error("Use a dead zone from 0 up to (excluding) 1 and a button threshold above 0 through 1.");let n=new Map,o=!1,f=!1;const c=new Map,h=()=>{o=!1},l=typeof window<"u"?window:void 0;l==null||l.addEventListener("blur",h),l==null||l.document.addEventListener("visibilitychange",h);const u=t.active??(()=>!!l&&!l.document.hidden&&l.document.hasFocus()),d=t.read??(()=>{if(typeof navigator>"u"||!navigator.getGamepads)throw new Error("Gamepad API unavailable");return navigator.getGamepads()});function y(R){const b=[...n.keys()];return n.clear(),c.clear(),o=!1,{status:R,players:[],connected:[],disconnected:b}}return{poll(){var _;if(f)return y("closed");let R;try{R=Array.from(d()).filter(M=>!!(M!=null&&M.connected))}catch(M){return y(M instanceof Error&&M.name==="SecurityError"?"blocked":"unavailable")}const b=u(),g=new Map,D=[],C=[];for(const M of R){const k=n.get(M.index),L=(k==null?void 0:k.id)===M.id&&k.mapping===M.mapping;L||(D.push(M.index),c.delete(M.index));const v=c.get(M.index),T=M.mapping==="standard"||(v==null?void 0:v.id)===M.id,A=(v==null?void 0:v.bindings)??e,H=M.axes.map(I=>b?Ot(I):0),j=M.buttons.map(I=>b?Ot(I.value,0):0),P=Object.create(null);for(const[I,Bt]of Object.entries(A)){let X=0,Z=0;if(b&&T)for(const $ of Bt){let q="button"in $?j[$.button]??0:H[$.axis]??0;"axis"in $&&(q=Math.sign(q)*Math.max(0,(Math.abs(q)-a)/(1-a))),q*=$.scale??1,X=Math.max(X,q),Z=Math.min(Z,q)}const ut=X===-Z?0:X>-Z?X:Z,ot=Math.abs(ut)>=s,pt=L&&!!((_=k.actions[I])!=null&&_.down);P[I]={value:ut,down:ot,pressed:b&&o&&L&&ot&&!pt,released:pt&&!ot}}g.set(M.index,{index:M.index,id:M.id,mapping:M.mapping,mapped:T,axes:H,buttons:j,actions:P})}for(const[M,k]of n){const L=g.get(M);(!L||L.id!==k.id||L.mapping!==k.mapping)&&(C.push(M),L||c.delete(M))}return n=g,o=b,{status:b?g.size?"ready":"waiting":"inactive",players:structuredClone([...g.values()]),connected:D,disconnected:C}},remap(R,b){const g=n.get(R);if(!g||f)throw new Error("Connect and poll this controller before mapping it.");c.set(R,{id:g.id,bindings:At(b)}),o=!1},reset:h,dispose(){f=!0,y("closed"),l==null||l.removeEventListener("blur",h),l==null||l.document.removeEventListener("visibilitychange",h)}}}const Ae={left:[{axis:0,scale:-1},{button:14}],right:[{axis:0},{button:15}],up:[{axis:1,scale:-1},{button:12}],down:[{axis:1},{button:13}],jump:[{button:0}],cast:[{button:4},{button:5},{button:6},{button:7}],start:[{button:9}]},Oe=["left","right","up","down"],He={left:"Left",right:"Right",up:"Up",down:"Down",cast:"Cast"},Ge=.5,Ce=.3;class Le{constructor(t){m(this,"slots",[null,null]);this.target=t}update(t){for(const e of t.disconnected){const a=this.slots.findIndex(s=>(s==null?void 0:s.index)===e);a>=0&&this.unplug(a)}for(const e of t.players){let a=this.slots.findIndex(s=>(s==null?void 0:s.index)===e.index);if(a<0){if(a=this.slots.indexOf(null),a<0)continue;this.slots[a]={index:e.index,dir:null,held:new Set},this.target.padConnected[a]=!0}this.drive(a,this.slots[a],e.actions)}}drive(t,e,a){var f,c,h,l,u;const s=d=>{var y;return Math.max(0,((y=a[d])==null?void 0:y.value)??0)};e.dir&&s(e.dir)<Ce&&(e.dir=null);let n=null;for(const d of Oe)s(d)>=Ge&&(!n||s(d)>s(n))&&(n=d);n&&(!e.dir||s(n)>s(e.dir))&&(e.dir=n);const o=new Set;e.dir&&o.add(this.code(t,e.dir)),(f=a.jump)!=null&&f.down&&o.add(this.code(t,"up")),(c=a.cast)!=null&&c.down&&o.add(this.code(t,"cast"));for(const d of e.held)o.has(d)||this.target.keyUp(d);for(const d of o)e.held.has(d)||this.target.keyDown(d,!1);e.held=o,(h=a.start)!=null&&h.pressed&&this.target.pausePressed(),((l=a.jump)!=null&&l.pressed||(u=a.start)!=null&&u.pressed)&&this.target.pointerStart()}unplug(t){const e=this.slots[t];this.target.padUnplugged(t);for(const a of e.held)this.target.keyUp(a);this.slots[t]=null,this.target.padConnected[t]=!1}code(t,e){return`Pad${t+1}${He[e]}`}}const nt=dt("#stage"),ht=dt("#game"),Pe=dt("#tooSmall");function dt(i){const t=document.querySelector(i);if(!t)throw new Error(`Missing required element: ${i}`);return t}const G=new Re(ht);G.touchOnly=matchMedia("(pointer: coarse)").matches&&!matchMedia("(any-pointer: fine)").matches;G.screenShake=!matchMedia("(prefers-reduced-motion: reduce)").matches;function Nt(){const i=nt.clientWidth,t=nt.clientHeight,e=Math.min(i/p,t/Y),a=e>=1?Math.floor(e):e;ht.style.width=`${Math.floor(p*a)}px`,ht.style.height=`${Math.floor(Y*a)}px`,Pe.hidden=i>=300&&t>=160}new ResizeObserver(Nt).observe(nt);Nt();window.addEventListener("keydown",i=>{Dt.has(i.code)&&(i.preventDefault(),G.keyDown(i.code,i.repeat))});window.addEventListener("keyup",i=>{Dt.has(i.code)&&(i.preventDefault(),G.keyUp(i.code))});window.addEventListener("blur",()=>G.releaseAll());document.addEventListener("visibilitychange",()=>{document.hidden&&G.releaseAll()});nt.addEventListener("pointerdown",()=>{window.focus(),G.pointerStart()});const Ie=ke(Ae),We=new Le(G);G.beforeUpdate=()=>We.update(Ie.poll());qt(Yt)&&Qt(i=>{ie(i.sound!==!1),G.screenShake=i.screenShake!==!1&&!matchMedia("(prefers-reduced-motion: reduce)").matches});G.start();
</script>
    <style>:root{--night: #120a1f;--gold: #ffd23f;--lilac: #d8c8ff;color-scheme:dark;font:14px/1.3 ui-monospace,Courier New,monospace}*{box-sizing:border-box}:where(html,body,body *){-webkit-user-select:none;user-select:none}html,body,#app{height:100%;margin:0}body{background:var(--night);color:var(--lilac);overflow:hidden}.stage{position:relative;display:grid;place-items:center;width:100%;height:100%}.game{display:block;image-rendering:pixelated;image-rendering:crisp-edges;outline:none}.too-small{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-items:center;padding:8px;text-align:center;background:color-mix(in srgb,var(--night) 88%,transparent);color:var(--gold);font-weight:700}.too-small[hidden]{display:none}
</style>
    <meta name="napplet-type" content="wizard-duel">
    <meta name="napplet-config-schema" content="{&quot;$schema&quot;:&quot;http://json-schema.org/draft-07/schema#&quot;,&quot;$version&quot;:1,&quot;type&quot;:&quot;object&quot;,&quot;additionalProperties&quot;:false,&quot;properties&quot;:{&quot;sound&quot;:{&quot;type&quot;:&quot;boolean&quot;,&quot;title&quot;:&quot;Sound effects&quot;,&quot;description&quot;:&quot;Play the zaps, boings, clucks and nose whistles.&quot;,&quot;default&quot;:true,&quot;x-napplet-section&quot;:&quot;Game&quot;,&quot;x-napplet-order&quot;:1},&quot;screenShake&quot;:{&quot;type&quot;:&quot;boolean&quot;,&quot;title&quot;:&quot;Screen shake&quot;,&quot;description&quot;:&quot;Shake the arena when big spells hit.&quot;,&quot;default&quot;:true,&quot;x-napplet-section&quot;:&quot;Game&quot;,&quot;x-napplet-order&quot;:2}}}">
  </head>
  <body>
    <main id="app" class="app">
      <div id="stage" class="stage">
        <canvas
          id="game"
          class="game"
          tabindex="0"
          aria-label="Wizard Duel arena. Player one: A and D to move, W to jump, hold Space and tap W A S D to cast. Player two: arrow keys to move and jump, hold Enter and tap arrows to cast."
        ></canvas>
        <div id="tooSmall" class="too-small" hidden>Make the window bigger to duel!</div>
      </div>
    </main>
  </body>
</html>