/** * @file llua.c * * @brief Contains some standard Lua binding libraries. * * Namely: * -- lephisto : General game stuff. * -- time : For manipulating time. * -- rnd : For access to the random number generator. * -- tk : Access to the toolkit. */ #include "lauxlib.h" #include "log.h" #include "lephisto.h" #include "rng.h" #include "ltime.h" #include "dialogue.h" #include "space.h" #include "land.h" #include "map.h" #include "pack.h" #include "lluadef.h" #include "llua.h" static int llua_packfileLoader(lua_State* L); /* -- Libraries. -- */ /* Lephisto. */ static int lephisto_lang(lua_State* L); static const luaL_reg lephisto_methods[] = { { "lang", lephisto_lang }, { 0, 0 } }; /* Time. */ static int time_get(lua_State* L); static int time_str(lua_State* L); static int time_units(lua_State* L); static const luaL_reg time_methods[] = { { "get", time_get }, { "str", time_str }, { "units", time_units }, {0, 0} }; /* RND. */ static int rnd_int(lua_State*L); static const luaL_reg rnd_methods[] = { { "int", rnd_int }, { 0, 0 } }; /* Toolkit. */ static int tk_msg(lua_State* L); static int tk_yesno(lua_State* L); static int tk_input(lua_State* L); static const luaL_reg tk_methods[] = { { "msg", tk_msg }, { "yesno", tk_yesno }, { "input", tk_input }, { 0, 0 } }; /* Wrapper around luaL_newstate. */ lua_State* llua_newState(void) { lua_State* L; /* Try to create the new state. */ L = luaL_newstate(); if(L == NULL) { WARN("Failed to create new lua state."); return NULL; } return L; } /* Open a lua library. */ int llua_load(lua_State* L, lua_CFunction f) { lua_pushcfunction(L, f); if(lua_pcall(L, 0, 0, 0)) WARN("llua include error: %s", lua_tostring(L, 1)); return 0; } /** * @fn int llua_loadBasic(lua_State* L) * * @brief Load specially modified basic stuff. * @param L Lua state to load the basic stuff into. * @return 0 on success. */ int llua_loadBasic(lua_State* L) { int i; /* Unsafe functions. */ const char* override[] = { "collectgarbage", "dofile", "getfenv", "getmetatable", "load", "loadfile", "loadstring", "rawequal", "rawget", "rawset", "setfenv", "setmetatable", "END" }; llua_load(L, luaopen_base); /* Open base. */ /* Replace non-safe functions. */ for(i = 0; strcmp(override[i], "END") != 0; i++) { lua_pushnil(L); lua_setglobal(L, override[i]); } /* Add our own. */ lua_register(L, "include", llua_packfileLoader); return 0; } static int llua_packfileLoader(lua_State* L) { char* buf, *filename; uint32_t bufsize; LLUA_MIN_ARGS(1); if(!lua_isstring(L, 1)) { LLUA_INVALID_PARAMETER(); return 0; } filename = (char*) lua_tostring(L, 1); /* Try to locate the data. */ buf = pack_readfile(DATA, filename, &bufsize); if(buf == NULL) { lua_pushfstring(L, "%s not found in packfile %s", filename, DATA); return 1; } /* Run the buffer. */ if(luaL_dobuffer(L, buf, bufsize, filename) != 0) { /* Will push the current error from the dobuffer. */ return 1; } /* Cleanup. */ free(buf); return 0; } /* Individual libraries. */ int lua_loadLephisto(lua_State* L) { luaL_register(L, "lephisto", lephisto_methods); return 0; } int lua_loadTime(lua_State* L, int readonly) { (void)readonly; luaL_register(L, "time", time_methods); return 0; } int lua_loadRnd(lua_State* L) { luaL_register(L, "rnd", rnd_methods); return 0; } int lua_loadTk(lua_State* L) { luaL_register(L, "tk", tk_methods); return 0; } /* -- Lephisto. -- */ static int lephisto_lang(lua_State* L) { /* @todo Multilanguage stuff. */ lua_pushstring(L, "en"); return 1; } /* -- Time. -- */ static int time_get(lua_State* L) { lua_pushnumber(L, ltime_get()); return 1; } static int time_str(lua_State* L) { char* lt; if((lua_gettop(L) > 0) && (lua_isnumber(L, 1))) lt = ltime_pretty((unsigned int) lua_tonumber(L, 1)); else lt = ltime_pretty(ltime_get()); lua_pushstring(L, lt); free(lt); return 1; } static int time_units(lua_State* L) { if((lua_gettop(L) > 0) && (lua_isnumber(L, 1))) lua_pushnumber(L, (unsigned int) lua_tonumber(L, 1) * LTIME_UNIT_LENGTH); else lua_pushnumber(L, LTIME_UNIT_LENGTH); return 1; } /* -- RND. -- */ static int rnd_int(lua_State* L) { int o; o = lua_gettop(L); if(o == 0) lua_pushnumber(L, RNGF()); /* Random double o <= x <= 1. */ else if(o == 1) { /* Random int 0 <= x <= param. */ if(lua_isnumber(L, 1)) lua_pushnumber(L, RNG(0, (int)lua_tonumber(L, 1))); else return 0; } else if(o >= 2) { /* Random int param 1 <= x <= param 2. */ if(lua_isnumber(L, 1) && lua_isnumber(L, 2)) lua_pushnumber(L, RNG((int)lua_tonumber(L, 1), (int)lua_tonumber(L, 2))); else return 0; } else LLUA_INVALID_PARAMETER(); /* Unless it's returned 0 already it'll always return param. */ return 1; } /* -- Toolkit. -- */ static int tk_msg(lua_State* L) { char* title, *str; LLUA_MIN_ARGS(2); if(lua_isstring(L, 1)) title = (char*) lua_tostring(L, 1); else LLUA_INVALID_PARAMETER(); if(lua_isstring(L, 2)) str = (char*) lua_tostring(L, 2); else LLUA_INVALID_PARAMETER(); dialogue_msg(title, str); return 0; } static int tk_yesno(lua_State* L) { int ret; char* title, *str; LLUA_MIN_ARGS(2); if(lua_isstring(L, 1)) title = (char*) lua_tostring(L, 1); else LLUA_INVALID_PARAMETER(); if(lua_isstring(L, 2)) str = (char*) lua_tostring(L, 2); else LLUA_INVALID_PARAMETER(); ret = dialogue_YesNo(title, str); lua_pushboolean(L, ret); return 1; } static int tk_input(lua_State* L) { char* title, *str; int min, max; LLUA_MIN_ARGS(4); if(lua_isstring(L, 1)) title = (char*) lua_tostring(L, 1); else LLUA_INVALID_PARAMETER(); if(lua_isnumber(L, 2)) min = (int) lua_tonumber(L, 2); else LLUA_INVALID_PARAMETER(); if(lua_isnumber(L, 3)) max = (int) lua_tonumber(L, 3); else LLUA_INVALID_PARAMETER(); if(lua_isstring(L, 4)) str = (char*) lua_tostring(L, 4); else LLUA_INVALID_PARAMETER(); dialogue_input(title, min, max, str); return 0; }