code wiki / (root) / nx_dungeon_alpha_wasm.nx

nx_dungeon_alpha_wasm.nx source

↩ module page · 326 lines · 10045 B

1// nx_dungeon_alpha_wasm.nx -- Generates a dungeon crawler game with combat, movement, and rendering in WebAssembly. 2const K_MAGIC_1024: i64 = 1024 3const K_MAGIC_1103515245: i64 = 1103515245 4const K_MAGIC_12345: i64 = 12345 5const K_MAGIC_32767: i64 = 32767 6const K_MAGIC_4096: i64 = 4096 7const K_MAGIC_65536: i64 = 65536 8// nx_dungeon_alpha_wasm.nx -- PLAYABLE dungeon-crawler ALPHA, compiled to WASM and run 9// in the browser (nx -> wat -> wasm -> WebAssembly). This is the SOVEREIGN web-game 10// path: the whole game engine (world gen, movement, collision, combat, render to a 11// framebuffer in linear memory) is Nishi-emitted WASM. The browser only blits the 12// framebuffer to a canvas and forwards keystrokes (the thin shim -- same sovereignty 13// status as the crypto wasm already shipped in web_assets/). 14// 15// HONEST AUTHORSHIP (operator law: do not pass tutor work off as team work): 16// - The COMBAT state machine (g_dungenc_step below) is the TEAM's emitter-authored 17// FSM: it is the exact transition table nx_auto_builder emitted into 18// runtime/_hdl_build/_pe_dungenc.nx from the data-only spec build_dungenc.spec. 19// The game genuinely runs the Nishi-authored game system. 20// - The rest of this module (world gen, render, input handling) is TUTOR-authored 21// (Claude) game code, hand-written for the WAT target. It is NOT emitter output. 22// - The named rung to make the WHOLE game emitter-authored from a spec is 23// X-GAME-GEN-001 (GAMESPEC-emitted) + a GAME_BIND/compose-loop emitter shape. 24// This alpha is the playable proof that the target is reachable, not a claim that 25// the Builder authored it. 26// 27// WAT-target constraints honored (per nx_sha256_wasm.nx notes): imports NOTHING, works 28// in fixed linear-memory offsets via local base pointers, integer-only, <=6 args/func, 29// no &&/||. Exported memory layout: 30// 1024.. : game state, 13 i64 slots (gs[]) 31// 4096.. : world grid, 16*12 bytes (cell types) 32// 65536..: framebuffer, 320*240 palette bytes (1 byte/pixel; shim maps to colors) 33// license_tier: ORIGINAL (combat FSM lineage: _pe_dungenc, emitter-authored) 34 35// ---- state accessors (fixed offsets; local base ptr = proven WAT-safe pattern) ---- 36// gs slots: 0=px 1=py 2=hp 3=score 4=level 5=mode 6=fsm_state 7=enemy_hp 37// 8=rng 9=enemies_left 10=msg 11=enemy_x 12=enemy_y 38// mode: 0=explore 1=combat 2=level_clear 3=dead 39 40func g_rng() -> i64 { 41 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 42 var s: i64 = gs[8] 43 s = s * K_MAGIC_1103515245 + K_MAGIC_12345 44 gs[8] = s 45 return (s >> 16) & K_MAGIC_32767 46} 47 48func g_cell_get(x: i64, y: i64) -> i64 { 49 if x < 0 { return 1 } 50 if y < 0 { return 1 } 51 if x >= 16 { return 1 } 52 if y >= 12 { return 1 } 53 let w: *u8 = (K_MAGIC_4096 as i64) as *u8 54 return w[y * 16 + x] as i64 55} 56 57func g_cell_set(x: i64, y: i64, v: i64) -> i64 { 58 let w: *u8 = (K_MAGIC_4096 as i64) as *u8 59 w[y * 16 + x] = v as u8 60 return 0 61} 62 63// place n items of `kind` on random interior floor cells (spawn area kept clear) 64func g_place(kind: i64, n: i64) -> i64 { 65 var placed: i64 = 0 66 var tries: i64 = 0 67 while placed < n { 68 if tries > 300 { return placed } 69 tries = tries + 1 70 let rx: i64 = 1 + (g_rng() % 14) 71 let ry: i64 = 1 + (g_rng() % 10) 72 var ok: i64 = 1 73 if g_cell_get(rx, ry) != 0 { ok = 0 } 74 if rx < 3 { if ry < 3 { ok = 0 } } 75 if ok == 1 { 76 g_cell_set(rx, ry, kind) 77 placed = placed + 1 78 } 79 } 80 return placed 81} 82 83func g_build_level() -> i64 { 84 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 85 var y: i64 = 0 86 while y < 12 { 87 var x: i64 = 0 88 while x < 16 { 89 var c: i64 = 0 90 if x == 0 { c = 1 } 91 if y == 0 { c = 1 } 92 if x == 15 { c = 1 } 93 if y == 11 { c = 1 } 94 g_cell_set(x, y, c) 95 x = x + 1 96 } 97 y = y + 1 98 } 99 var i: i64 = 0 100 while i < 20 { 101 let rx: i64 = 1 + (g_rng() % 14) 102 let ry: i64 = 1 + (g_rng() % 10) 103 var ok: i64 = 1 104 if rx < 3 { if ry < 3 { ok = 0 } } 105 if ok == 1 { g_cell_set(rx, ry, 1) } 106 i = i + 1 107 } 108 g_cell_set(1, 1, 0) 109 let lvl: i64 = gs[4] 110 var ne: i64 = 3 + lvl 111 if ne > 10 { ne = 10 } 112 gs[9] = g_place(2, ne) 113 g_place(3, 1) 114 g_place(5, 2) 115 gs[0] = 1 116 gs[1] = 1 117 gs[5] = 0 118 gs[6] = 0 119 return 0 120} 121 122// ---- the TEAM's emitter-authored combat FSM (exact _pe_dungenc transition table) ---- 123// states 0=ENC_START 1=PLAYER_TURN 2=ENEMY_TURN 3=PLAYER_WIN 4=FLEE 5=ENC_END 124// events 0=enter 1=attack 2=enemy_act 3=defeat 4=flee 5=resolve 125func g_dungenc_step(s: i64, e: i64) -> i64 { 126 if s == 0 { if e == 0 { return 1 } } 127 if s == 1 { if e == 1 { return 2 } } 128 if s == 1 { if e == 3 { return 3 } } 129 if s == 1 { if e == 4 { return 4 } } 130 if s == 2 { if e == 2 { return 1 } } 131 if s == 3 { if e == 5 { return 5 } } 132 if s == 4 { if e == 5 { return 5 } } 133 return 0 - 1 134} 135 136func gi_init(seed: i64) -> i64 { 137 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 138 gs[8] = seed 139 gs[2] = 100 140 gs[3] = 0 141 gs[4] = 1 142 gs[5] = 0 143 gs[6] = 0 144 gs[7] = 0 145 gs[10] = 0 146 g_build_level() 147 return 0 148} 149 150func gi_next() -> i64 { 151 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 152 gs[4] = gs[4] + 1 153 gs[3] = gs[3] + 100 154 gs[2] = gs[2] + 25 155 if gs[2] > 100 { gs[2] = 100 } 156 g_build_level() 157 return 0 158} 159 160func g_try_move(dx: i64, dy: i64) -> i64 { 161 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 162 let nx: i64 = gs[0] + dx 163 let ny: i64 = gs[1] + dy 164 let c: i64 = g_cell_get(nx, ny) 165 if c == 1 { return 0 } 166 if c == 2 { 167 gs[5] = 1 168 gs[6] = g_dungenc_step(0, 0) 169 gs[7] = 20 + gs[4] * 5 170 gs[11] = nx 171 gs[12] = ny 172 gs[10] = 1 173 return 1 174 } 175 gs[0] = nx 176 gs[1] = ny 177 if c == 3 { gs[5] = 2; gs[10] = 2; return 2 } 178 if c == 5 { g_cell_set(nx, ny, 0); gs[3] = gs[3] + 25; gs[10] = 3 } 179 return 0 180} 181 182func g_attack() -> i64 { 183 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 184 if gs[5] != 1 { return 0 } 185 let dmg: i64 = 8 + (g_rng() % 8) 186 gs[7] = gs[7] - dmg 187 if gs[7] <= 0 { 188 gs[6] = g_dungenc_step(1, 3) 189 gs[6] = g_dungenc_step(gs[6], 5) 190 g_cell_set(gs[11], gs[12], 0) 191 gs[3] = gs[3] + 50 192 gs[9] = gs[9] - 1 193 gs[5] = 0 194 gs[6] = 0 195 gs[10] = 4 196 return 1 197 } 198 gs[6] = g_dungenc_step(1, 1) 199 let edmg: i64 = 4 + (g_rng() % (6 + gs[4])) 200 gs[2] = gs[2] - edmg 201 gs[6] = g_dungenc_step(2, 2) 202 if gs[2] <= 0 { 203 gs[2] = 0 204 gs[5] = 3 205 gs[10] = 5 206 } 207 return 0 208} 209 210func g_flee() -> i64 { 211 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 212 if gs[5] != 1 { return 0 } 213 gs[6] = g_dungenc_step(1, 4) 214 gs[6] = g_dungenc_step(gs[6], 5) 215 gs[5] = 0 216 gs[6] = 0 217 gs[10] = 6 218 return 0 219} 220 221// key: 0=up 1=down 2=left 3=right 4=action 5=restart 222func gi_input(key: i64) -> i64 { 223 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 224 let mode: i64 = gs[5] 225 if mode == 3 { 226 if key == 4 { gi_init(gs[8] + 7) } 227 return 0 228 } 229 if mode == 2 { 230 if key == 4 { gi_next() } 231 return 0 232 } 233 if mode == 1 { 234 if key == 4 { g_attack() } 235 if key == 0 { g_flee() } 236 if key == 1 { g_flee() } 237 if key == 2 { g_flee() } 238 if key == 3 { g_flee() } 239 return 0 240 } 241 if key == 0 { g_try_move(0, 0 - 1) } 242 if key == 1 { g_try_move(0, 1) } 243 if key == 2 { g_try_move(0 - 1, 0) } 244 if key == 3 { g_try_move(1, 0) } 245 return 0 246} 247 248// ---- render to the framebuffer (palette bytes); shim maps palette -> RGBA ---- 249func g_fill_rect(x0: i64, y0: i64, ww: i64, hh: i64, col: i64) -> i64 { 250 let fb: *u8 = (K_MAGIC_65536 as i64) as *u8 251 var y: i64 = y0 252 while y < y0 + hh { 253 if y >= 0 { if y < 240 { 254 var x: i64 = x0 255 while x < x0 + ww { 256 if x >= 0 { if x < 320 { fb[y * 320 + x] = col as u8 } } 257 x = x + 1 258 } 259 } } 260 y = y + 1 261 } 262 return 0 263} 264 265func g_render_world() -> i64 { 266 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 267 var cy: i64 = 0 268 while cy < 12 { 269 var cx: i64 = 0 270 while cx < 16 { 271 let c: i64 = g_cell_get(cx, cy) 272 if c == 1 { 273 g_fill_rect(cx * 20, cy * 20, 20, 20, 1) 274 } else { 275 g_fill_rect(cx * 20, cy * 20, 20, 20, 0) 276 if c == 2 { g_fill_rect(cx * 20 + 4, cy * 20 + 4, 12, 12, 3) } 277 if c == 3 { g_fill_rect(cx * 20 + 3, cy * 20 + 3, 14, 14, 4) } 278 if c == 5 { g_fill_rect(cx * 20 + 6, cy * 20 + 6, 8, 8, 5) } 279 } 280 cx = cx + 1 281 } 282 cy = cy + 1 283 } 284 g_fill_rect(gs[0] * 20 + 3, gs[1] * 20 + 3, 14, 14, 2) 285 return 0 286} 287 288func g_render_combat() -> i64 { 289 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 290 g_fill_rect(0, 0, 320, 240, 6) 291 g_fill_rect(40, 90, 64, 80, 2) 292 g_fill_rect(216, 90, 64, 80, 3) 293 let phw: i64 = gs[2] * 120 / 100 294 g_fill_rect(20, 24, 120, 14, 1) 295 g_fill_rect(20, 24, phw, 14, 7) 296 let emax: i64 = 20 + gs[4] * 5 297 var ehp: i64 = gs[7] 298 if ehp < 0 { ehp = 0 } 299 let ehw: i64 = ehp * 120 / emax 300 g_fill_rect(180, 24, 120, 14, 1) 301 g_fill_rect(180, 24, ehw, 14, 3) 302 return 0 303} 304 305func gi_render() -> i64 { 306 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 307 if gs[5] == 1 { g_render_combat(); return 0 } 308 g_render_world() 309 return 0 310} 311 312func gi_get(field: i64) -> i64 { 313 let gs: *i64 = (K_MAGIC_1024 as i64) as *i64 314 if field >= 0 { if field < 13 { return gs[field] } } 315 return 0 316} 317func gi_fb_offset() -> i64 { return K_MAGIC_65536 } 318func gi_fb_w() -> i64 { return 320 } 319func gi_fb_h() -> i64 { return 240 } 320 321// headless self-test entrypoint: init + render, return enemies placed (level 1 => up to 4) 322func main() -> i64 { 323 gi_init(K_MAGIC_12345) 324 gi_render() 325 return gi_get(9) 326}