dev #305

Merged
lanta merged 17 commits from dev into main 2026-07-30 19:41:07 +00:00
Owner
No description provided.
DTCH Battlegrounds: mini battle royale onder /br/
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 20s
21165c8528
Neemt de Claude-of-Duty-engine (MIT) over die ook op lanta.eu/cod draait en
bouwt er een battle royale op: je begint met niets, wapens liggen onder
lichtbundels op de grond, en een cirkel sluit in vijf fases terwijl alles
erbuiten leegloopt. Laatste die staat wint.

Nieuw subsysteem src/br/ (zone, loot, eigen HUD, matchregels) plus drie
haakjes in src/weapons/ die het nodig had: unequip(), equip(id) en
addReserve(). Volgens het engine-contract staat src/br/ nu in de
ownership-map van ARCHITECTURE.md en zijn de drie br:*-events daar
gedocumenteerd.

Wat er niet in zit: bots vechten niet onderling. src/ai/ kijkt alleen naar de
speler en Agent.team wordt nergens gelezen, dus dat valt niet aan te zetten.
De cirkel dunt het veld uit in plaats daarvan.

Bron staat in br-src/ en wordt niet naar de webhost geupload (exclude in
dtchsyn.yaml); de gebouwde versie in br/ wel, want de deploy spiegelt
bestanden en heeft geen bouwstap. base staat op './' zodat dev en live
hetzelfde pad-onafhankelijke bundel gebruiken.

Getest met tools/br-rules.test.mjs (29 checks op de cirkelwiskunde en de
loot-regels, draait zonder browser) en tools/br-smoke.mjs voor een echte
browserrun; die laatste vraagt een GPU.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Diagnose waarom het spel niet start zonder GPU, plus tools/br-diag.mjs
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 28s
393a2c3679
Zonder GPU valt Chromium terug op SwiftShader, ontbreekt
KHR_parallel_shader_compile en compileren de shaders serieel op de
hoofdthread. Gemeten op de Pi: engine volledig opgebouwd in ~75 s, frame 1
getekend, frame 2 komt er in een half uur niet.

Het spel is daar niet stuk van: br-diag.mjs leest de live staat uit de
draaiende pagina en die klopt (prep, alive=10, armed=false, r=58.0,
loot=18 op de echte levelgeometrie), zonder enkele paginafout. Alleen te
traag om er een test op te draaien.

br-rules.test.mjs is daarom de dagelijkse poort; br-smoke.mjs hoort op een
machine met echte grafische kaart. Staat nu ook zo in BATTLEGROUNDS.md.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Nieuwe kaart: open Erangel-achtig veld in plaats van het straatlevel
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 28s
4e3514b3be
src/level/ vervangt src/world/ als 'world': 150 x 150 m golvend veld met een
heuvel van 14 m, een betonnen huisje met deuropening en drie raamgaten, twee
uitgebrande auto's, en rotsen en bomen als dekking. Door bladeren kun je heen
schieten, alleen de stam blokkeert. Het oude straatlevel blijft op schijf —
één import in main.js omwisselen en het staat er weer.

De grondhoogte is een formule en het botsingsmodel wordt uit diezelfde functie
gegenereerd, dus groundHeight() en de vloer waar je op staat kunnen niet uit
elkaar lopen. De heuvel blijft onder de 46° hellingsgrens van het navmesh
(gemeten steilste punt 31,7°), anders wordt het hoge terrein onbereikbaar voor
bots.

Zone en loot meegeschaald naar de grotere kaart: startstraal 66 m, fases naar
48/33/21/12/5, 16 wapens en 8 munitiekisten.

Ook gefixt: br/_fillRoster gebruikte grid.nearest() alsof het coordinaten
teruggaf, maar het geeft een cel-index. Bots werden daardoor nooit op het
navmesh gezet en konden in principe stil blijven staan.

