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}