/* NetHack 3.7 nhlua.c $NHDT-Date: 1580506559 2020/01/31 21:35:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" #include "dlb.h" /* #- include #- include #- include */ /* */ /* lua_CFunction prototypes */ static int FDECL(nhl_test, (lua_State *)); static int FDECL(nhl_getmap, (lua_State *)); static void FDECL(nhl_add_table_entry_bool, (lua_State *, const char *, BOOLEAN_P)); static char FDECL(splev_typ2chr, (SCHAR_P)); static int FDECL(nhl_gettrap, (lua_State *)); static int FDECL(nhl_deltrap, (lua_State *)); #if 0 static int FDECL(nhl_setmap, (lua_State *)); #endif static int FDECL(nhl_pline, (lua_State *)); static int FDECL(nhl_verbalize, (lua_State *)); static int FDECL(nhl_menu, (lua_State *)); static int FDECL(nhl_getlin, (lua_State *)); static int FDECL(nhl_makeplural, (lua_State *)); static int FDECL(nhl_makesingular, (lua_State *)); static int FDECL(nhl_s_suffix, (lua_State *)); static int FDECL(nhl_ing_suffix, (lua_State *)); static int FDECL(nhl_an, (lua_State *)); static int FDECL(nhl_rn2, (lua_State *)); static int FDECL(nhl_random, (lua_State *)); static int FDECL(nhl_level_difficulty, (lua_State *)); static void FDECL(init_nhc_data, (lua_State *)); static int FDECL(nhl_push_anything, (lua_State *, int, void *)); static int FDECL(nhl_meta_u_index, (lua_State *)); static int FDECL(nhl_meta_u_newindex, (lua_State *)); static int FDECL(nhl_u_clear_inventory, (lua_State *)); static int FDECL(nhl_u_giveobj, (lua_State *)); static void FDECL(init_u_data, (lua_State *)); static int FDECL(nhl_set_package_path, (lua_State *, const char *)); static int FDECL(traceback_handler, (lua_State *)); void nhl_error(L, msg) lua_State *L; const char *msg; { lua_Debug ar; char buf[BUFSZ]; lua_getstack(L, 1, &ar); lua_getinfo(L, "lS", &ar); Sprintf(buf, "%s (line %d ", msg, ar.currentline); Sprintf(eos(buf), "%.*s)", /* (max length of ar.short_src is actually LUA_IDSIZE so this is overkill for it, but crucial for ar.source) */ (int) (sizeof buf - (strlen(buf) + sizeof ")")), ar.short_src); /* (used to be 'ar.source' here) */ lua_pushstring(L, buf); #if 0 /* defined(PANICTRACE) && !defined(NO_SIGNALS) */ panictrace_setsignals(FALSE); #endif (void) lua_error(L); /*NOTREACHED*/ } /* Check that parameters are nothing but single table, or if no parameters given, put empty table there */ void lcheck_param_table(L) lua_State *L; { int argc = lua_gettop(L); if (argc < 1) lua_createtable(L, 0, 0); /* discard any extra arguments passed in */ lua_settop(L, 1); luaL_checktype(L, 1, LUA_TTABLE); } schar get_table_mapchr(L, name) lua_State *L; const char *name; { char *ter; xchar typ; ter = get_table_str(L, name); typ = check_mapchr(ter); if (typ == INVALID_TYPE) nhl_error(L, "Erroneous map char"); if (ter) free(ter); return typ; } schar get_table_mapchr_opt(L, name, defval) lua_State *L; const char *name; schar defval; { char *ter; xchar typ; ter = get_table_str_opt(L, name, emptystr); if (name && *ter) { typ = check_mapchr(ter); if (typ == INVALID_TYPE) nhl_error(L, "Erroneous map char"); } else typ = defval; if (ter) free(ter); return typ; } void nhl_add_table_entry_int(L, name, value) lua_State *L; const char *name; int value; { lua_pushstring(L, name); lua_pushinteger(L, value); lua_rawset(L, -3); } void nhl_add_table_entry_char(L, name, value) lua_State *L; const char *name; char value; { char buf[2]; Sprintf(buf, "%c", value); lua_pushstring(L, name); lua_pushstring(L, buf); lua_rawset(L, -3); } void nhl_add_table_entry_str(L, name, value) lua_State *L; const char *name; const char *value; { lua_pushstring(L, name); lua_pushstring(L, value); lua_rawset(L, -3); } void nhl_add_table_entry_bool(L, name, value) lua_State *L; const char *name; boolean value; { lua_pushstring(L, name); lua_pushboolean(L, value); lua_rawset(L, -3); } /* converting from special level "map character" to levl location type and back. order here is important. */ const struct { char ch; schar typ; } char2typ[] = { { ' ', STONE }, { '#', CORR }, { '.', ROOM }, { '-', HWALL }, { '-', TLCORNER }, { '-', TRCORNER }, { '-', BLCORNER }, { '-', BRCORNER }, { '-', CROSSWALL }, { '-', TUWALL }, { '-', TDWALL }, { '-', TLWALL }, { '-', TRWALL }, { '-', DBWALL }, { '|', VWALL }, { '+', DOOR }, { 'A', AIR }, { 'C', CLOUD }, { 'S', SDOOR }, { 'H', SCORR }, { '{', FOUNTAIN }, { '\\', THRONE }, { 'K', SINK }, { '}', MOAT }, { 'P', POOL }, { 'L', LAVAPOOL }, { 'I', ICE }, { 'W', WATER }, { 'T', TREE }, { 'F', IRONBARS }, /* Fe = iron */ { 'x', MAX_TYPE }, /* "see-through" */ { 'B', CROSSWALL }, /* hack: boundary location */ { 'w', MATCH_WALL }, /* IS_STWALL() */ { '\0', STONE }, }; schar splev_chr2typ(c) char c; { int i; for (i = 0; char2typ[i].ch; i++) if (c == char2typ[i].ch) return char2typ[i].typ; return (INVALID_TYPE); } schar check_mapchr(s) const char *s; { if (s && strlen(s) == 1) return splev_chr2typ(s[0]); return INVALID_TYPE; } static char splev_typ2chr(typ) schar typ; { int i; for (i = 0; char2typ[i].typ < MAX_TYPE; i++) if (typ == char2typ[i].typ) return char2typ[i].ch; return 'x'; } /* local t = gettrap(x,y); */ static int nhl_gettrap(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 2) { int x = (int) lua_tointeger(L, 1); int y = (int) lua_tointeger(L, 2); if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) { struct trap *ttmp = t_at(x,y); if (ttmp) { lua_newtable(L); nhl_add_table_entry_int(L, "tx", ttmp->tx); nhl_add_table_entry_int(L, "ty", ttmp->ty); nhl_add_table_entry_int(L, "ttyp", ttmp->ttyp); nhl_add_table_entry_str(L, "ttyp_name", get_trapname_bytype(ttmp->ttyp)); nhl_add_table_entry_bool(L, "tseen", ttmp->tseen); nhl_add_table_entry_bool(L, "madeby_u", ttmp->madeby_u); switch (ttmp->ttyp) { case SQKY_BOARD: nhl_add_table_entry_int(L, "tnote", ttmp->tnote); break; case ROLLING_BOULDER_TRAP: nhl_add_table_entry_int(L, "launchx", ttmp->launch.x); nhl_add_table_entry_int(L, "launchy", ttmp->launch.y); nhl_add_table_entry_int(L, "launch2x", ttmp->launch2.x); nhl_add_table_entry_int(L, "launch2y", ttmp->launch2.y); break; case PIT: case SPIKED_PIT: nhl_add_table_entry_int(L, "conjoined", ttmp->conjoined); break; } return 1; } else nhl_error(L, "No trap at location"); } else nhl_error(L, "Coordinates out of range"); } else nhl_error(L, "Wrong args"); return 0; } /* deltrap(x,y); */ static int nhl_deltrap(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 2) { int x = (int) lua_tointeger(L, 1); int y = (int) lua_tointeger(L, 2); if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) { struct trap *ttmp = t_at(x,y); if (ttmp) deltrap(ttmp); } } return 0; } /* local loc = getmap(x,y) */ static int nhl_getmap(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 2) { int x = (int) lua_tointeger(L, 1); int y = (int) lua_tointeger(L, 2); if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) { char buf[BUFSZ]; lua_newtable(L); /* FIXME: some should be boolean values */ nhl_add_table_entry_int(L, "glyph", levl[x][y].glyph); nhl_add_table_entry_int(L, "typ", levl[x][y].typ); nhl_add_table_entry_str(L, "typ_name", levltyp_to_name(levl[x][y].typ)); Sprintf(buf, "%c", splev_typ2chr(levl[x][y].typ)); nhl_add_table_entry_str(L, "mapchr", buf); nhl_add_table_entry_int(L, "seenv", levl[x][y].seenv); nhl_add_table_entry_bool(L, "horizontal", levl[x][y].horizontal); nhl_add_table_entry_bool(L, "lit", levl[x][y].lit); nhl_add_table_entry_bool(L, "waslit", levl[x][y].waslit); nhl_add_table_entry_int(L, "roomno", levl[x][y].roomno); nhl_add_table_entry_bool(L, "edge", levl[x][y].edge); nhl_add_table_entry_bool(L, "candig", levl[x][y].candig); nhl_add_table_entry_bool(L, "has_trap", t_at(x,y) ? 1 : 0); /* TODO: FIXME: levl[x][y].flags */ lua_pushliteral(L, "flags"); lua_newtable(L); if (IS_DOOR(levl[x][y].typ)) { nhl_add_table_entry_bool(L, "nodoor", (levl[x][y].flags & D_NODOOR)); nhl_add_table_entry_bool(L, "broken", (levl[x][y].flags & D_BROKEN)); nhl_add_table_entry_bool(L, "isopen", (levl[x][y].flags & D_ISOPEN)); nhl_add_table_entry_bool(L, "closed", (levl[x][y].flags & D_CLOSED)); nhl_add_table_entry_bool(L, "locked", (levl[x][y].flags & D_LOCKED)); nhl_add_table_entry_bool(L, "trapped", (levl[x][y].flags & D_TRAPPED)); } else if (IS_ALTAR(levl[x][y].typ)) { /* TODO: bits 0, 1, 2 */ nhl_add_table_entry_bool(L, "shrine", (levl[x][y].flags & AM_SHRINE)); } else if (IS_THRONE(levl[x][y].typ)) { nhl_add_table_entry_bool(L, "looted", (levl[x][y].flags & T_LOOTED)); } else if (levl[x][y].typ == TREE) { nhl_add_table_entry_bool(L, "looted", (levl[x][y].flags & TREE_LOOTED)); nhl_add_table_entry_bool(L, "swarm", (levl[x][y].flags & TREE_SWARM)); } else if (IS_FOUNTAIN(levl[x][y].typ)) { nhl_add_table_entry_bool(L, "looted", (levl[x][y].flags & F_LOOTED)); nhl_add_table_entry_bool(L, "warned", (levl[x][y].flags & F_WARNED)); } else if (IS_SINK(levl[x][y].typ)) { nhl_add_table_entry_bool(L, "pudding", (levl[x][y].flags & S_LPUDDING)); nhl_add_table_entry_bool(L, "dishwasher", (levl[x][y].flags & S_LDWASHER)); nhl_add_table_entry_bool(L, "ring", (levl[x][y].flags & S_LRING)); } /* TODO: drawbridges, walls, ladders, room=>ICED_xxx */ lua_settable(L, -3); return 1; } else { /* TODO: return zerorm instead? */ nhl_error(L, "Coordinates out of range"); return 0; } } else { nhl_error(L, "Incorrect arguments"); return 0; } return 1; } /* pline("It hits!") */ static int nhl_pline(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) pline("%s", luaL_checkstring(L, 1)); else nhl_error(L, "Wrong args"); return 0; } /* verbalize("Fool!") */ static int nhl_verbalize(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) verbalize("%s", luaL_checkstring(L, 1)); else nhl_error(L, "Wrong args"); return 0; } /* str = getlin("What do you want to call this dungeon level?"); */ static int nhl_getlin(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) { const char *prompt = luaL_checkstring(L, 1); char buf[BUFSZ]; getlin(prompt, buf); lua_pushstring(L, buf); return 1; } nhl_error(L, "Wrong args"); return 0; } /* selected = menu("prompt", default, pickX, { "a" = "option a", "b" = "option b", ...}) pickX = 0,1,2, or "none", "one", "any" (PICK_X in code) selected = menu("prompt", default, pickX, { {key:"a", text:"option a"}, {key:"b", text:"option b"}, ... } ) */ static int nhl_menu(L) lua_State *L; { static const char *const pickX[] = {"none", "one", "any"}; /* PICK_x */ int argc = lua_gettop(L); const char *prompt; const char *defval = ""; int pick = PICK_ONE, pick_cnt; winid tmpwin; anything any; menu_item *picks = (menu_item *) 0; if (argc < 2 || argc > 4) { nhl_error(L, "Wrong args"); return 0; } prompt = luaL_checkstring(L, 1); if (lua_isstring(L, 2)) defval = luaL_checkstring(L, 2); if (lua_isstring(L, 3)) pick = luaL_checkoption(L, 3, "one", pickX); luaL_checktype(L, argc, LUA_TTABLE); tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); lua_pushnil(L); /* first key */ while (lua_next(L, argc) != 0) { const char *str = ""; const char *key = ""; /* key @ index -2, value @ index -1 */ if (lua_istable(L, -1)) { lua_pushliteral(L, "key"); lua_gettable(L, -2); key = lua_tostring(L, -1); lua_pop(L, 1); lua_pushliteral(L, "text"); lua_gettable(L, -2); str = lua_tostring(L, -1); lua_pop(L, 1); /* TODO: glyph, attr, accel, group accel (all optional) */ } else if (lua_isstring(L, -1)) { str = luaL_checkstring(L, -1); key = luaL_checkstring(L, -2); } any = cg.zeroany; if (*key) any.a_char = key[0]; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, str, (*defval && *key && defval[0] == key[0]) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); lua_pop(L, 1); /* removes 'value'; keeps 'key' for next iteration */ } end_menu(tmpwin, prompt); pick_cnt = select_menu(tmpwin, pick, &picks); destroy_nhwindow(tmpwin); if (pick_cnt > 0) { char buf[2]; buf[0] = picks[0].item.a_char; if (pick == PICK_ONE && pick_cnt > 1 && *defval && defval[0] == picks[0].item.a_char) buf[0] = picks[1].item.a_char; buf[1] = '\0'; lua_pushstring(L, buf); /* TODO: pick any */ } else { char buf[2]; buf[0] = defval[0]; buf[1] = '\0'; lua_pushstring(L, buf); } return 1; } /* makeplural("zorkmid") */ static int nhl_makeplural(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushstring(L, makeplural(luaL_checkstring(L, 1))); else nhl_error(L, "Wrong args"); return 1; } /* makesingular("zorkmids") */ static int nhl_makesingular(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushstring(L, makesingular(luaL_checkstring(L, 1))); else nhl_error(L, "Wrong args"); return 1; } /* s_suffix("foo") */ static int nhl_s_suffix(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushstring(L, s_suffix(luaL_checkstring(L, 1))); else nhl_error(L, "Wrong args"); return 1; } /* ing_suffix("foo") */ static int nhl_ing_suffix(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushstring(L, ing_suffix(luaL_checkstring(L, 1))); else nhl_error(L, "Wrong args"); return 1; } /* an("foo") */ static int nhl_an(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushstring(L, an(luaL_checkstring(L, 1))); else nhl_error(L, "Wrong args"); return 1; } /* rn2(10) */ static int nhl_rn2(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1))); else nhl_error(L, "Wrong args"); return 1; } /* random(10); -- is the same as rn2(10); */ /* random(5,8); -- same as 5 + rn2(8); */ static int nhl_random(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 1) lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1))); else if (argc == 2) lua_pushinteger(L, luaL_checkinteger(L, 1) + rn2((int) luaL_checkinteger(L, 2))); else nhl_error(L, "Wrong args"); return 1; } /* level_difficulty() */ static int nhl_level_difficulty(L) lua_State *L; { int argc = lua_gettop(L); if (argc == 0) { lua_pushinteger(L, level_difficulty()); } else { nhl_error(L, "level_difficulty should not have any args"); } return 1; } /* get mandatory integer value from table */ int get_table_int(L, name) lua_State *L; const char *name; { int ret; lua_getfield(L, -1, name); ret = (int) luaL_checkinteger(L, -1); lua_pop(L, 1); return ret; } /* get optional integer value from table */ int get_table_int_opt(L, name, defval) lua_State *L; const char *name; int defval; { int ret = defval; lua_getfield(L, -1, name); if (!lua_isnil(L, -1)) { ret = (int) luaL_checkinteger(L, -1); } lua_pop(L, 1); return ret; } char * get_table_str(L, name) lua_State *L; const char *name; { char *ret; lua_getfield(L, -1, name); ret = dupstr(luaL_checkstring(L, -1)); lua_pop(L, 1); return ret; } /* get optional string value from table. return value must be freed by caller. */ char * get_table_str_opt(L, name, defval) lua_State *L; const char *name; char *defval; { const char *ret; lua_getfield(L, -1, name); ret = luaL_optstring(L, -1, defval); if (ret) { lua_pop(L, 1); return dupstr(ret); } lua_pop(L, 1); return NULL; } int get_table_boolean(L, name) lua_State *L; const char *name; { static const char *const boolstr[] = { "true", "false", "yes", "no", NULL }; /* static const int boolstr2i[] = { TRUE, FALSE, TRUE, FALSE, -1 }; */ int ltyp; int ret = -1; lua_getfield(L, -1, name); ltyp = lua_type(L, -1); if (ltyp == LUA_TSTRING) { ret = luaL_checkoption(L, -1, NULL, boolstr); /* nhUse(boolstr2i[0]); */ } else if (ltyp == LUA_TBOOLEAN) { ret = lua_toboolean(L, -1); } else if (ltyp == LUA_TNUMBER) { ret = (int) luaL_checkinteger(L, -1); if ( ret < 0 || ret > 1) ret = -1; } lua_pop(L, 1); if (ret == -1) nhl_error(L, "Expected a boolean"); return ret; } int get_table_boolean_opt(L, name, defval) lua_State *L; const char *name; int defval; { int ret = defval; lua_getfield(L, -1, name); if (lua_type(L, -1) != LUA_TNIL) { lua_pop(L, 1); return get_table_boolean(L, name); } lua_pop(L, 1); return ret; } int get_table_option(L, name, defval, opts) lua_State *L; const char *name; const char *defval; const char *const opts[]; /* NULL-terminated list */ { int ret; lua_getfield(L, -1, name); ret = luaL_checkoption(L, -1, defval, opts); lua_pop(L, 1); return ret; } /* test( { x = 123, y = 456 } ); */ static int nhl_test(L) lua_State *L; { int x, y; char *name, Player[] = "Player"; /* discard any extra arguments passed in */ lua_settop(L, 1); luaL_checktype(L, 1, LUA_TTABLE); x = get_table_int(L, "x"); y = get_table_int(L, "y"); name = get_table_str_opt(L, "name", Player); pline("TEST:{ x=%i, y=%i, name=\"%s\" }", x,y, name); free(name); return 1; } static const struct luaL_Reg nhl_functions[] = { {"test", nhl_test}, {"getmap", nhl_getmap}, #if 0 {"setmap", nhl_setmap}, #endif {"gettrap", nhl_gettrap}, {"deltrap", nhl_deltrap}, {"pline", nhl_pline}, {"verbalize", nhl_verbalize}, {"menu", nhl_menu}, {"getlin", nhl_getlin}, {"makeplural", nhl_makeplural}, {"makesingular", nhl_makesingular}, {"s_suffix", nhl_s_suffix}, {"ing_suffix", nhl_ing_suffix}, {"an", nhl_an}, {"rn2", nhl_rn2}, {"random", nhl_random}, {"level_difficulty", nhl_level_difficulty}, {NULL, NULL} }; static const struct { const char *name; long value; } nhl_consts[] = { { "COLNO", COLNO }, { "ROWNO", ROWNO }, { NULL, 0 }, }; /* register and init the constants table */ static void init_nhc_data(L) lua_State *L; { int i; lua_newtable(L); for (i = 0; nhl_consts[i].name; i++) { lua_pushstring(L, nhl_consts[i].name); lua_pushinteger(L, nhl_consts[i].value); lua_rawset(L, -3); } lua_setglobal(L, "nhc"); } static int nhl_push_anything(L, anytype, src) lua_State *L; int anytype; void *src; { anything any = cg.zeroany; switch (anytype) { case ANY_INT: any.a_int = *(int *) src; lua_pushinteger(L, any.a_int); break; case ANY_UCHAR: any.a_uchar = *(uchar *) src; lua_pushinteger(L, any.a_uchar); break; case ANY_SCHAR: any.a_schar = *(schar *) src; lua_pushinteger(L, any.a_schar); break; } return 1; } static int nhl_meta_u_index(L) lua_State *L; { static const struct { const char *name; void *ptr; int type; } ustruct[] = { { "ux", &(u.ux), ANY_UCHAR }, { "uy", &(u.uy), ANY_UCHAR }, { "dx", &(u.dx), ANY_SCHAR }, { "dy", &(u.dy), ANY_SCHAR }, { "dz", &(u.dz), ANY_SCHAR }, { "tx", &(u.tx), ANY_UCHAR }, { "ty", &(u.ty), ANY_UCHAR }, { "ulevel", &(u.ulevel), ANY_INT }, { "ulevelmax", &(u.ulevelmax), ANY_INT }, { "uhunger", &(u.uhunger), ANY_INT }, { "nv_range", &(u.nv_range), ANY_INT }, { "xray_range", &(u.xray_range), ANY_INT }, { "umonster", &(u.umonster), ANY_INT }, { "umonnum", &(u.umonnum), ANY_INT }, { "mh", &(u.mh), ANY_INT }, { "mhmax", &(u.mhmax), ANY_INT }, { "mtimedone", &(u.mtimedone), ANY_INT }, { "dlevel", &(u.uz.dlevel), ANY_SCHAR }, /* actually xchar */ { "dnum", &(u.uz.dnum), ANY_SCHAR }, /* actually xchar */ { "uluck", &(u.uluck), ANY_SCHAR }, { "uhp", &(u.uhp), ANY_INT }, { "uhpmax", &(u.uhpmax), ANY_INT }, { "uen", &(u.uen), ANY_INT }, { "uenmax", &(u.uenmax), ANY_INT }, }; const char *tkey = luaL_checkstring(L, 2); int i; /* FIXME: doesn't really work, eg. negative values for u.dx */ for (i = 0; i < SIZE(ustruct); i++) if (!strcmp(tkey, ustruct[i].name)) { return nhl_push_anything(L, ustruct[i].type, ustruct[i].ptr); } if (!strcmp(tkey, "inventory")) { nhl_push_obj(L, g.invent); return 1; } else if (!strcmp(tkey, "role")) { lua_pushstring(L, g.urole.name.m); return 1; } nhl_error(L, "Unknown u table index"); return 0; } static int nhl_meta_u_newindex(L) lua_State *L; { nhl_error(L, "Cannot set u table values"); return 0; } static int nhl_u_clear_inventory(L) lua_State *L UNUSED; { while (g.invent) useupall(g.invent); return 0; } /* Put object into player's inventory */ /* u.giveobj(obj.new("rock")); */ static int nhl_u_giveobj(L) lua_State *L; { return nhl_obj_u_giveobj(L); } static const struct luaL_Reg nhl_u_functions[] = { { "clear_inventory", nhl_u_clear_inventory }, { "giveobj", nhl_u_giveobj }, { NULL, NULL } }; static void init_u_data(L) lua_State *L; { lua_newtable(L); luaL_setfuncs(L, nhl_u_functions, 0); lua_newtable(L); lua_pushcfunction(L, nhl_meta_u_index); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, nhl_meta_u_newindex); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); lua_setglobal(L, "u"); } static int nhl_set_package_path(L, path) lua_State *L; const char *path; { lua_getglobal(L, "package"); lua_pushstring(L, path); lua_setfield(L, -2, "path"); lua_pop(L, 1); return 0; } static int traceback_handler(L) lua_State *L; { luaL_traceback(L, L, lua_tostring(L, 1), 0); /* TODO: call impossible() if fuzzing? */ return 1; } /* read lua code/data from a dlb module or an external file into a string buffer and feed that to lua */ boolean nhl_loadlua(L, fname) lua_State *L; const char *fname; { #define LOADCHUNKSIZE (1L << 13) /* 8K */ boolean ret = TRUE; dlb *fh; char *buf = (char *) 0, *bufin, *bufout, *p, *nl, *altfname; long buflen, ct, cnt; int llret; altfname = (char *) alloc(strlen(fname) + 3); /* 3: '('...')\0' */ /* don't know whether 'fname' is inside a dlb container; if we did, we could choose between "nhdat()" and "" but since we don't, compromise */ Sprintf(altfname, "(%s)", fname); fh = dlb_fopen(fname, "r"); if (!fh) { impossible("nhl_loadlua: Error loading %s", altfname); ret = FALSE; goto give_up; } dlb_fseek(fh, 0L, SEEK_END); buflen = dlb_ftell(fh); dlb_fseek(fh, 0L, SEEK_SET); /* extra +1: room to add final '\n' if missing */ buf = bufout = (char *) alloc(buflen + 1 + 1); buf[0] = '\0'; bufin = bufout = buf; ct = 0L; while (buflen > 0 || ct) { /* * Semi-arbitrarily limit reads to 8K at a time. That's big * enough to cover the majority of our Lua files in one bite * but small enough to fully exercise the partial record * handling (when processing the castle's level description). * * [For an external file (non-DLB), VMS may only be able to * read at most 32K-1 at a time depending on the file format * in use, and fseek(SEEK_END) only yields an upper bound on * the actual amount of data in that situation.] */ if ((cnt = dlb_fread(bufin, 1, min(buflen, LOADCHUNKSIZE), fh)) < 0L) break; buflen -= cnt; /* set up for next iteration, if any */ if (cnt == 0L) { *bufin = '\n'; /* very last line is unterminated? */ cnt = 1; } bufin[cnt] = '\0'; /* fread() doesn't do this */ /* in case partial line was leftover from previous fread */ bufin -= ct, cnt += ct, ct = 0; while (cnt > 0) { if ((nl = index(bufin, '\n')) != 0) { /* normal case, newline is present */ ct = (long) (nl - bufin + 1L); /* +1: keep the newline */ for (p = bufin; p <= nl; ++p) *bufout++ = *bufin++; if (*bufin == '\r') ++bufin, ++ct; /* update for next loop iteration */ cnt -= ct; ct = 0; } else if (strlen(bufin) < LOADCHUNKSIZE) { /* no newline => partial record; move unprocessed chars to front of input buffer (bufin portion of buf[]) */ ct = cnt = (long) (eos(bufin) - bufin); for (p = bufout; cnt > 0; --cnt) *p++ = *bufin++; *p = '\0'; bufin = p; /* next fread() populates buf[] starting here */ /* cnt==0 so inner loop will terminate */ } else { /* LOADCHUNKSIZE portion of buffer already completely full */ impossible("(%s) line too long", altfname); goto give_up; } } } *bufout = '\0'; (void) dlb_fclose(fh); llret = luaL_loadbuffer(L, buf, strlen(buf), altfname); if (llret != LUA_OK) { impossible("luaL_loadbuffer: Error loading %s: %s", altfname, lua_tostring(L, -1)); ret = FALSE; goto give_up; } else { lua_pushcfunction(L, traceback_handler); lua_insert(L, 1); if (lua_pcall(L, 0, LUA_MULTRET, -2)) { impossible("Lua error: %s", lua_tostring(L, -1)); ret = FALSE; goto give_up; } } give_up: if (altfname) free((genericptr_t) altfname); if (buf) free((genericptr_t) buf); return ret; } lua_State * nhl_init() { lua_State *L = luaL_newstate(); luaL_openlibs(L); nhl_set_package_path(L, "./?.lua"); /* register nh -table, and functions for it */ lua_newtable(L); luaL_setfuncs(L, nhl_functions, 0); lua_setglobal(L, "nh"); /* init nhc -table */ init_nhc_data(L); /* init u -table */ init_u_data(L); l_selection_register(L); l_register_des(L); l_obj_register(L); if (!nhl_loadlua(L, "nhlib.lua")) { lua_close(L); return (lua_State *) 0; } return L; } boolean load_lua(name) const char *name; { boolean ret = TRUE; lua_State *L = nhl_init(); if (!L) { ret = FALSE; goto give_up; } if (!nhl_loadlua(L, name)) { ret = FALSE; goto give_up; } give_up: lua_close(L); return ret; } const char * get_lua_version() { size_t len = (size_t) 0; const char *vs = (const char *) 0; lua_State *L; if (g.lua_ver[0] == 0) { L = nhl_init(); if (L) { lua_getglobal(L, "_VERSION"); if (lua_isstring(L, -1)) vs = lua_tolstring (L, -1, &len); if (vs && len < sizeof g.lua_ver) { if (!strncmpi(vs, "Lua", 3)) { vs += 3; if (*vs == '-' || *vs == ' ') vs += 1; } Strcpy(g.lua_ver, vs); } } lua_close(L); #ifdef LUA_COPYRIGHT if (sizeof LUA_COPYRIGHT <= sizeof g.lua_copyright) Strcpy(g.lua_copyright, LUA_COPYRIGHT); #endif } return (const char *) g.lua_ver; }