Getest met tools/level.test.mjs (37 checks: begaanbaarheid, vlakke bouwplaat,
botsingsmodel gelijk aan de hoogtefunctie, huis hol en betreedbaar, alles bij
physics geregistreerd, spawns in het open veld) plus de bestaande
br-rules.test.mjs. In de browser bevestigd: engine start nu in 36 s in plaats
van 105 s, en meldt "prep alive=10 armed=false r=66.0 loot=24" — de bots zijn
geplaatst, dus het navmesh op het nieuwe terrein werkt.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Fix: bomen met gaten erin, en spawnen middenin een rots
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 33s
444d16b341
Twee echte fouten achter de melding "ik zie vreemde objecten en kan niet
bewegen".

1. De boomkruinen gebruikten het foliage-materiaal. Dat is geschreven voor
   platte bladkaarten: alpha-masked (alphaTest 0.45) en dubbelzijdig. Op een
   massieve kegel prikt dat de kruin vol gaten waar je de binnenkant
   doorheen ziet. Nu zonder masker, enkelzijdig en ondoorzichtig.

2. isOpen() beweerde "hier kun je staan" voor punten binnen een rots, een
   wrak of een boomstam — het kende alleen het huis en de kaartrand. Daar
   kiest alles een plek mee: de dropplek van de speler, de botspawns en waar
   loot valt. De engine kiest per start een andere willekeurige plek, dus dit
   sloeg de ene keer wel en de andere keer niet toe. Nu houdt het level een
   lijst van massieve objecten bij en checkt br daarnaast met
   physics.checkCapsule of een staande capsule er echt past.

De nieuwe test vond meteen dat het startpunt op de heuveltop in een prop lag;
de spawnpunten worden nu vastgelegd voordat rotsen en bomen verspreid worden.

Let op bij het meten: checkCapsule telt ook rakend contact, dus een capsule
die op de voeten begint raakt altijd de vloer en meldt "geblokkeerd". Vandaar
de 8 cm speling.

level.test.mjs uitgebreid met: geen enkel massief object mag als open gelden,
en 20.000 steekproeven waarbij geen open punt in een prop mag liggen.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
tools/whats-in-view.mjs: uitsluiten dat de boot-placeholder het probleem is
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 17s
79bd1842dc
Vraagt de scene-graph welke objecten vlak voor de camera staan, zonder extra
frames nodig te hebben. Gemeten: probeActive=false met 174 foreign meshes op
frame 2, dus de placeholder van de renderer is bij het spelen allang weg en
kan de gemelde "vreemde objecten" niet verklaren.

Nuttig detail voor later: een bounding-box-test geeft afstand 0 voor alles
waar de camera in zit, en dat zijn hier altijd het terrein (150x150) en de
zonecilinder (132x70x132). Alleen de derde regel en verder zegt iets.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Fix de vreemde objecten: unequip liet de armen staan
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 29s
e38fea4ea6
Dit was het echte probleem achter "ik zie vreemde objecten en kan niet
bewegen". De first-person armen en handschoenen hangen aan het
viewmodel-anker, LOS van het wapen, en Viewmodel.update() stopt meteen als er
geen wapen actief is. Mijn unequip() verstopte alleen het wapen, dus bleven de
armen over: nooit geposeerd, en hun anker volgde de camera niet. Gemeten: 111
meshes werden op centimeters van het oog getekend. viewmodel.js waarschuwt er
zelf voor ("a 100 mm-wide sleeve fills half the screen").

Dat verklaart ook waarom het verdween zodra je verder liep: je pakte een wapen
op, update() ging weer draaien en poseerde alles netjes.

unequip() verbergt nu het hele anker, setWeaponImmediate() zet het terug.

tools/viewmodel-check.mjs erbij: telt wat er in de viewScene zichtbaar is.
Ongewapend 0 meshes, na equip('rifle') 111 en het anker zichtbaar, na unequip
weer 0.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
HALF 75 -> 112,5. Tweede heuvel erbij (10 m, zuidoosten) naast de grote van
18 m, zodat het open midden vanaf twee kanten onder vuur ligt. Terrein van
120 naar 180 segmenten zodat de driehoekdichtheid gelijk blijft.

Nieuwe dekking: zeecontainers, betonnen barrieres en kratstapels, plus meer
rotsen (26), bomen (38) en wrakken (4). Samen 94 objecten; 95,7% van het veld
blijft beloopbaar, dus het speelt als open terrein en niet als doolhof.

