#include "AppHdr.h" #include "cluautil.h" #include "l_libs.h" #include "delay.h" #include "dlua.h" #include "initfile.h" #include "mon-util.h" #include "mon-stuff.h" ///////////////////////////////////////////////////////////////////// // Monster handling #define MONS_METATABLE "monster.monsaccess" struct MonsterWrap { monsters *mons; long turn; }; void push_monster(lua_State *ls, monsters *mons) { MonsterWrap *mw = clua_new_userdata< MonsterWrap >(ls, MONS_METATABLE); mw->turn = you.num_turns; mw->mons = mons; } #define MDEF(name) \ static int l_mons_##name(lua_State *ls, monsters *mons, \ const char *attr) \ #define MDEFN(name, closure) \ static int l_mons_##name(lua_State *ls, monsters *mons, const char *attrs) \ { \ lua_pushlightuserdata(ls, mons); \ lua_pushcclosure(ls, l_mons_##closure, 1); \ return (1); \ } MDEF(name) { PLUARET(string, mons->name(DESC_PLAIN, true).c_str()); } MDEF(base_name) { PLUARET(string, mons->base_name(DESC_PLAIN, true).c_str()); } MDEF(full_name) { PLUARET(string, mons->full_name(DESC_PLAIN, true).c_str()); } MDEF(db_name) { PLUARET(string, mons->name(DESC_DBNAME, true).c_str()); } MDEF(type_name) { PLUARET(string, mons_type_name(mons->type, DESC_PLAIN).c_str()); } MDEF(x) { PLUARET(number, int(mons->pos().x) - int(you.pos().x)); } MDEF(y) { PLUARET(number, int(mons->pos().y) - int(you.pos().y)); } MDEF(hd) { PLUARET(number, mons->hit_dice); } static const char *_monuse_names[] = { "nothing", "open_doors", "starting_equipment", "weapons_armour", "magic_items" }; static const char *_monuse_to_str(mon_itemuse_type utyp) { COMPILE_CHECK(ARRAYSZ(_monuse_names) == NUM_MONUSE, c1); return _monuse_names[utyp]; } MDEF(muse) { if (const monsterentry *me = mons->find_monsterentry()) { PLUARET(string, _monuse_to_str(me->gmon_use)); } return (0); } static const char *_moneat_names[] = { "nothing", "items", "corpses", "food" }; static const char *_moneat_to_str(mon_itemeat_type etyp) { COMPILE_CHECK(ARRAYSZ(_moneat_names) == NUM_MONEAT, c1); return _moneat_names[etyp]; } MDEF(meat) { if (const monsterentry *me = mons->find_monsterentry()) { PLUARET(string, _moneat_to_str(me->gmon_eat)); } return (0); } static int l_mons_do_dismiss(lua_State *ls) { // dismiss is only callable from dlua, not from managed VMs (i.e. // end-user scripts cannot dismiss monsters). ASSERT_DLUA; monsters *mons = util_get_userdata(ls, lua_upvalueindex(1)); if (mons->alive()) { mons->flags |= MF_HARD_RESET; monster_die(mons, KILL_DISMISSED, NON_MONSTER); } return (0); } MDEFN(dismiss, do_dismiss) static int l_mons_do_random_teleport(lua_State *ls) { // We should only be able to teleport monsters from dlua. ASSERT_DLUA; monsters *mons = util_get_userdata(ls, lua_upvalueindex(1)); if (mons->alive()) mons->teleport(true); return (0); } MDEFN(random_teleport, do_random_teleport) MDEF(experience) { ASSERT_DLUA; PLUARET(number, exper_value(mons)); } static int l_mons_do_set_prop(lua_State *ls) { // We should only be able to set properties from dlua. ASSERT_DLUA; monsters *mons = util_get_userdata(ls, lua_upvalueindex(1)); const char *prop_name = luaL_checkstring(ls, 1); if (lua_isnoneornil(ls, 2)) mons->props.erase(prop_name); else if (lua_isboolean(ls, 2)) mons->props[prop_name] = (bool) lua_toboolean(ls, 2); // NOTE: number has to be before string, or numbers will get converted // into strings. else if (lua_isnumber(ls, 2)) mons->props[prop_name] = luaL_checklong(ls, 2); else if (lua_isstring(ls, 2)) mons->props[prop_name] = lua_tostring(ls, 2); else if (lua_isfunction(ls, 2)) { dlua_chunk chunk(ls); mons->props[prop_name] = chunk; } else { std::string err = make_stringf("Don't know how to set monster property of the " "given value type for property '%s'", prop_name); luaL_argerror(ls, 2, err.c_str()); } return (0); } MDEFN(set_prop, do_set_prop) MDEF(you_can_see) { ASSERT_DLUA; PLUARET(boolean, you.can_see(mons)); } struct MonsAccessor { const char *attribute; int (*accessor)(lua_State *ls, monsters *mons, const char *attr); }; static MonsAccessor mons_attrs[] = { { "name", l_mons_name }, { "base_name", l_mons_base_name }, { "full_name", l_mons_full_name }, { "db_name", l_mons_db_name }, { "type_name", l_mons_type_name }, { "x" , l_mons_x }, { "y" , l_mons_y }, { "hd" , l_mons_hd }, { "muse", l_mons_muse }, { "meat", l_mons_meat }, { "dismiss", l_mons_dismiss }, { "experience", l_mons_experience }, { "random_teleport", l_mons_random_teleport }, { "set_prop", l_mons_set_prop }, { "you_can_see", l_mons_you_can_see } }; static int monster_get(lua_State *ls) { MonsterWrap *mw = clua_get_userdata< MonsterWrap >(ls, MONS_METATABLE); if (!mw || mw->turn != you.num_turns || !mw->mons) return (0); const char *attr = luaL_checkstring(ls, 2); if (!attr) return (0); for (unsigned i = 0; i < sizeof(mons_attrs) / sizeof(mons_attrs[0]); ++i) if (!strcmp(attr, mons_attrs[i].attribute)) return (mons_attrs[i].accessor(ls, mw->mons, attr)); return (0); } static const char *_monster_behaviour_names[] = { "sleep", "wander", "seek", "flee", "cornered", "panic", "lurk" }; static beh_type behaviour_by_name(const std::string &name) { ASSERT(ARRAYSZ(_monster_behaviour_names) == NUM_BEHAVIOURS); for (unsigned i = 0; i < ARRAYSZ(_monster_behaviour_names); ++i) if (name == _monster_behaviour_names[i]) return static_cast(i); return NUM_BEHAVIOURS; } static int monster_set(lua_State *ls) { // Changing monster behaviour is for the dungeon builder only, // never for user scripts. ASSERT_DLUA; MonsterWrap *mw = clua_get_userdata< MonsterWrap >(ls, MONS_METATABLE); if (!mw || !mw->mons) return (0); const char *attr = luaL_checkstring(ls, 2); if (!attr) return (0); if (!strcmp(attr, "beh")) { const beh_type beh = lua_isnumber(ls, 3) ? static_cast(luaL_checkint(ls, 3)) : lua_isstring(ls, 3) ? behaviour_by_name(lua_tostring(ls, 3)) : NUM_BEHAVIOURS; if (beh != NUM_BEHAVIOURS) mw->mons->behaviour = beh; } return (0); } static int mons_behaviour(lua_State *ls) { if (lua_gettop(ls) < 1) return (0); if (lua_isnumber(ls, 1)) { lua_pushvalue(ls, 1); return (1); } else if (lua_isstring(ls, 1)) { const beh_type beh = behaviour_by_name(lua_tostring(ls, 1)); if (beh != NUM_BEHAVIOURS) { lua_pushnumber(ls, beh); return (1); } } return (0); } static const struct luaL_reg mons_lib[] = { { "behaviour", mons_behaviour }, { NULL, NULL } }; void dluaopen_monsters(lua_State *ls) { luaL_newmetatable(ls, MONS_METATABLE); lua_pushstring(ls, "__index"); lua_pushcfunction(ls, monster_get); lua_settable(ls, -3); lua_pushstring(ls, "__newindex"); lua_pushcfunction(ls, monster_set); lua_settable(ls, -3); // Pop the metatable off the stack. lua_pop(ls, 1); luaL_openlib(ls, "mons", mons_lib, 0); }