diff --git a/include/obj.h b/include/obj.h index 3f8784309..b31181394 100644 --- a/include/obj.h +++ b/include/obj.h @@ -66,7 +66,8 @@ struct obj { #define OBJ_MIGRATING 5 /* object sent off to another level */ #define OBJ_BURIED 6 /* object buried */ #define OBJ_ONBILL 7 /* object on shk bill */ -#define NOBJ_STATES 8 +#define OBJ_LUAFREE 8 /* object has been dealloc'd, but is ref'd by lua */ +#define NOBJ_STATES 9 xchar timed; /* # of fuses (timers) attached to this obj */ Bitfield(cursed, 1); diff --git a/include/patchlevel.h b/include/patchlevel.h index 0a4629457..ba9d75a5e 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -14,7 +14,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 7 +#define EDITLEVEL 8 #define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2019" #define COPYRIGHT_BANNER_B \ diff --git a/src/mkobj.c b/src/mkobj.c index dbbd9c974..53eacdf1c 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1969,6 +1969,7 @@ struct monst *mtmp; * OBJ_MIGRATING migrating chain * OBJ_BURIED level.buriedobjs chain * OBJ_ONBILL on g.billobjs chain + * OBJ_LUAFREE obj is dealloc'd from core, but still used by lua */ void obj_extract_self(obj) @@ -1976,6 +1977,7 @@ struct obj *obj; { switch (obj->where) { case OBJ_FREE: + case OBJ_LUAFREE: break; case OBJ_FLOOR: remove_object(obj); @@ -2161,7 +2163,7 @@ void dealloc_obj(obj) struct obj *obj; { - if (obj->where != OBJ_FREE) + if (obj->where != OBJ_FREE && obj->where != OBJ_LUAFREE) panic("dealloc_obj: obj not free"); if (obj->nobj) panic("dealloc_obj with nobj"); @@ -2192,8 +2194,11 @@ struct obj *obj; if (obj->oextra) dealloc_oextra(obj); - if (obj->lua_ref_cnt) - return; /* obj is referenced from a lua script, let lua gc free it */ + if (obj->lua_ref_cnt) { + /* obj is referenced from a lua script, let lua gc free it */ + obj->where = OBJ_LUAFREE; + return; + } free((genericptr_t) obj); } @@ -2430,7 +2435,8 @@ const char *mesg; static const char *obj_state_names[NOBJ_STATES] = { "free", "floor", "contained", "invent", "minvent", "migrating", - "buried", "onbill" }; + "buried", "onbill", + "luafree" }; static const char * where_name(obj) diff --git a/src/nhlobj.c b/src/nhlobj.c index 1223603b8..acaf6366f 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -22,6 +22,8 @@ struct _lua_obj { struct obj *obj; }; +#define lobj_is_ok(lo) ((lo) && (lo)->obj && (lo)->obj->where != OBJ_LUAFREE) + struct _lua_obj * l_obj_check(L, index) lua_State *L; @@ -46,7 +48,8 @@ lua_State *L; if (lo->obj->lua_ref_cnt > 0) lo->obj->lua_ref_cnt--; /* free-floating objects with no other refs are deallocated. */ - if (lo->obj->where == OBJ_FREE && !lo->obj->lua_ref_cnt) { + if (!lo->obj->lua_ref_cnt + && (lo->obj->where == OBJ_FREE || lo->obj->where == OBJ_LUAFREE)) { if (Has_contents(lo->obj)) { struct obj *otmp; while ((otmp = lo->obj->cobj) != 0) { @@ -98,13 +101,12 @@ lua_State *L; if (!obj) nhl_error(L, "l_obj_getcontents: no obj"); - if (!obj->cobj) - nhl_error(L, "l_obj_getcontents: no cobj"); (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")); */ @@ -115,19 +117,21 @@ 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 (!lo->obj || !lobox->obj) - nhl_error(L, "l_obj_add_to_container: no obj"); + if (!lobj_is_ok(lo) || !lobj_is_ok(lobox)) + return 0; - if (!Is_container(lobox->obj)) - nhl_error(L, "l_obj_add_to_container: not a container"); + refs = lo->obj->lua_ref_cnt; + obj_extract_self(lo->obj); otmp = add_to_container(lobox->obj, lo->obj); - /* was lo->obj merged? FIXME: causes problems if both lo->obj and - the one it merged with are handled by lua. Use lo->state? */ - if (otmp != lo->obj) + /* was lo->obj merged? */ + if (otmp != lo->obj) { + lo->obj->lua_ref_cnt += refs; lo->obj = otmp; + } return 0; } @@ -135,6 +139,7 @@ lua_State *L; /* 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(L) lua_State *L; @@ -217,7 +222,7 @@ lua_State *L; lua_newtable(L); - if (!obj) { + if (!obj || obj->where == OBJ_LUAFREE) { nhl_add_table_entry_int(L, "NO_OBJ", 1); return 1; } @@ -352,20 +357,19 @@ lua_State *L; { int argc = lua_gettop(L); struct _lua_obj *lo = l_obj_check(L, 1); + int x, y; if (argc != 3) nhl_error(L, "l_obj_placeobj: Wrong args"); - if (lo && lo->obj) { - int x, y; - - x = (int) luaL_checkinteger(L, 2); - y = (int) luaL_checkinteger(L, 3); - lua_pop(L, 3); + x = (int) luaL_checkinteger(L, 2); + y = (int) luaL_checkinteger(L, 3); + lua_pop(L, 3); + if (lobj_is_ok(lo)) { + obj_extract_self(lo->obj); place_object(lo->obj, x, y); - } else - nhl_error(L, "l_obj_placeobj: Wrong args"); + } return 0; } @@ -393,11 +397,10 @@ lua_State *L; { struct _lua_obj *lo = l_obj_check(L, 1); - if (lo && lo->obj && lo->obj->where == OBJ_CONTAINED) { + if (lo && lo->obj && lo->obj->where == OBJ_CONTAINED) (void) l_obj_push(L, lo->obj->ocontainer); - return 1; - } - (void) l_obj_push(L, NULL); + else + (void) l_obj_push(L, NULL); return 1; } @@ -409,7 +412,7 @@ lua_State *L; { struct _lua_obj *lo = l_obj_check(L, 1); - lua_pushboolean(L, lo && lo->obj); + lua_pushboolean(L, lobj_is_ok(lo)); return 1; } diff --git a/test/test_obj.lua b/test/test_obj.lua index a72232465..d56a5ba55 100644 --- a/test/test_obj.lua +++ b/test/test_obj.lua @@ -64,3 +64,21 @@ end if (oc3.class ~= "*") then error("object class is not *, part 3"); end + +local oc4 = o:class(); +if (oc4.name ~= "rock") then + error("object class is not rock, part 4"); +end +if (oc4.class ~= "*") then + error("object class is not *, part 4"); +end + + +-- placing obj into container even when obj is somewhere else already +local o5 = obj.new("dagger"); +o5:placeobj(u.ux, u.uy); +box:addcontent(o5); + + +local o6 = obj.new("statue"); +o6:addcontent(obj.new("spellbook"));