Zone en loot meegeschaald: startstraal 99 m, fases 72/50/32/18/7, 26 wapens en
13 munitiekisten. Elf spawnpunten in plaats van acht.

Gemeten: 70.504 driehoeken in 335 draws, steilste helling 30,4 graden (ruim
onder de 46 waar het navmesh cellen laat vallen), en de opstarttijd blijft 37 s
tegen 36 s op de kleine kaart — het grotere navmesh kost dus vrijwel niets. In
de browser bevestigd: alive=10, r=99.0, loot=39.

De test "open veld is open" prikte op twee vaste coordinaten en werd onbetrouw-
baar met 94 props; die meet nu of het veld als geheel grotendeels open blijft.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Wapenrooster AR / SMG / DMR, met een scope die echt inzoomt
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 29s
e75454eb60
Het pistool is vervangen door een MK-14 EBR: alleen semi, 300 rpm, 62 schade,
mag van 20, 700 m bereik en bar slecht vanaf de heup (4,2 graden spreiding) —
je moet er echt mee richten.

Het model is buildRifle() plus een scope op de rail. Alles waar de rig van
afhangt (de polsdoelen voor de arm-IK, magazijnzitting, grendel- en
trekkerslag, monding en huls) is in rifle.js opgelost tegen die geometrie en
wordt zo overgenomen; een tweede ontvanger natekenen zou dat allemaal opnieuw
moeten afleiden voor niets. Alleen nodes.sight verhuist naar de oculairlens,
want dat is waar de viewmodel het oog op uitlijnt bij richten.

De scope zelf: elk wapen had al een adsFov in zijn definitie, maar niets las
het — iedereen deelde de globale config.adsFovScale. Nu doorverbonden van
weapons via player.setAdsProgress() naar player/camera.js, dus elk wapen kan
zijn eigen vergroting hebben. Gemeten gezichtsveld: heup 80 graden, AR 59,2,
SMG 62,4, DMR 25,6 — dat is 2,3x de vergroting van de AR.

Muisgevoeligheid schaalt mee met de vergroting, anders is met zo'n zoom niet te
mikken; wapens zonder eigen zoom komen op factor 1 uit en voelen exact als
voorheen.

class: 'sniper' is hier load-bearing: fx/muzzle.js kiest daarop de zwaardere
mondingsflits en audio/weapons.js het diepere schot met langere staart. Het id
'dmr' matcht dezelfde audioregel, dus die twee spreken elkaar niet tegen.

Ook bijgewerkt: Digit3 pakt nu de DMR, br-loot kent de kleur en het silhouet
ervan (zonder kleur krijgt de pickup stilzwijgend een wit standaardmateriaal),
en preview.js toont hem naast het pistoolmodel dat blijft bestaan.

tools/weapons-check.mjs erbij: controleert het rooster, dat elk wapen bouwt en
equipt, en rekent de zoom door door de camera-rig met de hand te stappen — dat
hoeft niet te wachten op frames.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Wedstrijd start pas na de klik, met 15s aftellen; bots vluchten de cirkel in
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 28s
8d9096a5b7
1. Niets loopt meer tijdens het laadscherm. br blijft in 'waiting' tot
   ctx.input.enabled aangaat — precies wat boot.ready() doet zodra je op CLICK
   TO PLAY klikt. Geen bots, geen cirkel, geen schade daarvoor.

2. Daarna 15 seconden aftellen (BR.startCountdown). Je kunt al rondlopen en
   loot oppakken; het veld verschijnt pas op nul, in een keer. De AI kreeg
   daarvoor een schakelaar autoPopulate: hij zette anders zijn eigen garnizoen
   neer tijdens het laden en die waren al aan het jagen voordat het potje begon.

