Lua: Persistent variables
Add a way for the lua scripts to set and retrieve variables that are persistent - saved and restored with the game. Invalidates saves.
This commit is contained in:
@@ -4,6 +4,15 @@
|
|||||||
-- This file contains lua code used by NetHack core.
|
-- This file contains lua code used by NetHack core.
|
||||||
-- Is it loaded once, at game start, and kept in memory until game exit.
|
-- Is it loaded once, at game start, and kept in memory until game exit.
|
||||||
|
|
||||||
|
-- Data in nh_lua_variables table can be set and queried with nh.variable()
|
||||||
|
-- This table is saved and restored along with the game.
|
||||||
|
nh_lua_variables = {
|
||||||
|
};
|
||||||
|
|
||||||
|
-- wrapper to simplify calling from nethack core
|
||||||
|
function get_variables_string()
|
||||||
|
return "nh_lua_variables=" .. table_stringify(nh_lua_variables) .. ";";
|
||||||
|
end
|
||||||
|
|
||||||
-- This is an example of generating an external file during gameplay,
|
-- This is an example of generating an external file during gameplay,
|
||||||
-- which is updated periodically.
|
-- which is updated periodically.
|
||||||
|
|||||||
@@ -107,3 +107,35 @@ end
|
|||||||
function pline(fmt, ...)
|
function pline(fmt, ...)
|
||||||
nh.pline(string.format(fmt, table.unpack({...})));
|
nh.pline(string.format(fmt, table.unpack({...})));
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- wrapper to make calling from nethack core easier
|
||||||
|
function nh_set_variables_string(key, tbl)
|
||||||
|
return "nh_lua_variables[\"" .. key .. "\"]=" .. table_stringify(tbl) .. ";";
|
||||||
|
end
|
||||||
|
|
||||||
|
-- wrapper to make calling from nethack core easier
|
||||||
|
function nh_get_variables_string(tbl)
|
||||||
|
return "return " .. table_stringify(tbl) .. ";";
|
||||||
|
end
|
||||||
|
|
||||||
|
-- return the (simple) table tbl converted into a string
|
||||||
|
function table_stringify(tbl)
|
||||||
|
local str = "";
|
||||||
|
for key, value in pairs(tbl) do
|
||||||
|
local typ = type(value);
|
||||||
|
if (typ == "table") then
|
||||||
|
str = str .. "[\"" .. key .. "\"]=" .. table_stringify(value);
|
||||||
|
elseif (typ == "string") then
|
||||||
|
str = str .. "[\"" .. key .. "\"]=[[" .. value .. "]]";
|
||||||
|
elseif (typ == "boolean") then
|
||||||
|
str = str .. "[\"" .. key .. "\"]=" .. tostring(value);
|
||||||
|
elseif (typ == "number") then
|
||||||
|
str = str .. "[\"" .. key .. "\"]=" .. value;
|
||||||
|
elseif (typ == "nil") then
|
||||||
|
str = str .. "[\"" .. key .. "\"]=nil";
|
||||||
|
end
|
||||||
|
str = str .. ",";
|
||||||
|
end
|
||||||
|
-- pline("table_stringify:(%s)", str);
|
||||||
|
return "{" .. str .. "}";
|
||||||
|
end
|
||||||
|
|||||||
13
doc/lua.adoc
13
doc/lua.adoc
@@ -376,6 +376,19 @@ Example:
|
|||||||
nh.stop_timer_at({x=5,y=6}, "melt-ice");
|
nh.stop_timer_at({x=5,y=6}, "melt-ice");
|
||||||
|
|
||||||
|
|
||||||
|
=== variable
|
||||||
|
|
||||||
|
Set or get a global variable. These are persistent, saved and restored along with the game.
|
||||||
|
Supports only strings, booleans, numbers, or tables.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
nh.variable("test", 10);
|
||||||
|
local ten = nh.variable("test");
|
||||||
|
nh.variable("tbl", { a = 1, b = "foo" });
|
||||||
|
local tbl = nh.variable("tbl");
|
||||||
|
|
||||||
|
|
||||||
=== verbalize
|
=== verbalize
|
||||||
|
|
||||||
Show the text in the message area as if someone said it, obeying eg. hero's deafness.
|
Show the text in the message area as if someone said it, obeying eg. hero's deafness.
|
||||||
|
|||||||
@@ -1839,6 +1839,9 @@ extern void l_nhcore_call(int);
|
|||||||
extern lua_State * nhl_init(nhl_sandbox_info *);
|
extern lua_State * nhl_init(nhl_sandbox_info *);
|
||||||
extern void nhl_done(lua_State *);
|
extern void nhl_done(lua_State *);
|
||||||
extern boolean nhl_loadlua(lua_State *, const char *);
|
extern boolean nhl_loadlua(lua_State *, const char *);
|
||||||
|
extern char *get_nh_lua_variables(void);
|
||||||
|
extern void save_luadata(NHFILE *);
|
||||||
|
extern void restore_luadata(NHFILE *);
|
||||||
extern int nhl_pcall(lua_State *, int, int);
|
extern int nhl_pcall(lua_State *, int, int);
|
||||||
extern boolean load_lua(const char *, nhl_sandbox_info *);
|
extern boolean load_lua(const char *, nhl_sandbox_info *);
|
||||||
ATTRNORETURN extern void nhl_error(lua_State *, const char *) NORETURN;
|
ATTRNORETURN extern void nhl_error(lua_State *, const char *) NORETURN;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* Incrementing EDITLEVEL can be used to force invalidation of old bones
|
* Incrementing EDITLEVEL can be used to force invalidation of old bones
|
||||||
* and save files.
|
* and save files.
|
||||||
*/
|
*/
|
||||||
#define EDITLEVEL 68
|
#define EDITLEVEL 69
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Development status possibilities.
|
* Development status possibilities.
|
||||||
|
|||||||
134
src/nhlua.c
134
src/nhlua.c
@@ -1030,6 +1030,139 @@ nhl_dnum_name(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set or get variables which are saved and restored along the game.
|
||||||
|
nh.variable("test", 10);
|
||||||
|
local ten = nh.variable("test"); */
|
||||||
|
static int
|
||||||
|
nhl_variable(lua_State *L)
|
||||||
|
{
|
||||||
|
int argc = lua_gettop(L);
|
||||||
|
int typ;
|
||||||
|
const char *key;
|
||||||
|
|
||||||
|
if (!gl.luacore) {
|
||||||
|
nhl_error(L, "nh luacore not inited");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getglobal(gl.luacore, "nh_lua_variables");
|
||||||
|
if (!lua_istable(gl.luacore, -1)) {
|
||||||
|
impossible("nh_lua_variables is not a lua table");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
|
key = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(gl.luacore, -1, key);
|
||||||
|
typ = lua_type(gl.luacore, -1);
|
||||||
|
if (typ == LUA_TSTRING)
|
||||||
|
lua_pushstring(L, lua_tostring(gl.luacore, -1));
|
||||||
|
else if (typ == LUA_TNIL)
|
||||||
|
lua_pushnil(L);
|
||||||
|
else if (typ == LUA_TBOOLEAN)
|
||||||
|
lua_pushboolean(L, lua_toboolean(gl.luacore, -1));
|
||||||
|
else if (typ == LUA_TNUMBER)
|
||||||
|
lua_pushinteger(L, lua_tointeger(gl.luacore, -1));
|
||||||
|
else if (typ == LUA_TTABLE) {
|
||||||
|
lua_getglobal(gl.luacore, "nh_get_variables_string");
|
||||||
|
lua_pushvalue(gl.luacore, -2);
|
||||||
|
nhl_pcall(gl.luacore, 1, 1);
|
||||||
|
luaL_loadstring(L, lua_tostring(gl.luacore, -1));
|
||||||
|
nhl_pcall(L, 0, 1);
|
||||||
|
} else
|
||||||
|
nhl_error(L, "Cannot get variable of that type");
|
||||||
|
return 1;
|
||||||
|
} else if (argc == 2) {
|
||||||
|
/* set nh_lua_variables[key] = value;
|
||||||
|
nh.variable("key", value); */
|
||||||
|
key = luaL_checkstring(L, 1);
|
||||||
|
//pline("SETVAR:%s", key);
|
||||||
|
typ = lua_type(L, -1);
|
||||||
|
|
||||||
|
if (typ == LUA_TSTRING) {
|
||||||
|
lua_pushstring(gl.luacore, lua_tostring(L, -1));
|
||||||
|
lua_setfield(gl.luacore, -2, key);
|
||||||
|
} else if (typ == LUA_TNIL) {
|
||||||
|
lua_pushnil(gl.luacore);
|
||||||
|
lua_setfield(gl.luacore, -2, key);
|
||||||
|
} else if (typ == LUA_TBOOLEAN) {
|
||||||
|
lua_pushboolean(gl.luacore, lua_toboolean(L, -1));
|
||||||
|
lua_setfield(gl.luacore, -2, key);
|
||||||
|
} else if (typ == LUA_TNUMBER) {
|
||||||
|
lua_pushinteger(gl.luacore, lua_tointeger(L, -1));
|
||||||
|
lua_setfield(gl.luacore, -2, key);
|
||||||
|
} else if (typ == LUA_TTABLE) {
|
||||||
|
lua_getglobal(L, "nh_set_variables_string");
|
||||||
|
lua_pushstring(L, key);
|
||||||
|
lua_pushvalue(L, -3); /* copy value to top */
|
||||||
|
nhl_pcall(L, 2, 1);
|
||||||
|
luaL_loadstring(gl.luacore, lua_tostring(L, -1));
|
||||||
|
nhl_pcall(gl.luacore, 0, 0);
|
||||||
|
} else
|
||||||
|
nhl_error(L, "Cannot set variable of that type");
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
nhl_error(L, "Wrong number of arguments");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return nh_lua_variable lua table as a string */
|
||||||
|
char *
|
||||||
|
get_nh_lua_variables(void)
|
||||||
|
{
|
||||||
|
char *key = NULL;
|
||||||
|
|
||||||
|
if (!gl.luacore) {
|
||||||
|
nhl_error(gl.luacore, "nh luacore not inited");
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getglobal(gl.luacore, "nh_lua_variables");
|
||||||
|
if (!lua_istable(gl.luacore, -1)) {
|
||||||
|
impossible("nh_lua_variables is not a lua table");
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getglobal(gl.luacore, "get_variables_string");
|
||||||
|
if (lua_type(gl.luacore, -1) == LUA_TFUNCTION) {
|
||||||
|
if (nhl_pcall(gl.luacore, 0, 1)) {
|
||||||
|
impossible("Lua error: %s", lua_tostring(gl.luacore, -1));
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
key = dupstr(lua_tostring(gl.luacore, -1));
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save nh_lua_variables table to file */
|
||||||
|
void
|
||||||
|
save_luadata(NHFILE *nhfp)
|
||||||
|
{
|
||||||
|
char *lua_data = get_nh_lua_variables();
|
||||||
|
long lua_data_len = strlen(lua_data) + 1;
|
||||||
|
|
||||||
|
bwrite(nhfp->fd, (genericptr_t) &lua_data_len, sizeof lua_data_len);
|
||||||
|
bwrite(nhfp->fd, (genericptr_t) lua_data, strlen(lua_data) + 1);
|
||||||
|
free(lua_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restore nh_lua_variables table from file */
|
||||||
|
void
|
||||||
|
restore_luadata(NHFILE *nhfp)
|
||||||
|
{
|
||||||
|
long lua_data_len;
|
||||||
|
char *lua_data;
|
||||||
|
|
||||||
|
mread(nhfp->fd, (genericptr_t) &lua_data_len, sizeof lua_data_len);
|
||||||
|
lua_data = (char *) alloc(lua_data_len);
|
||||||
|
mread(nhfp->fd, (genericptr_t) lua_data, lua_data_len);
|
||||||
|
if (!gl.luacore)
|
||||||
|
l_nhcore_init();
|
||||||
|
luaL_loadstring(gl.luacore, lua_data);
|
||||||
|
nhl_pcall(gl.luacore, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* local stairs = stairways(); */
|
/* local stairs = stairways(); */
|
||||||
static int
|
static int
|
||||||
nhl_stairways(lua_State *L)
|
nhl_stairways(lua_State *L)
|
||||||
@@ -1319,6 +1452,7 @@ static const struct luaL_Reg nhl_functions[] = {
|
|||||||
{"dump_fmtstr", nhl_dump_fmtstr},
|
{"dump_fmtstr", nhl_dump_fmtstr},
|
||||||
#endif /* DUMPLOG */
|
#endif /* DUMPLOG */
|
||||||
{"dnum_name", nhl_dnum_name},
|
{"dnum_name", nhl_dnum_name},
|
||||||
|
{"variable", nhl_variable},
|
||||||
{"stairways", nhl_stairways},
|
{"stairways", nhl_stairways},
|
||||||
{"pushkey", nhl_pushkey},
|
{"pushkey", nhl_pushkey},
|
||||||
{"doturn", nhl_doturn},
|
{"doturn", nhl_doturn},
|
||||||
|
|||||||
@@ -688,6 +688,7 @@ restgamestate(NHFILE* nhfp, unsigned int* stuckid, unsigned int* steedid)
|
|||||||
restnames(nhfp);
|
restnames(nhfp);
|
||||||
restore_msghistory(nhfp);
|
restore_msghistory(nhfp);
|
||||||
restore_gamelog(nhfp);
|
restore_gamelog(nhfp);
|
||||||
|
restore_luadata(nhfp);
|
||||||
/* must come after all mons & objs are restored */
|
/* must come after all mons & objs are restored */
|
||||||
relink_timers(FALSE);
|
relink_timers(FALSE);
|
||||||
relink_light_sources(FALSE);
|
relink_light_sources(FALSE);
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ savegamestate(NHFILE *nhfp)
|
|||||||
savenames(nhfp);
|
savenames(nhfp);
|
||||||
save_msghistory(nhfp);
|
save_msghistory(nhfp);
|
||||||
save_gamelog(nhfp);
|
save_gamelog(nhfp);
|
||||||
|
save_luadata(nhfp);
|
||||||
if (nhfp->structlevel)
|
if (nhfp->structlevel)
|
||||||
bflush(nhfp->fd);
|
bflush(nhfp->fd);
|
||||||
gp.program_state.saving--;
|
gp.program_state.saving--;
|
||||||
|
|||||||
Reference in New Issue
Block a user