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'>&larr; &rarr; (or A / D) to move &middot; Space to shoot &middot; 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}