#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<monsters>(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<monsters>(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<monsters>(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<beh_type>(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<beh_type>(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);
}