nx_wasm_shmup.nx source
↩ module page · 134 lines · 5983 B
1// nx_wasm_shmup.nx -- BROWSER-PLAYABLE sovereign Shoot-'em-up: BASE-RELATIVE integer core, the SAME code runs
2// native (base = a real mmap -> render a frame to PNG for eyeball verify) AND in the browser (base = 0 = our
3// sovereign WASM linear memory). NO float, NO JS logic, NO 3rd-party canvas drawing. Real-time: the shim's
4// rAF loop calls tick(input) every frame with the held-key bitmask (1=left 2=right 4=shoot); the core moves
5// the ship, fires a bullet, advances it, kills descending enemies, respawns waves endlessly, and game-overs
6// if a wave reaches the ship line (shoot restarts). HUD score drawn by our OWN font (no JS/canvas text).
7// init()/tick(input)/render()/score()/ww()/hh()/fb_off() are the wasm interface (auto-exported by nx_wasm.nx).
8// license_tier: ORIGINAL
9import "nx_nishi_font_core.nx"
10const W: i64 = 160
11const H: i64 = 192
12const NE: i64 = 6
13const SY: i64 = 176 // ship row
14const EYSTART: i64 = 20 // wave spawn y
15const O_FB: i64 = 0 // framebuffer W*H i64 (packed RGB) at offset 0
16const O_EX: i64 = 245760 // enemy x[16] (W*H*8)
17const O_EA: i64 = 245888 // enemy alive[16]
18const O_ST: i64 = 246016 // scalars[16]: 0=sx 1=bx 2=by 3=bactive 4=score 5=wave_count 6=ey 7=gameover 8=tick
19const O_MSK: i64 = 246144 // HUD glyph scratch mask (base-relative -> no sys_mmap, lowers to wasm)
20const O_STR: i64 = 254336 // HUD score string scratch
21
22func rgb(r: i64, g: i64, b: i64) -> i64 { return (r & 255) | ((g & 255) << 8) | ((b & 255) << 16) }
23
24func fillr(base: i64, x0: i64, y0: i64, x1: i64, y1: i64, c: i64) -> i64 {
25 let fb: *i64 = (base + O_FB) as *i64
26 var yy: i64 = y0; if yy < 0 { yy = 0 }
27 var ye: i64 = y1; if ye > H { ye = H }
28 while yy < ye {
29 var xx: i64 = x0; if xx < 0 { xx = 0 }
30 var xe: i64 = x1; if xe > W { xe = W }
31 while xx < xe { fb[yy*W + xx] = c; xx = xx + 1 }
32 yy = yy + 1
33 }
34 return 0
35}
36
37func spawn_wave(base: i64) -> i64 {
38 let ex: *i64 = (base + O_EX) as *i64
39 let ea: *i64 = (base + O_EA) as *i64
40 let st: *i64 = (base + O_ST) as *i64
41 var i: i64 = 0
42 while i < NE { ex[i] = 18 + i*24; ea[i] = 1; i = i + 1 }
43 st[5] = NE
44 st[6] = EYSTART
45 return 0
46}
47
48func init_impl(base: i64) -> i64 {
49 let st: *i64 = (base + O_ST) as *i64
50 st[0] = W/2; st[1] = 0; st[2] = 0; st[3] = 0; st[4] = 0; st[7] = 0; st[8] = 0; st[9] = 0
51 spawn_wave(base)
52 return 0
53}
54
55func tick_impl(base: i64, input: i64) -> i64 {
56 let ex: *i64 = (base + O_EX) as *i64
57 let ea: *i64 = (base + O_EA) as *i64
58 let st: *i64 = (base + O_ST) as *i64
59 if st[7] == 1 { if (input & 4) != 0 { init_impl(base) } return 0 }
60 if (input & 1) != 0 { st[0] = st[0] - 3; if st[0] < 8 { st[0] = 8 } }
61 if (input & 2) != 0 { st[0] = st[0] + 3; if st[0] > W-8 { st[0] = W-8 } }
62 if (input & 4) != 0 { if st[3] == 0 { st[1] = st[0]; st[2] = SY - 10; st[3] = 1; st[9] = 1 } }
63 if st[3] == 1 {
64 st[2] = st[2] - 6
65 if st[2] < 0 { st[3] = 0 } else {
66 var i: i64 = 0
67 while i < NE {
68 if ea[i] == 1 {
69 var dx: i64 = st[1] - ex[i]; if dx < 0 { dx = 0 - dx }
70 var dy: i64 = st[2] - st[6]; if dy < 0 { dy = 0 - dy }
71 if dx < 9 { if dy < 9 { ea[i] = 0; st[3] = 0; st[4] = st[4] + 10; st[5] = st[5] - 1; st[9] = 2; i = NE } else { i = i + 1 } } else { i = i + 1 }
72 } else { i = i + 1 }
73 }
74 }
75 }
76 if (st[8] % 24) == 0 { st[6] = st[6] + 5; if st[6] >= SY - 10 { st[7] = 1; st[9] = 3 } }
77 if st[5] <= 0 { spawn_wave(base); st[4] = st[4] + 5 }
78 st[8] = st[8] + 1
79 return 0
80}
81
82func render_impl(base: i64) -> i64 {
83 let fb: *i64 = (base + O_FB) as *i64
84 let ex: *i64 = (base + O_EX) as *i64
85 let ea: *i64 = (base + O_EA) as *i64
86 let st: *i64 = (base + O_ST) as *i64
87 var p: i64 = 0
88 let sky: i64 = rgb(8, 10, 24)
89 while p < W*H { fb[p] = sky; p = p + 1 }
90 // stars (deterministic scatter)
91 var s: i64 = 0
92 while s < 40 { let sx2: i64 = (s*53) % W; let sy2: i64 = (s*97) % H; fb[sy2*W + sx2] = rgb(110,110,150); s = s + 1 }
93 // enemies
94 var i: i64 = 0
95 while i < NE {
96 if ea[i] == 1 { fillr(base, ex[i]-7, st[6]-6, ex[i]+7, st[6]+6, rgb(224,80,58)); fillr(base, ex[i]-4, st[6]+6, ex[i]+4, st[6]+9, rgb(150,50,40)) }
97 i = i + 1
98 }
99 // bullet
100 if st[3] == 1 { fillr(base, st[1]-1, st[2]-4, st[1]+2, st[2]+3, rgb(250,240,120)) }
101 // ship
102 fillr(base, st[0]-7, SY-3, st[0]+8, SY+5, rgb(90,210,122))
103 fillr(base, st[0]-2, SY-7, st[0]+3, SY-3, rgb(120,230,150))
104 // game-over red border
105 if st[7] == 1 {
106 fillr(base, 0, 0, W, 3, rgb(200,40,40)); fillr(base, 0, H-3, W, H, rgb(200,40,40))
107 fillr(base, 0, 0, 3, H, rgb(200,40,40)); fillr(base, W-3, 0, W, H, rgb(200,40,40))
108 }
109 // HUD score (our sovereign font)
110 let sbuf: *u8 = (base + O_STR) as *u8
111 var slen: i64 = 0
112 var sv: i64 = st[4]
113 if sv <= 0 { sbuf[0] = 48 as u8; slen = 1 } else {
114 let tmp: *u8 = (base + O_STR + 16) as *u8
115 var k: i64 = 0
116 while sv > 0 { tmp[k] = (48 + (sv % 10)) as u8; sv = sv / 10; k = k + 1 }
117 var q: i64 = 0
118 while q < k { sbuf[q] = tmp[k - 1 - q]; q = q + 1 }
119 slen = k
120 }
121 let mask: *u8 = (base + O_MSK) as *u8
122 nf_draw_text_mem(fb, W, H, mask, 4, 3, 16, sbuf, slen, rgb(235, 240, 250))
123 return 0
124}
125
126// ---- WASM exports (base = 0 = our sovereign linear memory) ----
127func init() -> i64 { return init_impl(0) }
128func tick(input: i64) -> i64 { return tick_impl(0, input) }
129func sfx_event() -> i64 { let st: *i64 = (0 + O_ST) as *i64; let e: i64 = st[9]; st[9] = 0; return e } // 1=shoot 2=kill 3=gameover; cleared on read
130func render() -> i64 { return render_impl(0) }
131func score() -> i64 { let st: *i64 = (0 + O_ST) as *i64; return st[4] }
132func fb_off() -> i64 { return O_FB }
133func ww() -> i64 { return W }
134func hh() -> i64 { return H }