3. Bots lopen nu terug de cirkel in. Dat kon niet van buitenaf: de AI kiest elke
   tick zelf een bestemming zodra hij de speler ziet, dus een van buiten gezette
   route wordt binnen twee ticks overschreven. Kleinste nette ingreep is
   agent.externalGoal, dat _think() honoreert vóór zijn eigen toestandsmachine;
   br zet het zolang een bot buiten staat en haalt het weg zodra hij binnen is,
   waarna normaal gevecht vanzelf hervat. Ze rennen (4,6 m/s) en schieten niet
   onderweg, net als de gewonde-terugtrekking al deed.

4. Haperingen tijdens het spelen: de voorverwarming van shaders richtte zich nog
   op vier camerastandpunten uit het oude straatlevel — op 225 m open veld kijken
   die naar gras, dus half de kaart compileerde alsnog tijdens het spelen. Die
   standpunten wijzen nu naar het huis, de heuvel en de dekking, en src/level/
   en src/br/ hebben allebei de gedocumenteerde prewarmMaterials-haak gekregen.

   Gemeten blijft er dan nog een gat: direct na het opstarten compileert een
   tweede run 14 programma's die de opstart-voorverwarming niet ving (de scene
   staat er bij het opstarten net anders bij dan tijdens het spelen). Daarom
   verwarmt br nog een keer zodra de aftelling begint — dat is het enige moment
   waarop een paar honderd milliseconde compileren niets kost. Na die tweede
   ronde is de teller nul.

tools/matchstart-check.mjs erbij: 18 checks over de hele startvolgorde en het
terugsturen van bots, plus de meting van wat de voorverwarming laat liggen.
Draait zonder GPU door de spellogica met de hand te stappen.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Speelbaar op de telefoon: schermbesturing
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 19s
db110cc5f2
src/core/touch.js: duimstick links (verschijnt onder je duim, helemaal vooruit
= sprinten), rechterhelft slepen om te kijken en tikken om te schieten, plus
knoppen voor vuur, richten, herladen, springen en bukken.

Alles loopt via dezelfde momentopname die toetsenbord en muis vullen: de
knoppen drukken letterlijk de codes in die het spel al kent (Mouse0, Mouse2,
Space, KeyR, ControlLeft) en de kijkbeweging telt op bij dezelfde ruwe
look-delta als een muis. Aan de gameplay is dus niets veranderd. Input kreeg er
drie deurtjes voor: injectDown/injectUp/injectLook, plus tapFire.

Een tik houdt de trekker precies een frame vast, met firePressed erbij — genoeg
voor een schot, en de frame erna is hij weer los, anders zou tikken automatisch
vuur worden.

Verder voor mobiel: geen pinch-zoom meer (viewport), volledig scherm en
landschap-lock bij het starten (best-effort, een weigering mag het spel niet
tegenhouden), een hint als je de telefoon rechtop houdt, en herstarten na
afloop luistert nu naar pointerup in plaats van click — de schermbesturing roept
preventDefault aan en dan hoeft een browser geen click meer te synthetiseren.

Aanzetten gebeurt op '(pointer: coarse)' plus maxTouchPoints; op een desktop te
testen met ?touch=1.

tools/touch-check.mjs erbij: 14 checks op een geemuleerde telefoon (780x360,
hasTouch), die synthetische pointer-events afvuurt zoals een duim dat doet.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Het spel opgenomen in de site: menu-item en blok op de homepage
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 28s
17b8e36188
'Play' in de navigatie (verwijst naar de map br/, niet naar een script, dus de
actief-check ernaast matcht hem nooit — dat klopt ook, het is geen pagina van
deze site) en een promoblok bovenaan de homepage in de bestaande kaartstijl.

Het blok is een panel-brede link: card-link eroverheen, inhoud op
pointer-events:none, zodat de hele kaart klikbaar is en niet alleen de tekst.
Stapelt onder 640px zodat het op de telefoon leesbaar blijft.

