code wiki / _hdl_build / nx_game_emit.nx
nx_game_emit.nx source
↩ module page · 290 lines · 11049 B
1// nx_game_emit.nx -- the GAME EMITTER: a data game-spec in, a complete playable game
2// module out, hands-off. This is the autonomous game-gen lever (operator's choice):
3// feed specs -> get games. It reads a game-spec (difficulty/theme/title + the combat
4// FSM transition table) and WRITES a per-game .nx module that fills the engine config +
5// FSM table and calls eng_run (nx_game_engine_lib). The generated game is then built
6// native by Nishi's own toolchain and proven playable by a playthrough gate.
7//
8// Spec grammar (one key per line):
9// name _game_foo module name (-> runtime/_hdl_build/_game_foo.nx)
10// title THE CRYPT HUD title (rest of line)
11// walls 22 interior wall count
12// enemy_base 2 enemies at depth 1
13// enemy_per_level 1 extra enemies per depth
14// ehp_base 18 enemy hp at depth 1
15// ehp_per_level 4 extra enemy hp per depth
16// loot 3 loot pickups per level
17// dmg_base 8 player base damage
18// dmg_rand 8 player random damage span
19// fsm 6 6 combat FSM dims (nstates nevents; dungenc convention)
20// row ... one transition-table row per state (nevents ints, -1=none)
21//
22// HONEST AUTHORSHIP: this emitter + the engine library are TUTOR-authored tooling. The
23// GAMES it writes are emitter output (the autonomy). Usage: nx_game_emit <specpath>.
24// license_tier: ORIGINAL
25import "nx_syscalls.nx"
26const K_MAGIC_2048: i64 = 2048
27
28func p_w(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
29func p_c(fd: i64, c: i64) -> i64 { let b: *u8 = sys_mmap(8); b[0] = c as u8; sys_write(fd, b, 1); return 0 }
30func p_wn(fd: i64, v: i64) -> i64 {
31 var m: i64 = v
32 if m < 0 { p_c(fd, 45); m = 0 - m }
33 let bb: *u8 = sys_mmap(28)
34 let t: *u8 = sys_mmap(28)
35 var k: i64 = 0
36 if m == 0 { t[0] = 48 as u8; k = 1 }
37 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
38 var i: i64 = 0
39 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
40 sys_write(fd, bb, k)
41 return 0
42}
43
44func gcstr_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
45func gmatch(buf: *u8, pos: i64, key: *u8) -> i64 {
46 var i: i64 = 0
47 while key[i] != (0 as u8) { if buf[pos + i] != key[i] { return 0 } i = i + 1 }
48 return 1
49}
50func gline_start(buf: *u8, pos: i64) -> i64 {
51 if pos == 0 { return 1 }
52 if buf[pos - 1] == (10 as u8) { return 1 }
53 return 0
54}
55func gfind(buf: *u8, len: i64, key: *u8) -> i64 {
56 let kl: i64 = gcstr_len(key)
57 var i: i64 = 0
58 while i < len {
59 if gline_start(buf, i) == 1 {
60 if gmatch(buf, i, key) == 1 {
61 if buf[i + kl] == (32 as u8) { return i + kl + 1 }
62 }
63 }
64 i = i + 1
65 }
66 return 0 - 1
67}
68func gpint(buf: *u8, pos: i64) -> i64 {
69 var p: i64 = pos
70 while buf[p] == (32 as u8) { p = p + 1 }
71 var neg: i64 = 0
72 if buf[p] == (45 as u8) { neg = 1; p = p + 1 }
73 var v: i64 = 0
74 var go: i64 = 1
75 while go == 1 {
76 let c: i64 = buf[p] as i64
77 if c < 48 { go = 0 }
78 if c > 57 { go = 0 }
79 if go == 1 { v = v * 10 + (c - 48); p = p + 1 }
80 }
81 if neg == 1 { return 0 - v }
82 return v
83}
84func gint(buf: *u8, len: i64, key: *u8, dflt: i64) -> i64 {
85 let p: i64 = gfind(buf, len, key)
86 if p < 0 { return dflt }
87 return gpint(buf, p)
88}
89// copy "key <value-to-EOL>" into out (NUL-terminated); returns length
90func gstr(buf: *u8, len: i64, key: *u8, out: *u8) -> i64 {
91 let p: i64 = gfind(buf, len, key)
92 if p < 0 { out[0] = 0 as u8; return 0 }
93 var r: i64 = 0
94 var w: i64 = 0
95 var go: i64 = 1
96 while go == 1 {
97 let c: i64 = buf[p + r] as i64
98 if c == 10 { go = 0 }
99 if c == 0 { go = 0 }
100 if go == 1 {
101 if c != 13 { out[w] = c as u8; w = w + 1 }
102 r = r + 1
103 }
104 }
105 out[w] = 0 as u8
106 return w
107}
108// collect all "row " lines' ints (in order) into tbl; returns count
109func gtable(buf: *u8, len: i64, tbl: *i64) -> i64 {
110 var count: i64 = 0
111 var i: i64 = 0
112 while i < len {
113 if gline_start(buf, i) == 1 {
114 if gmatch(buf, i, "row" as *u8) == 1 {
115 if buf[i + 3] == (32 as u8) {
116 var p: i64 = i + 3
117 var go: i64 = 1
118 while go == 1 {
119 while buf[p] == (32 as u8) { p = p + 1 }
120 let c: i64 = buf[p] as i64
121 if c == 10 { go = 0 }
122 if c == 13 { go = 0 }
123 if c == 0 { go = 0 }
124 if go == 1 {
125 tbl[count] = gpint(buf, p)
126 count = count + 1
127 if buf[p] == (45 as u8) { p = p + 1 }
128 var d: i64 = 1
129 while d == 1 {
130 let cc: i64 = buf[p] as i64
131 if cc < 48 { d = 0 }
132 if cc > 57 { d = 0 }
133 if d == 1 { p = p + 1 }
134 }
135 }
136 }
137 }
138 }
139 }
140 i = i + 1
141 }
142 return count
143}
144
145// parse `needs <part>` lines into a bitmask (bit0=save 1=networking 2=audio 3=accounts)
146func gneeds(buf: *u8, len: i64) -> i64 {
147 var m: i64 = 0
148 var i: i64 = 0
149 while i < len {
150 if gline_start(buf, i) == 1 {
151 if gmatch(buf, i, "needs " as *u8) == 1 {
152 let p: i64 = i + 6
153 if gmatch(buf, p, "save" as *u8) == 1 { m = m | 1 }
154 if gmatch(buf, p, "networking" as *u8) == 1 { m = m | 2 }
155 if gmatch(buf, p, "audio" as *u8) == 1 { m = m | 4 }
156 if gmatch(buf, p, "accounts" as *u8) == 1 { m = m | 8 }
157 }
158 }
159 i = i + 1
160 }
161 return m
162}
163
164func build_path(name: *u8, out: *u8) -> i64 {
165 let pre: *u8 = "runtime/_hdl_build/" as *u8
166 var w: i64 = 0
167 var i: i64 = 0
168 while pre[i] != (0 as u8) { out[w] = pre[i]; w = w + 1; i = i + 1 }
169 i = 0
170 while name[i] != (0 as u8) { out[w] = name[i]; w = w + 1; i = i + 1 }
171 let suf: *u8 = ".nx" as *u8
172 i = 0
173 while suf[i] != (0 as u8) { out[w] = suf[i]; w = w + 1; i = i + 1 }
174 out[w] = 0 as u8
175 return w
176}
177
178func emit_assign(fd: i64, idx: i64, val: i64) -> i64 {
179 p_w(fd, " cfg[" as *u8); p_wn(fd, idx); p_w(fd, "] = " as *u8); p_wn(fd, val); p_w(fd, "\n" as *u8)
180 return 0
181}
182func emit_head(fd: i64) -> i64 {
183 p_w(fd, "// AUTHORED BY nx_game_emit from a data game-spec -- per-game module over nx_game_engine_lib\n" as *u8)
184 p_w(fd, "import " as *u8); p_c(fd, 34); p_w(fd, "nx_syscalls.nx" as *u8); p_c(fd, 34); p_w(fd, "\n" as *u8)
185 p_w(fd, "import " as *u8); p_c(fd, 34); p_w(fd, "nx_game_engine_lib.nx" as *u8); p_c(fd, 34); p_w(fd, "\n" as *u8)
186 p_w(fd, "func main() -> i64 {\n" as *u8)
187 p_w(fd, " let cfg: *i64 = sys_mmap(256) as *i64\n" as *u8)
188 return 0
189}
190func emit_cfg(fd: i64, vals: *i64) -> i64 {
191 emit_assign(fd, 13, vals[0])
192 emit_assign(fd, 14, vals[1])
193 emit_assign(fd, 15, vals[2])
194 emit_assign(fd, 16, vals[3])
195 emit_assign(fd, 17, vals[4])
196 emit_assign(fd, 18, vals[5])
197 emit_assign(fd, 19, vals[6])
198 emit_assign(fd, 20, vals[7])
199 return 0
200}
201// wire composed parts: cfg[25]=needs-mask, cfg[26]=save-file path (the parts-composition mechanism)
202func emit_needs(fd: i64, name: *u8, mask: i64) -> i64 {
203 emit_assign(fd, 25, mask)
204 p_w(fd, " cfg[26] = " as *u8); p_c(fd, 34)
205 p_w(fd, "knowledge/store/" as *u8); p_w(fd, name); p_w(fd, ".save" as *u8)
206 p_c(fd, 34); p_w(fd, " as *u8 as i64\n" as *u8)
207 return 0
208}
209func emit_title(fd: i64, title: *u8) -> i64 {
210 p_w(fd, " cfg[21] = " as *u8); p_c(fd, 34); p_w(fd, title); p_c(fd, 34); p_w(fd, " as *u8 as i64\n" as *u8)
211 return 0
212}
213func emit_table(fd: i64, tbl: *i64, n: i64) -> i64 {
214 p_w(fd, " let t: *i64 = sys_mmap(2048) as *i64\n" as *u8)
215 var k: i64 = 0
216 while k < n {
217 p_w(fd, " t[" as *u8); p_wn(fd, k); p_w(fd, "] = " as *u8)
218 let v: i64 = tbl[k]
219 if v < 0 { p_w(fd, "0 - " as *u8); p_wn(fd, 0 - v) } else { p_wn(fd, v) }
220 p_w(fd, "\n" as *u8)
221 k = k + 1
222 }
223 return 0
224}
225func emit_tail(fd: i64, ns: i64, ne: i64) -> i64 {
226 p_w(fd, " cfg[22] = t as i64\n" as *u8)
227 emit_assign(fd, 23, ns)
228 emit_assign(fd, 24, ne)
229 p_w(fd, " return eng_run(cfg)\n" as *u8)
230 p_w(fd, "}\n" as *u8)
231 return 0
232}
233
234func main(argc: i64, argv: *i64) -> i64 {
235 if argc < 2 { p_w(1, "usage: nx_game_emit <specpath>\n" as *u8); sys_exit(2); return 2 }
236 let sp: *u8 = argv[1] as *u8
237 let lenp: *i64 = sys_mmap(16) as *i64
238 let buf: *u8 = sys_read_file(sp, lenp)
239 if buf == (0 as i64) as *u8 { p_w(1, "GAMEEMIT REFUSED: cannot read spec\n" as *u8); sys_exit(1); return 1 }
240 let len: i64 = lenp[0]
241
242 let name: *u8 = sys_mmap(128)
243 let title: *u8 = sys_mmap(128)
244 gstr(buf, len, "name" as *u8, name)
245 gstr(buf, len, "title" as *u8, title)
246 if name[0] == (0 as u8) { p_w(1, "GAMEEMIT REFUSED: no name\n" as *u8); sys_exit(1); return 1 }
247
248 let vals: *i64 = sys_mmap(128) as *i64
249 vals[0] = gint(buf, len, "walls" as *u8, 20)
250 vals[1] = gint(buf, len, "enemy_base" as *u8, 3)
251 vals[2] = gint(buf, len, "enemy_per_level" as *u8, 1)
252 vals[3] = gint(buf, len, "ehp_base" as *u8, 20)
253 vals[4] = gint(buf, len, "ehp_per_level" as *u8, 5)
254 vals[5] = gint(buf, len, "loot" as *u8, 2)
255 vals[6] = gint(buf, len, "dmg_base" as *u8, 8)
256 vals[7] = gint(buf, len, "dmg_rand" as *u8, 8)
257 let ns: i64 = gint(buf, len, "fsm" as *u8, 6)
258 let nep: i64 = gfind(buf, len, "fsm" as *u8)
259 var ne: i64 = 6
260 if nep >= 0 {
261 var q: i64 = nep
262 while buf[q] == (32 as u8) { q = q + 1 }
263 var d: i64 = 1
264 while d == 1 { let c: i64 = buf[q] as i64; if c < 48 { d = 0 } if c > 57 { d = 0 } if d == 1 { q = q + 1 } }
265 ne = gpint(buf, q)
266 }
267 let tbl: *i64 = sys_mmap(K_MAGIC_2048) as *i64
268 let tcount: i64 = gtable(buf, len, tbl)
269 let needs: i64 = gneeds(buf, len)
270
271 let modpath: *u8 = sys_mmap(256)
272 build_path(name, modpath)
273 let fd: i64 = sys_openat_wr(modpath, 0x1a4)
274 if fd < 0 { p_w(1, "GAMEEMIT REFUSED: cannot open module path\n" as *u8); sys_exit(1); return 1 }
275 emit_head(fd)
276 emit_cfg(fd, vals)
277 emit_needs(fd, name, needs)
278 emit_title(fd, title)
279 emit_table(fd, tbl, tcount)
280 emit_tail(fd, ns, ne)
281 sys_close(fd)
282
283 p_w(1, "GAMEEMIT GREEN name=" as *u8); p_w(1, name)
284 p_w(1, " title=" as *u8); p_w(1, title)
285 p_w(1, " fsm=" as *u8); p_wn(1, ns); p_w(1, "x" as *u8); p_wn(1, ne)
286 p_w(1, " tbl=" as *u8); p_wn(1, tcount)
287 p_w(1, " -> " as *u8); p_w(1, modpath); p_w(1, "\n" as *u8)
288 sys_exit(0)
289 return 0
290}