1 /* NetHack 3.7 nhlua.c $NHDT-Date: 1580506559 2020/01/31 21:35:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */
2 /* Copyright (c) 2018 by Pasi Kallinen */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include "hack.h"
6 #include "dlb.h"
7
8 /*
9 #- include <lua5.3/lua.h>
10 #- include <lua5.3/lualib.h>
11 #- include <lua5.3/lauxlib.h>
12 */
13
14 /* */
15
16 /* lua_CFunction prototypes */
17 static int nhl_test(lua_State *);
18 static int nhl_getmap(lua_State *);
19 static void nhl_add_table_entry_bool(lua_State *, const char *, boolean);
20 static char splev_typ2chr(schar);
21 static int nhl_gettrap(lua_State *);
22 static int nhl_deltrap(lua_State *);
23 #if 0
24 static int nhl_setmap(lua_State *);
25 #endif
26 static int nhl_pline(lua_State *);
27 static int nhl_verbalize(lua_State *);
28 static int nhl_parse_config(lua_State *);
29 static int nhl_menu(lua_State *);
30 static int nhl_getlin(lua_State *);
31 static int nhl_makeplural(lua_State *);
32 static int nhl_makesingular(lua_State *);
33 static int nhl_s_suffix(lua_State *);
34 static int nhl_ing_suffix(lua_State *);
35 static int nhl_an(lua_State *);
36 static int nhl_rn2(lua_State *);
37 static int nhl_random(lua_State *);
38 static int nhl_level_difficulty(lua_State *);
39 static void init_nhc_data(lua_State *);
40 static int nhl_push_anything(lua_State *, int, void *);
41 static int nhl_meta_u_index(lua_State *);
42 static int nhl_meta_u_newindex(lua_State *);
43 static int nhl_u_clear_inventory(lua_State *);
44 static int nhl_u_giveobj(lua_State *);
45 static void init_u_data(lua_State *);
46 static int nhl_set_package_path(lua_State *, const char *);
47 static int traceback_handler(lua_State *);
48
49 void
nhl_error(lua_State * L,const char * msg)50 nhl_error(lua_State *L, const char *msg)
51 {
52 lua_Debug ar;
53 char buf[BUFSZ];
54
55 lua_getstack(L, 1, &ar);
56 lua_getinfo(L, "lS", &ar);
57 Sprintf(buf, "%s (line %d ", msg, ar.currentline);
58 Sprintf(eos(buf), "%.*s)",
59 /* (max length of ar.short_src is actually LUA_IDSIZE
60 so this is overkill for it, but crucial for ar.source) */
61 (int) (sizeof buf - (strlen(buf) + sizeof ")")),
62 ar.short_src); /* (used to be 'ar.source' here) */
63 lua_pushstring(L, buf);
64 #if 0 /* defined(PANICTRACE) && !defined(NO_SIGNALS) */
65 panictrace_setsignals(FALSE);
66 #endif
67 (void) lua_error(L);
68 /*NOTREACHED*/
69 }
70
71 /* Check that parameters are nothing but single table,
72 or if no parameters given, put empty table there */
73 void
lcheck_param_table(lua_State * L)74 lcheck_param_table(lua_State *L)
75 {
76 int argc = lua_gettop(L);
77
78 if (argc < 1)
79 lua_createtable(L, 0, 0);
80
81 /* discard any extra arguments passed in */
82 lua_settop(L, 1);
83
84 luaL_checktype(L, 1, LUA_TTABLE);
85 }
86
87 schar
get_table_mapchr(lua_State * L,const char * name)88 get_table_mapchr(lua_State *L, const char *name)
89 {
90 char *ter;
91 xchar typ;
92
93 ter = get_table_str(L, name);
94 typ = check_mapchr(ter);
95 if (typ == INVALID_TYPE)
96 nhl_error(L, "Erroneous map char");
97 if (ter)
98 free(ter);
99 return typ;
100 }
101
102 schar
get_table_mapchr_opt(lua_State * L,const char * name,schar defval)103 get_table_mapchr_opt(lua_State *L, const char *name, schar defval)
104 {
105 char *ter;
106 xchar typ;
107
108 ter = get_table_str_opt(L, name, emptystr);
109 if (name && *ter) {
110 typ = check_mapchr(ter);
111 if (typ == INVALID_TYPE)
112 nhl_error(L, "Erroneous map char");
113 } else
114 typ = defval;
115 if (ter)
116 free(ter);
117 return typ;
118 }
119
120 void
nhl_add_table_entry_int(lua_State * L,const char * name,int value)121 nhl_add_table_entry_int(lua_State *L, const char *name, int value)
122 {
123 lua_pushstring(L, name);
124 lua_pushinteger(L, value);
125 lua_rawset(L, -3);
126 }
127
128 void
nhl_add_table_entry_char(lua_State * L,const char * name,char value)129 nhl_add_table_entry_char(lua_State *L, const char *name, char value)
130 {
131 char buf[2];
132 Sprintf(buf, "%c", value);
133 lua_pushstring(L, name);
134 lua_pushstring(L, buf);
135 lua_rawset(L, -3);
136 }
137
138 void
nhl_add_table_entry_str(lua_State * L,const char * name,const char * value)139 nhl_add_table_entry_str(lua_State *L, const char *name, const char *value)
140 {
141 lua_pushstring(L, name);
142 lua_pushstring(L, value);
143 lua_rawset(L, -3);
144 }
145 void
nhl_add_table_entry_bool(lua_State * L,const char * name,boolean value)146 nhl_add_table_entry_bool(lua_State *L, const char *name, boolean value)
147 {
148 lua_pushstring(L, name);
149 lua_pushboolean(L, value);
150 lua_rawset(L, -3);
151 }
152
153 /* converting from special level "map character" to levl location type
154 and back. order here is important. */
155 const struct {
156 char ch;
157 schar typ;
158 } char2typ[] = {
159 { ' ', STONE },
160 { '#', CORR },
161 { '.', ROOM },
162 { '-', HWALL },
163 { '-', TLCORNER },
164 { '-', TRCORNER },
165 { '-', BLCORNER },
166 { '-', BRCORNER },
167 { '-', CROSSWALL },
168 { '-', TUWALL },
169 { '-', TDWALL },
170 { '-', TLWALL },
171 { '-', TRWALL },
172 { '-', DBWALL },
173 { '|', VWALL },
174 { '+', DOOR },
175 { 'A', AIR },
176 { 'C', CLOUD },
177 { 'S', SDOOR },
178 { 'H', SCORR },
179 { '{', FOUNTAIN },
180 { '\\', THRONE },
181 { 'K', SINK },
182 { '}', MOAT },
183 { 'P', POOL },
184 { 'L', LAVAPOOL },
185 { 'I', ICE },
186 { 'W', WATER },
187 { 'T', TREE },
188 { 'F', IRONBARS }, /* Fe = iron */
189 { ',', GRASS },
190 { 'g', GRASS },
191 { 'x', MAX_TYPE }, /* "see-through" */
192 { 'B', CROSSWALL }, /* hack: boundary location */
193 { 'w', MATCH_WALL }, /* IS_STWALL() */
194 { '\0', STONE },
195 };
196
197 schar
splev_chr2typ(char c)198 splev_chr2typ(char c)
199 {
200 int i;
201
202 for (i = 0; char2typ[i].ch; i++)
203 if (c == char2typ[i].ch)
204 return char2typ[i].typ;
205 return (INVALID_TYPE);
206 }
207
208 schar
check_mapchr(const char * s)209 check_mapchr(const char *s)
210 {
211 if (s && strlen(s) == 1)
212 return splev_chr2typ(s[0]);
213 return INVALID_TYPE;
214 }
215
216 static char
splev_typ2chr(schar typ)217 splev_typ2chr(schar typ)
218 {
219 int i;
220
221 for (i = 0; char2typ[i].typ < MAX_TYPE; i++)
222 if (typ == char2typ[i].typ)
223 return char2typ[i].ch;
224 return 'x';
225 }
226
227 /* local t = gettrap(x,y); */
228 static int
nhl_gettrap(lua_State * L)229 nhl_gettrap(lua_State *L)
230 {
231 int argc = lua_gettop(L);
232
233 if (argc == 2) {
234 int x = (int) lua_tointeger(L, 1);
235 int y = (int) lua_tointeger(L, 2);
236
237 if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) {
238 struct trap *ttmp = t_at(x,y);
239
240 if (ttmp) {
241 lua_newtable(L);
242
243 nhl_add_table_entry_int(L, "tx", ttmp->tx);
244 nhl_add_table_entry_int(L, "ty", ttmp->ty);
245 nhl_add_table_entry_int(L, "ttyp", ttmp->ttyp);
246 nhl_add_table_entry_str(L, "ttyp_name",
247 get_trapname_bytype(ttmp->ttyp));
248 nhl_add_table_entry_bool(L, "tseen", ttmp->tseen);
249 nhl_add_table_entry_bool(L, "madeby_u", ttmp->madeby_u);
250 switch (ttmp->ttyp) {
251 case SQKY_BOARD:
252 nhl_add_table_entry_int(L, "tnote", ttmp->tnote);
253 break;
254 case ROLLING_BOULDER_TRAP:
255 nhl_add_table_entry_int(L, "launchx", ttmp->launch.x);
256 nhl_add_table_entry_int(L, "launchy", ttmp->launch.y);
257 nhl_add_table_entry_int(L, "launch2x", ttmp->launch2.x);
258 nhl_add_table_entry_int(L, "launch2y", ttmp->launch2.y);
259 break;
260 case PIT:
261 case SPIKED_PIT:
262 nhl_add_table_entry_int(L, "conjoined", ttmp->conjoined);
263 break;
264 }
265 return 1;
266 } else
267 nhl_error(L, "No trap at location");
268 } else
269 nhl_error(L, "Coordinates out of range");
270 } else
271 nhl_error(L, "Wrong args");
272 return 0;
273 }
274
275 /* deltrap(x,y); */
276 static int
nhl_deltrap(lua_State * L)277 nhl_deltrap(lua_State *L)
278 {
279 int argc = lua_gettop(L);
280
281 if (argc == 2) {
282 int x = (int) lua_tointeger(L, 1);
283 int y = (int) lua_tointeger(L, 2);
284
285 if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) {
286 struct trap *ttmp = t_at(x,y);
287
288 if (ttmp)
289 deltrap(ttmp);
290 }
291 }
292 return 0;
293 }
294
295 DISABLE_WARNING_UNREACHABLE_CODE
296
297 /* local loc = getmap(x,y) */
298 static int
nhl_getmap(lua_State * L)299 nhl_getmap(lua_State *L)
300 {
301 int argc = lua_gettop(L);
302
303 if (argc == 2) {
304 int x = (int) lua_tointeger(L, 1);
305 int y = (int) lua_tointeger(L, 2);
306
307 if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) {
308 char buf[BUFSZ];
309 lua_newtable(L);
310
311 /* FIXME: some should be boolean values */
312 nhl_add_table_entry_int(L, "glyph", levl[x][y].glyph);
313 nhl_add_table_entry_int(L, "typ", levl[x][y].typ);
314 nhl_add_table_entry_str(L, "typ_name",
315 levltyp_to_name(levl[x][y].typ));
316 Sprintf(buf, "%c", splev_typ2chr(levl[x][y].typ));
317 nhl_add_table_entry_str(L, "mapchr", buf);
318 nhl_add_table_entry_int(L, "seenv", levl[x][y].seenv);
319 nhl_add_table_entry_bool(L, "horizontal", levl[x][y].horizontal);
320 nhl_add_table_entry_bool(L, "lit", levl[x][y].lit);
321 nhl_add_table_entry_bool(L, "waslit", levl[x][y].waslit);
322 nhl_add_table_entry_int(L, "roomno", levl[x][y].roomno);
323 nhl_add_table_entry_bool(L, "edge", levl[x][y].edge);
324 nhl_add_table_entry_bool(L, "candig", levl[x][y].candig);
325
326 nhl_add_table_entry_bool(L, "has_trap", t_at(x,y) ? 1 : 0);
327
328 /* TODO: FIXME: levl[x][y].flags */
329
330 lua_pushliteral(L, "flags");
331 lua_newtable(L);
332
333 if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) {
334 nhl_add_table_entry_bool(L, "nodoor",
335 doorstate(&levl[x][y]) == D_NODOOR);
336 nhl_add_table_entry_bool(L, "broken",
337 doorstate(&levl[x][y]) == D_BROKEN);
338 nhl_add_table_entry_bool(L, "isopen",
339 doorstate(&levl[x][y]) == D_ISOPEN);
340 nhl_add_table_entry_bool(L, "closed",
341 doorstate(&levl[x][y]) == D_CLOSED);
342 nhl_add_table_entry_bool(L, "locked",
343 door_is_locked(&levl[x][y]));
344 nhl_add_table_entry_bool(L, "trapped",
345 door_is_trapped(&levl[x][y]));
346 nhl_add_table_entry_bool(L, "iron",
347 door_is_iron(&levl[x][y]));
348 } else if (IS_ALTAR(levl[x][y].typ)) {
349 /* TODO: bits 0, 1, 2 */
350 nhl_add_table_entry_bool(L, "shrine",
351 (levl[x][y].flags & AM_SHRINE));
352 } else if (IS_THRONE(levl[x][y].typ)) {
353 nhl_add_table_entry_bool(L, "looted",
354 (levl[x][y].flags & T_LOOTED));
355 } else if (levl[x][y].typ == TREE) {
356 nhl_add_table_entry_bool(L, "looted",
357 (levl[x][y].flags & TREE_LOOTED));
358 nhl_add_table_entry_bool(L, "swarm",
359 (levl[x][y].flags & TREE_SWARM));
360 } else if (IS_FOUNTAIN(levl[x][y].typ)) {
361 nhl_add_table_entry_bool(L, "looted",
362 (levl[x][y].flags & F_LOOTED));
363 nhl_add_table_entry_bool(L, "warned",
364 (levl[x][y].flags & F_WARNED));
365 } else if (IS_SINK(levl[x][y].typ)) {
366 nhl_add_table_entry_bool(L, "pudding",
367 (levl[x][y].flags & S_LPUDDING));
368 nhl_add_table_entry_bool(L, "dishwasher",
369 (levl[x][y].flags & S_LDWASHER));
370 /* vanilla NetHack allows "ring" to be read from this location;
371 * xNetHack just has the ring buried under the sink, and
372 * possibly multiple rings. For now, we don't bother searching
373 * through buriedobjlist to see if there's a ring at (x, y), and
374 * the lua table won't contain a 'ring' entry. */
375 }
376 /* TODO: drawbridges, walls, ladders, room=>ICED_xxx */
377
378 lua_settable(L, -3);
379
380 return 1;
381 } else {
382 /* TODO: return zerorm instead? */
383 nhl_error(L, "Coordinates out of range");
384 return 0;
385 }
386 } else {
387 nhl_error(L, "Incorrect arguments");
388 return 0;
389 }
390 return 1;
391 }
392
393 RESTORE_WARNING_CONDEXPR_IS_CONSTANT
394
395 /* pline("It hits!") */
396 static int
nhl_pline(lua_State * L)397 nhl_pline(lua_State *L)
398 {
399 int argc = lua_gettop(L);
400
401 if (argc == 1)
402 pline("%s", luaL_checkstring(L, 1));
403 else
404 nhl_error(L, "Wrong args");
405
406 return 0;
407 }
408
409 /* verbalize("Fool!") */
410 static int
nhl_verbalize(lua_State * L)411 nhl_verbalize(lua_State *L)
412 {
413 int argc = lua_gettop(L);
414
415 if (argc == 1)
416 verbalize("%s", luaL_checkstring(L, 1));
417 else
418 nhl_error(L, "Wrong args");
419
420 return 0;
421 }
422
423 /* parse_config("OPTIONS=!color") */
424 static int
nhl_parse_config(lua_State * L)425 nhl_parse_config(lua_State *L)
426 {
427 int argc = lua_gettop(L);
428
429 if (argc == 1)
430 parse_conf_str(luaL_checkstring(L, 1), parse_config_line);
431 else
432 nhl_error(L, "Wrong args");
433
434 return 0;
435 }
436
437 /* local windowtype = get_config("windowtype"); */
438 static int
nhl_get_config(lua_State * L)439 nhl_get_config(lua_State *L)
440 {
441 int argc = lua_gettop(L);
442
443 if (argc == 1) {
444 lua_pushstring(L, get_option_value(luaL_checkstring(L, 1)));
445 return 1;
446 } else
447 nhl_error(L, "Wrong args");
448
449 return 0;
450 }
451
452 /*
453 str = getlin("What do you want to call this dungeon level?");
454 */
455 static int
nhl_getlin(lua_State * L)456 nhl_getlin(lua_State *L)
457 {
458 int argc = lua_gettop(L);
459
460 if (argc == 1) {
461 const char *prompt = luaL_checkstring(L, 1);
462 char buf[BUFSZ];
463
464 getlin(prompt, buf);
465 lua_pushstring(L, buf);
466 return 1;
467 }
468
469 nhl_error(L, "Wrong args");
470 return 0;
471 }
472
473 /*
474 selected = menu("prompt", default, pickX, { "a" = "option a", "b" = "option b", ...})
475 pickX = 0,1,2, or "none", "one", "any" (PICK_X in code)
476
477 selected = menu("prompt", default, pickX,
478 { {key:"a", text:"option a"}, {key:"b", text:"option b"}, ... } ) */
479 static int
nhl_menu(lua_State * L)480 nhl_menu(lua_State *L)
481 {
482 static const char *const pickX[] = {"none", "one", "any"}; /* PICK_x */
483 int argc = lua_gettop(L);
484 const char *prompt;
485 const char *defval = "";
486 int pick = PICK_ONE, pick_cnt;
487 winid tmpwin;
488 anything any;
489 menu_item *picks = (menu_item *) 0;
490
491 if (argc < 2 || argc > 4) {
492 nhl_error(L, "Wrong args");
493 return 0;
494 }
495
496 prompt = luaL_checkstring(L, 1);
497 if (lua_isstring(L, 2))
498 defval = luaL_checkstring(L, 2);
499 if (lua_isstring(L, 3))
500 pick = luaL_checkoption(L, 3, "one", pickX);
501 luaL_checktype(L, argc, LUA_TTABLE);
502
503 tmpwin = create_nhwindow(NHW_MENU);
504 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
505
506 lua_pushnil(L); /* first key */
507 while (lua_next(L, argc) != 0) {
508 const char *str = "";
509 const char *key = "";
510
511 /* key @ index -2, value @ index -1 */
512 if (lua_istable(L, -1)) {
513 lua_pushliteral(L, "key");
514 lua_gettable(L, -2);
515 key = lua_tostring(L, -1);
516 lua_pop(L, 1);
517
518 lua_pushliteral(L, "text");
519 lua_gettable(L, -2);
520 str = lua_tostring(L, -1);
521 lua_pop(L, 1);
522
523 /* TODO: glyph, attr, accel, group accel (all optional) */
524 } else if (lua_isstring(L, -1)) {
525 str = luaL_checkstring(L, -1);
526 key = luaL_checkstring(L, -2);
527 }
528
529 any = cg.zeroany;
530 if (*key)
531 any.a_char = key[0];
532 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, str,
533 (*defval && *key && defval[0] == key[0])
534 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
535
536 lua_pop(L, 1); /* removes 'value'; keeps 'key' for next iteration */
537 }
538
539 end_menu(tmpwin, prompt);
540 pick_cnt = select_menu(tmpwin, pick, &picks);
541 destroy_nhwindow(tmpwin);
542
543 if (pick_cnt > 0) {
544 char buf[2];
545 buf[0] = picks[0].item.a_char;
546
547 if (pick == PICK_ONE && pick_cnt > 1
548 && *defval && defval[0] == picks[0].item.a_char)
549 buf[0] = picks[1].item.a_char;
550
551 buf[1] = '\0';
552 lua_pushstring(L, buf);
553 /* TODO: pick any */
554 } else {
555 char buf[2];
556 buf[0] = defval[0];
557 buf[1] = '\0';
558 lua_pushstring(L, buf);
559 }
560
561 return 1;
562 }
563
564 /* makeplural("zorkmid") */
565 static int
nhl_makeplural(lua_State * L)566 nhl_makeplural(lua_State *L)
567 {
568 int argc = lua_gettop(L);
569
570 if (argc == 1)
571 lua_pushstring(L, makeplural(luaL_checkstring(L, 1)));
572 else
573 nhl_error(L, "Wrong args");
574
575 return 1;
576 }
577
578 /* makesingular("zorkmids") */
579 static int
nhl_makesingular(lua_State * L)580 nhl_makesingular(lua_State *L)
581 {
582 int argc = lua_gettop(L);
583
584 if (argc == 1)
585 lua_pushstring(L, makesingular(luaL_checkstring(L, 1)));
586 else
587 nhl_error(L, "Wrong args");
588
589 return 1;
590 }
591
592 /* s_suffix("foo") */
593 static int
nhl_s_suffix(lua_State * L)594 nhl_s_suffix(lua_State *L)
595 {
596 int argc = lua_gettop(L);
597
598 if (argc == 1)
599 lua_pushstring(L, s_suffix(luaL_checkstring(L, 1)));
600 else
601 nhl_error(L, "Wrong args");
602
603 return 1;
604 }
605
606 /* ing_suffix("foo") */
607 static int
nhl_ing_suffix(lua_State * L)608 nhl_ing_suffix(lua_State *L)
609 {
610 int argc = lua_gettop(L);
611
612 if (argc == 1)
613 lua_pushstring(L, ing_suffix(luaL_checkstring(L, 1)));
614 else
615 nhl_error(L, "Wrong args");
616
617 return 1;
618 }
619
620 /* an("foo") */
621 static int
nhl_an(lua_State * L)622 nhl_an(lua_State *L)
623 {
624 int argc = lua_gettop(L);
625
626 if (argc == 1)
627 lua_pushstring(L, an(luaL_checkstring(L, 1)));
628 else
629 nhl_error(L, "Wrong args");
630
631 return 1;
632 }
633
634 /* rn2(10) */
635 static int
nhl_rn2(lua_State * L)636 nhl_rn2(lua_State *L)
637 {
638 int argc = lua_gettop(L);
639
640 if (argc == 1)
641 lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1)));
642 else
643 nhl_error(L, "Wrong args");
644
645 return 1;
646 }
647
648 /* random(10); -- is the same as rn2(10); */
649 /* random(5,8); -- same as 5 + rn2(8); */
650 static int
nhl_random(lua_State * L)651 nhl_random(lua_State *L)
652 {
653 int argc = lua_gettop(L);
654
655 if (argc == 1)
656 lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1)));
657 else if (argc == 2)
658 lua_pushinteger(L, luaL_checkinteger(L, 1) + rn2((int) luaL_checkinteger(L, 2)));
659 else
660 nhl_error(L, "Wrong args");
661
662 return 1;
663 }
664
665 /* level_difficulty() */
666 static int
nhl_level_difficulty(lua_State * L)667 nhl_level_difficulty(lua_State *L)
668 {
669 int argc = lua_gettop(L);
670 if (argc == 0) {
671 lua_pushinteger(L, level_difficulty());
672 }
673 else {
674 nhl_error(L, "level_difficulty should not have any args");
675 }
676 return 1;
677 }
678
679 /* mon_difficulty("water troll") => mons[PM_WATER_TROLL].difficulty */
680 static int
nhl_mon_difficulty(lua_State * L)681 nhl_mon_difficulty(lua_State *L)
682 {
683 int argc = lua_gettop(L);
684 if (argc == 1) {
685 const char *species = luaL_checkstring(L, 1);
686 int mnum = find_montype(L, species, (int *) 0);
687 if (mnum != NON_PM) {
688 lua_pushinteger(L, mons[mnum].difficulty);
689 }
690 else {
691 nhl_error(L, "Unknown monster for difficulty lookup");
692 }
693 }
694 else {
695 nhl_error(L, "mon_difficulty takes only one arg");
696 }
697 return 1;
698 }
699
700 /* get mandatory integer value from table */
701 int
get_table_int(lua_State * L,const char * name)702 get_table_int(lua_State *L, const char *name)
703 {
704 int ret;
705
706 lua_getfield(L, -1, name);
707 ret = (int) luaL_checkinteger(L, -1);
708 lua_pop(L, 1);
709 return ret;
710 }
711
712 /* get optional integer value from table */
713 int
get_table_int_opt(lua_State * L,const char * name,int defval)714 get_table_int_opt(lua_State *L, const char *name, int defval)
715 {
716 int ret = defval;
717
718 lua_getfield(L, -1, name);
719 if (!lua_isnil(L, -1)) {
720 ret = (int) luaL_checkinteger(L, -1);
721 }
722 lua_pop(L, 1);
723 return ret;
724 }
725
726 char *
get_table_str(lua_State * L,const char * name)727 get_table_str(lua_State *L, const char *name)
728 {
729 char *ret;
730
731 lua_getfield(L, -1, name);
732 ret = dupstr(luaL_checkstring(L, -1));
733 lua_pop(L, 1);
734 return ret;
735 }
736
737 /* get optional string value from table.
738 return value must be freed by caller. */
739 char *
get_table_str_opt(lua_State * L,const char * name,char * defval)740 get_table_str_opt(lua_State *L, const char *name, char *defval)
741 {
742 const char *ret;
743
744 lua_getfield(L, -1, name);
745 ret = luaL_optstring(L, -1, defval);
746 if (ret) {
747 lua_pop(L, 1);
748 return dupstr(ret);
749 }
750 lua_pop(L, 1);
751 return NULL;
752 }
753
754 int
get_table_boolean(lua_State * L,const char * name)755 get_table_boolean(lua_State *L, const char *name)
756 {
757 static const char *const boolstr[] = {
758 "true", "false", "yes", "no", NULL
759 };
760 /* static const int boolstr2i[] = { TRUE, FALSE, TRUE, FALSE, -1 }; */
761 int ltyp;
762 int ret = -1;
763
764 lua_getfield(L, -1, name);
765 ltyp = lua_type(L, -1);
766 if (ltyp == LUA_TSTRING) {
767 ret = luaL_checkoption(L, -1, NULL, boolstr);
768 /* nhUse(boolstr2i[0]); */
769 } else if (ltyp == LUA_TBOOLEAN) {
770 ret = lua_toboolean(L, -1);
771 } else if (ltyp == LUA_TNUMBER) {
772 ret = (int) luaL_checkinteger(L, -1);
773 if ( ret < 0 || ret > 1)
774 ret = -1;
775 }
776 lua_pop(L, 1);
777 if (ret == -1)
778 nhl_error(L, "Expected a boolean");
779 return ret;
780 }
781
782 int
get_table_boolean_opt(lua_State * L,const char * name,int defval)783 get_table_boolean_opt(lua_State *L, const char *name, int defval)
784 {
785 int ret = defval;
786
787 lua_getfield(L, -1, name);
788 if (lua_type(L, -1) != LUA_TNIL) {
789 lua_pop(L, 1);
790 return get_table_boolean(L, name);
791 }
792 lua_pop(L, 1);
793 return ret;
794 }
795
796 /* opts[] is a null-terminated list */
797 int
get_table_option(lua_State * L,const char * name,const char * defval,const char * const opts[])798 get_table_option(lua_State *L,
799 const char *name,
800 const char *defval,
801 const char *const opts[])
802 {
803 int ret;
804
805 lua_getfield(L, -1, name);
806 ret = luaL_checkoption(L, -1, defval, opts);
807 lua_pop(L, 1);
808 return ret;
809 }
810
811 /*
812 test( { x = 123, y = 456 } );
813 */
814 static int
nhl_test(lua_State * L)815 nhl_test(lua_State *L)
816 {
817 int x, y;
818 char *name, Player[] = "Player";
819
820 /* discard any extra arguments passed in */
821 lua_settop(L, 1);
822
823 luaL_checktype(L, 1, LUA_TTABLE);
824
825 x = get_table_int(L, "x");
826 y = get_table_int(L, "y");
827 name = get_table_str_opt(L, "name", Player);
828
829 pline("TEST:{ x=%i, y=%i, name=\"%s\" }", x,y, name);
830
831 free(name);
832
833 return 1;
834 }
835
836 static const struct luaL_Reg nhl_functions[] = {
837 {"test", nhl_test},
838
839 {"getmap", nhl_getmap},
840 #if 0
841 {"setmap", nhl_setmap},
842 #endif
843 {"gettrap", nhl_gettrap},
844 {"deltrap", nhl_deltrap},
845
846 {"pline", nhl_pline},
847 {"verbalize", nhl_verbalize},
848 {"menu", nhl_menu},
849 {"getlin", nhl_getlin},
850
851 {"makeplural", nhl_makeplural},
852 {"makesingular", nhl_makesingular},
853 {"s_suffix", nhl_s_suffix},
854 {"ing_suffix", nhl_ing_suffix},
855 {"an", nhl_an},
856 {"rn2", nhl_rn2},
857 {"random", nhl_random},
858 {"level_difficulty", nhl_level_difficulty},
859 {"mon_difficulty", nhl_mon_difficulty},
860 {"parse_config", nhl_parse_config},
861 {"get_config", nhl_get_config},
862 {"get_config_errors", l_get_config_errors},
863 {NULL, NULL}
864 };
865
866 static const struct {
867 const char *name;
868 long value;
869 } nhl_consts[] = {
870 { "COLNO", COLNO },
871 { "ROWNO", ROWNO },
872 { NULL, 0 },
873 };
874
875 /* register and init the constants table */
876 static void
init_nhc_data(lua_State * L)877 init_nhc_data(lua_State *L)
878 {
879 int i;
880
881 lua_newtable(L);
882
883 for (i = 0; nhl_consts[i].name; i++) {
884 lua_pushstring(L, nhl_consts[i].name);
885 lua_pushinteger(L, nhl_consts[i].value);
886 lua_rawset(L, -3);
887 }
888
889 lua_setglobal(L, "nhc");
890 }
891
892 static int
nhl_push_anything(lua_State * L,int anytype,void * src)893 nhl_push_anything(lua_State *L, int anytype, void *src)
894 {
895 anything any = cg.zeroany;
896
897 switch (anytype) {
898 case ANY_INT: any.a_int = *(int *) src;
899 lua_pushinteger(L, any.a_int);
900 break;
901 case ANY_UCHAR: any.a_uchar = *(uchar *) src;
902 lua_pushinteger(L, any.a_uchar);
903 break;
904 case ANY_SCHAR: any.a_schar = *(schar *) src;
905 lua_pushinteger(L, any.a_schar);
906 break;
907 }
908 return 1;
909 }
910
911 static int
nhl_meta_u_index(lua_State * L)912 nhl_meta_u_index(lua_State *L)
913 {
914 static const struct {
915 const char *name;
916 void *ptr;
917 int type;
918 } ustruct[] = {
919 { "ux", &(u.ux), ANY_UCHAR },
920 { "uy", &(u.uy), ANY_UCHAR },
921 { "dx", &(u.dx), ANY_SCHAR },
922 { "dy", &(u.dy), ANY_SCHAR },
923 { "dz", &(u.dz), ANY_SCHAR },
924 { "tx", &(u.tx), ANY_UCHAR },
925 { "ty", &(u.ty), ANY_UCHAR },
926 { "ulevel", &(u.ulevel), ANY_INT },
927 { "ulevelmax", &(u.ulevelmax), ANY_INT },
928 { "uhunger", &(u.uhunger), ANY_INT },
929 { "nv_range", &(u.nv_range), ANY_INT },
930 { "xray_range", &(u.xray_range), ANY_INT },
931 { "umonster", &(u.umonster), ANY_INT },
932 { "umonnum", &(u.umonnum), ANY_INT },
933 { "mh", &(u.mh), ANY_INT },
934 { "mhmax", &(u.mhmax), ANY_INT },
935 { "mtimedone", &(u.mtimedone), ANY_INT },
936 { "dlevel", &(u.uz.dlevel), ANY_SCHAR }, /* actually xchar */
937 { "dnum", &(u.uz.dnum), ANY_SCHAR }, /* actually xchar */
938 { "uluck", &(u.uluck), ANY_SCHAR },
939 { "uhp", &(u.uhp), ANY_INT },
940 { "uhpmax", &(u.uhpmax), ANY_INT },
941 { "uen", &(u.uen), ANY_INT },
942 { "uenmax", &(u.uenmax), ANY_INT },
943 };
944 const char *tkey = luaL_checkstring(L, 2);
945 int i;
946
947 /* FIXME: doesn't really work, eg. negative values for u.dx */
948 for (i = 0; i < SIZE(ustruct); i++)
949 if (!strcmp(tkey, ustruct[i].name)) {
950 return nhl_push_anything(L, ustruct[i].type, ustruct[i].ptr);
951 }
952
953 if (!strcmp(tkey, "inventory")) {
954 nhl_push_obj(L, g.invent);
955 return 1;
956 } else if (!strcmp(tkey, "role")) {
957 lua_pushstring(L, g.urole.name.m);
958 return 1;
959 }
960
961 nhl_error(L, "Unknown u table index");
962 return 0;
963 }
964
965 static int
nhl_meta_u_newindex(lua_State * L)966 nhl_meta_u_newindex(lua_State *L)
967 {
968 nhl_error(L, "Cannot set u table values");
969 return 0;
970 }
971
972 static int
nhl_u_clear_inventory(lua_State * L UNUSED)973 nhl_u_clear_inventory(lua_State *L UNUSED)
974 {
975 while (g.invent)
976 useupall(g.invent);
977 return 0;
978 }
979
980 /* Put object into player's inventory */
981 /* u.giveobj(obj.new("rock")); */
982 static int
nhl_u_giveobj(lua_State * L)983 nhl_u_giveobj(lua_State *L)
984 {
985 return nhl_obj_u_giveobj(L);
986 }
987
988 static const struct luaL_Reg nhl_u_functions[] = {
989 { "clear_inventory", nhl_u_clear_inventory },
990 { "giveobj", nhl_u_giveobj },
991 { NULL, NULL }
992 };
993
994 static void
init_u_data(lua_State * L)995 init_u_data(lua_State *L)
996 {
997 lua_newtable(L);
998 luaL_setfuncs(L, nhl_u_functions, 0);
999 lua_newtable(L);
1000 lua_pushcfunction(L, nhl_meta_u_index);
1001 lua_setfield(L, -2, "__index");
1002 lua_pushcfunction(L, nhl_meta_u_newindex);
1003 lua_setfield(L, -2, "__newindex");
1004 lua_setmetatable(L, -2);
1005 lua_setglobal(L, "u");
1006 }
1007
1008 static int
nhl_set_package_path(lua_State * L,const char * path)1009 nhl_set_package_path(lua_State *L, const char *path)
1010 {
1011 lua_getglobal(L, "package");
1012 lua_pushstring(L, path);
1013 lua_setfield(L, -2, "path");
1014 lua_pop(L, 1);
1015 return 0;
1016 }
1017
1018 static int
traceback_handler(lua_State * L)1019 traceback_handler(lua_State *L)
1020 {
1021 luaL_traceback(L, L, lua_tostring(L, 1), 0);
1022 /* TODO: call impossible() if fuzzing? */
1023 return 1;
1024 }
1025
1026 /* read lua code/data from a dlb module or an external file
1027 into a string buffer and feed that to lua */
1028 boolean
nhl_loadlua(lua_State * L,const char * fname)1029 nhl_loadlua(lua_State *L, const char *fname)
1030 {
1031 #define LOADCHUNKSIZE (1L << 13) /* 8K */
1032 boolean ret = TRUE;
1033 dlb *fh;
1034 char *buf = (char *) 0, *bufin, *bufout, *p, *nl, *altfname;
1035 long buflen, ct, cnt;
1036 int llret;
1037
1038 altfname = (char *) alloc(strlen(fname) + 3); /* 3: '('...')\0' */
1039 /* don't know whether 'fname' is inside a dlb container;
1040 if we did, we could choose between "nhdat(<fname>)" and "<fname>"
1041 but since we don't, compromise */
1042 Sprintf(altfname, "(%s)", fname);
1043 fh = dlb_fopen(fname, "r");
1044 if (!fh) {
1045 impossible("nhl_loadlua: Error loading %s", altfname);
1046 ret = FALSE;
1047 goto give_up;
1048 }
1049
1050 dlb_fseek(fh, 0L, SEEK_END);
1051 buflen = dlb_ftell(fh);
1052 dlb_fseek(fh, 0L, SEEK_SET);
1053
1054 /* extra +1: room to add final '\n' if missing */
1055 buf = bufout = (char *) alloc(buflen + 1 + 1);
1056 buf[0] = '\0';
1057 bufin = bufout = buf;
1058
1059 ct = 0L;
1060 while (buflen > 0 || ct) {
1061 /*
1062 * Semi-arbitrarily limit reads to 8K at a time. That's big
1063 * enough to cover the majority of our Lua files in one bite
1064 * but small enough to fully exercise the partial record
1065 * handling (when processing the castle's level description).
1066 *
1067 * [For an external file (non-DLB), VMS may only be able to
1068 * read at most 32K-1 at a time depending on the file format
1069 * in use, and fseek(SEEK_END) only yields an upper bound on
1070 * the actual amount of data in that situation.]
1071 */
1072 if ((cnt = dlb_fread(bufin, 1, min(buflen, LOADCHUNKSIZE), fh)) < 0L)
1073 break;
1074 buflen -= cnt; /* set up for next iteration, if any */
1075 if (cnt == 0L) {
1076 *bufin = '\n'; /* very last line is unterminated? */
1077 cnt = 1;
1078 }
1079 bufin[cnt] = '\0'; /* fread() doesn't do this */
1080
1081 /* in case partial line was leftover from previous fread */
1082 bufin -= ct, cnt += ct, ct = 0;
1083
1084 while (cnt > 0) {
1085 if ((nl = index(bufin, '\n')) != 0) {
1086 /* normal case, newline is present */
1087 ct = (long) (nl - bufin + 1L); /* +1: keep the newline */
1088 for (p = bufin; p <= nl; ++p)
1089 *bufout++ = *bufin++;
1090 if (*bufin == '\r')
1091 ++bufin, ++ct;
1092 /* update for next loop iteration */
1093 cnt -= ct;
1094 ct = 0;
1095 } else if (strlen(bufin) < LOADCHUNKSIZE) {
1096 /* no newline => partial record; move unprocessed chars
1097 to front of input buffer (bufin portion of buf[]) */
1098 ct = cnt = (long) (eos(bufin) - bufin);
1099 for (p = bufout; cnt > 0; --cnt)
1100 *p++ = *bufin++;
1101 *p = '\0';
1102 bufin = p; /* next fread() populates buf[] starting here */
1103 /* cnt==0 so inner loop will terminate */
1104 } else {
1105 /* LOADCHUNKSIZE portion of buffer already completely full */
1106 impossible("(%s) line too long", altfname);
1107 goto give_up;
1108 }
1109 }
1110 }
1111 *bufout = '\0';
1112 (void) dlb_fclose(fh);
1113
1114 llret = luaL_loadbuffer(L, buf, strlen(buf), altfname);
1115 if (llret != LUA_OK) {
1116 impossible("luaL_loadbuffer: Error loading %s: %s",
1117 altfname, lua_tostring(L, -1));
1118 ret = FALSE;
1119 goto give_up;
1120 } else {
1121 lua_pushcfunction(L, traceback_handler);
1122 lua_insert(L, 1);
1123 if (lua_pcall(L, 0, LUA_MULTRET, -2)) {
1124 impossible("Lua error: %s", lua_tostring(L, -1));
1125 ret = FALSE;
1126 goto give_up;
1127 }
1128 }
1129
1130 give_up:
1131 if (altfname)
1132 free((genericptr_t) altfname);
1133 if (buf)
1134 free((genericptr_t) buf);
1135 return ret;
1136 }
1137
1138 lua_State *
nhl_init(void)1139 nhl_init(void)
1140 {
1141 lua_State *L = luaL_newstate();
1142
1143 iflags.in_lua = TRUE;
1144 luaL_openlibs(L);
1145 nhl_set_package_path(L, "./?.lua");
1146
1147 /* register nh -table, and functions for it */
1148 lua_newtable(L);
1149 luaL_setfuncs(L, nhl_functions, 0);
1150 lua_setglobal(L, "nh");
1151
1152 /* init nhc -table */
1153 init_nhc_data(L);
1154
1155 /* init u -table */
1156 init_u_data(L);
1157
1158 l_selection_register(L);
1159 l_register_des(L);
1160
1161 l_obj_register(L);
1162
1163 if (!nhl_loadlua(L, "nhlib.lua")) {
1164 nhl_done(L);
1165 return (lua_State *) 0;
1166 }
1167
1168 return L;
1169 }
1170
1171 void
nhl_done(lua_State * L)1172 nhl_done(lua_State *L)
1173 {
1174 lua_close(L);
1175 iflags.in_lua = FALSE;
1176 }
1177
1178 boolean
load_lua(const char * name)1179 load_lua(const char *name)
1180 {
1181 boolean ret = TRUE;
1182 lua_State *L = nhl_init();
1183
1184 if (!L) {
1185 ret = FALSE;
1186 goto give_up;
1187 }
1188
1189 if (!nhl_loadlua(L, name)) {
1190 ret = FALSE;
1191 goto give_up;
1192 }
1193
1194 give_up:
1195 nhl_done(L);
1196
1197 return ret;
1198 }
1199
1200 DISABLE_WARNING_CONDEXPR_IS_CONSTANT
1201
1202 const char *
get_lua_version(void)1203 get_lua_version(void)
1204 {
1205 size_t len = (size_t) 0;
1206 const char *vs = (const char *) 0;
1207 lua_State *L;
1208
1209 if (g.lua_ver[0] == 0) {
1210 L = nhl_init();
1211
1212 if (L) {
1213 lua_getglobal(L, "_VERSION");
1214 if (lua_isstring(L, -1))
1215 vs = lua_tolstring (L, -1, &len);
1216 if (vs && len < sizeof g.lua_ver) {
1217 if (!strncmpi(vs, "Lua", 3)) {
1218 vs += 3;
1219 if (*vs == '-' || *vs == ' ')
1220 vs += 1;
1221 }
1222 Strcpy(g.lua_ver, vs);
1223 }
1224 }
1225 lua_close(L);
1226 #ifdef LUA_COPYRIGHT
1227 if (sizeof LUA_COPYRIGHT <= sizeof g.lua_copyright)
1228 Strcpy(g.lua_copyright, LUA_COPYRIGHT);
1229 #endif
1230 }
1231 return (const char *) g.lua_ver;
1232 }
1233
1234 RESTORE_WARNINGS
1235
1236
1237
1238