Gecontroleerd met php -l en door navigation.php te renderen; de site heeft
verder geen buildstap.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Nieuw potje zonder herladen, en een scope waar je door kunt kijken
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 29s
00c3641282
1. Doodgaan of winnen herlaadt de pagina niet meer. br.restart() zet alles
   terug: bots weg, loot terug, cirkel terug op de openingsstraal, leven vol,
   handen leeg, munitie bijgevuld, eindscherm weg, en dan dezelfde aftelling
   van 15 seconden als bij een normale start. Een herlaadactie draaide de hele
   opstart opnieuw — alle textures, meshes en het navmesh — voor een level dat
   niet veranderd is.

   Het enige gat zat in de AI: die kon zichzelf compleet afbreken maar had geen
   manier om alleen zijn actoren op te ruimen, waardoor lijken en hun
   squad-verwijzingen zich zouden opstapelen. Daar is clearAgents() voor
   toegevoegd, met precies de drie regels die dispose() ook al deed.

   Niet teruggezet, met opzet: kogelgaten, hulzen en bloed van het vorige potje.
   Die recyclen zichzelf, en de pools weggooien kost meer dan het oplevert.

2. De scope van de DMR was een massieve cilinder. Van buiten een scope, van
   binnen een muur — je keek tegen dicht aluminium aan. Nu gebouwd met
   buildOptic, dezelfde functie die het rode-puntvizier van de AR maakt: een
   echte buis met doorkijk, oculairlens, lichtval en een vrije opening waar het
   dradenkruis tegenaan vignetteert. Gemeten 42 mm dik, 85 mm lang, opening
   31 mm.

   buildRifle() accepteert daarvoor { optic: 'none' }, zodat de DMR niet twee
   vizieren op dezelfde rail heeft staan, en geeft de railhoogte terug zodat het
   monteren niet opnieuw afgeleid hoeft te worden.

   Proporties zijn niet vrij te kiezen: rifle.js legt uit dat het zichtbeeld de
   objectiefboring is, ondersteund op (eyeRelief + len), dus een langere buis
   geeft een KLEINER beeld. Vandaar dat de buis en de oogafstand meegroeien:
   21 mm / 85 mm / 145 mm geeft 0,63 van de behuizing, tegen 0,69 bij het rode
   punt. De vergroting zelf is geen geometrie maar adsFov.

Testen uitgebreid: matchstart-check doet nu winnen -> herstarten -> tweede potje
(9 nieuwe checks, waaronder dat de pagina niet herlaadt en dat squads leeg zijn),
weapons-check controleert dat de scope een echte doorkijk heeft.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Eindscherm blijft staan: knop na 4,5 s in plaats van klik-overal
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 19s
2b29791763
Winnen of doodgaan liet het resultaat vaak niet zien: herstarten hing aan
"klik ergens", en de klik waarmee je de laatste bot neerschoot telde meteen
mee. Nu staat er een PLAY AGAIN-knop op het eindscherm die pas na BR.endGrace
(4,5 s) infadet; tot die tijd doet klikken en tikken helemaal niets.

Ook: bij het eindscherm wordt de muis vrijgegeven (exitPointerLock). Zonder dat
is er op de desktop geen cursor om die knop mee te raken, en bleef je met een
gelockte muis over je eigen resultaat heen zwaaien. Bij het starten van het
volgende potje wordt de lock weer aangevraagd; op een telefoon is dat een no-op.

matchstart-check controleert nu ook dat het scherm niet meteen weg te klikken
is, dat trigger-rammen het niet overslaat, dat de knop daarna verschijnt, en
herstart voortaan via die knop in plaats van via de methode.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Eigen speelpagina: het spel in een kader, je blijft op de site
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 20s
7897574e8a
play.php erbij, met de gewone navigatie, header en footer eromheen. Het spel
draait in een 16:9-kader op de pagina in plaats van als losse volledige pagina.
Daaronder vier kaarten: toetsenbord+muis, de besturing op de telefoon, hoe de
cirkel werkt en de drie wapens.

De iframe wordt pas aangemaakt als je op Start game drukt. De bundel is 1,5 MB
en begint bij binnenkomst meteen shaders te compileren; dat wil je niet doen bij
iemand die alleen even doorklikte om te lezen. Op de iframe staat
allow="pointer-lock; fullscreen", anders kan het spel de muis niet vangen en
doet de knop hieronder niets.

Volledig scherm is nu een knop naast het kader, geen automatisme meer: het spel
vroeg het bij het starten zelf aan op telefoons, en dat trekt je precies van de
site af die je juist niet wilde verlaten.

