code wiki / (root) / nx_dungeon_native.nx

nx_dungeon_native.nx source

↩ module page · 442 lines · 13997 B

1// nx_dungeon_native.nx -- PLAYABLE native terminal dungeon-crawler ALPHA. 2// 3// Built by Nishi's OWN toolchain to a NATIVE binary (nx_cc -> nxasm_x86, no gcc, no 4// WebAssembly, no browser, no borrowed VM). Runs natively on the x86 host; the only 5// OS surface used is the Nishi syscall layer (write/read/ioctl/mmap). The interactive 6// surface is the TERMINAL (ANSI truecolor render to stdout + single-key input from 7// stdin via termios raw mode) -- the most portable display/input, so the SAME engine 8// retargets a framebuffer/UART driver on the operator's own hardware later ("hardware 9// up = prep to run on mine"). 10// 11// HONEST AUTHORSHIP (operator law: don't pass tutor work off as team work): 12// - The COMBAT state machine g_dungenc_step is the TEAM's emitter-authored FSM: the 13// exact transition table nx_auto_builder emitted into _pe_dungenc.nx from the 14// data-only spec build_dungenc.spec. The game runs the Nishi-authored game system. 15// - World gen, terminal render, input loop = TUTOR-authored (Claude) game code. NOT 16// emitter output. The named rung to make the WHOLE game emitter-authored from a 17// spec is X-GAME-GEN-001. This alpha is the playable proof, not that claim. 18// 19// LAWS honored: <=6 args/func, flat ifs, no &&/||, integer-only, additive, <21 same-fn 20// call sites per function (nxasm rc=6 landmine). 21// license_tier: ORIGINAL (combat FSM lineage: _pe_dungenc, emitter-authored) 22import "nx_syscalls.nx" 23const K_MAGIC_1103515245: i64 = 1103515245 24const K_MAGIC_12345: i64 = 12345 25const K_MAGIC_32767: i64 = 32767 26const K_MAGIC_4096: i64 = 4096 27const K_MAGIC_16384: i64 = 16384 28 29// state slots (st[]): 0=px 1=py 2=hp 3=score 4=level 5=mode 6=fsm 7=enemy_hp 30// 8=rng 9=foes 10=msg 11=enemy_x 12=enemy_y 31// mode: 0=explore 1=combat 2=level_clear 3=dead. world grid (16x12 bytes) at st+256. 32 33func g_rng(st: *i64) -> i64 { 34 var s: i64 = st[8] 35 s = s * K_MAGIC_1103515245 + K_MAGIC_12345 36 st[8] = s 37 return (s >> 16) & K_MAGIC_32767 38} 39 40func g_cell_get(st: *i64, x: i64, y: i64) -> i64 { 41 if x < 0 { return 1 } 42 if y < 0 { return 1 } 43 if x >= 16 { return 1 } 44 if y >= 12 { return 1 } 45 let w: *u8 = ((st as i64) + 256) as *u8 46 return w[y * 16 + x] as i64 47} 48 49func g_cell_set(st: *i64, x: i64, y: i64, v: i64) -> i64 { 50 let w: *u8 = ((st as i64) + 256) as *u8 51 w[y * 16 + x] = v as u8 52 return 0 53} 54 55func g_place(st: *i64, kind: i64, n: i64) -> i64 { 56 var placed: i64 = 0 57 var tries: i64 = 0 58 while placed < n { 59 if tries > 300 { return placed } 60 tries = tries + 1 61 let rx: i64 = 1 + (g_rng(st) % 14) 62 let ry: i64 = 1 + (g_rng(st) % 10) 63 var ok: i64 = 1 64 if g_cell_get(st, rx, ry) != 0 { ok = 0 } 65 if rx < 3 { if ry < 3 { ok = 0 } } 66 if ok == 1 { 67 g_cell_set(st, rx, ry, kind) 68 placed = placed + 1 69 } 70 } 71 return placed 72} 73 74func g_build_level(st: *i64) -> i64 { 75 var y: i64 = 0 76 while y < 12 { 77 var x: i64 = 0 78 while x < 16 { 79 var c: i64 = 0 80 if x == 0 { c = 1 } 81 if y == 0 { c = 1 } 82 if x == 15 { c = 1 } 83 if y == 11 { c = 1 } 84 g_cell_set(st, x, y, c) 85 x = x + 1 86 } 87 y = y + 1 88 } 89 var i: i64 = 0 90 while i < 20 { 91 let rx: i64 = 1 + (g_rng(st) % 14) 92 let ry: i64 = 1 + (g_rng(st) % 10) 93 var ok: i64 = 1 94 if rx < 3 { if ry < 3 { ok = 0 } } 95 if ok == 1 { g_cell_set(st, rx, ry, 1) } 96 i = i + 1 97 } 98 g_cell_set(st, 1, 1, 0) 99 let lvl: i64 = st[4] 100 var ne: i64 = 3 + lvl 101 if ne > 10 { ne = 10 } 102 st[9] = g_place(st, 2, ne) 103 g_place(st, 3, 1) 104 g_place(st, 5, 2) 105 st[0] = 1 106 st[1] = 1 107 st[5] = 0 108 st[6] = 0 109 return 0 110} 111 112// the TEAM's emitter-authored combat FSM (exact _pe_dungenc transition table) 113// states 0=ENC_START 1=PLAYER_TURN 2=ENEMY_TURN 3=PLAYER_WIN 4=FLEE 5=ENC_END 114// events 0=enter 1=attack 2=enemy_act 3=defeat 4=flee 5=resolve 115func g_dungenc_step(s: i64, e: i64) -> i64 { 116 if s == 0 { if e == 0 { return 1 } } 117 if s == 1 { if e == 1 { return 2 } } 118 if s == 1 { if e == 3 { return 3 } } 119 if s == 1 { if e == 4 { return 4 } } 120 if s == 2 { if e == 2 { return 1 } } 121 if s == 3 { if e == 5 { return 5 } } 122 if s == 4 { if e == 5 { return 5 } } 123 return 0 - 1 124} 125 126func gi_init(st: *i64, seed: i64) -> i64 { 127 st[8] = seed 128 st[2] = 100 129 st[3] = 0 130 st[4] = 1 131 st[5] = 0 132 st[6] = 0 133 st[7] = 0 134 st[10] = 0 135 g_build_level(st) 136 return 0 137} 138 139func gi_next(st: *i64) -> i64 { 140 st[4] = st[4] + 1 141 st[3] = st[3] + 100 142 st[2] = st[2] + 25 143 if st[2] > 100 { st[2] = 100 } 144 g_build_level(st) 145 return 0 146} 147 148func g_try_move(st: *i64, dx: i64, dy: i64) -> i64 { 149 let nx: i64 = st[0] + dx 150 let ny: i64 = st[1] + dy 151 let c: i64 = g_cell_get(st, nx, ny) 152 if c == 1 { return 0 } 153 if c == 2 { 154 st[5] = 1 155 st[6] = g_dungenc_step(0, 0) 156 st[7] = 20 + st[4] * 5 157 st[11] = nx 158 st[12] = ny 159 st[10] = 1 160 return 1 161 } 162 st[0] = nx 163 st[1] = ny 164 if c == 3 { st[5] = 2; st[10] = 2; return 2 } 165 if c == 5 { g_cell_set(st, nx, ny, 0); st[3] = st[3] + 25; st[10] = 3 } 166 return 0 167} 168 169func g_attack(st: *i64) -> i64 { 170 if st[5] != 1 { return 0 } 171 let dmg: i64 = 8 + (g_rng(st) % 8) 172 st[7] = st[7] - dmg 173 if st[7] <= 0 { 174 st[6] = g_dungenc_step(1, 3) 175 st[6] = g_dungenc_step(st[6], 5) 176 g_cell_set(st, st[11], st[12], 0) 177 st[3] = st[3] + 50 178 st[9] = st[9] - 1 179 st[5] = 0 180 st[6] = 0 181 st[10] = 4 182 return 1 183 } 184 st[6] = g_dungenc_step(1, 1) 185 let edmg: i64 = 4 + (g_rng(st) % (6 + st[4])) 186 st[2] = st[2] - edmg 187 st[6] = g_dungenc_step(2, 2) 188 if st[2] <= 0 { 189 st[2] = 0 190 st[5] = 3 191 st[10] = 5 192 } 193 return 0 194} 195 196func g_flee(st: *i64) -> i64 { 197 if st[5] != 1 { return 0 } 198 st[6] = g_dungenc_step(1, 4) 199 st[6] = g_dungenc_step(st[6], 5) 200 st[5] = 0 201 st[6] = 0 202 st[10] = 6 203 return 0 204} 205 206// key: 0=up 1=down 2=left 3=right 4=action 5=restart 9=noop 207func gi_input(st: *i64, key: i64) -> i64 { 208 let mode: i64 = st[5] 209 if mode == 3 { 210 if key == 4 { gi_init(st, st[8] + 7) } 211 if key == 5 { gi_init(st, st[8] + 7) } 212 return 0 213 } 214 if mode == 2 { 215 if key == 4 { gi_next(st) } 216 return 0 217 } 218 if mode == 1 { 219 if key == 4 { g_attack(st) } 220 if key == 0 { g_flee(st) } 221 if key == 1 { g_flee(st) } 222 if key == 2 { g_flee(st) } 223 if key == 3 { g_flee(st) } 224 return 0 225 } 226 if key == 0 { g_try_move(st, 0, 0 - 1) } 227 if key == 1 { g_try_move(st, 0, 1) } 228 if key == 2 { g_try_move(st, 0 - 1, 0) } 229 if key == 3 { g_try_move(st, 1, 0) } 230 return 0 231} 232 233// ---- terminal frame buffer append helpers ---- 234func bputc(buf: *u8, off: *i64, c: i64) -> i64 { 235 buf[off[0]] = c as u8 236 off[0] = off[0] + 1 237 return 0 238} 239func bput(buf: *u8, off: *i64, s: *u8) -> i64 { 240 var i: i64 = 0 241 while s[i] != (0 as u8) { 242 buf[off[0] + i] = s[i] 243 i = i + 1 244 } 245 off[0] = off[0] + i 246 return 0 247} 248func bputn(buf: *u8, off: *i64, v: i64) -> i64 { 249 var m: i64 = v 250 if m < 0 { m = 0 } 251 if m == 0 { bputc(buf, off, 48); return 0 } 252 let tmp: *u8 = sys_mmap(32) 253 var k: i64 = 0 254 while m > 0 { tmp[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 255 var j: i64 = 0 256 while j < k { bputc(buf, off, tmp[k - 1 - j] as i64); j = j + 1 } 257 return 0 258} 259func bput_bg(buf: *u8, off: *i64, r: i64, g: i64, b: i64) -> i64 { 260 bputc(buf, off, 27) 261 bput(buf, off, "[48;2;" as *u8) 262 bputn(buf, off, r); bputc(buf, off, 59) 263 bputn(buf, off, g); bputc(buf, off, 59) 264 bputn(buf, off, b); bputc(buf, off, 109) 265 return 0 266} 267func bput_fg(buf: *u8, off: *i64, r: i64, g: i64, b: i64) -> i64 { 268 bputc(buf, off, 27) 269 bput(buf, off, "[38;2;" as *u8) 270 bputn(buf, off, r); bputc(buf, off, 59) 271 bputn(buf, off, g); bputc(buf, off, 59) 272 bputn(buf, off, b); bputc(buf, off, 109) 273 return 0 274} 275func bput_reset(buf: *u8, off: *i64) -> i64 { 276 bputc(buf, off, 27); bput(buf, off, "[0m" as *u8); return 0 277} 278func bput_clear(buf: *u8, off: *i64) -> i64 { 279 bputc(buf, off, 27); bput(buf, off, "[2J" as *u8) 280 bputc(buf, off, 27); bput(buf, off, "[H" as *u8) 281 return 0 282} 283 284// display code: 0 floor 1 wall 2 player 3 enemy 4 exit 5 loot 285func draw_cell(buf: *u8, off: *i64, d: i64) -> i64 { 286 if d == 1 { bput_bg(buf, off, 70, 80, 110); bput(buf, off, " " as *u8); bput_reset(buf, off); return 0 } 287 bput_bg(buf, off, 24, 22, 30) 288 if d == 0 { bput(buf, off, " " as *u8) } 289 if d == 2 { bput_fg(buf, off, 90, 230, 120); bput(buf, off, "()" as *u8) } 290 if d == 3 { bput_fg(buf, off, 220, 60, 60); bput(buf, off, "gg" as *u8) } 291 if d == 4 { bput_fg(buf, off, 240, 220, 72); bput(buf, off, ">>" as *u8) } 292 if d == 5 { bput_fg(buf, off, 248, 176, 44); bput(buf, off, "**" as *u8) } 293 bput_reset(buf, off) 294 return 0 295} 296 297func render_hud(buf: *u8, off: *i64, st: *i64) -> i64 { 298 bput(buf, off, " NISHI DUNGEON HP " as *u8); bputn(buf, off, st[2]) 299 bput(buf, off, "/100 Score " as *u8); bputn(buf, off, st[3]) 300 bput(buf, off, " Depth " as *u8); bputn(buf, off, st[4]) 301 bput(buf, off, " Foes " as *u8); bputn(buf, off, st[9]) 302 bputc(buf, off, 10); bputc(buf, off, 10) 303 return 0 304} 305 306func render_grid(buf: *u8, off: *i64, st: *i64) -> i64 { 307 let px: i64 = st[0] 308 let py: i64 = st[1] 309 var y: i64 = 0 310 while y < 12 { 311 var x: i64 = 0 312 while x < 16 { 313 var d: i64 = 0 314 let cell: i64 = g_cell_get(st, x, y) 315 if cell == 1 { d = 1 } 316 if cell == 2 { d = 3 } 317 if cell == 3 { d = 4 } 318 if cell == 5 { d = 5 } 319 if x == px { if y == py { d = 2 } } 320 draw_cell(buf, off, d) 321 x = x + 1 322 } 323 bput_reset(buf, off) 324 bputc(buf, off, 10) 325 y = y + 1 326 } 327 return 0 328} 329 330func render_bar(buf: *u8, off: *i64, val: i64, max: i64, r: i64, g: i64) -> i64 { 331 var n: i64 = val * 20 / max 332 if n < 0 { n = 0 } 333 if n > 20 { n = 20 } 334 bputc(buf, off, 91) 335 bput_fg(buf, off, r, g, 60) 336 var i: i64 = 0 337 while i < 20 { 338 if i < n { bputc(buf, off, 35) } else { bputc(buf, off, 45) } 339 i = i + 1 340 } 341 bput_reset(buf, off) 342 bputc(buf, off, 93); bputc(buf, off, 32); bputn(buf, off, val); bputc(buf, off, 10) 343 return 0 344} 345 346func render_combat(buf: *u8, off: *i64, st: *i64) -> i64 { 347 bputc(buf, off, 10) 348 bput(buf, off, " -- COMBAT (dungenc FSM) --" as *u8); bputc(buf, off, 10); bputc(buf, off, 10) 349 bput(buf, off, " YOU " as *u8); render_bar(buf, off, st[2], 100, 90, 230) 350 var emax: i64 = 20 + st[4] * 5 351 var ehp: i64 = st[7] 352 if ehp < 0 { ehp = 0 } 353 bput(buf, off, " FOE " as *u8); render_bar(buf, off, ehp, emax, 220, 60) 354 bputc(buf, off, 10) 355 return 0 356} 357 358func render_foot(buf: *u8, off: *i64, st: *i64) -> i64 { 359 let mode: i64 = st[5] 360 bputc(buf, off, 10) 361 if mode == 0 { bput(buf, off, " move: w a s d reach >> to descend quit: q" as *u8) } 362 if mode == 1 { bput(buf, off, " space = attack w/a/s/d = flee quit: q" as *u8) } 363 if mode == 2 { bput(buf, off, " LEVEL CLEAR! space = descend deeper quit: q" as *u8) } 364 if mode == 3 { bput(buf, off, " YOU DIED. r = new run quit: q" as *u8) } 365 bputc(buf, off, 10) 366 return 0 367} 368 369func render(buf: *u8, st: *i64) -> i64 { 370 let off: *i64 = sys_mmap(16) as *i64 371 off[0] = 0 372 bput_clear(buf, off) 373 render_hud(buf, off, st) 374 if st[5] == 1 { render_combat(buf, off, st) } else { render_grid(buf, off, st) } 375 render_foot(buf, off, st) 376 sys_write(1, buf, off[0]) 377 return 0 378} 379 380func map_key(c: i64) -> i64 { 381 if c == 119 { return 0 } // w 382 if c == 115 { return 1 } // s 383 if c == 97 { return 2 } // a 384 if c == 100 { return 3 } // d 385 if c == 32 { return 4 } // space 386 if c == 102 { return 4 } // f 387 if c == 13 { return 4 } // enter 388 if c == 10 { return 4 } // newline 389 if c == 114 { return 5 } // r 390 return 9 391} 392 393// enable termios raw single-key mode (returns 1 if stdin is a tty, else 0) 394func term_raw_on(orig: *u8, raw: *u8) -> i64 { 395 let g: i64 = sys_ioctl(0, 0x5401, orig as i64) // TCGETS 396 if g != 0 { return 0 } 397 var i: i64 = 0 398 while i < 60 { raw[i] = orig[i]; i = i + 1 } 399 var lf: i64 = raw[12] as i64 // c_lflag low byte 400 lf = lf & (255 - 2) // clear ICANON 401 lf = lf & (255 - 8) // clear ECHO 402 raw[12] = lf as u8 403 raw[17 + 6] = 1 as u8 // VMIN = 1 404 raw[17 + 5] = 0 as u8 // VTIME = 0 405 sys_ioctl(0, 0x5402, raw as i64) // TCSETS 406 return 1 407} 408func term_raw_off(orig: *u8) -> i64 { 409 sys_ioctl(0, 0x5402, orig as i64) 410 return 0 411} 412 413func main() -> i64 { 414 let st: *i64 = sys_mmap(K_MAGIC_4096) as *i64 415 let orig: *u8 = sys_mmap(128) 416 let raw: *u8 = sys_mmap(128) 417 let is_tty: i64 = term_raw_on(orig, raw) 418 gi_init(st, sys_now_ms()) 419 let buf: *u8 = sys_mmap(K_MAGIC_16384) 420 let ib: *u8 = sys_mmap(8) 421 var running: i64 = 1 422 while running == 1 { 423 render(buf, st) 424 let n: i64 = sys_read(0, ib, 1) 425 if n <= 0 { 426 running = 0 427 } else { 428 let c: i64 = ib[0] as i64 429 if c == 113 { running = 0 } 430 if c == 81 { running = 0 } 431 if c != 113 { if c != 81 { gi_input(st, map_key(c)) } } 432 } 433 } 434 if is_tty == 1 { term_raw_off(orig) } 435 let off: *i64 = sys_mmap(16) as *i64 436 off[0] = 0 437 bput_clear(buf, off) 438 bput(buf, off, "thanks for playing -- NISHI DUNGEON alpha\n" as *u8) 439 sys_write(1, buf, off[0]) 440 sys_exit(0) 441 return 0 442}