code wiki / _hdl_build / nx_platformer_term.nx

nx_platformer_term.nx source

↩ module page · 121 lines · 5614 B

1// nx_platformer_term.nx -- LIB: a PURE-NISHI, bottom-up terminal renderer for the platformer. NO blit, NO 2// browser, NO JS, NO third-party -- the integer game state is drawn straight to the terminal as ANSI truecolor 3// cells via sys_write (the same sovereign path the dungeon engine uses). The SAME integer no-float logic that 4// nx_wasm_platformer runs (init_impl/tick_impl) drives it; here we just render to a character grid instead of a 5// pixel framebuffer. (That integer framebuffer is itself the bare-metal-ready primitive: on Nishi hardware we 6// own the linear framebuffer and write pixels directly -- no blit. The terminal is the sovereign display today.) 7// license_tier: ORIGINAL 8import "nx_wasm_platformer.nx" 9import "nx_syscalls.nx" 10 11const COLS: i64 = 80 12const ROWS: i64 = 20 13 14// append NUL-terminated s into buf at off; return new off. 15func pt_s(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { buf[o] = s[i]; o = o + 1; i = i + 1 } return o } 16// write the ESC byte (27) then s -- NishiLang strings have no \x escape, so ESC is emitted as a raw byte. 17func pt_esc(buf: *u8, off: i64, s: *u8) -> i64 { buf[off] = 27 as u8; return pt_s(buf, off + 1, s) } 18// append a 0..255 decimal into buf; return new off. 19func pt_n(buf: *u8, off: i64, v: i64) -> i64 { 20 var o: i64 = off; var m: i64 = v 21 if m < 0 { m = 0 } 22 if m == 0 { buf[o] = 48 as u8; return o + 1 } 23 let t: *u8 = sys_mmap(8); var k: i64 = 0 24 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 25 var j: i64 = 0 26 while j < k { buf[o] = t[k - 1 - j]; o = o + 1; j = j + 1 } 27 return o 28} 29 30// fill `grid` (ROWS*COLS packed-RGB i64) from the game state at `base`. Pure integer mapping world(W,H)->grid. 31func pt_frame(base: i64, grid: *i64) -> i64 { 32 let SKY: i64 = rgb(28, 38, 60) 33 let GROUND: i64 = rgb(46, 84, 52) 34 let HEROC: i64 = rgb(90, 210, 122) 35 let COINC: i64 = rgb(255, 210, 70) 36 let FOEC: i64 = rgb(224, 80, 58) 37 let EXITC: i64 = rgb(60, 200, 210) 38 var i: i64 = 0 39 while i < ROWS*COLS { grid[i] = SKY; i = i + 1 } 40 let gground: i64 = (GROUNDY + HEROH) * ROWS / H 41 var gy: i64 = gground 42 while gy < ROWS { var gx: i64 = 0; while gx < COLS { grid[gy*COLS + gx] = GROUND; gx = gx + 1 } gy = gy + 1 } 43 let egx: i64 = EXITX * COLS / W 44 gy = 0 45 while gy < gground { if egx < COLS { grid[gy*COLS + egx] = EXITC } gy = gy + 1 } 46 i = 0 47 while i < NCOIN { 48 if cget(base, i) == 0 { let cgx: i64 = coin_x(i) * COLS / W; let cgy: i64 = COINY * ROWS / H; if cgx < COLS { if cgy < ROWS { grid[cgy*COLS + cgx] = COINC } } } 49 i = i + 1 50 } 51 var j: i64 = 0 52 while j < NFOE { 53 if fget(base, j) == 0 { 54 let fgx: i64 = foe_x(j) * COLS / W 55 var fgy: i64 = GROUNDY * ROWS / H 56 while fgy < gground { if fgx < COLS { grid[fgy*COLS + fgx] = FOEC } fgy = fgy + 1 } 57 } 58 j = j + 1 59 } 60 let hgx: i64 = sget(base, 0) * COLS / W 61 var hgy: i64 = sget(base, 1) * ROWS / H 62 let hgy2: i64 = (sget(base, 1) + HEROH) * ROWS / H 63 while hgy <= hgy2 { if hgy >= 0 { if hgy < ROWS { if hgx < COLS { grid[hgy*COLS + hgx] = HEROC } } } hgy = hgy + 1 } 64 return 0 65} 66 67// emit `grid` as ANSI truecolor cells (bg color + space) into buf; append a HUD line with the score. Returns length. 68func pt_emit(base: i64, grid: *i64, buf: *u8) -> i64 { 69 var o: i64 = 0 70 o = pt_esc(buf, o, "[H" as *u8) // cursor home (caller clears once) 71 var gy: i64 = 0 72 while gy < ROWS { 73 var gx: i64 = 0 74 while gx < COLS { 75 let c: i64 = grid[gy*COLS + gx] 76 o = pt_esc(buf, o, "[48;2;" as *u8) 77 o = pt_n(buf, o, c & 255); buf[o] = 59 as u8; o = o + 1 78 o = pt_n(buf, o, (c >> 8) & 255); buf[o] = 59 as u8; o = o + 1 79 o = pt_n(buf, o, (c >> 16) & 255); buf[o] = 109 as u8; o = o + 1 80 buf[o] = 32 as u8; o = o + 1 81 gx = gx + 1 82 } 83 o = pt_esc(buf, o, "[0m" as *u8); buf[o] = 10 as u8; o = o + 1 84 gy = gy + 1 85 } 86 o = pt_s(buf, o, " score=" as *u8); o = pt_n(buf, o, sget(base, 4)) 87 o = pt_s(buf, o, " coins=" as *u8); o = pt_n(buf, o, sget(base, 5)) 88 if sget(base, 7) == 1 { o = pt_s(buf, o, " *** REACHED THE EXIT -- YOU WIN ***" as *u8) } else { o = pt_s(buf, o, " [space]=jump q=quit" as *u8) } 89 o = pt_s(buf, o, " \n" as *u8) 90 buf[o] = 0 as u8 91 return o 92} 93 94// human-viewable ASCII dump of the SAME grid (sky=space, ground='#', coin='o', foe='X', exit='|', hero='@') -- 95// a sovereign, terminal-free way to eyeball the frame in a log/file. `grid` must be filled by pt_frame first. 96func pt_ascii(base: i64, grid: *i64, buf: *u8) -> i64 { 97 let GROUND: i64 = rgb(46, 84, 52); let COINC: i64 = rgb(255, 210, 70); let FOEC: i64 = rgb(224, 80, 58) 98 let EXITC: i64 = rgb(60, 200, 210); let HEROC: i64 = rgb(90, 210, 122) 99 var o: i64 = 0 100 var gy: i64 = 0 101 while gy < ROWS { 102 var gx: i64 = 0 103 while gx < COLS { 104 let c: i64 = grid[gy*COLS + gx] 105 var ch: i64 = 32 106 if c == GROUND { ch = 35 } 107 if c == COINC { ch = 111 } 108 if c == FOEC { ch = 88 } 109 if c == EXITC { ch = 124 } 110 if c == HEROC { ch = 64 } 111 buf[o] = ch as u8; o = o + 1 112 gx = gx + 1 113 } 114 buf[o] = 10 as u8; o = o + 1 115 gy = gy + 1 116 } 117 o = pt_s(buf, o, " score=" as *u8); o = pt_n(buf, o, sget(base, 4)) 118 o = pt_s(buf, o, " @=hero o=coin X=foe |=exit #=ground\n" as *u8) 119 buf[o] = 0 as u8 120 return o 121}