Menu en het blok op de homepage wijzen naar play.php in plaats van rechtstreeks
naar br/, zodat 'Play' ook als actieve pagina oplicht.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
tools/embed-check.mjs: bewaakt de site-integratie
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 29s
f2712d8d0a
Draait de echte play.php: geen iframe tot je op Start game drukt, de site
eromheen staat er, allow="pointer-lock" staat erop (zonder dat kan het spel de
muis niet vangen), en de engine komt daadwerkelijk op ín het kader.

Dat laatste stuk kan alleen vanaf de site-kant breken, en daar kijkt geen enkele
test van het spel zelf naar. Gedraaid tegen dev: alle checks groen, engine start
in het kader en meldt "waiting" met 39 stuks loot.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Fix: pauzemenu klemde met een onzichtbare cursor
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 29s
62f78920be
Escape opende het menu en gaf de muis vrij, maar browsers weigeren een nieuwe
pointer-lock tot ongeveer een seconde na een Escape. De regel "we zijn de lock
kwijt, dus toon het menu" gebruikte een vlag die alleen gewist werd op het
moment dat hij het menu heropende — nooit zolang het menu al open stond. Dus:

  Escape -> menu open, vlag blijft staan (de exit is asynchroon)
  Resume -> menu dicht, lock geweigerd
  volgende frame -> vlag staat er nog, menu heropent

En won de lock die race toevallig wel, dan zat je met een verborgen cursor
tegen een open menu aan te kijken, dat je per definitie niet kunt aanklikken.

Twee dingen aangepast:
- ui/index.js wist de vlag zolang het menu open is, dus een geweigerde re-lock
  kan het menu niet meer heropenen;
- input.js vraagt de lock alleen nog aan bij een klik op het speelvlak zelf.
  Een klik op een knop (Resume, Play again) hoort de muis niet te vangen — dat
  was wat de cursor boven een open menu liet verdwijnen.

tools/pause-check.mjs erbij: speelt de sequentie na met een browser die de
re-lock weigert, en controleert dat het menu dicht blijft en dat een knopklik
de muis niet grijpt terwijl een klik op het spel dat wel doet.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
Doorlopende cirkel, grote aftelling, en geen hapering meer bij oppakken
All checks were successful
Publish Website / Deploy based on branch (push) Successful in 20s
9b8095920b
1. De hapering bij het oppakken van een wapen: renderer.compile() loopt de scene
   af met traverseVisible, en er is altijd maar één wapen zichtbaar — in battle
   royale even helemaal geen. De twee die je nog niet had waren dus nooit
   voorverwarmd en compileerden op het frame dat je ze voor het eerst vasthield.
   weapons heeft nu een prewarmMaterials() die alle drie tijdelijk zichtbaar
   maakt en tegen viewScene/viewCamera compileert, en de zichtbaarheid exact
   terugzet — ook de leeghandige staat waar de wedstrijd hem al in had gezet.

2. Fases eruit. De cirkel sluit nu doorlopend van 99 naar 6 meter in 5,5 minuut,
   met de schade die over datzelfde venster oploopt van 1,5 naar 12 per seconde.

   De eerlijkheid komt uit één voorwaarde: middelpunt en straal lopen lineair van
   een begin- naar een eindcirkel, en die eindcirkel wordt bínnen de begincirkel
   gekozen. Over elk interval verplaatst het middelpunt (t2-t1)*D en verliest de
   straal (t2-t1)*(R0-R1), dus de latere cirkel ligt binnen de eerdere precies
   als D <= R0-R1 — wat bij de keuze al is afgedwongen. De test loopt de hele
   sluiting af en controleert dat, plus dat hij nergens stilstaat.

3. De aftelling bij de start staat nu groot midden in beeld in plaats van in het
   balkje bovenin. Dat balkje toont voortaan de huidige straal van de cirkel in
   plaats van een fase-teller, want die bestaat niet meer.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShjhDbk1XUdJ3pMicrvRCf
lanta merged commit ea653381e2 into main 2026-07-30 19:41:07 +00:00
Sign in to join this conversation.
No description provided.