Files
nethack/src/nhlobj.c
nhmall 02a48aa8cf split g into multiple structures
The consolidation of global variables from scattered source
files into decl.c and declared in decl.h was begun in 3.7.0.
Their placement in common files was done for centralized
initialization and potential re-initialization during a
"play again" scenario.

It wasn't really necessary for all of them to be housed in a
single huge structure to meet the "play again" requirement,
and the single huge structure has been a little unwieldy when
it comes to maintenance.

Following this commit, instead of one single extremely large structure
named 'g' to house all of the relocated global variables, they
are distributed into several ga through gz.

To make things easy for the developer, each variable is placed
into the struct corresponding to the starting letter of the variable.
That way, no lookup is required in order to know which struct houses
a particular variable, it is a simple match to the starting letter
for all the centralized global variables.

A global variable named 'amulets', would be found in ga.
    ga.amulets
     ^ ^
A global varable named 'move', would be found in gm.
    gm.moves
     ^ ^
A global variable named 'val_for_n_or_more' would be found in gv.
    gv.val_for_n_or_more
     ^ ^
A global variable named 'youmonst' would be found in gy.
    gy.youmonst
     ^ ^
2022-11-29 21:53:21 -05:00

658 lines
19 KiB
C

/* NetHack 3.7 nhlobj.c $NHDT-Date: 1576097301 2019/12/11 20:48:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ */
/* Copyright (c) 2019 by Pasi Kallinen */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "sp_lev.h"
struct _lua_obj {
int state; /* UNUSED */
struct obj *obj;
};
static struct _lua_obj *l_obj_check(lua_State *, int);
static int l_obj_add_to_container(lua_State *);
static int l_obj_gc(lua_State *);
static int l_obj_getcontents(lua_State *);
static int l_obj_isnull(lua_State *);
static int l_obj_new_readobjnam(lua_State *);
static int l_obj_nextobj(lua_State *);
static int l_obj_objects_to_table(lua_State *);
static int l_obj_placeobj(lua_State *);
static int l_obj_to_table(lua_State *);
static int l_obj_at(lua_State *);
static int l_obj_container(lua_State *);
static int l_obj_timer_has(lua_State *);
static int l_obj_timer_peek(lua_State *);
static int l_obj_timer_stop(lua_State *);
static int l_obj_timer_start(lua_State *);
static int l_obj_bury(lua_State *);
#define lobj_is_ok(lo) ((lo) && (lo)->obj && (lo)->obj->where != OBJ_LUAFREE)
static struct _lua_obj *
l_obj_check(lua_State *L, int indx)
{
struct _lua_obj *lo;
luaL_checktype(L, indx, LUA_TUSERDATA);
lo = (struct _lua_obj *) luaL_checkudata(L, indx, "obj");
if (!lo)
nhl_error(L, "Obj error");
return lo;
}
static int
l_obj_gc(lua_State *L)
{
struct obj *obj, *otmp;
struct _lua_obj *lo = l_obj_check(L, 1);
if (lo && (obj = lo->obj) != 0) {
if (obj->lua_ref_cnt > 0)
obj->lua_ref_cnt--;
/* free-floating objects with no other refs are deallocated. */
if (!obj->lua_ref_cnt
&& (obj->where == OBJ_FREE || obj->where == OBJ_LUAFREE)) {
if (Has_contents(obj)) {
while ((otmp = obj->cobj) != 0) {
obj_extract_self(otmp);
dealloc_obj(otmp);
}
}
obj->where = OBJ_FREE;
dealloc_obj(obj), obj = 0;
}
lo->obj = NULL;
}
return 0;
}
static struct _lua_obj *
l_obj_push(lua_State *L, struct obj *otmp)
{
struct _lua_obj *lo = (struct _lua_obj *)lua_newuserdata(L, sizeof(struct _lua_obj));
luaL_getmetatable(L, "obj");
lua_setmetatable(L, -2);
lo->state = 0;
lo->obj = otmp;
if (otmp)
otmp->lua_ref_cnt++;
return lo;
}
void
nhl_push_obj(lua_State *L, struct obj *otmp)
{
(void) l_obj_push(L, otmp);
}
/* local o = obj.new("large chest");
local cobj = o:contents(); */
static int
l_obj_getcontents(lua_State *L)
{
struct _lua_obj *lo = l_obj_check(L, 1);
struct obj *obj = lo->obj;
if (!obj)
nhl_error(L, "l_obj_getcontents: no obj");
(void) l_obj_push(L, obj->cobj);
return 1;
}
/* Puts object inside another object. */
/* local box = obj.new("large chest");
box.addcontent(obj.new("rock"));
*/
static int
l_obj_add_to_container(lua_State *L)
{
struct _lua_obj *lobox = l_obj_check(L, 1);
struct _lua_obj *lo = l_obj_check(L, 2);
struct obj *otmp;
int refs;
if (!lobj_is_ok(lo) || !lobj_is_ok(lobox))
return 0;
refs = lo->obj->lua_ref_cnt;
obj_extract_self(lo->obj);
otmp = add_to_container(lobox->obj, lo->obj);
/* was lo->obj merged? */
if (otmp != lo->obj) {
lo->obj->lua_ref_cnt += refs;
lo->obj = otmp;
}
return 0;
}
/* Put object into player's inventory */
/* u.giveobj(obj.new("rock")); */
int
nhl_obj_u_giveobj(lua_State *L)
{
struct _lua_obj *lo = l_obj_check(L, 1);
struct obj *otmp;
int refs;
if (!lobj_is_ok(lo) || lo->obj->where == OBJ_INVENT)
return 0;
refs = lo->obj->lua_ref_cnt;
obj_extract_self(lo->obj);
otmp = addinv(lo->obj);
if (otmp != lo->obj) {
lo->obj->lua_ref_cnt += refs;
lo->obj = otmp;
}
return 0;
}
DISABLE_WARNING_UNREACHABLE_CODE
/* Get a table of object class data. */
/* local odata = obj.class(otbl.otyp); */
/* local odata = obj.class(obj.new("rock")); */
/* local odata = o:class(); */
static int
l_obj_objects_to_table(lua_State *L)
{
int argc = lua_gettop(L);
int otyp = -1;
struct objclass *o;
if (argc != 1) {
nhl_error(L, "l_obj_objects_to_table: Wrong args");
/*NOTREACHED*/
return 0;
}
if (lua_type(L, 1) == LUA_TNUMBER) {
otyp = (int) luaL_checkinteger(L, 1);
} else if (lua_type(L, 1) == LUA_TUSERDATA) {
struct _lua_obj *lo = l_obj_check(L, 1);
if (lo && lo->obj)
otyp = lo->obj->otyp;
}
lua_pop(L, 1);
if (otyp == -1) {
nhl_error(L, "l_obj_objects_to_table: Wrong args");
return 0;
}
o = &objects[otyp];
lua_newtable(L);
if (OBJ_NAME(objects[otyp]))
nhl_add_table_entry_str(L, "name", OBJ_NAME(objects[otyp]));
if (OBJ_DESCR(objects[otyp]))
nhl_add_table_entry_str(L, "descr",
OBJ_DESCR(objects[otyp]));
if (o->oc_uname)
nhl_add_table_entry_str(L, "uname", o->oc_uname);
nhl_add_table_entry_int(L, "name_known", o->oc_name_known);
nhl_add_table_entry_int(L, "merge", o->oc_merge);
nhl_add_table_entry_int(L, "uses_known", o->oc_uses_known);
nhl_add_table_entry_int(L, "pre_discovered", o->oc_pre_discovered);
nhl_add_table_entry_int(L, "magic", o->oc_magic);
nhl_add_table_entry_int(L, "charged", o->oc_charged);
nhl_add_table_entry_int(L, "unique", o->oc_unique);
nhl_add_table_entry_int(L, "nowish", o->oc_nowish);
nhl_add_table_entry_int(L, "big", o->oc_big);
/* TODO: oc_bimanual, oc_bulky */
nhl_add_table_entry_int(L, "tough", o->oc_tough);
nhl_add_table_entry_int(L, "dir", o->oc_dir); /* TODO: convert to text */
nhl_add_table_entry_int(L, "material", o->oc_material); /* TODO: convert to text */
/* TODO: oc_subtyp, oc_skill, oc_armcat */
nhl_add_table_entry_int(L, "oprop", o->oc_oprop);
nhl_add_table_entry_char(L, "class",
def_oc_syms[(uchar) o->oc_class].sym);
nhl_add_table_entry_int(L, "delay", o->oc_delay);
nhl_add_table_entry_int(L, "color", o->oc_color); /* TODO: text? */
nhl_add_table_entry_int(L, "prob", o->oc_prob);
nhl_add_table_entry_int(L, "weight", o->oc_weight);
nhl_add_table_entry_int(L, "cost", o->oc_cost);
nhl_add_table_entry_int(L, "damage_small", o->oc_wsdam);
nhl_add_table_entry_int(L, "damage_large", o->oc_wldam);
/* TODO: oc_oc1, oc_oc2, oc_hitbon, a_ac, a_can, oc_level */
nhl_add_table_entry_int(L, "nutrition", o->oc_nutrition);
return 1;
}
RESTORE_WARNING_UNREACHABLE_CODE
/* Create a lua table representation of the object, unpacking all the
object fields.
local o = obj.new("rock");
local otbl = o:totable(); */
static int
l_obj_to_table(lua_State *L)
{
struct _lua_obj *lo = l_obj_check(L, 1);
struct obj *obj = lo->obj;
lua_newtable(L);
if (!obj || obj->where == OBJ_LUAFREE) {
nhl_add_table_entry_int(L, "NO_OBJ", 1);
return 1;
}
nhl_add_table_entry_int(L, "has_contents", Has_contents(obj));
nhl_add_table_entry_int(L, "is_container", Is_container(obj));
nhl_add_table_entry_int(L, "o_id", obj->o_id);
nhl_add_table_entry_int(L, "ox", obj->ox);
nhl_add_table_entry_int(L, "oy", obj->oy);
nhl_add_table_entry_int(L, "otyp", obj->otyp);
if (OBJ_NAME(objects[obj->otyp]))
nhl_add_table_entry_str(L, "otyp_name", OBJ_NAME(objects[obj->otyp]));
if (OBJ_DESCR(objects[obj->otyp]))
nhl_add_table_entry_str(L, "otyp_descr",
OBJ_DESCR(objects[obj->otyp]));
nhl_add_table_entry_int(L, "owt", obj->owt);
nhl_add_table_entry_int(L, "quan", obj->quan);
nhl_add_table_entry_int(L, "spe", obj->spe);
if (obj->otyp == STATUE)
nhl_add_table_entry_int(L, "historic",
(obj->spe & CORPSTAT_HISTORIC) != 0);
if (obj->otyp == CORPSE || obj->otyp == STATUE) {
nhl_add_table_entry_int(L, "male",
(obj->spe & CORPSTAT_MALE) != 0);
nhl_add_table_entry_int(L, "female",
(obj->spe & CORPSTAT_FEMALE) != 0);
}
nhl_add_table_entry_char(L, "oclass",
def_oc_syms[(uchar) obj->oclass].sym);
nhl_add_table_entry_char(L, "invlet", obj->invlet);
/* TODO: nhl_add_table_entry_char(L, "oartifact", obj->oartifact);*/
nhl_add_table_entry_int(L, "where", obj->where);
/* TODO: nhl_add_table_entry_int(L, "timed", obj->timed); */
nhl_add_table_entry_int(L, "cursed", obj->cursed);
nhl_add_table_entry_int(L, "blessed", obj->blessed);
nhl_add_table_entry_int(L, "unpaid", obj->unpaid);
nhl_add_table_entry_int(L, "no_charge", obj->no_charge);
nhl_add_table_entry_int(L, "known", obj->known);
nhl_add_table_entry_int(L, "dknown", obj->dknown);
nhl_add_table_entry_int(L, "bknown", obj->bknown);
nhl_add_table_entry_int(L, "rknown", obj->rknown);
if (obj->oclass == POTION_CLASS)
nhl_add_table_entry_int(L, "odiluted", obj->odiluted);
else
nhl_add_table_entry_int(L, "oeroded", obj->oeroded);
nhl_add_table_entry_int(L, "oeroded2", obj->oeroded2);
/* TODO: orotten, norevive */
nhl_add_table_entry_int(L, "oerodeproof", obj->oerodeproof);
nhl_add_table_entry_int(L, "olocked", obj->olocked);
nhl_add_table_entry_int(L, "obroken", obj->obroken);
if (is_poisonable(obj))
nhl_add_table_entry_int(L, "opoisoned", obj->opoisoned);
else
nhl_add_table_entry_int(L, "otrapped", obj->otrapped);
/* TODO: degraded_horn */
nhl_add_table_entry_int(L, "recharged", obj->recharged);
/* TODO: on_ice */
nhl_add_table_entry_int(L, "lamplit", obj->lamplit);
nhl_add_table_entry_int(L, "globby", obj->globby);
nhl_add_table_entry_int(L, "greased", obj->greased);
nhl_add_table_entry_int(L, "nomerge", obj->nomerge);
nhl_add_table_entry_int(L, "was_thrown", obj->was_thrown);
nhl_add_table_entry_int(L, "in_use", obj->in_use);
nhl_add_table_entry_int(L, "bypass", obj->bypass);
nhl_add_table_entry_int(L, "cknown", obj->cknown);
nhl_add_table_entry_int(L, "lknown", obj->lknown);
nhl_add_table_entry_int(L, "corpsenm", obj->corpsenm);
if (obj->corpsenm != NON_PM
&& (obj->otyp == TIN || obj->otyp == CORPSE || obj->otyp == EGG
|| obj->otyp == FIGURINE || obj->otyp == STATUE))
nhl_add_table_entry_str(L, "corpsenm_name",
mons[obj->corpsenm].pmnames[NEUTRAL]);
/* TODO: leashmon, fromsink, novelidx, record_achieve_special */
nhl_add_table_entry_int(L, "usecount", obj->usecount);
/* TODO: spestudied */
nhl_add_table_entry_int(L, "oeaten", obj->oeaten);
nhl_add_table_entry_int(L, "age", obj->age);
nhl_add_table_entry_int(L, "owornmask", obj->owornmask);
/* TODO: more of oextra */
nhl_add_table_entry_int(L, "has_oname", has_oname(obj));
if (has_oname(obj))
nhl_add_table_entry_str(L, "oname", ONAME(obj));
return 1;
}
DISABLE_WARNING_UNREACHABLE_CODE
/* create a new object via wishing routine */
/* local o = obj.new("rock"); */
static int
l_obj_new_readobjnam(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 1) {
char buf[BUFSZ];
struct obj *otmp;
Sprintf(buf, "%s", luaL_checkstring(L, 1));
lua_pop(L, 1);
otmp = readobjnam(buf, NULL);
(void) l_obj_push(L, otmp);
return 1;
} else
nhl_error(L, "l_obj_new_readobjname: Wrong args");
/*NOTREACHED*/
return 0;
}
/* Get the topmost object on the map at x,y */
/* local o = obj.at(x, y); */
static int
l_obj_at(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 2) {
coordxy x, y;
x = (coordxy) luaL_checkinteger(L, 1);
y = (coordxy) luaL_checkinteger(L, 2);
cvt_to_abscoord(&x, &y);
lua_pop(L, 2);
(void) l_obj_push(L, gl.level.objects[x][y]);
return 1;
} else
nhl_error(L, "l_obj_at: Wrong args");
/*NOTREACHED*/
return 0;
}
/* Place an object on the map at (x,y).
local o = obj.new("rock");
o:placeobj(u.ux, u.uy); */
static int
l_obj_placeobj(lua_State *L)
{
int argc = lua_gettop(L);
struct _lua_obj *lo = l_obj_check(L, 1);
coordxy x, y;
if (argc != 3)
nhl_error(L, "l_obj_placeobj: Wrong args");
x = (coordxy) luaL_checkinteger(L, 2);
y = (coordxy) luaL_checkinteger(L, 3);
cvt_to_abscoord(&x, &y);
lua_pop(L, 3);
if (lobj_is_ok(lo)) {
obj_extract_self(lo->obj);
place_object(lo->obj, x, y);
newsym(x, y);
}
return 0;
}
RESTORE_WARNING_UNREACHABLE_CODE
/* Get the next object in the object chain */
/* local o = obj.at(x, y);
local o2 = o:next(true);
local firstobj = obj.next();
*/
static int
l_obj_nextobj(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 0) {
(void) l_obj_push(L, fobj);
} else {
struct _lua_obj *lo = l_obj_check(L, 1);
boolean use_nexthere = FALSE;
if (argc == 2)
use_nexthere = lua_toboolean(L, 2);
if (lo && lo->obj)
(void) l_obj_push(L, (use_nexthere && lo->obj->where == OBJ_FLOOR)
? lo->obj->nexthere
: lo->obj->nobj);
}
return 1;
}
/* Get the container object is in */
/* local box = o:container(); */
static int
l_obj_container(lua_State *L)
{
struct _lua_obj *lo = l_obj_check(L, 1);
if (lo && lo->obj && lo->obj->where == OBJ_CONTAINED)
(void) l_obj_push(L, lo->obj->ocontainer);
else
(void) l_obj_push(L, NULL);
return 1;
}
/* Is the object a null? */
/* local badobj = o:isnull(); */
static int
l_obj_isnull(lua_State *L)
{
struct _lua_obj *lo = l_obj_check(L, 1);
lua_pushboolean(L, !lobj_is_ok(lo));
return 1;
}
DISABLE_WARNING_UNREACHABLE_CODE
/* does object have a timer of certain type? */
/* local hastimer = o:has_timer("rot-organic"); */
static int
l_obj_timer_has(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 2) {
struct _lua_obj *lo = l_obj_check(L, 1);
short timertype = nhl_get_timertype(L, 2);
if (timer_is_obj(timertype) && lo && lo->obj) {
lua_pushboolean(L, obj_has_timer(lo->obj, timertype));
return 1;
} else {
lua_pushboolean(L, FALSE);
return 1;
}
} else
nhl_error(L, "l_obj_timer_has: Wrong args");
return 0;
}
/* peek at an object timer. return the turn when timer triggers.
returns 0 if no such timer attached to the object. */
/* local timeout = o:peek_timer("hatch-egg"); */
static int
l_obj_timer_peek(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 2) {
struct _lua_obj *lo = l_obj_check(L, 1);
short timertype = nhl_get_timertype(L, 2);
if (timer_is_obj(timertype) && lo && lo->obj) {
lua_pushinteger(L, peek_timer(timertype, obj_to_any(lo->obj)));
return 1;
} else {
lua_pushinteger(L, 0);
return 1;
}
} else
nhl_error(L, "l_obj_timer_peek: Wrong args");
/*NOTREACHED*/
return 0;
}
/* stop object timer(s). return the turn when timer triggers.
returns 0 if no such timer attached to the object.
without a timer type parameter, stops all timers for the object. */
/* local timeout = o:stop_timer("rot-organic"); */
/* o:stop_timer(); */
static int
l_obj_timer_stop(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 1) {
struct _lua_obj *lo = l_obj_check(L, 1);
if (lo && lo->obj)
obj_stop_timers(lo->obj);
return 0;
} else if (argc == 2) {
struct _lua_obj *lo = l_obj_check(L, 1);
short timertype = nhl_get_timertype(L, 2);
if (timer_is_obj(timertype) && lo && lo->obj) {
lua_pushinteger(L, stop_timer(timertype, obj_to_any(lo->obj)));
return 1;
} else {
lua_pushinteger(L, 0);
return 1;
}
} else
nhl_error(L, "l_obj_timer_stop: Wrong args");
return 0;
}
RESTORE_WARNING_UNREACHABLE_CODE
/* start an object timer. */
/* o:start_timer("hatch-egg", 10); */
static int
l_obj_timer_start(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 3) {
struct _lua_obj *lo = l_obj_check(L, 1);
short timertype = nhl_get_timertype(L, 2);
long when = luaL_checkinteger(L, 3);
if (timer_is_obj(timertype) && lo && lo->obj && when > 0) {
if (obj_has_timer(lo->obj, timertype))
stop_timer(timertype, obj_to_any(lo->obj));
start_timer(when, TIMER_OBJECT, timertype, obj_to_any(lo->obj));
}
} else
nhl_error(L, "l_obj_timer_start: Wrong args");
return 0;
}
/* bury an obj. returns true if object is gone (merged with ground),
false otherwise. */
/* local ogone = o:bury(); */
/* local ogone = o:bury(5,5); */
static int
l_obj_bury(lua_State *L)
{
int argc = lua_gettop(L);
boolean dealloced = FALSE;
struct _lua_obj *lo = l_obj_check(L, 1);
coordxy x = 0, y = 0;
if (argc == 1) {
x = lo->obj->ox;
y = lo->obj->oy;
} else if (argc == 3) {
x = (coordxy) lua_tointeger(L, 2);
y = (coordxy) lua_tointeger(L, 3);
cvt_to_abscoord(&x, &y);
} else
nhl_error(L, "l_obj_bury: Wrong args");
if (lobj_is_ok(lo) && isok(x, y)) {
lo->obj->ox = x;
lo->obj->oy = y;
(void) bury_an_obj(lo->obj, &dealloced);
}
lua_pushboolean(L, dealloced);
return 1;
}
static const struct luaL_Reg l_obj_methods[] = {
{ "new", l_obj_new_readobjnam },
{ "isnull", l_obj_isnull },
{ "at", l_obj_at },
{ "next", l_obj_nextobj },
{ "totable", l_obj_to_table },
{ "class", l_obj_objects_to_table },
{ "placeobj", l_obj_placeobj },
{ "container", l_obj_container },
{ "contents", l_obj_getcontents },
{ "addcontent", l_obj_add_to_container },
{ "has_timer", l_obj_timer_has },
{ "peek_timer", l_obj_timer_peek },
{ "stop_timer", l_obj_timer_stop },
{ "start_timer", l_obj_timer_start },
{ "bury", l_obj_bury },
{ NULL, NULL }
};
static const luaL_Reg l_obj_meta[] = {
{ "__gc", l_obj_gc },
{ NULL, NULL }
};
int
l_obj_register(lua_State *L)
{
/* Table of instance methods (e.g. an_object:isnull())
and static methods (e.g. obj.new("dagger")). */
luaL_newlib(L, l_obj_methods);
/* metatable = { __name = "obj", __gc = l_obj_gc } */
luaL_newmetatable(L, "obj");
luaL_setfuncs(L, l_obj_meta, 0);
/* metatable.__index points at the object method table. */
lua_pushvalue(L, -2);
lua_setfield(L, -2, "__index");
/* Don't let lua code mess with the real metatable.
Instead offer a fake one that only contains __gc. */
luaL_newlib(L, l_obj_meta);
lua_setfield(L, -2, "__metatable");
/* We don't need the metatable anymore. It's safe in the
Lua registry for use by luaL_setmetatable. */
lua_pop(L, 1);
/* global obj = the method table we created at the start */
lua_setglobal(L, "obj");
return 0;
}