code wiki / _hdl_build / nx_wasm_shmup_html.nx
nx_wasm_shmup_html.nx source
↩ module page · 57 lines · 5361 B
1// nx_wasm_shmup_html.nx -- SOVEREIGN packager: the shmup wasm -> a self-contained REAL-TIME playable .html WITH
2// AUDIO. The packager PRE-GENERATES the SFX natively via our own engine (nx_audio_sfx laser/explosion/tone ->
3// nx_audio_wav PCM/WAV), embeds them as base64, and the JS plays them via Web Audio when the wasm core signals
4// an event (ex.sfx_event(): 1=shoot 2=kill 3=gameover). JS still does ONLY blit + input + the rAF loop + sound
5// playback; all game logic/render/SFX-synthesis are sovereign. usage: nx_wasm_shmup_html <in.wasm> <out.html>
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_base64.nx"
9import "nx_audio_sfx.nx"
10import "nx_audio_wav.nx"
11const K_MAGIC_9000: i64 = 9000
12const K_MAGIC_22050: i64 = 22050
13
14func hw(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
15
16// generate one SFX (kind 1=laser 2=explosion 3=low-tone) -> WAV -> base64; returns b64 len, sets b64out[0]=ptr
17func sfx_b64(kind: i64, ms: i64, rate: i64, b64out: *i64) -> i64 {
18 let n: i64 = (rate * ms) / 1000
19 let samp: *i64 = sys_mmap(n * 8) as *i64
20 if kind == 1 { sfx_laser(samp, n, rate) }
21 else { if kind == 2 { sfx_explosion(samp, n, rate) } else { sfx_tone(samp, n, 150, rate, K_MAGIC_9000) } }
22 let wav: *u8 = sys_mmap(64 + n * 4)
23 let wl: i64 = wav_render_stereo(samp, samp, n, rate, wav)
24 let b64: *u8 = sys_mmap(wl * 2 + 64)
25 let bl: i64 = b64_encode(wav, wl, b64)
26 b64out[0] = b64 as i64
27 return bl
28}
29
30func main(argc: i64, argv: *i64) -> i64 {
31 if argc < 3 { hw(2, "usage: nx_wasm_shmup_html <in.wasm> <out.html>\n" as *u8); return 2 }
32 let wlen_p: *i64 = sys_mmap(8) as *i64
33 let wbytes: *u8 = sys_read_file(argv[1] as *u8, wlen_p)
34 if (wbytes as i64) == 0 { hw(2, "read wasm failed\n" as *u8); return 3 }
35 let wlen: i64 = wlen_p[0]
36 let b64: *u8 = sys_mmap(wlen * 2 + 64) as *u8
37 let b64len: i64 = b64_encode(wbytes, wlen, b64)
38
39 // pre-generate SFX (sovereign) -> base64
40 let rate: i64 = K_MAGIC_22050
41 let p1: *i64 = sys_mmap(8) as *i64; let l1: i64 = sfx_b64(1, 140, rate, p1) // shoot = laser
42 let p2: *i64 = sys_mmap(8) as *i64; let l2: i64 = sfx_b64(2, 320, rate, p2) // kill = explosion
43 let p3: *i64 = sys_mmap(8) as *i64; let l3: i64 = sfx_b64(3, 420, rate, p3) // over = low tone
44
45 let fd: i64 = sys_openat_wr(argv[2] as *u8, 0x1a4)
46 if fd < 0 { hw(2, "open html failed\n" as *u8); return 4 }
47 hw(fd, "<!doctype html><html><head><meta charset='utf-8'><title>Nishi Shmup (sovereign wasm + audio)</title><style>body{background:#0b0f1a;margin:0;display:flex;flex-direction:column;justify-content:center;align-items:center;height:100vh;font-family:sans-serif;color:#cdd}canvas{image-rendering:pixelated;width:320px;height:384px;border:1px solid #2a3140;background:#0a0d12}p{margin:8px}</style></head><body><h2>Nishi Shmup</h2><canvas id='c'></canvas><p id='hud'>← → (or A / D) to move · Space to shoot · sound on!</p><p style='color:#667;font-size:12px'>100% Nishi: logic + render + HUD + SFX synthesis all sovereign; the browser only blits our framebuffer, forwards keys, and plays our pre-rendered WAVs on game events</p><script>\n" as *u8)
48 hw(fd, "const B='" as *u8); sys_write(fd, b64, b64len); hw(fd, "';\n" as *u8)
49 hw(fd, "const SFX1='" as *u8); sys_write(fd, p1[0] as *u8, l1); hw(fd, "';\n" as *u8)
50 hw(fd, "const SFX2='" as *u8); sys_write(fd, p2[0] as *u8, l2); hw(fd, "';\n" as *u8)
51 hw(fd, "const SFX3='" as *u8); sys_write(fd, p3[0] as *u8, l3); hw(fd, "';\n" as *u8)
52 hw(fd, "const bytes=Uint8Array.from(atob(B),c=>c.charCodeAt(0));\nlet actx=null;const SB={};\nfunction ab(b){return Uint8Array.from(atob(b),c=>c.charCodeAt(0)).buffer;}\nfunction ia(){if(actx)return;actx=new(window.AudioContext||window.webkitAudioContext)();[[1,SFX1],[2,SFX2],[3,SFX3]].forEach(function(p){actx.decodeAudioData(ab(p[1])).then(function(b){SB[p[0]]=b;});});}\nfunction ps(id){if(!actx)return;const b=SB[id];if(!b)return;const s=actx.createBufferSource();s.buffer=b;s.connect(actx.destination);s.start();}\nWebAssembly.instantiate(bytes).then(function(r){const ex=r.instance.exports;const W=Number(ex.ww()),H=Number(ex.hh()),off=Number(ex.fb_off());const cv=document.getElementById('c');cv.width=W;cv.height=H;const ctx=cv.getContext('2d');const img=ctx.createImageData(W,H);function blit(){const mem=new Uint8Array(ex.memory.buffer);for(let i=0;i<W*H;i++){const b=off+i*8;img.data[i*4]=mem[b];img.data[i*4+1]=mem[b+1];img.data[i*4+2]=mem[b+2];img.data[i*4+3]=255;}ctx.putImageData(img,0,0);}let inp=0;function bit(k){if(k=='ArrowLeft'||k=='a')return 1;if(k=='ArrowRight'||k=='d')return 2;if(k==' '||k=='ArrowUp'||k=='w')return 4;return 0;}document.addEventListener('keydown',function(e){ia();const b=bit(e.key);if(b){e.preventDefault();inp|=b;}});document.addEventListener('keyup',function(e){const b=bit(e.key);if(b){e.preventDefault();inp&=~b;}});ex.init();function loop(){ex.tick(BigInt(inp));ex.render();blit();const ev=Number(ex.sfx_event());if(ev>0)ps(ev);requestAnimationFrame(loop);}requestAnimationFrame(loop);}).catch(function(e){document.body.innerHTML+='<pre>'+e.message+'</pre>';});\n" as *u8)
53 hw(fd, "</script></body></html>\n" as *u8)
54 sys_close(fd)
55 hw(1, "wrote shmup html (with sovereign SFX)\n" as *u8)
56 return 0
57}