&$.ausloeser==="frosttreffer"&&l.element==="frost"&&(r.schubStapel=g.min($.maxStapel,r.schubStapel+1))}function fn(r,l,c,k){const b=n.gegner[r.art],v=[];b.ruestungMilli>0&&v.push(ct("gegner.ruestung",b.ruestungMilli)),b.bannPromille>0&&l.element!==Rt&&v.push(dt("gegner.bann",b.bannPromille));const $=k===1e3?c.schadenMilli:g.permille(c.schadenMilli,k);Fe(r,ut($,v)),dn(r,l)}function Be(){for(let r=p.length-1;r>=0;r-=1){const l=p[r];if(l.hpMilli>0)continue;p.splice(r,1);const c=n.kopfgeldJeWelle[m]??0;h+=S>0?g.max(1,g.idiv(g.mulI(c,n.gegner[l.art].kopfgeldAnteilPromille),S)):0}}function hn(){for(const r of E){if(r.abklingTicks>0){r.abklingTicks-=1;continue}const l=n.tuerme[r.art],c=l.stufen[r.stufe],k=n.brett.bauplaetze[r.slot].position,b=p.filter(N=>de(N,l.zielt)&&te(N,k,c.reichweiteMilli)),v=r.zielwahl===q.erster?N=>-N.fortschrittMilli:r.zielwahl===q.entferntester?N=>N.fortschrittMilli:r.zielwahl===q.staerkster?N=>-N.hpMilli:N=>De(N,k),$=Oe(b,v);if(!$)continue;for(const N of cn(l,c,$,b,k))fn(N.ziel,l,c,N.anteilPromille);r.abklingTicks=c.taktTicks;const ie=C($.pfad,$.fortschrittMilli);d.push({id:t.naechste(),vonX:k.xMilli,vonY:k.yMilli,zuX:ie.xMilli,zuY:ie.yMilli,restTicks:ye})}Be()}function gn(){for(const r of p)r.brandRestTicks<=0||(Fe(r,g.idiv(g.mulI(r.brandProSekundeMilli,r.brandStapel),pe)),r.brandRestTicks-=1,r.brandRestTicks===0&&(r.brandStapel=0));for(const r of me(p,l=>l.id)){if(r.hpMilli<=0)continue;const l=le(n.gegner[r.art].eigenschaften??[],"heilaura");if(!l)continue;const c=g.idiv(l.heilungProSekundeMilli,pe),k=C(r.pfad,r.fortschrittMilli);for(const b of p)b===r||b.hpMilli<=0||b.brandRestTicks>0&&b.brandBlockiertHeilung||te(b,k,l.radiusMilli)&&(b.hpMilli=g.min(b.maxHpMilli,b.hpMilli+c))}for(const r of p)r.schildMaxMilli>0&&(r.schildMilli=g.min(r.schildMaxMilli,r.schildMilli+r.schildRegenJeTickMilli)),r.langsamRestTicks>0&&(r.langsamRestTicks-=1,r.langsamRestTicks===0&&(r.langsamPromille=0));Be()}function mn(){for(let r=d.length-1;r>=0;r-=1){const l=d[r];l.restTicks-=1,l.restTicks<=0&&d.splice(r,1)}}function pn(){if(M<=0){u=I.niederlage;return}u===I.welle&&(z.length>0||p.length>0||(m+=1,u=m>=n.wellen.length?I.sieg:I.bau))}const Se={get tick(){return f},get phase(){return u},get gold(){return h},get kernpunkte(){return M},get welleNummer(){return m+1},absicht(r,l){i.anfuegen(f,r,l)},step(){if(!(u===I.sieg||u===I.niederlage))try{ln(),an(),on(),hn(),gn(),mn(),pn(),f+=1}catch(r){throw yn(r)}},vorspulen(r){if(!Number.isInteger(r)||r<0)throw new R(P.invarianteVerletzt,"vorspulen erwartet eine Anzahl ab 0.",{kontext:{ticks:String(r)}});for(let l=0;l{const l=C(r.pfad,r.fortschrittMilli);return{id:r.id,x:l.xMilli,y:l.yMilli,art:r.art,hpPromille:g.toMilli(g.div(g.fromMilli(g.max(0,r.hpMilli)),g.fromMilli(r.maxHpMilli)))}})},turmListe(){return E.map(r=>{const l=n.brett.bauplaetze[r.slot];return{id:r.id,slot:r.slot,x:l.position.xMilli,y:l.position.yMilli,art:r.art,stufe:r.stufe,zielwahl:r.zielwahl}})},geschossListe(){return d.map(r=>({id:r.id,vonX:r.vonX,vonY:r.vonY,zuX:r.zuX,zuY:r.zuY,fortschrittPromille:g.idiv(g.mulI(ye-r.restTicks,1e3),g.max(1,ye))}))},bauplaetze(){return n.brett.bauplaetze.map((r,l)=>{const c=E.find(k=>k.slot===l);return{slot:l,frei:c===void 0,optionen:n.tuerme.map((k,b)=>{const v=c?k.stufen[c.stufe+1]?.kosten??0:k.stufen[0].kosten;return{turm:b,kosten:v,erlaubt:c===void 0&&h>=v}})}})},hashFelder(){const r=[["tick",f],["phase",u],["gold",h],["kernpunkte",M],["welle",m],["gegnerAnzahl",p.length],["ausstehend",z.length],["idNaechste",t.sichern().naechste]];for(const l of[...p].sort((c,k)=>c.id-k.id))r.push([`g${l.id}`,`${l.art}:${l.fortschrittMilli}:${l.hpMilli}:${l.schildMilli}:${l.brandRestTicks}:${l.brandStapel}:${l.langsamRestTicks}:${l.langsamPromille}:${l.schubStapel}`]);for(const l of[...E].sort((c,k)=>c.id-k.id))r.push([`t${l.id}`,`${l.art}:${l.slot}:${l.stufe}:${l.abklingTicks}:${l.zielwahl}`]);return r},hash(){return kt(Se.hashFelder())},intentLog(){return i}};return Se}function Nt(e,n,t){if(!Number.isInteger(e)||e<0)throw new R(P.invarianteVerletzt,"Eine Kurve hat keine gültige Schrittzahl.",{kontext:{schritte:String(e)}});const i=[];let s=n;for(let a=0;aa===i);if(s===void 0)throw new ne(t,`einer von [${n.join(", ")}] erwartet, war "${i}"`);return s}function ve(e,n,t){const i=e[n];if(i===void 0)throw new ne(t,`Eintrag ${n} fehlt`);return i}const je=1e3,It=["boden","luft"],Dt=[Ee.standard,Ee.hoehensockel,Ee.grabensteg];function Me(e,n){const t=ze(e,n);return{x:we(t.xMilli,`${n}.xMilli`)/je,y:we(t.yMilli,`${n}.yMilli`)/je}}function Lt(e,n){const t=ze(e,n),i=V(t.wegpunkte,`${n}.wegpunkte`);if(i.length<2)throw new Error(`${n}.wegpunkte: mindestens zwei Wegpunkte`);return{id:Q(t.id,`${n}.id`),art:en(t.art,It,`${n}.art`),wegpunkte:i.map((s,a)=>Me(s,`${n}.wegpunkte[${a}]`))}}function Ft(e,n,t){const i=ze(e,t);return{slot:n,id:Q(i.id,`${t}.id`),sorte:en(i.sorte,Dt,`${t}.sorte`),position:Me(i.position,`${t}.position`)}}function Bt(e,n){const t=V(e,"bretter");if(t.length===0)throw new Error("bretter: leer");const i=ve(t,0,"bretter");if(i===void 0)throw new Error('bretter: Brett "" nicht gefunden');const s=ze(i,"brett"),a=Q(s.id,"brett.id"),o=V(s.bauplaetze,`${a}.bauplaetze`).map((h,M)=>Ft(h,M,`${a}.bauplaetze[${M}]`)),f=new Map(o.map(h=>[h.id,h.slot])),u=V(s.bindeglieder,`${a}.bindeglieder`).map((h,M)=>{const m=V(h,`${a}.bindeglieder[${M}]`),p=f.get(Q(ve(m,0,`${a}.bindeglieder[${M}]`),"slot-id")),E=f.get(Q(ve(m,1,`${a}.bindeglieder[${M}]`),"slot-id"));if(p===void 0||E===void 0)throw new Error(`${a}.bindeglieder[${M}]: verweist auf einen unbekannten Bauplatz`);return[p,E]});return{id:a,spalten:we(s.spalten,`${a}.spalten`),reihen:we(s.reihen,`${a}.reihen`),tor:Me(s.tor,`${a}.tor`),eingaenge:V(s.eingaenge,`${a}.eingaenge`).map((h,M)=>Me(h,`${a}.eingaenge[${M}]`)),pfade:V(s.pfade,`${a}.pfade`).map((h,M)=>Lt(h,`${a}.pfade[${M}]`)),bauplaetze:o,bindeglieder:u}}const Gt=`[ { "id": "board.die-enge", "_note": "Layout A aus 01-kampfebene.md 2.3: Scharmuetzel-Lernbrett, 10 x 14 Felder, 8 Slots, eine Route als S-Kurve. Koordinaten in Feld-Milli: xMilli 5000 ist die Mitte von Spalte 5. Der Luftpfad steht nicht im Original-ASCII - er ist hier ergaenzt, weil die Aschekraehe (GDD 5.3) sonst kein Brett haette, auf dem sie fliegen kann; Flieger ziehen stur geradeaus zum Tor.", "spalten": 10, "reihen": 14, "tor": { "xMilli": 8000, "yMilli": 14000 }, "eingaenge": [{ "xMilli": 5000, "yMilli": 1000 }], "pfade": [ { "id": "path.enge-boden", "_note": "Wegpunkte sind die Ecken der S-Kurve, nicht jede Kachel: (5,1) runter bis (5,4), rechts bis (9,4), runter bis (9,7), links bis (2,7), runter bis (2,10), rechts bis (8,10), runter zum Tor (8,14).", "art": "boden", "wegpunkte": [ { "xMilli": 5000, "yMilli": 1000 }, { "xMilli": 5000, "yMilli": 4000 }, { "xMilli": 9000, "yMilli": 4000 }, { "xMilli": 9000, "yMilli": 7000 }, { "xMilli": 2000, "yMilli": 7000 }, { "xMilli": 2000, "yMilli": 10000 }, { "xMilli": 8000, "yMilli": 10000 }, { "xMilli": 8000, "yMilli": 14000 } ] }, { "id": "path.enge-luft", "_note": "Zwei Wegpunkte, weil Flieger alle Bodenkurven ignorieren. Ein Sprite und eine Bewegungsregel - und trotzdem eine komplett eigene Verteidigungsanforderung.", "art": "luft", "wegpunkte": [ { "xMilli": 5000, "yMilli": 1000 }, { "xMilli": 8000, "yMilli": 14000 } ] } ], "bauplaetze": [ { "id": "slot.enge-a", "sorte": "hoehensockel", "position": { "xMilli": 3000, "yMilli": 2000 } }, { "id": "slot.enge-b", "sorte": "standard", "position": { "xMilli": 7000, "yMilli": 2000 } }, { "id": "slot.enge-c", "sorte": "standard", "position": { "xMilli": 3000, "yMilli": 5000 } }, { "id": "slot.enge-d", "sorte": "grabensteg", "position": { "xMilli": 6000, "yMilli": 6000 } }, { "id": "slot.enge-e", "sorte": "grabensteg", "position": { "xMilli": 5000, "yMilli": 8000 } }, { "id": "slot.enge-f", "sorte": "standard", "position": { "xMilli": 7000, "yMilli": 8000 } }, { "id": "slot.enge-g", "sorte": "hoehensockel", "position": { "xMilli": 4000, "yMilli": 11000 } }, { "id": "slot.enge-h", "sorte": "standard", "position": { "xMilli": 6000, "yMilli": 12000 } } ], "bindeglieder": [ ["slot.enge-a", "slot.enge-c"], ["slot.enge-b", "slot.enge-d"], ["slot.enge-c", "slot.enge-e"], ["slot.enge-d", "slot.enge-f"], ["slot.enge-e", "slot.enge-g"], ["slot.enge-f", "slot.enge-h"], ["slot.enge-g", "slot.enge-h"] ] } ] `,_t=`[ { "id": "enemy.zunder-schwarm", "_note": "STARTWERTE, GDD 5.3: 20 x 18 HP, 1,8 Felder/s. Ueberlastet Einzelziel-Tuerme. hpMilli ist die HP EINER Einheit; \`gruppengroesse\` sagt, wie viele davon eine Gruppe sind.", "familie": "masse", "hpMilli": 18000, "tempoMilliProSekunde": 1800, "ruestungMilli": 0, "bannPromille": 0, "gruppengroesse": 20, "kopfgeldAnteilPromille": 15, "bewegtSichAuf": "boden", "eigenschaften": [], "antworten": [ "tower.glutwacht", "tower.funkenhorn", "endform.aschekessel", "endform.kettenschlag" ], "falscheAntworten": ["endform.jagdspeer"] }, { "id": "enemy.wallbrecher", "_note": "PHASE-0-KALIBRIERUNG, gemessen mit \`pnpm balance:messen\`. GDD 5.3: 2.400 HP, 30 flache Ruestung. Seine gueltigen Antworten sind ausschliesslich Endformen (Schmelzofen, Jagdspeer, Erdungsstab), die es in Phase 0 nicht gibt - mit den Kanonwerten war er rechnerisch unverwundbar. Auch 900/12 blieben unloesbar: Bei 4 bis 18 Turmschaden frisst Ruestung 12 zwischen 33 und 85 Prozent jedes Treffers, und KEIN Bauplan kam ueber Welle 8. 350 HP und 6 Ruestung lassen die Ruestung ihre Rolle behalten - Aufwertungen sind die Antwort - ohne ihn immun zu machen. Ab AP-1.10 zurueck Richtung GDD.", "familie": "panzer", "hpMilli": 350000, "tempoMilliProSekunde": 800, "ruestungMilli": 6000, "bannPromille": 0, "gruppengroesse": 1, "kopfgeldAnteilPromille": 120, "bewegtSichAuf": "boden", "eigenschaften": [], "antworten": ["endform.schmelzofen", "endform.jagdspeer", "endform.erdungsstab"], "falscheAntworten": ["endform.bolzenbank"] }, { "id": "enemy.schlundgeist", "_note": "STARTWERTE, GDD 5.3: 480 HP, heilt Verbuendete 40/s in Radius 2,0, 0,9 Felder/s. Die zweite Antwort ist die Kette: Sie erreicht den Heiler mitten im Pulk, ohne dass ein Turm freie Sicht auf ihn braucht.", "familie": "stoer", "hpMilli": 480000, "tempoMilliProSekunde": 900, "ruestungMilli": 0, "bannPromille": 0, "gruppengroesse": 1, "kopfgeldAnteilPromille": 60, "bewegtSichAuf": "boden", "eigenschaften": [ { "art": "heilaura", "heilungProSekundeMilli": 40000, "radiusMilli": 2000 } ], "antworten": ["tower.glutwacht", "endform.aschekessel", "endform.kettenschlag"], "falscheAntworten": ["endform.bolzenbank"] }, { "id": "enemy.frostbrand-zelot", "_note": "STARTWERTE, GDD 5.3: 520 HP, verlangsamungsimmun, +40 % Tempo nach jedem Frosttreffer. Antwort ist Verwurzelung (Fusion Nagelschuss) oder die Frostwacht stilllegen - der kostenlose Knopf im Turm-Panel. Ohne ihn waere dieser Gegner unfair.", "familie": "stoer", "hpMilli": 520000, "tempoMilliProSekunde": 1100, "ruestungMilli": 0, "bannPromille": 0, "gruppengroesse": 1, "kopfgeldAnteilPromille": 65, "bewegtSichAuf": "boden", "eigenschaften": [ { "art": "verlangsamungsimmun" }, { "art": "temposchub", "ausloeser": "frosttreffer", "bonusPromille": 400, "maxStapel": 5 } ], "antworten": ["fusion.nagelschuss", "tower.steinwacht", "endform.erdungsstab"], "falscheAntworten": ["endform.frostgruft"] }, { "id": "enemy.aschekraehe", "_note": "PHASE-0-KALIBRIERUNG, gemessen. GDD 5.3: 8 x 190 HP, 2,2 Felder/s, Luftkorridor. Der Luftweg ist 13,3 Felder lang und war damit in 6 Sekunden durchflogen - 1.520 Lebenspunkte in 6 Sekunden verlangen rund 250 Schaden je Sekunde, und Welle 7 kostete in JEDER Messung alle acht Durchbrueche. 70 HP und 1,5 Felder/s machen daraus eine Pruefung statt einer Mauer. Glutwacht und Schutthorn koennen sie physisch nicht treffen - hier ist nur die Glutwacht im Minimalsatz.", "familie": "flug", "hpMilli": 70000, "tempoMilliProSekunde": 1500, "ruestungMilli": 0, "bannPromille": 0, "gruppengroesse": 8, "kopfgeldAnteilPromille": 25, "bewegtSichAuf": "luft", "eigenschaften": [], "antworten": ["tower.steinwacht", "tower.frostwacht", "tower.funkenhorn"], "falscheAntworten": ["tower.glutwacht"] }, { "id": "enemy.banntraeger", "_note": "PHASE-0-KALIBRIERUNG, gemessen. GDD 5.3: 900 HP, Bann 65 %, Schild 400 (regeneriert). Bann wirkt NUR auf Feuer/Frost/Blitz/Arkan - Stich geht durch; die Simulation wandte ihn bis AP-0.16 auf jeden Turm an, womit die Steinwacht als vorgesehene Antwort keine war. Mit korrektem Bann und 400 HP / 150 Schild ist er in Phase 0 loesbar. Der Deckel 700 Promille steht im Schema, damit kein Content-Patch daraus eine Immunitaet macht.", "familie": "stoer", "hpMilli": 400000, "tempoMilliProSekunde": 1000, "ruestungMilli": 0, "bannPromille": 650, "gruppengroesse": 1, "kopfgeldAnteilPromille": 90, "bewegtSichAuf": "boden", "eigenschaften": [ { "art": "schild", "punkteMilli": 150000, "regenerationProSekundeMilli": 20000 } ], "antworten": ["endform.erdungsstab", "tower.steinwacht", "endform.jagdspeer"], "falscheAntworten": ["tower.glutwacht"] } ] `,Ht=`[ { "id": "curve.wellen-hp", "_note": "HP-Skalierung je Welle in PROMILLE: 1000 = Grundwert, 1025 = +2,5 %. PHASE-0-KALIBRIERUNG, gemessen. Der Kanon (01-kampfebene.md 6) nennt 1,145, also x4,4 auf Welle 12 - gerechnet gegen den VOLLEN Werkzeugkasten aus Endformen, Fusionen, Affixen, Draft und Ausruestung. In Phase 0 waechst die Spielermacht nur ueber drei Aufwertungsstufen und acht Bauplaetze; mit 1,145 kam kein Bauplan ueber Welle 8. Ab AP-1.10 zurueck Richtung Kanon. Ausgewertet wird iterativ ueber N.scaleMilli, ein Schritt je Welle - nie als Potenz. Die Sim multipliziert die Gegner-HP einmal beim Spawn.", "schritte": 12, "einheit": "promille", "kurve": { "kind": "geometric", "base": 1000, "factorPermille": 1025 } }, { "id": "curve.kopfgeld", "_note": "Wellenkopfgeld in Gold, aus 01-kampfebene.md 6: Kopfgeld(w) = (40 + 12w) x 1,13^(w-1), Richtung Null abgeschnitten. Als \`table\` und nicht \`geometric\`, weil die Formel ein Produkt aus einem LINEAREN und einem geometrischen Anteil ist; eine geometrische Naeherung waere eine stille Abweichung vom Kanon. ACHTUNG: Im Kanon wachsen die HP schneller als das Einkommen, und diese Schere IST der Schwierigkeitsgrad. In Phase 0 steht die HP-Kurve auf 1,025, die Schere laeuft also andersherum; der Anstieg kommt aus der Wellenzusammensetzung. Beide Kurven nur GEMEINSAM bewegen.", "schritte": 12, "einheit": "gold", "kurve": { "kind": "table", "values": [52, 72, 97, 126, 163, 206, 258, 319, 393, 480, 583, 705] } } ] `,xt=`[ { "id": "tower.steinwacht", "_note": "STARTWERTE ZUM KALIBRIEREN. Grundwerte aus GDD 5.2: 90 Gold, 14 Schaden, 0,55 s Takt, Reichweite 3,2. 0,55 s sind bei 30 Hz 16,5 Ticks - aufgerundet auf 17 (0,567 s, 24,7 DPS statt 25,5). Aufwertung nach Kanon: +55 % Schaden, +0,2 Reichweite, Kosten 0,8x und 1,6x Basis, Endform 2,6x Basis.", "element": "stich", "zielt": "boden-und-luft", "zielwahl": "erster", "botRolle": "einzelziel", "stufen": [ { "stufe": 1, "kosten": 90, "schadenMilli": 14000, "taktTicks": 17, "reichweiteMilli": 3200 }, { "stufe": 2, "kosten": 72, "schadenMilli": 21700, "taktTicks": 17, "reichweiteMilli": 3400 }, { "stufe": 3, "kosten": 144, "schadenMilli": 33635, "taktTicks": 17, "reichweiteMilli": 3600 } ], "wirkungen": [], "endformen": [ { "id": "endform.bolzenbank", "_note": "3er-Salve a 60 % (GDD 5.2). Ergibt 180 % Gesamtschaden gegen ein Ziel, verteilt aber auf drei Treffer - gegen flache Ruestung ist das deutlich schlechter als ein grosser Treffer. Genau das macht Wallbrecher zum Raetsel.", "kosten": 234, "wirkungen": [ { "art": "salve", "schuesse": 3, "anteilPromille": 600 } ] }, { "id": "endform.jagdspeer", "_note": "1,8 s Aufladung = 54 Ticks, +180 % gegen das Ziel mit den meisten HP (GDD 5.2).", "kosten": 234, "wirkungen": [ { "art": "aufladung", "ladezeitTicks": 54, "bonusPromille": 1800, "ziel": "meiste-hp" } ] } ] }, { "id": "tower.glutwacht", "_note": "STARTWERTE. GDD 5.2: 110 Gold, 12 Schaden + Brand 6/s ueber 3 s, Takt 0,80 s = 24 Ticks, Reichweite 2,5, Splash 0,9, nur Boden. Der Brand blockiert Heilung vollstaendig - das ist seine zweite Rolle und die Antwort auf den Schlundgeist.", "element": "feuer", "zielt": "boden", "zielwahl": "erster", "botRolle": "flaeche", "stufen": [ { "stufe": 1, "kosten": 110, "schadenMilli": 12000, "taktTicks": 24, "reichweiteMilli": 2500 }, { "stufe": 2, "kosten": 88, "schadenMilli": 18600, "taktTicks": 24, "reichweiteMilli": 2700 }, { "stufe": 3, "kosten": 176, "schadenMilli": 28830, "taktTicks": 24, "reichweiteMilli": 2900 } ], "wirkungen": [ { "art": "splash", "radiusMilli": 900 }, { "art": "schaden-ueber-zeit", "schadenProSekundeMilli": 6000, "dauerTicks": 90, "maxStapel": 1, "blockiertHeilung": true } ], "endformen": [ { "id": "endform.aschekessel", "_note": "Einschlaege hinterlassen 4 s (120 Ticks) brennende Pfadflaechen mit 14/s (GDD 5.2). Zonenverweigerung.", "kosten": 286, "wirkungen": [ { "art": "splash", "radiusMilli": 900 }, { "art": "schaden-ueber-zeit", "schadenProSekundeMilli": 6000, "dauerTicks": 90, "maxStapel": 1, "blockiertHeilung": true }, { "art": "pfadflaeche", "dauerTicks": 120, "schadenProSekundeMilli": 14000, "radiusMilli": 900 } ] }, { "id": "endform.schmelzofen", "_note": "Brand stapelt bis 5x, je Stapel -4 Ruestung (GDD 5.2). Gegen Wallbrecher (Ruestung 30) traegt das 20 Ruestung ab.", "kosten": 286, "wirkungen": [ { "art": "splash", "radiusMilli": 900 }, { "art": "schaden-ueber-zeit", "schadenProSekundeMilli": 6000, "dauerTicks": 90, "maxStapel": 5, "blockiertHeilung": true }, { "art": "ruestungsabbau", "betragMilli": 4000, "maxStapel": 5, "bedingung": "brennend" } ] } ] }, { "id": "tower.frostwacht", "_note": "PHASE-0-KALIBRIERUNG, gemessen. GDD 5.2: 80 Gold, 4 Schaden, Verlangsamung 35 % / 2 s, Takt 1,00 s, Reichweite 2,8 als 60-Grad-Kegel. Damit war sie eine Falle: Wer zwei Steinwachten durch zwei Frostwachten ersetzt, verliert 41 Schaden je Sekunde - und in Phase 0 gibt es nichts, was Kontrolle in Schaden verwandelt (Splitterfrost, Nagelschuss fehlen noch). 7 Schaden (halbe Steinwacht), 45 % / 3 s und 90 Grad geben ihr einen eigenen Boden. Ab AP-1.10 zurueck Richtung GDD. 90 Grad sind 1024 Binaergrad.", "element": "frost", "zielt": "boden-und-luft", "zielwahl": "erster", "botRolle": "kontrolle", "stufen": [ { "stufe": 1, "kosten": 80, "schadenMilli": 7000, "taktTicks": 30, "reichweiteMilli": 3000 }, { "stufe": 2, "kosten": 64, "schadenMilli": 10850, "taktTicks": 30, "reichweiteMilli": 3200 }, { "stufe": 3, "kosten": 128, "schadenMilli": 16817, "taktTicks": 30, "reichweiteMilli": 3400 } ], "wirkungen": [ { "art": "kegel", "oeffnungBinaergrad": 1024 }, { "art": "verlangsamung", "staerkePromille": 450, "dauerTicks": 90 } ], "endformen": [ { "id": "endform.frostgruft", "_note": "Verlangsamung 60 %, Radius 2,4 STATT Kegel (GDD 5.2). Der fehlende \`kegel\`-Eintrag ist die Aussage: Endform-Wirkungen ersetzen die Grundwirkungen vollstaendig.", "kosten": 208, "wirkungen": [ { "art": "radiuswirkung", "radiusMilli": 2400 }, { "art": "verlangsamung", "staerkePromille": 600, "dauerTicks": 60 } ] }, { "id": "endform.splitterfrost", "_note": "Verlangsamte Ziele erleiden +30 % Schaden aus allen Quellen (GDD 5.2). Kegel und Grundverlangsamung bleiben und stehen deshalb wieder mit drin.", "kosten": 208, "wirkungen": [ { "art": "kegel", "oeffnungBinaergrad": 683 }, { "art": "verlangsamung", "staerkePromille": 350, "dauerTicks": 60 }, { "art": "schadensfaktor", "anteilPromille": 300, "bedingung": "verlangsamt" } ] } ] }, { "id": "tower.funkenhorn", "_note": "PHASE-0-KALIBRIERUNG, gemessen. GDD 5.2: 130 Gold, 18 Schaden, Kette 3 mit -35 % je Sprung, Takt 1,40 s, Reichweite 3,0. Damit lag sein Schaden je Gold bei 40 Prozent der Steinwacht, und ein Bauplan mit Funkenhorn kam nie so weit wie derselbe ohne. Kette 4 mit -25 % bringt ihn auf Augenhoehe. \`spruenge\` zaehlt GETROFFENE ZIELE, das erste eingeschlossen. Ueberlaedt Schilde statt sie abzutragen.", "element": "blitz", "zielt": "boden-und-luft", "zielwahl": "erster", "botRolle": "flaeche", "stufen": [ { "stufe": 1, "kosten": 130, "schadenMilli": 18000, "taktTicks": 42, "reichweiteMilli": 3000 }, { "stufe": 2, "kosten": 104, "schadenMilli": 27900, "taktTicks": 42, "reichweiteMilli": 3200 }, { "stufe": 3, "kosten": 208, "schadenMilli": 43245, "taktTicks": 42, "reichweiteMilli": 3400 } ], "wirkungen": [ { "art": "kette", "spruenge": 4, "abfallPromille": 250 } ], "endformen": [ { "id": "endform.kettenschlag", "_note": "6 Spruenge, -20 % je Sprung (GDD 5.2).", "kosten": 338, "wirkungen": [ { "art": "kette", "spruenge": 6, "abfallPromille": 200 } ] }, { "id": "endform.erdungsstab", "_note": "Einzelziel, x3 Schaden (= Zuschlag 2000 Promille), ignoriert Bann vollstaendig (GDD 5.2). Dass hier keine \`kette\` steht, IST der Einzelziel-Teil: Endform-Wirkungen ersetzen die Grundwirkungen.", "kosten": 338, "wirkungen": [ { "art": "schadensfaktor", "anteilPromille": 2000, "bedingung": "immer" }, { "art": "bannbruch", "anteilPromille": 1000 } ] } ] } ] `,Kt=`[ { "id": "wave.enge-01", "_note": "Rolle Einfuehrung (01-kampfebene.md 6): ein Gegnertyp, kein Raetsel. Alle Zeiten in Ticks bei 30 Hz - 18 Ticks Abstand sind 0,6 s, die Welle laeuft rund 24 s bei 1x.", "brett": "board.die-enge", "nummer": 1, "prueft": "einfuehrung", "pauseNach": [], "gruppen": [ { "gegner": "enemy.zunder-schwarm", "anzahl": 14, "startTick": 0, "abstandTicks": 18, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-02", "_note": "Weiter Einfuehrung, dichter. Erster Draft danach - Kanon GDD 4.", "brett": "board.die-enge", "nummer": 2, "prueft": "masse", "pauseNach": ["draft"], "gruppen": [ { "gegner": "enemy.zunder-schwarm", "anzahl": 24, "startTick": 0, "abstandTicks": 12, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-03", "_note": "Erstes Raetsel: Wallbrecher, flache Ruestung. EIN Wallbrecher und nicht zwei - gemessen kostete die Doppelung einem mittelmaessigen Bau 8 der 20 Kernpunkte, und das auf der Welle, die die Mechanik erst einfuehrt. Die Verdopplung steht in Welle 5. Beutetisch 1 (Kanon: Beutetische nach 3/7/11).", "brett": "board.die-enge", "nummer": 3, "prueft": "ruestung", "pauseNach": ["beutetisch"], "gruppen": [ { "gegner": "enemy.wallbrecher", "anzahl": 1, "startTick": 0, "abstandTicks": 120, "pfad": "path.enge-boden" }, { "gegner": "enemy.zunder-schwarm", "anzahl": 16, "startTick": 60, "abstandTicks": 14, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-04", "_note": "Zweites Raetsel: Heilung. Der Schlundgeist macht jeden fast toten Zunder wieder ganz - Brand blockiert das, mehr Rohschaden nicht.", "brett": "board.die-enge", "nummer": 4, "prueft": "heilung", "pauseNach": ["draft"], "gruppen": [ { "gegner": "enemy.zunder-schwarm", "anzahl": 22, "startTick": 0, "abstandTicks": 11, "pfad": "path.enge-boden" }, { "gegner": "enemy.schlundgeist", "anzahl": 2, "startTick": 120, "abstandTicks": 90, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-05", "_note": "Mischwelle: zwei Raetsel gleichzeitig (Ruestung und Heilung). Orakel danach - Kanon GDD 4: Orakel nach 5 und 9.", "brett": "board.die-enge", "nummer": 5, "prueft": "gemischt", "pauseNach": ["orakel"], "gruppen": [ { "gegner": "enemy.wallbrecher", "anzahl": 2, "startTick": 0, "abstandTicks": 150, "pfad": "path.enge-boden" }, { "gegner": "enemy.zunder-schwarm", "anzahl": 20, "startTick": 30, "abstandTicks": 12, "pfad": "path.enge-boden" }, { "gegner": "enemy.schlundgeist", "anzahl": 2, "startTick": 90, "abstandTicks": 120, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-06", "_note": "Mini-Boss-Rolle: eine Elite mit genau einer Mechanik. Der Banntraeger bestraft Elementar-Monokultur.", "brett": "board.die-enge", "nummer": 6, "prueft": "bann", "pauseNach": ["draft"], "gruppen": [ { "gegner": "enemy.banntraeger", "anzahl": 1, "startTick": 0, "abstandTicks": 0, "pfad": "path.enge-boden" }, { "gegner": "enemy.zunder-schwarm", "anzahl": 22, "startTick": 30, "abstandTicks": 11, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-07", "_note": "Erste Luftwelle. Wer nur Glutwacht gebaut hat, sieht hier zum ersten Mal, dass sie physisch nicht treffen kann. Beutetisch 2.", "brett": "board.die-enge", "nummer": 7, "prueft": "luft", "pauseNach": ["beutetisch"], "gruppen": [ { "gegner": "enemy.aschekraehe", "anzahl": 8, "startTick": 0, "abstandTicks": 24, "pfad": "path.enge-luft" }, { "gegner": "enemy.zunder-schwarm", "anzahl": 24, "startTick": 0, "abstandTicks": 10, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-08", "_note": "Eskalation: der Frostbrand-Zelot zwingt zum Stilllegen der Frostwacht oder zur Fusion Nagelschuss - und gleichzeitig steht Ruestung auf dem Brett.", "brett": "board.die-enge", "nummer": 8, "prueft": "tempo", "pauseNach": ["draft"], "gruppen": [ { "gegner": "enemy.frostbrand-zelot", "anzahl": 3, "startTick": 0, "abstandTicks": 90, "pfad": "path.enge-boden" }, { "gegner": "enemy.wallbrecher", "anzahl": 2, "startTick": 60, "abstandTicks": 150, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-09", "_note": "Bruchwelle: der Typ, der die bisherige Hauptantwort kontert. Wer auf Elementarschaden gesetzt hat, laeuft hier in Bann plus Verlangsamungsimmunitaet plus Heilung. Orakel danach.", "brett": "board.die-enge", "nummer": 9, "prueft": "gemischt", "pauseNach": ["orakel"], "gruppen": [ { "gegner": "enemy.banntraeger", "anzahl": 2, "startTick": 0, "abstandTicks": 120, "pfad": "path.enge-boden" }, { "gegner": "enemy.frostbrand-zelot", "anzahl": 3, "startTick": 60, "abstandTicks": 90, "pfad": "path.enge-boden" }, { "gegner": "enemy.schlundgeist", "anzahl": 2, "startTick": 150, "abstandTicks": 120, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-10", "_note": "Doppelraetsel: Luft und Ruestung gleichzeitig - zwei Antworten, die sich raeumlich widersprechen.", "brett": "board.die-enge", "nummer": 10, "prueft": "luft", "pauseNach": ["draft"], "gruppen": [ { "gegner": "enemy.aschekraehe", "anzahl": 8, "startTick": 0, "abstandTicks": 20, "pfad": "path.enge-luft" }, { "gegner": "enemy.wallbrecher", "anzahl": 3, "startTick": 30, "abstandTicks": 150, "pfad": "path.enge-boden" }, { "gegner": "enemy.zunder-schwarm", "anzahl": 28, "startTick": 0, "abstandTicks": 9, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-11", "_note": "Zweites Doppelraetsel: Heilung hinter Bann, dazu Tempo. Beutetisch 3 - der letzte, danach faellt Beute nur noch nach der Schlacht.", "brett": "board.die-enge", "nummer": 11, "prueft": "heilung", "pauseNach": ["beutetisch"], "gruppen": [ { "gegner": "enemy.schlundgeist", "anzahl": 3, "startTick": 0, "abstandTicks": 120, "pfad": "path.enge-boden" }, { "gegner": "enemy.frostbrand-zelot", "anzahl": 4, "startTick": 30, "abstandTicks": 60, "pfad": "path.enge-boden" }, { "gegner": "enemy.banntraeger", "anzahl": 2, "startTick": 90, "abstandTicks": 150, "pfad": "path.enge-boden" } ] }, { "id": "wave.enge-12", "_note": "Abschlusswelle. Im Kanon steht hier der Boss \`Der Verzehrer\` - er ist der 9. Gegnertyp und kommt mit AP-1.10. Bis dahin ist die zwoelfte Welle eine Elitewelle, die alle fuenf Raetsel gleichzeitig auf das Brett legt: Ruestung, Bann, Heilung, Tempo, Luft. Sie ist damit dieselbe Pruefung wie der Boss - drei Antworten gleichzeitig -, nur ohne Phasen.", "brett": "board.die-enge", "nummer": 12, "prueft": "gemischt", "pauseNach": ["draft"], "gruppen": [ { "gegner": "enemy.zunder-schwarm", "anzahl": 36, "startTick": 0, "abstandTicks": 7, "pfad": "path.enge-boden" }, { "gegner": "enemy.wallbrecher", "anzahl": 4, "startTick": 0, "abstandTicks": 120, "pfad": "path.enge-boden" }, { "gegner": "enemy.aschekraehe", "anzahl": 8, "startTick": 30, "abstandTicks": 24, "pfad": "path.enge-luft" }, { "gegner": "enemy.frostbrand-zelot", "anzahl": 4, "startTick": 60, "abstandTicks": 90, "pfad": "path.enge-boden" }, { "gegner": "enemy.banntraeger", "anzahl": 3, "startTick": 90, "abstandTicks": 150, "pfad": "path.enge-boden" } ] } ] `,nn=JSON.parse(Gt),Wt=JSON.parse(_t),tn=JSON.parse(Ht),Ot=JSON.parse(xt),Ut=JSON.parse(Kt),Ae=1e3,X=e=>e/Ae;function jt(){return tn.find(n=>n.id==="curve.kopfgeld")?.kurve.values??[]}function Ct(){const e=tn.find(n=>n.id==="curve.wellen-hp");return!e||e.kurve.kind!=="geometric"?[]:Nt(e.schritte,e.kurve.base??1e3,e.kurve.factorPermille??1e3)}function Zt(){return{brett:nn[0],tuerme:Ot,gegner:Wt,wellen:Ut,kopfgeldJeWelle:jt(),hpFaktorJeWelle:Ct()}}const Vt={[I.bau]:he.bauphase,[I.welle]:he.welle,[I.sieg]:he.ende,[I.niederlage]:he.ende};function Jt(e){const n=Bt(nn),t=Zt(),i=$t({content:t}),s=new Map;t.tuerme.forEach((m,p)=>s.set(m.id,p));const a=m=>t.tuerme[m]?.id??null,o=zn(Y);let f=1,u=!1,h=i.kernpunkte;function M(){const m=o.schreiben();m.tick=i.tick;const p=i.gegnerListe(),E=Math.min(p.length,Y.gegner);for(let w=0;w{const E=n.bauplaetze[p.slot],d=E?m.find(S=>S.x===E.position.x*Ae&&S.y===E.position.y*Ae):void 0,z=p.frei?p.optionen.map(S=>({aktion:"bauen",turm:a(S.turm),kosten:S.kosten,erloes:0,erlaubt:S.erlaubt,hindernis:S.erlaubt?L.keines:L.gold})):[{aktion:"aufwerten",turm:d?a(d.art):null,kosten:p.optionen[d?.art??0]?.kosten??0,erloes:0,erlaubt:(p.optionen[d?.art??0]?.erlaubt??!1)||!1,hindernis:L.gold},{aktion:"verkaufen",turm:d?a(d.art):null,kosten:0,erloes:0,erlaubt:!0,hindernis:L.keines}];return{slot:p.slot,sorte:E?.sorte??"standard",turmId:d?.id??null,turm:d?a(d.art):null,stufe:d?d.stufe+1:0,zielwahl:d?d.zielwahl:null,bindeglieder:[],angebote:z}})},absicht(m){if(m.intent===A.pause){u=m.payload.pausiert??!u;return}if(m.intent===A.zeitraffer){f=m.payload.stufe??1;return}if(m.intent==="zielwahl"){i.absicht("zielwahl",m.payload);return}if(m.intent===A.bauen){const p=m.payload,E=s.get(p.turm);if(E===void 0)return;i.absicht(A.bauen,{slot:p.slot,turm:E});return}i.absicht(m.intent,m.payload)},stateHash(){return i.hash()}}}const qt=12,Xt={[L.keines]:"",[L.gold]:"zu wenig Gold",[L.phase]:"in dieser Phase nicht",[L.belegt]:"Bauplatz belegt",[L.leer]:"Bauplatz leer",[L.kontingent]:"Bau-Kontingent erschoepft",[L.aufgebot]:"nicht im Aufgebot",[L.endstufe]:"Endstufe erreicht",[L.verbraucht]:"schon verbraucht",[L.unbekannt]:"unbekannt"};function W(e){const n=document.getElementById(e);if(n===null)throw new Error(`Bedienung: Element #${e} fehlt in index.html`);return n}function Yt(e){switch(e.aktion){case O.bauen:return`${e.turm??"?"} · ${e.kosten}g`;case O.aufwerten:return`aufwerten · ${e.kosten}g`;case O.verkaufen:return`verkaufen · +${e.erloes}g`;case O.stilllegen:return"stilllegen";case O.aktivieren:return"aktivieren"}}function Qt(e,n){switch(n.aktion){case O.bauen:return n.turm===null?null:{intent:A.bauen,payload:{slot:e,turm:n.turm}};case O.aufwerten:return{intent:A.aufwerten,payload:{slot:e}};case O.verkaufen:return{intent:A.verkaufen,payload:{slot:e}};case O.stilllegen:return{intent:A.stilllegen,payload:{slot:e,stillgelegt:!0}};case O.aktivieren:return{intent:A.stilllegen,payload:{slot:e,stillgelegt:!1}}}}function ei(e){if(e===null)return"-";const n=e.angebote.map(t=>`${t.aktion}:${t.turm??""}:${t.kosten}:${t.erloes}:${t.erlaubt?1:0}:${t.hindernis}`);return`${e.slot}|${e.turm??""}|${e.stufe}|${e.zielwahl??""}|${n.join(",")}`}function ni(e){const n=W("vitals"),t=W("debug"),i=W("bauleiste"),s=W("protokoll"),a=W("seed"),o=W("knopf-pause"),f=W("zeitraffer");let u=!1,h="";W("knopf-angriff").addEventListener("click",()=>{e.absicht({intent:A.welleStarten,payload:{}})}),W("knopf-feldruf").addEventListener("click",()=>{e.absicht({intent:A.feldruf,payload:{}})}),o.addEventListener("click",()=>{e.absicht({intent:A.pause,payload:{pausiert:!u}})}),W("knopf-neu").addEventListener("click",()=>{e.neustart(a.value)}),a.addEventListener("keydown",d=>{d.key==="Enter"&&e.neustart(a.value)});for(const d of bn){const z=document.createElement("button");z.textContent=`${d}x`,z.dataset.stufe=String(d),z.addEventListener("click",()=>{e.absicht({intent:A.zeitraffer,payload:{stufe:d}})}),f.append(z)}function M(d){u=d.zeitraffer===0,o.textContent=u?"Weiter":"Pause",n.textContent=`Gold ${d.gold} · Kern ${d.kernpunkte}/${d.kernpunkteMax} · Welle ${d.welle}/${d.wellenGesamt} · ${d.phase} · Gegner ${d.gegnerAktiv} · Fortschritt ${d.wellenFortschritt}‰`+(d.feldrufBereit?" · Feldruf bereit":"");for(const z of Array.from(f.querySelectorAll("button")))z.classList.toggle("aktiv",z.dataset.stufe===String(d.zeitraffer))}function m(d,z,S){t.textContent=`hash ${d} · ${z.toFixed(0)} fps · ${S.toFixed(0)} tps`}function p(d){const z=ei(d);if(z===h)return;if(h=z,i.replaceChildren(),d===null){const y=document.createElement("span");y.className="hinweis",y.textContent="Bauplatz anklicken",i.append(y);return}const S=document.createElement("span");if(S.className="hinweis",S.textContent=`Slot ${d.slot} (${d.sorte})`+(d.turm===null?" · frei":` · ${d.turm} St.${d.stufe}`),i.append(S),d.zielwahl!==null){const y=document.createElement("span");y.className="hinweis",y.textContent="zielt auf",i.append(y);for(const[w,T,H]of[[ge.erster,"⇥","Vorderster — am weitesten auf dem Weg"],[ge.naechster,"◎","Nächster — kürzeste Entfernung zum Turm"],[ge.entferntester,"⇤","Entferntester — am wenigsten weit"],[ge.staerkster,"❤","Stärkster — höchste verbliebene Lebenspunkte"]]){const D=document.createElement("button");D.textContent=T,D.title=H,D.disabled=d.zielwahl===w,d.zielwahl===w&&(D.title=`${H} (gewählt)`),D.addEventListener("click",()=>{e.absicht({intent:A.zielwahl,payload:{slot:d.slot,modus:w}})}),i.append(D)}}for(const y of d.angebote){const w=document.createElement("button");w.textContent=Yt(y),w.disabled=!y.erlaubt,y.erlaubt||(w.title=Xt[y.hindernis]),w.addEventListener("click",()=>{const T=Qt(d.slot,y);T!==null&&e.absicht(T)}),i.append(w)}}function E(d){for(const z of d){const S=document.createElement("li");S.textContent=`t${z.tick} ${z.art} ${JSON.stringify(z.nutzlast)}`,z.art==="intent-abgelehnt"&&(S.className="abgelehnt"),s.prepend(S)}for(;s.childElementCount>qt;)s.lastElementChild?.remove()}return{zeigeVitals:M,zeigeDebug:m,zeigeBauplatz:p,protokolliere:E,seed:()=>a.value}}function ti(e){let n=e>>>0;return()=>{n=n+1831565813>>>0;let t=n;return t=Math.imul(t^t>>>15,t|1),t^=t+Math.imul(t^t>>>7,t|61),((t^t>>>14)>>>0)/4294967296}}const Ce=1e3/30,ii=250;function ri(e){let n=!0,t=performance.now(),i=0,s=60,a=0,o=t,f=0;function u(h){if(!n)return;const M=Math.max(1,h-t);for(t=h,s=s*.9+1e3/M*.1,i=Math.min(i+M,ii);i>=Ce;){i-=Ce;const p=e.tempo();p>0&&(e.schritt(p),a+=p)}const m=h-o;m>=500&&(f=a*1e3/m,a=0,o=h),e.bild(s,f),requestAnimationFrame(u)}return requestAnimationFrame(u),()=>{n=!1}}const li=4096,Ze=["#e6b422","#4fa3ff","#7ed957","#ff6b6b","#c792ea","#ffa94d","#4dd0c1","#f06292"];function Ie(e){return Ze[e%Ze.length]??"#ffffff"}function rn(e){return e/li*Math.PI*2}function ee(e,n,t,i){e.beginPath(),e.arc(n,t,i,0,Math.PI*2)}function ai(e,n,t,i){e.save(),e.strokeStyle="#1d232e",e.lineWidth=1;for(let o=0;o<=t.spalten;o+=1)e.beginPath(),e.moveTo(B(n,o),G(n,0)),e.lineTo(B(n,o),G(n,t.reihen)),e.stroke();for(let o=0;o<=t.reihen;o+=1)e.beginPath(),e.moveTo(B(n,0),G(n,o)),e.lineTo(B(n,t.spalten),G(n,o)),e.stroke();e.strokeStyle="#2f3a4d",e.setLineDash([4,4]),e.lineWidth=1;for(const o of t.bindeglieder){const f=t.bauplaetze[o[0]],u=t.bauplaetze[o[1]];f===void 0||u===void 0||(e.beginPath(),e.moveTo(B(n,f.position.x),G(n,f.position.y)),e.lineTo(B(n,u.position.x),G(n,u.position.y)),e.stroke())}e.setLineDash([]);for(const o of t.pfade)e.strokeStyle=o.art==="luft"?"#3a4f6b":"#5a6a4a",e.lineWidth=o.art==="luft"?2:Math.max(3,n.skala*.5),e.lineJoin="round",e.lineCap="round",o.art==="luft"&&e.setLineDash([10,8]),e.beginPath(),o.wegpunkte.forEach((f,u)=>{const h=B(n,f.x),M=G(n,f.y);u===0?e.moveTo(h,M):e.lineTo(h,M)}),e.stroke(),e.setLineDash([]);e.fillStyle="#8fbf5a";for(const o of t.eingaenge)ee(e,B(n,o.x),G(n,o.y),Math.max(4,n.skala*.22)),e.fill();const s=n.skala*1.4,a=n.skala*.5;e.fillStyle="#c8443c",e.fillRect(B(n,t.tor.x)-s/2,G(n,t.tor.y)-a,s,a),e.fillStyle="#0b0e13",e.font="10px monospace",e.textAlign="center",e.fillText("KERN",B(n,t.tor.x),G(n,t.tor.y)-a/4);for(const o of t.bauplaetze){const f=B(n,o.position.x),u=G(n,o.position.y),h=n.skala*.4;e.lineWidth=o.slot===i?3:1.5,e.strokeStyle=o.slot===i?"#ffffff":o.sorte==="hoehensockel"?"#8f7dd6":o.sorte==="grabensteg"?"#5aa9bf":"#6f7a8a",e.fillStyle="#141922",ee(e,f,u,h),e.fill(),e.stroke(),e.fillStyle="#7d8798",e.font="10px monospace",e.textAlign="center",e.textBaseline="middle",e.fillText(String(o.slot),f,u+h+8)}e.restore()}function si(e,n,t,i){const s=t.tuerme;e.save(),e.textAlign="center",e.textBaseline="middle";for(let a=0;a fehlt in index.html');const t=n,i=t.getContext("2d");if(i===null)throw new Error("haupt: kein 2D-Kontext");const s=i;let a=e(fi),o=a.brett(),f=null,u=ae;const h=ti(24301),M=ni({absicht:d=>{a.absicht(d)},neustart:d=>{a=e(d),o=a.brett(),f=null,u=ae}});function m(){return Sn(o,t.clientWidth,t.clientHeight)}t.addEventListener("click",d=>{const z=t.getBoundingClientRect(),S=m(),y=Tn(S,d.clientX-z.left),w=En(S,d.clientY-z.top);let T=null,H=di;for(const D of o.bauplaetze){const ue=Math.hypot(D.position.x-y,D.position.y-w);ue{if(!(document.activeElement instanceof HTMLInputElement))switch(d.key){case" ":d.preventDefault(),a.absicht({intent:A.pause,payload:{pausiert:a.vitals().zeitraffer!==0}});break;case"Enter":a.absicht({intent:A.welleStarten,payload:{}});break;case"f":a.absicht({intent:A.feldruf,payload:{}});break;case"1":a.absicht({intent:A.zeitraffer,payload:{stufe:1}});break;case"2":a.absicht({intent:A.zeitraffer,payload:{stufe:2}});break;case"4":a.absicht({intent:A.zeitraffer,payload:{stufe:4}});break;case"Escape":f=null,u=ae;break}});function p(){const d=window.devicePixelRatio>0?window.devicePixelRatio:1,z=Math.round(t.clientWidth*d),S=Math.round(t.clientHeight*d);t.width!==z&&(t.width=z),t.height!==S&&(t.height=S),s.setTransform(d,0,0,d,0,0)}function E(){return f===null?null:a.bauplaetze().find(d=>d.slot===f)??null}ri({tempo:()=>a.vitals().zeitraffer,schritt:d=>{a.schritt(d)},bild:(d,z)=>{p();const S=m(),y=a.snapshot();s.fillStyle="#0b0e13",s.fillRect(0,0,S.breite,S.hoehe),ai(s,S,o,f),ci(s,S,o,f),oi(s,S,y),si(s,S,y,h),ui(s,S,y),M.protokolliere(a.ereignisse()),u+=1e3/Math.max(d,1),u>=ae&&(u=0,M.zeigeVitals(a.vitals()),M.zeigeDebug(a.stateHash(),d,z),M.zeigeBauplatz(E()))}})}try{hi(Jt)}catch(e){const n=document.createElement("pre");throw n.className="absturz",n.textContent=e instanceof Error?`${e.name}: ${e.message}`:"Absturz",document.body.prepend(n),e}