Kuva "100228.jpg" on klassinen! Se paljastaa heti olennaisen arkkitehtuurisen
vaatimuksen: kyseessC$ ei ole enC$C$ pelkkC$ staattinen $y = f(x)$ tyyppinen
korkeuskartta (kuten perus Lunar Landerissa), vaan luolasto, jossa on
ulokkeita (overhangs) ja tunneleita. Samaan X-koordinaattiin voi osua sekC$
kiveC$ ettC$ tyhjC$C$ ilmaa useissa eri Y-koordinaateissa.
TC$tC$ ei voi enC$C$ tallentaa yhtenC$ 1D-taulukkona. TC$ssC$ on suunnitelma,
miten tC$mC$ viedC$C$n elegantisti BASIC-kieleen ja Node.js-moottorin
taustalle.
### 1. Uusi BASIC-komento (Eleganssi ja selkeys)
Tarvitsemme komennon, joka ei vain generoi maastoa, vaan erottelee selkeC$sti
sen, ettC$ kyseessC$ on globaali ympC$ristC6, ei yksittC$inen manipuloitava
objekti.
Ehdotan syntaksia, joka sallii parametrien hienosC$C$dC6n:
```basic
60 REM --- LUODAAN LUOLASTOTAUSTA ---
70 MAPGEN "CAVE", SEED 1337, WIDTH 40, ROUGHNESS 5
80 RECT 1, 20, 160, 40, 165, 2: REM PADIT LISCTCCN MASKIIN JCLKIKCTEEN
```
* **`MAPGEN`**: SelkeC$ toiminto.
* **`"CAVE"`**: Kertoo moottorille, mitC$ algoritmia kC$ytetC$C$n
(tulevaisuudessa voi olla `"SURFACE"` tai `"ASTEROID"`).
* **`SEED`**: Takaa, ettC$ luola on identtinen joka kerta kun peli ajetaan
samalla arvolla.
* **`WIDTH` & `ROUGHNESS**`: Helposti ymmC$rrettC$vC$t muuttujat
pelisuunnittelijalle tunnelin keskimC$C$rC$isen leveyden ja seinC$mien
rosoisuuden sC$C$tC6C6n.
### 2. Matemaattiset algoritmit
TC$llaisen luolaston voi generoida pC$C$asiassa kahdella erittC$in tehokkaalla
tavalla, jotka sopivat retro-estetiikkaan:
**Vaihtoehto A: Wandering Trench (Tupla-1D Kohina)**
TC$mC$ on laskennallisesti kevyin ja todennC$kC6isesti lC$hempC$nC$ sitC$,
miten alkuperC$iset 8-bittiset pelit on koodattu.
1. **Keskiviiva:** EdetC$C$n Y-akselia ylhC$C$ltC$ alas ja lasketaan tunnelin
"keskipiste" $X$-akselilla kC$yttC$mC$llC$ matalataajuuksista 1D-kohinaa (tai
siniaaltojen summaa).
2. **Leveys:** Lasketaan toisella, itsenC$isellC$ 1D-kohinalla tunnelin leveys
kyseisessC$ Y-pisteessC$.
3. **SeinC$mC$t:** Vasen seinC$ piirretC$C$n vC$lille $0 \dots (X_{keski} -
Leveys/2)$ ja oikea seinC$ vC$lille $(X_{keski} + Leveys/2) \dots MaxX$.
**Vaihtoehto B: 2D Density Map (Perlin / Simplex Noise)**
TC$mC$ tuottaa orgaanisempaa ja nykyaikaisemman nC$kC6istC$ jC$lkeC$, jossa
voi olla umpinaisia saarekkeita ilmassa.
1. Generoidaan 2D-kohinakenttC$, josta saadaan arvo $N \in [0, 1]$ jokaiselle
$(x, y)$ koordinaatille.
2. MC$C$ritellC$C$n kynnysarvo (esim. 0.5). Jos $N > 0.5$, pikseli on kiveC$;
jos $N \le 0.5$, se on ilmaa.
3. TC$hC$n voidaan kertoa pC$C$lle "gradienttimaski", joka pakottaa reunoille
suurempia arvoja (aina kiveC$) ja keskelle pienempiC$ arvoja (suurempi
todennC$kC6isyys ilmalle).
### 3. JavaScript-taustatoteutus (BACKGROUND vs. SPRITE)
TC$tC$ **ei missC$C$n nimessC$** kannata kC$sitellC$ SpritenC$. Spritet on
tarkoitettu dynaamisille, liikkuville ja itsenC$isille objekteille. Koko
nC$ytC6n kokoinen tC6rmC$ysmaski spritenC$ tappaisi suorituskyvyn ja
vaikeuttaisi pikselintarkkaa tC6rmC$ystarkistusta.
Tarvitsemme ehdottomasti `BACKGROUND`-konseptin.
**Tietorakenne Node.js:ssC$:**
Paras tietorakenne taustalla on litteC$ 1D-taulukko, joka edustaa 2D-verkkoa
(esim. `Uint8Array`). Jos resoluutio on 176x184, taulukon koko on 32384 tavua.
Arvo `0` on tyhjC$C$, arvo `1` kiveC$ ja arvot `2+` voivat olla
laskeutumisalustoja (padeja).
TC6rmC$ystarkistus moottorin `WAIT VBLANK` -syklissC$ on silloin naurettavan
nopea $O(1)$ operaatio:
```javascript
let isSolid = state.retro.bgMask[y * 176 + x];
```
**Seedaaminen JS:ssC$:**
JavaScriptin oma `Math.random()` ei ota vastaan seed-arvoa. Laajennukseen on
kirjoitettava oma kevyt pseudo-satunnaislukugeneraattori (PRNG), esimerkiksi
LCG (Linear Congruential Generator) tai Mulberry32. Kun BASIC antaa komennon
`SEED 1337`, tC$mC$ PRNG alustetaan sillC$, taaten deterministisen luolaston.
**RenderC6inti (HUD & Canvas):**
Aiemmin HTML5 Canvasin ja 2D RGBA-taulukoiden kanssa rakentamasi
korkeuskarttarenderC6ijC$n arkkitehtuuri on tC$ssC$ tC$ydellinen pohja.
Moottorin ei tarvitse lC$hettC$C$ massiivista luolastoa WebSocketin yli joka
ikinen frame.
1. **Init:** Kun `MAPGEN` ajetaan, Node.js luo bitmaskin (kivet ja padit) ja
lC$hettC$C$ tC$mC$n paketin Canvas-HUD:lle tasan kerran.
2. **Offscreen Canvas:** HUD ottaa taulukon vastaan, maalaa sen
pikselipuskuriin (tai piirtC$C$ Vic-20 tyylisillC$ blokeilla)
offscreen-canvakselle.
3. **Loop:** RenderC6intiloopissa (60fps) HUD yksinkertaisesti piirtC$C$
staattisen offscreen-canvasin taustalle kerralla, ja sen pC$C$lle piirretC$C$n
vain dynaamiset spritet (`PX`, `PY`), joiden uudet koordinaatit saadaan Noden
`WAIT VBLANK` -syklistC$.