SOURCE / PINNED RELEASE
Made of little things.
Pünktlich!
- Release
- 9dc6c97948c0…
- Author-recorded commit
- aebcea49a666…
- License
- Check the original source
- Author’s source reference
- nostr://npub1sj2vketepqw9v6fmge5fdzuj7atj9sv6qv2jss05n2z6rl64lcfqumdsnt/wss%3A%2F%2Fgit.napplet.soy%2F/n-bf1ae466031
The hash-verified playable HTML, which may contain bundled or minified code. This view does not execute it.
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pünktlich!</title>
<script type="module" crossorigin>function ve(){const e=new Map;function n(a,r){if(e.has(a))throw new Error(`NAP domain "${a}" is already registered`);e.set(a,r)}function l(a){const r=a.type.indexOf(".");if(r<=0)return!1;const f=a.type.slice(0,r),u=e.get(f);return u?(u(a),!0):!1}function i(){return Array.from(e.keys())}return{registerNap:n,dispatch:l,getRegisteredDomains:i}}var Ee=ve(),fe=Ee.registerNap,Q="storage";fe(Q,e=>{});var ce="keys";fe(ce,e=>{});function Pe(){const e=window;if(!e.napplet)throw new Error("window.napplet is unavailable -- runtime did not inject the napplet namespace");return e.napplet}function d(e){const l=Pe()[e];if(!l)throw new Error(`window.napplet.${String(e)} is unavailable -- runtime did not inject this domain`);return l}var de={getItem(e){return d("storage").getItem(e)},setItem(e,n){return d("storage").setItem(e,n)},removeItem(e){return d("storage").removeItem(e)},keys(){return d("storage").keys()},instance:{getItem(e){return d("storage").instance.getItem(e)},setItem(e,n){return d("storage").instance.setItem(e,n)},removeItem(e){return d("storage").instance.removeItem(e)},keys(){return d("storage").instance.keys()}}},Te={registerAction(e){return d("keys").registerAction(e)},unregisterAction(e){d("keys").unregisterAction(e)},onAction(e,n){return d("keys").onAction(e,n)},async register(e,n){const l=d("keys"),i=await l.registerAction(e),a=l.onAction(e.id,n);return{...i,close(){a.close(),l.unregisterAction(e.id)}}}},_={get(){return d("config").get()},subscribe(e){return d("config").subscribe(e)},openSettings(e){d("config").openSettings(e)},registerSchema(e,n){return d("config").registerSchema(e,n)},onSchemaError(e){return d("config").onSchemaError(e)},get schema(){return d("config").schema}};function Ae(e,n){return!!(e!=null&&e[n])}function K(e){return Ae(window.napplet,e)}const Ne="http://json-schema.org/draft-07/schema#",Le=2,Ie="object",Be=!1,De={timeLimit:{type:"integer",title:"Seconds until the train leaves",description:"Lower is harder. Takes effect on the next run.",minimum:45,maximum:90,default:60,"x-napplet-section":"Gameplay","x-napplet-order":1}},$e={$schema:Ne,$version:Le,type:Ie,additionalProperties:Be,properties:De},ue={timeLimit:60};K("config")&&(async()=>{_.schema||await _.registerSchema($e);const e=_.subscribe(n=>{const l=n.timeLimit;typeof l=="number"&&l>=45&&l<=90&&(ue.timeLimit=l)});window.addEventListener("pagehide",()=>e.close(),{once:!0})})().catch(()=>{});const je=280,q=new Map;let V=null;function ze(e,n){const l=new Map,i=new Map,a=r=>{const f=V;if(f){r.onPress(f.repeat),r.onRelease&&l.set(r.id,f.code);return}r.onPress(!1),r.onRelease&&(window.clearTimeout(i.get(r.id)),i.set(r.id,window.setTimeout(r.onRelease,je)))};if(window.addEventListener("keydown",r=>{V=r},!0),window.addEventListener("keydown",r=>{if(V=null,r.defaultPrevented)return;const f=e.find(u=>!q.has(u.id)&&u.fallbackCodes.includes(r.code));f&&(r.preventDefault(),f.onPress(r.repeat),f.onRelease&&l.set(f.id,r.code))}),window.addEventListener("keyup",r=>{var f,u;for(const[k,B]of l)B===r.code&&(l.delete(k),(u=(f=e.find(O=>O.id===k))==null?void 0:f.onRelease)==null||u.call(f))}),window.addEventListener("blur",()=>{var r,f;for(const u of l.keys())(f=(r=e.find(k=>k.id===u))==null?void 0:r.onRelease)==null||f.call(r);l.clear()}),!!K(ce))for(const r of e)Te.register({id:r.id,label:r.label,defaultKey:r.defaultKey},()=>a(r)).then(f=>{f.binding&&(q.set(r.id,f.binding),n(),window.addEventListener("pagehide",()=>f.close(),{once:!0}))}).catch(()=>{})}function G(e){const n=q.get(e.id);return n?n===" "?"Leertaste":n:e.fallbackLabel}const m=p("#scene"),t=m.getContext("2d"),h={overlay:p("#overlay"),kicker:p("#cardKicker"),title:p("#cardTitle"),body:p("#cardBody"),actions:p("#cardActions"),hint:p("#cardHint"),ordnungFill:p("#ordnungFill"),ordnungValue:p("#ordnungValue"),walk:p("#walkButton"),jump:p("#jumpButton")};function p(e){const n=document.querySelector(e);if(!n)throw new Error(`Missing required element: ${e}`);return n}const j=300,s=246,I=7400,Ce=230,Ge=1900,me="best-score";let P=1,g=600,D=0,x=1,T=600;function he(){x=window.devicePixelRatio||1;const e=m.clientWidth,n=m.clientHeight;T=e,m.width=Math.max(1,Math.round(e*x)),m.height=Math.max(1,Math.round(n*x)),P=Math.min(m.height/j,m.width/420),g=m.width/P,D=j-m.height/P}new ResizeObserver(he).observe(m);he();const W=[{id:"papier",label:"Papier",sub:"blau",color:"#1d4fa8"},{id:"gelb",label:"Gelber Sack",sub:"Verpackung",color:"#d9a400"},{id:"glas",label:"Glas",sub:"grün/weiß/braun",color:"#2f7d3a"},{id:"bio",label:"Bio",sub:"braun",color:"#6b4423"},{id:"rest",label:"Restmüll",sub:"schwarz",color:"#333333"},{id:"pfand",label:"Pfand",sub:"zum Automaten",color:"#8a2be2"}],Ke=[{name:"Alte Zeitung",emoji:"📰",bin:"papier",wrong:"Papier ist PAPIER, Klaus."},{name:"Joghurtbecher (ausgespült!)",emoji:"🥛",bin:"gelb",wrong:"Verpackung! Gelber Sack!"},{name:"Weinflasche (grün)",emoji:"🍾",bin:"glas",wrong:"Glas. Nach Farbe. Immer."},{name:"Bananenschale",emoji:"🍌",bin:"bio",wrong:"Das ist BIO. Schäm dich."},{name:"Fettiger Pizzakarton",emoji:"🍕",bin:"rest",wrong:"Fettig = Restmüll! Jeder weiß das!"},{name:"Bierflasche mit Pfand",emoji:"🍺",bin:"pfand",wrong:"8 Cent PFAND! Wegwerfen?! NEIN!"},{name:"Eierschalen",emoji:"🥚",bin:"bio",wrong:"Eierschalen sind Bio!"},{name:"Staubsaugerbeutel",emoji:"🧹",bin:"rest",wrong:"Staubsaugerbeutel: Restmüll."},{name:"Müslikarton",emoji:"📦",bin:"papier",wrong:"Karton ist Papier!"},{name:"Alufolie",emoji:"🥡",bin:"gelb",wrong:"Alu gehört in den Gelben Sack!"}];function We(e){const n=[...e];for(let l=n.length-1;l>0;l--){const i=Math.floor(Math.random()*(l+1));[n[l],n[i]]=[n[i],n[l]]}return n}function He(){const e=We(Ke),n=(r,f)=>{const u={gnome:[22,34],crate:[34,30],dackel:[46,22]}[r];return{kind:r,x:f,home:f,w:u[0],h:u[1],vx:r==="dackel"?-45:0}},l=r=>({kind:"trash",x:r,done:!1,item:e.pop()}),i=r=>({kind:"ampel",x:r,phase:"green",t:0,triggered:!1,ran:!1}),a=(r,f,u)=>({kind:r,x:f,y:u,taken:!1});return[n("gnome",520),l(900),a("brezel",1250,70),i(1550),n("dackel",2100),a("wurst",2350,20),l(2650),n("crate",3e3),n("crate",3260),{kind:"amt",x:3600,done:!1},{kind:"ruhe",x:3950,end:4550},i(4800),a("mass",5150,20),n("gnome",5400),l(5700),n("dackel",6150),a("brezel",6350,90),l(6550),n("crate",6900)]}const o={x:80,y:0,vy:0,stumble:0,safe:0,wurst:0,mass:0,walkPhase:0};let A=[],c="title",v=60,ie=60,y=100,S=0,N=[],ae=performance.now(),Z=0,$=!1;const b={walk:!1,jumpQueued:!1};function z(e){y=Math.max(0,Math.min(100,e)),h.ordnungFill.style.width=`${y}%`,h.ordnungValue.textContent=String(Math.round(y))}function L(e,n="#dd0000"){N.push({text:e,x:o.x,y:s-90,t:1.8,color:n})}function C(e,n){z(y-e),L(`${n} −${e}`)}function ge(){ie=ue.timeLimit,v=ie,Object.assign(o,{x:80,y:0,vy:0,stumble:0,safe:0,wurst:0,mass:0,walkPhase:0}),A=He(),N=[],$=!1,z(100),Y(),c="play"}function H(e){var n;h.kicker.textContent=e.kicker??"",h.title.textContent=e.title,h.body.textContent=e.body??"",h.hint.textContent=e.hint??"",h.actions.replaceChildren(...e.actions.map(l=>{const i=document.createElement("button");if(i.type="button",i.className=l.className??"",l.color&&(i.style.background=l.color),i.textContent=l.label,l.sub){const a=document.createElement("small");a.textContent=l.sub,i.append(a)}return i.addEventListener("click",()=>l.onPick(i)),i})),h.overlay.hidden=!1,(n=h.actions.querySelector("button"))==null||n.focus({preventScroll:!0})}function Y(){h.overlay.hidden=!0,m.focus({preventScroll:!0})}function J(){c="title",H({kicker:"Formular P-7 · Pendlerantrag",title:"Pünktlich!",body:`Dein Zug fährt um 7:00:00 Uhr. Nicht 7:00:01.
Trenne den Müll. Warte an jeder roten Ampel. Stempel alles.
Verliere keine ORDNUNG.`,hint:`Halten: ${G(pe)} / GEHEN · Springen: ${G(ee)} · Müll: Tasten ${we()}${S?` · Rekord: ${S}`:""}`,actions:[{label:"Los geht’s",className:"primary",onPick:ge}]})}function Oe(e){c="bin",b.walk=!1;const n=e.item;H({kicker:"Mülltrennung · Pflicht",title:`${n.emoji} ${n.name}`,body:"Wohin damit? Die Nachbarn schauen zu.",hint:`Tasten ${we()}. Die Uhr läuft weiter.`,actions:W.map(l=>({label:l.label,sub:l.sub,className:"bin",color:l.color,onPick:i=>be(e,l.id,i)}))})}function be(e,n,l){c==="bin"&&(n===e.item.bin?(e.done=!0,z(y+2),L("Richtig getrennt. Gut.","#1f7a3a"),Y(),c="play"):(C(10,e.item.wrong),h.body.textContent=`FALSCH! ${e.item.wrong}`,l&&(l.classList.remove("shake"),l.offsetWidth,l.classList.add("shake"))))}const re=["Stempel 1 von 3","Stempel 2 von 3","Stempel 3 von 3","Unterschreiben","Durchschlag abgeben"];function Fe(e){c="amt",b.walk=!1;let n=0;const l=()=>{H({kicker:"Bürgeramt · Schalter 3 von 47",title:"Formular A38",body:`Ohne Passierschein A38 darf niemand zum Bahnhof.
In dreifacher Ausfertigung, bitte.`,hint:`${G(ee)} oder Enter drückt den Knopf.`,actions:[{label:re[n],className:"primary",onPick:()=>{n+=1,n>=re.length?(e.done=!0,L("Genehmigt. Nächstes Mal online? Nein.","#1f7a3a"),Y(),c="play"):l()}}]})};l()}async function se(e){c="end",$=e,Z=0,b.walk=!1;const n=Math.max(0,v),l=e?Math.round(y*10+n*20):0,i=e?y>=90?"Sehr gut. Akzeptabel.":y>=70?"Befriedigend. Gerade so.":y>=40?"Mangelhaft. Wir sind enttäuscht.":"Chaos! Bist du Italiener?":"Ungenügend";let a="";l>S&&(S=l,a=" · NEUER REKORD",Ve()),H({kicker:e?"Ankunft · Gleis 7":"Abfahrt · 7:00:00",title:e?"Pünktlich!":"NEIN!",body:e?`Du bist ${n.toFixed(1)} Sekunden zu früh. Gut.
Note: ${i}
Durchsage: „Der ICE 700 hat heute ca. 45 Minuten Verspätung.
Grund: Verzögerungen im Betriebsablauf.“
Du hättest nicht rennen müssen.`:`Der Zug ist weg. Um exakt 7:00:00.
Ausgerechnet heute war die Deutsche Bahn pünktlich.`,hint:`Punkte: ${l} · Rekord: ${S}${a}`,actions:[{label:"Nochmal",className:"primary",onPick:ge}]})}async function _e(){if(K(Q))try{const e=await de.getItem(me),n=Number(e);Number.isFinite(n)&&n>S&&(S=n,c==="title"&&J())}catch{}}async function Ve(){if(K(Q))try{await de.setItem(me,String(S))}catch{}}const U=22,Ue=58;function qe(e){for(const i of N)i.t-=e,i.y-=22*e;if(N=N.filter(i=>i.t>0),c==="end"){Z+=e;return}if(c==="title")return;if(v-=e,v<=0){v=0,se(!1);return}for(const i of A)i.kind==="dackel"&&(i.x+=i.vx*e,i.x<i.home-110&&(i.vx=Math.abs(i.vx)),i.x>i.home&&(i.vx=-Math.abs(i.vx))),i.kind==="ampel"&&Qe(i,e);if(c!=="play")return;o.wurst=Math.max(0,o.wurst-e),o.mass=Math.max(0,o.mass-e),o.safe=Math.max(0,o.safe-e);const n=o.x;if(o.stumble>0)o.stumble-=e;else if(b.walk){const i=Ce*(o.wurst>0?1.6:1);o.x+=i*e,o.walkPhase+=e*(o.wurst>0?16:10)}const l=o.y<=0;b.jumpQueued&&l&&o.stumble<=0&&(o.vy=o.mass>0?820:640,A.find(a=>a.kind==="ruhe"&&o.x>a.x&&o.x<a.end)&&C(15,"PSST! Ruhezone! Nachbar ruft Polizei.")),b.jumpQueued=!1,o.vy-=Ge*e,o.y=Math.max(0,o.y+o.vy*e),o.y===0&&(o.vy=0);for(const i of A)switch(i.kind){case"gnome":case"crate":case"dackel":o.safe<=0&&oe(o.x,U,i.x,i.w)&&o.y<i.h&&(o.stumble=.6,o.safe=1.6,o.x=Math.min(o.x,i.x-U/2-i.w/2-20),C(5,`${{gnome:"Gartenzwerg",crate:"Bierkasten",dackel:"Dackel"}[i.kind]} angerempelt!`));break;case"wurst":case"brezel":case"mass":!i.taken&&oe(o.x,U,i.x,24)&&Math.abs(o.y+Ue/2-i.y-12)<40&&(i.taken=!0,i.kind==="wurst"&&(o.wurst=4,L("Bratwurst! Schneller!","#1f7a3a")),i.kind==="mass"&&(o.mass=7,L("Eine Maß! Höher springen!","#1f7a3a")),i.kind==="brezel"&&(z(y+8),L("Brezel! +8 Ordnung","#1f7a3a")));break;case"trash":if(!i.done&&o.x>=i.x){o.x=i.x,Oe(i);return}break;case"amt":if(!i.done&&o.x>=i.x){o.x=i.x,Fe(i);return}break;case"ampel":!i.ran&&n<i.x&&o.x>=i.x&&i.phase==="red"&&(i.ran=!0,C(25,"BEI ROT! Denk an die Kinder!"));break}o.x>=I&&(o.x=I,se(!0))}function oe(e,n,l,i){return Math.abs(e-l)<(n+i)/2}function Qe(e,n){!e.triggered&&o.x>e.x-260&&(e.triggered=!0,e.phase="yellow",e.t=.7),e.phase!=="green"&&(e.t-=n,!(e.t>0)&&(e.phase==="yellow"?(e.phase="red",e.t=3.2):e.phase="green"))}let w=0;function Ze(){t.setTransform(P,0,0,P,0,-D*P);const e=Math.min(o.x-g*.3,I+260-g);w=Math.max(0,e),Ye(),t.save(),t.translate(-w,0),et();for(const n of A)Je(n);tt();for(const n of N)t.globalAlpha=Math.min(1,n.t),X(n.text,n.x,n.y,n.color);t.globalAlpha=1,t.restore(),t.setTransform(x,0,0,x,0,0),nt(),lt()}function Ye(){const e=t.createLinearGradient(0,D,0,s);e.addColorStop(0,"#7fb4d8"),e.addColorStop(1,"#e8dfc9"),t.fillStyle=e,t.fillRect(0,D,g,j-D);const n=w*.35;for(let i=Math.floor(n/90)-1;i<(n+g)/90+1;i++){const a=i*90-n,r=70+i*37%5*10,f=s-30-r;t.fillStyle="#efe6d2",t.fillRect(a,f,80,r),t.strokeStyle="#5a3a22",t.lineWidth=3,t.strokeRect(a,f,80,r),t.beginPath(),t.moveTo(a,f+r/2),t.lineTo(a+80,f+r/2),t.moveTo(a,f),t.lineTo(a+40,f+r/2),t.lineTo(a+80,f),t.moveTo(a+40,f+r/2),t.lineTo(a+40,f+r),t.stroke(),t.fillStyle="#a83a2a",t.beginPath(),t.moveTo(a-6,f),t.lineTo(a+40,f-34),t.lineTo(a+86,f),t.fill(),t.fillStyle="#5b86a8",t.fillRect(a+12,f+10,14,14),t.fillRect(a+54,f+10,14,14)}t.fillStyle="#b9b3a7",t.fillRect(0,s-30,g,30),t.fillStyle="#7d776c",t.fillRect(0,s,g,j-s),t.fillStyle="#6a645a";const l=w%24;for(let i=0;i<3;i++)for(let a=-l-i%2*12;a<g;a+=24)t.fillRect(a+2,s+6+i*16,20,12);t.fillStyle="#ffffff",t.fillRect(0,s,g,3)}function X(e,n,l,i){t.font='700 13px "DIN Alternate", "Bahnschrift", "Arial Narrow", sans-serif';const a=t.measureText(e).width+12,r=Math.max(w+4,Math.min(n-a/2,w+g-a-4));t.fillStyle="#f5f1e6",t.fillRect(r,l-14,a,20),t.strokeStyle=i,t.lineWidth=2,t.strokeRect(r,l-14,a,20),t.fillStyle=i,t.fillText(e,r+6,l+1)}function E(e,n,l,i,a="#fff"){t.font='800 11px "DIN Alternate", "Bahnschrift", "Arial Narrow", sans-serif';const r=t.measureText(e).width+10;t.fillStyle=i,t.fillRect(n-r/2,l-12,r,16),t.fillStyle=a,t.fillText(e,n-r/2+5,l)}function Je(e){if(!(e.x<w-300||e.x>w+g+300))switch(e.kind){case"gnome":{const n=e.x-e.w/2;t.fillStyle="#2f6fd0",t.fillRect(n+3,s-18,16,18),t.fillStyle="#f2c9a0",t.fillRect(n+5,s-26,12,9),t.fillStyle="#ffffff",t.fillRect(n+5,s-20,12,6),t.fillStyle="#dd0000",t.beginPath(),t.moveTo(n+2,s-25),t.lineTo(n+11,s-40),t.lineTo(n+20,s-25),t.fill();break}case"crate":{const n=e.x-e.w/2;t.fillStyle="#8a5a2b",t.fillRect(n,s-e.h,e.w,e.h),t.fillStyle="#5a3a1b",t.fillRect(n+4,s-e.h+4,e.w-8,6);for(let l=0;l<4;l++)t.fillStyle="#2f5d24",t.fillRect(n+4+l*7,s-e.h-10,5,12);E("BIER",e.x,s-8,"#5a3a1b","#ffcc00");break}case"dackel":{const n=e.x-e.w/2,l=Math.sin(performance.now()/80)*2;t.save(),e.vx>0&&(t.translate(e.x*2,0),t.scale(-1,1)),t.fillStyle="#7a3e14",t.fillRect(n+6,s-16,34,10),t.fillRect(n,s-22,12,10),t.fillRect(n-6,s-18,6,5),t.fillRect(n+8,s-6,4,6+l),t.fillRect(n+32,s-6,4,6-l),t.fillRect(n+40,s-18,8,3),t.fillStyle="#4a2408",t.fillRect(n+6,s-22,4,9),t.restore();break}case"trash":{W.slice(0,5).forEach((n,l)=>{const i=e.x+24+l*22;t.fillStyle=n.color,t.fillRect(i,s-32,18,32),t.fillStyle="#111",t.fillRect(i-1,s-35,20,4)}),e.done||(t.font="22px sans-serif",t.fillText(e.item.emoji,e.x-4,s-4));break}case"ampel":{t.fillStyle="#ffffff";for(let l=0;l<5;l++)t.fillRect(e.x+16+l*14,s,8,j-s);t.fillStyle="#333",t.fillRect(e.x-3,s-120,6,120),t.fillStyle="#111",t.fillRect(e.x-12,s-160,24,46);const n=e.phase!=="green";t.fillStyle=n?"#ff2a2a":"#3a1010",t.fillRect(e.x-8,s-156,16,18),t.fillStyle=n?"#3a1010":"#2aff6a",t.fillRect(e.x-8,s-136,16,18),t.fillStyle="#111",n?t.fillRect(e.x-3,s-152,6,10):(t.fillRect(e.x-4,s-132,3,10),t.fillRect(e.x+1,s-130,3,8)),Xe(e.x-46,n&&o.x>e.x-200&&o.x<e.x+200),n&&o.x>e.x-260&&o.x<e.x&&X("ROT. WARTEN.",e.x,s-176,"#dd0000");break}case"amt":{t.fillStyle="#d8d2c4",t.fillRect(e.x-20,s-140,150,140),t.fillStyle="#9b9486";for(let n=0;n<3;n++)for(let l=0;l<4;l++)t.fillRect(e.x-8+l*34,s-128+n*34,22,22);t.fillStyle="#5a3a22",t.fillRect(e.x-4,s-40,26,40),E("BÜRGERAMT",e.x+55,s-146,"#1d4fa8");break}case"ruhe":{t.fillStyle="rgba(29, 79, 168, 0.12)",t.fillRect(e.x,s-200,e.end-e.x,200),E("RUHEZONE · NICHT SPRINGEN",e.x+110,s-180,"#1d4fa8"),E("ENDE RUHEZONE",e.end-50,s-180,"#555"),t.fillStyle="#efe6d2",t.fillRect(e.x+220,s-120,110,90),t.fillStyle="#ffd86b",t.fillRect(e.x+240,s-105,26,22),t.fillStyle="#111",t.font="700 14px sans-serif";const n=performance.now()/400%3;t.fillText("z".repeat(1+Math.floor(n)),e.x+270,s-110);break}case"wurst":case"brezel":case"mass":{if(e.taken)break;const n=s-e.y-24+Math.sin(performance.now()/250+e.x)*3;t.font="24px sans-serif",t.fillText({wurst:"🌭",brezel:"🥨",mass:"🍺"}[e.kind],e.x-12,n+22);break}}}function Xe(e,n){t.fillStyle="#7a5c8a",t.fillRect(e-8,s-38,16,38),t.fillStyle="#f2c9a0",t.fillRect(e-6,s-50,12,12),t.fillStyle="#cfcfcf",t.fillRect(e-7,s-54,14,6),t.fillStyle="#5a3a22",t.fillRect(e+9,s-30,6,10),n&&X("Kinder schauen zu!",e,s-64,"#7a5c8a")}function et(){const e=I;if(!(e<w-400||e>w+g+400)&&(t.fillStyle="#c9c2b3",t.fillRect(e-60,s-180,320,180),t.fillStyle="#a63a2a",t.fillRect(e-70,s-196,340,18),E("HAUPTBAHNHOF",e+100,s-160,"#1d4fa8"),t.fillStyle="#0d2a5a",t.fillRect(e+10,s-140,180,46),t.font="700 11px ui-monospace, monospace",t.fillStyle="#ffffff",t.fillText("07:00 ICE 700 Gleis 7",e+16,s-124),t.fillStyle=$||c==="end"?"#ffcc00":"#ffffff",t.fillText($?"ca. +45 Min":"pünktlich",e+16,s-106),c==="end"&&!$)){const n=e+40+Z*260;t.fillStyle="#ffffff",t.fillRect(n,s-60,260,46),t.fillStyle="#dd0000",t.fillRect(n,s-30,260,5),t.fillStyle="#223";for(let l=0;l<7;l++)t.fillRect(n+14+l*34,s-52,24,14)}}function tt(){const e=o.x,n=s-o.y,l=o.stumble>0?0:Math.sin(o.walkPhase)*6;t.save(),o.safe>0&&Math.floor(o.safe*10)%2&&(t.globalAlpha=.5),o.stumble>0&&(t.translate(e,n),t.rotate(-.4),t.translate(-e,-n));for(const i of[-1,1]){const a=e-4+i*4+l*i*.6;t.fillStyle="#f2c9a0",t.fillRect(a-2,n-24,5,10),t.fillStyle="#ffffff",t.fillRect(a-2,n-14,5,11),t.fillStyle="#6b4423",t.fillRect(a-3,n-3,9,3)}t.fillStyle="#6b4423",t.fillRect(e-10,n-34,20,12),t.fillStyle="#e8e8e8",t.fillRect(e-10,n-50,20,16),t.fillStyle="#6b4423",t.fillRect(e-7,n-50,3,16),t.fillRect(e+4,n-50,3,16),t.fillRect(e-7,n-44,14,3),t.fillStyle="#f2c9a0",t.fillRect(e-14,n-48-l*.3,4,14),t.fillRect(e+10,n-48+l*.3,4,14),o.mass>0&&(t.fillStyle="#ffcc00",t.fillRect(e+12,n-44,9,12),t.fillStyle="#ffffff",t.fillRect(e+12,n-47,9,4)),t.fillStyle="#f2c9a0",t.fillRect(e-7,n-62,14,12),t.fillStyle="#5a3a1b",t.fillRect(e-5,n-54,12,3),t.fillStyle="#111",t.fillRect(e+2,n-59,2,2),t.fillStyle="#2f5d24",t.fillRect(e-10,n-64,20,3),t.fillRect(e-6,n-70,12,7),t.fillStyle="#dd0000",t.fillRect(e-6,n-66,12,2),t.strokeStyle="#ffffff",t.lineWidth=2,t.beginPath(),t.moveTo(e-5,n-68),t.lineTo(e-12,n-80),t.stroke(),t.restore(),o.wurst>0&&E("WURST-POWER",e,n-88,"#dd0000")}function nt(){const e=m.height/x<240||T<300,n=e?18:m.height/x>700?44:30,l=T-n-10,i=n+8,a=7*3600-v,r=a%60,f=a/60%60,u=a/3600%12;t.fillStyle="#ffffff",t.strokeStyle="#111",t.lineWidth=3,t.beginPath(),t.arc(l,i,n,0,Math.PI*2),t.fill(),t.stroke();for(let R=0;R<12;R++){const M=R/12*Math.PI*2;t.lineWidth=R%3===0?4:2,t.beginPath(),t.moveTo(l+Math.sin(M)*n*.72,i-Math.cos(M)*n*.72),t.lineTo(l+Math.sin(M)*n*.9,i-Math.cos(M)*n*.9),t.stroke()}const k=(R,M,Re,Me)=>{t.strokeStyle=Me,t.lineWidth=Re,t.beginPath(),t.moveTo(l,i),t.lineTo(l+Math.sin(R)*M,i-Math.cos(R)*M),t.stroke()};k(u/12*Math.PI*2,n*.5,4,"#111"),k(f/60*Math.PI*2,n*.8,3,"#111");const B=Math.floor(r)/60*Math.PI*2;k(B,n*.62,1.5,"#dd0000"),t.fillStyle="#dd0000",t.beginPath(),t.arc(l+Math.sin(B)*n*.62,i-Math.cos(B)*n*.62,n*.11,0,Math.PI*2),t.fill();const O=Math.floor(r),Se=c==="play"||c==="bin"||c==="amt"?v<10:!1;t.font=`800 ${e?10:13}px ui-monospace, monospace`;const te=`0${Math.floor(a/3600)}:${String(Math.floor(f)).padStart(2,"0")}:${String(O).padStart(2,"0")}`,F=t.measureText(te).width,ne=Math.min(l-F/2,T-F-8),le=e?13:17;t.fillStyle=Se&&Math.floor(performance.now()/250)%2?"#dd0000":"#111",t.fillRect(ne-4,i+n+4,F+8,le),t.fillStyle="#ffffff",t.fillText(te,ne,i+n+le)}function lt(){const e=Math.max(40,Math.min(200,T-110)),n=10,l=T<300||m.height/x<240?38:46;t.fillStyle="#111",t.fillRect(n,l,e,4),t.fillStyle="#ffcc00",t.fillRect(n,l,e*Math.min(o.x,I)/I,4),t.font="12px sans-serif",t.fillText("🚉",n+e+2,l+9)}function it(){const e=document.activeElement;e instanceof HTMLButtonElement&&h.actions.contains(e)&&e.click()}function at(e){if(c!=="bin")return;const n=A.find(l=>l.kind==="trash"&&!l.done&&o.x>=l.x);n&&be(n,W[e].id,h.actions.children[e])}const pe={id:"run.walk",label:"Gehen (halten)",defaultKey:"ArrowRight",fallbackCodes:["ArrowRight","KeyD"],fallbackLabel:"→ / D",onPress:()=>{c==="play"&&(b.walk=!0)},onRelease:()=>{b.walk=!1}},ee={id:"run.jump",label:"Springen",defaultKey:"Space",fallbackCodes:["Space","ArrowUp","KeyW"],fallbackLabel:"Leertaste / ↑",onPress:e=>{e||(c==="play"?b.jumpQueued=!0:c!=="bin"&&it())}},ye=W.map((e,n)=>({id:`bins.${e.id}`,label:`Müll: ${e.label}`,defaultKey:String(n+1),fallbackCodes:[`Digit${n+1}`,`Numpad${n+1}`],fallbackLabel:String(n+1),onPress:l=>{l||at(n)}}));function we(){const e=ye.map(G);return e.join("")==="123456"?"1–6":e.join(" ")}ze([pe,ee,...ye],()=>{c==="title"&&J()});function ke(e,n,l){e.addEventListener("pointerdown",a=>{a.preventDefault(),e.setPointerCapture(a.pointerId),e.classList.add("is-down"),n()});const i=()=>{e.classList.remove("is-down"),l==null||l()};e.addEventListener("pointerup",i),e.addEventListener("pointercancel",i),e.addEventListener("lostpointercapture",i)}ke(h.walk,()=>{c==="play"&&(b.walk=!0)},()=>{b.walk=!1});ke(h.jump,()=>{c==="play"&&(b.jumpQueued=!0)});function xe(e){const n=Math.min(.05,(e-ae)/1e3);ae=e,qe(n),Ze(),requestAnimationFrame(xe)}m.tabIndex=-1;z(100);J();_e();requestAnimationFrame(xe);
</script>
<style>:root{--ink: #111111;--paper: #f5f1e6;--schwarz: #111111;--rot: #dd0000;--gold: #ffcc00;--ok: #1f7a3a;--control-h: 52px;--napplet-text-selection: none;color-scheme:light;font:15px/1.3 DIN Alternate,Bahnschrift,Arial Narrow,ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}:where(html,body,body *){-webkit-user-select:var(--napplet-text-selection, none);user-select:var(--napplet-text-selection, none)}html,body,#app{height:100%;margin:0;background:#9fc6e0;color:var(--ink);overflow:hidden}button{font:inherit;color:inherit}.app{position:relative;touch-action:none}.scene{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block}.hud{position:absolute;top:8px;left:8px;pointer-events:none}.ordnung{display:flex;align-items:center;gap:6px;padding:4px 8px;background:var(--paper);border:2px solid var(--ink);font-weight:700;letter-spacing:.06em;font-size:12px}.ordnung-bar{width:90px;height:10px;border:2px solid var(--ink);background:#fff}.ordnung-fill{display:block;height:100%;width:100%;background:linear-gradient(90deg,var(--schwarz) 0 33%,var(--rot) 33% 66%,var(--gold) 66%);transition:width .2s}.ordnung-value{min-width:2.2em;text-align:right;font-variant-numeric:tabular-nums}.controls{position:absolute;left:8px;right:8px;bottom:8px;display:flex;justify-content:space-between;pointer-events:none}.ctrl{pointer-events:auto;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:var(--control-h);height:var(--control-h);padding:0 16px;border:3px solid var(--ink);background:var(--paper);font-weight:800;text-transform:uppercase;letter-spacing:.05em;cursor:pointer;touch-action:none;opacity:.92}.ctrl:active,.ctrl.is-down{background:var(--gold)}.ctrl:focus-visible,.card button:focus-visible{outline:3px solid var(--rot);outline-offset:2px}.overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-items:center;padding:10px;background:#11111159}.overlay[hidden]{display:none}.card{width:min(460px,100%);max-height:100%;overflow:auto;background:var(--paper);border:3px solid var(--ink);box-shadow:6px 6px 0 var(--ink);padding:14px 16px;border-top:10px solid var(--ink);position:relative}.card:before{content:"";position:absolute;top:-10px;left:33%;right:0;height:10px;background:linear-gradient(90deg,var(--rot) 0 50%,var(--gold) 50%)}.kicker{margin:0 0 4px;font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--rot)}.card-title{margin:0 0 6px;font-size:22px;line-height:1.1;text-transform:uppercase;letter-spacing:.02em}.card-body{margin:0 0 10px;white-space:pre-line}.card-body:empty,.card-hint:empty,.kicker:empty{display:none}.card-actions{display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:8px}.card-actions button{min-height:44px;padding:6px 10px;border:3px solid var(--ink);background:#fff;font-weight:800;text-transform:uppercase;cursor:pointer}.card-actions button:hover{background:var(--gold)}.card-actions button.primary{background:var(--rot);color:#fff}.card-actions .bin{color:#fff;text-shadow:0 1px 0 #000}.card-actions .bin small{display:block;font-size:10px;opacity:.85}.card-actions .shake{animation:shake .3s}@keyframes shake{25%{transform:translate(-5px)}75%{transform:translate(5px)}}.card-hint{margin:10px 0 0;font-size:12px;opacity:.7}@media(max-width:420px),(max-height:320px){:root{--control-h: 44px;font-size:13px}.ctrl .label{display:none}.ordnung-bar{width:50px}.card-title{font-size:17px}}@media(max-height:220px),(max-width:260px){:root{--control-h: 34px}.ordnung-label{display:none}.card{padding:8px}.card-hint,.card-body,.kicker{display:none}.card-actions{grid-template-columns:repeat(auto-fit,minmax(64px,1fr));gap:4px}.card-actions button{min-height:30px;padding:2px 4px;font-size:11px}}@media(prefers-reduced-motion:reduce){.card-actions .shake{animation:none}}
</style>
<meta name="napplet-type" content="puenktlich">
<meta name="napplet-config-schema" content="{"$schema":"http://json-schema.org/draft-07/schema#","$version":2,"type":"object","additionalProperties":false,"properties":{"timeLimit":{"type":"integer","title":"Seconds until the train leaves","description":"Lower is harder. Takes effect on the next run.","minimum":45,"maximum":90,"default":60,"x-napplet-section":"Gameplay","x-napplet-order":1}}}">
</head>
<body>
<main id="app" class="app">
<canvas id="scene" class="scene" aria-label="Klaus auf dem Weg zum Bahnhof"></canvas>
<div class="hud" aria-live="polite">
<div class="ordnung" title="Ordnung">
<span class="ordnung-label">ORDNUNG</span>
<span class="ordnung-bar"><span id="ordnungFill" class="ordnung-fill"></span></span>
<span id="ordnungValue" class="ordnung-value">100</span>
</div>
</div>
<div class="controls" role="group" aria-label="Steuerung">
<button id="jumpButton" type="button" class="ctrl ctrl-jump" aria-label="Springen">
<span aria-hidden="true">▲</span><span class="label">Springen</span>
</button>
<button id="walkButton" type="button" class="ctrl ctrl-walk" aria-label="Gehen (gedrückt halten)">
<span class="label">Gehen</span><span aria-hidden="true">▶</span>
</button>
</div>
<section id="overlay" class="overlay" hidden>
<div class="card" role="dialog" aria-modal="true" aria-labelledby="cardTitle">
<p id="cardKicker" class="kicker"></p>
<h2 id="cardTitle" class="card-title"></h2>
<p id="cardBody" class="card-body"></p>
<div id="cardActions" class="card-actions"></div>
<p id="cardHint" class="card-hint"></p>
</div>
</section>
</main>
</body>
</html>
