scummvm/engines/glk/agt/object.cpp
2022-10-23 22:46:19 +02:00

1064 lines
28 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/agt/agility.h"
#include "glk/agt/interp.h"
#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
/* */
/* This module contains most of the routines that implement the */
/* "physics" of the AGT world, including the scope routines. */
/* ------------------------------------------------------------------- */
/* Functions for manipulating parse_recs */
/* ------------------------------------------------------------------- */
/* Make artificial parse record for an object */
parse_rec *make_parserec(int obj, parse_rec *rec) {
if (rec == nullptr) rec = (parse_rec *)rmalloc(sizeof(parse_rec));
rec->obj = obj;
rec->info = D_NOUN;
rec->noun = it_name(obj);
rec->adj = it_adj(obj);
rec->num = 0;
return rec;
}
/* This is used by the parser to initialize a blank parse_rec */
void tmpobj(parse_rec *objrec) {
objrec->info = D_NOUN;
objrec->num = 0;
objrec->noun = objrec->adj = 0;
objrec->obj = 0;
}
parse_rec *copy_parserec(parse_rec *rec) {
parse_rec *newrec;
if (rec == nullptr) return nullptr;
newrec = (parse_rec *)rmalloc(sizeof(parse_rec));
memcpy(newrec, rec, sizeof(parse_rec));
return newrec;
}
void free_all_parserec(void) {
rfree(actor_rec);
rfree(dobj_rec);
rfree(iobj_rec);
}
/* ------------------------------------------------------------------- */
/* Functions for doing basic manipulation of items and extracting */
/* nformation about these items */
/* (often used to blackbox the difference between nouns and creatures) */
rbool matchclass(int obj, int oclass) {
int i;
if (oclass == 0) return 0;
for (i = obj; i != oclass && i != 0; i = it_class(i));
return i == oclass;
}
/* Functions for getting bascic information about items */
static const char *it_sdesc(int item) {
if (tnoun(item)) return noun[item - first_noun].shortdesc;
if (tcreat(item)) return creature[item - first_creat].shortdesc;
if (item < 0) return dict[-item];
return nullptr;
}
rbool it_possess(int item) {
int l;
l = it_loc(item);
return (l == 1 || l == 1000);
}
rbool it_proper(int item) {
if (tcreat(item))
return (!PURE_PROPER) || creature[item - first_creat].proper;
if (tnoun(item))
return noun[item - first_noun].proper;
return 0;
}
static rbool invischeck(const char *s) {
while (rspace(*s)) s++;
return strncasecmp(s, "INVISIBLE", 9) == 0;
}
static rbool it_invisible(int item, rbool sdesc_flag) {
if (sdesc_flag)
return invischeck(it_sdesc(item));
else {
char *s;
rbool tmp;
if (it_name(item) == 0 && it_adj(item) == 0) return 1;
s = objname(item); /* Must remember to rfree s before exiting */
tmp = invischeck(s);
rfree(s);
return tmp;
}
}
static rbool it_appears_empty(int item) {
int i;
int sdesc_flag;
if (item < 0) return 1;
sdesc_flag = !player_has(item);
contloop(i, item)
if (!it_invisible(i, sdesc_flag)) return 0;
return 1;
}
/* We classify something as a weapon if it kills something. */
rbool it_isweapon(int objnum) {
int i;
creatloop(i)
if (matchclass(objnum, creature[i].weapon)) return 1;
return 0;
}
/* This used to be a macro like troom, tnoun, and tcreat, but it
got too complicated and isn't used in time-critical areas,
anyhow */
rbool it_door(int obj, word nword) {
if (aver >= AGX00) return 0; /* No doors under Magx */
if (tdoor(obj)) return 1; /* The basic door */
if (it_loc(obj) == loc + first_room) return 0;
return (nword == ext_code[wdoor]);
}
/* ------------------------------------------------------------------- */
/* Routines that manipulate the linked lists representing containment */
/* information */
static void set_contents(int p, int newval) {
if (troom(p)) room[p - first_room].contents = newval;
else if (p == 1) player_contents = newval;
else if (p == 1000) player_worn = newval;
else if (tnoun(p)) noun[p - first_noun].contents = newval;
else if (tcreat(p)) creature[p - first_creat].contents = newval;
else {
writeln("INT ERR: Invalid object heading chain.");
return;
}
}
static void set_next(int p, int newval) {
if (tnoun(p)) noun[p - first_noun].next = newval;
else if (tcreat(p)) creature[p - first_creat].next = newval;
else {
writeln("INT ERR: Invalid object in chain.");
return;
}
}
void add_object(int loc_, int item) {
int p, q;
set_next(item, 0);
if (loc_ == 0) return;
p = it_contents(loc_);
if (p == 0 || p > item) {
set_contents(loc_, item);
set_next(item, p);
} else { /* Figure out where to put the item */
do {
q = p;
p = it_next(p);
} while (p != 0 && p < item);
set_next(q, item);
set_next(item, p);
}
}
static void set_location(int item, int newloc)
/* This routine assumes item is either a noun or a creature */
{
int p, q;
p = it_loc(item);
if (p != 0) { /* Fix .next values */
q = it_contents(p);
if (q == item) set_contents(p, it_next(item));
else {
while (q != item && q != 0) {
p = q;
q = it_next(p);
}
assert(q != 0); /* This would mean the list structure was corrupted */
set_next(p, it_next(item));
}
}
/* We've unlinked it from the list at this point. */
if (tnoun(item))
noun[item - first_noun].location = newloc;
else if (tcreat(item))
creature[item - first_creat].location = newloc;
add_object(newloc, item);
}
void it_reposition(int item, int newloc, rbool save_pos) {
integer i;
if (tnoun(item)) {
if (player_has(item)) totwt -= noun[item - first_noun].weight;
if (it_loc(item) == 1) totsize -= noun[item - first_noun].size;
/* Set position to NULL */
if (!save_pos) {
noun[item - first_noun].pos_prep = 0;
noun[item - first_noun].pos_name = 0;
noun[item - first_noun].nearby_noun = 0;
noun[item - first_noun].position = nullptr;
#if 0 /* I think this was wrong, so I'm commenting it out. */
noun[item - first_noun].initdesc = 0;
#endif
}
set_location(item, newloc);
if (player_has(item)) {
totwt += noun[item - first_noun].weight;
if (noun[item - first_noun].win)
winflag = 1;
}
if (it_loc(item) == 1) /* only things you are carrying directly count vs.
size limit. */
totsize += noun[item - first_noun].size;
} else if (tcreat(item)) {
if (newloc == 0) {
creature[item - first_creat].timecounter = 0; /* Reset attack counter */
creature[item - first_creat].counter = 0;
}
set_location(item, newloc);
}
nounloop(i)
if (noun[i].nearby_noun == item) {
noun[i].nearby_noun = 0;
noun[i].pos_prep = 0;
noun[i].pos_name = 0;
noun[i].position = nullptr;
}
}
/* ------------------------------------------------------------------- */
/* Routines to deal with size and weight */
static long contsize(integer obj) {
int i;
long net;
net = 0;
contloop(i, obj) {
if (tnoun(i))
net += noun[i - first_noun].size;
if (aver < AGX00) /* Under Magx, size isn't recursive */
net += contsize(i);
}
return net;
}
static long contweight(integer obj) {
int i;
long net;
net = 0;
contloop(i, obj) {
if (tnoun(i))
net += noun[i - first_noun].weight;
net += contweight(i);
}
return net;
}
rbool is_within(integer obj1, integer obj2, rbool stop_if_closed)
/* True if obj1 is contained in obj2 */
{
int i;
long cnt;
for (i = obj1, cnt = 0;
i != obj2 && i >= maxroom && i != 1000 && cnt < 40000L;
cnt++) {
i = it_loc(i);
if (stop_if_closed && !it_open(i)) break;
}
if (cnt >= 40000L) {
/* writeln("GAME ERROR: Loop in object tree.");*/
return 0;
}
if (i == obj2) return 1;
return 0;
}
int check_fit(int obj1, int obj2)
/* Does obj1 fit inside obj2? Return one of the FIT_... values
defined in exec.h */
{
int size, weight;
long net;
assert(tnoun(obj1)); /* This should have been checked earlier */
if (obj2 == 1000) obj2 = 1;
if (obj2 == 1) size = weight = 100;
else {
assert(tnoun(obj2)); /* check_fit shouldn't be called otherwise */
size = noun[obj2 - first_noun].size;
weight = noun[obj2 - first_noun].weight;
}
/* Weight */
if (obj2 == 1 || (aver > AGTME15 && aver < AGX00)) {
/* Pre-1.56 interpreters forgot to check this;
Magx deliberatly *doesn't* check this */
net = noun[obj1 - first_noun].weight;
if (aver >= AGX00) net += contweight(obj1);
if (net > weight) return FIT_WEIGHT;
if (obj2 == 1) {
if (is_within(obj1, 1, 0) || is_within(obj1, 1000, 0)) net = 0;
net += contweight(1);
if (aver >= AGX00)
net += contweight(1000);
if (!PURE_SIZE) net = 0;
} else {
if (is_within(obj1, obj2, 0)) net = 0; /* Avoid double-counting */
net += contweight(obj2); /* Net size of contents of obj2 */
}
if (net > weight) return FIT_NETWEIGHT;
}
net = noun[obj1 - first_noun].size;
if (net > size) return FIT_SIZE;
if (obj2 == 1 && !PURE_SIZE) return FIT_OK;
if (obj2 == 1 || aver > AGTME15) {
/* Pre-ME/1.56 interpreters didn't check this except for the player's
inventory */
if (it_loc(obj1) == obj2
|| (aver < AGX00 && is_within(obj1, obj2, 0)))
net = 0; /* Avoid double-counting */
net += contsize(obj2); /* Net size of contents of obj2 */
if (net > size) return FIT_NETSIZE;
}
return FIT_OK;
}
/* ------------------------------------------------------------------- */
/* Scope and visibility routines */
integer it_room(int item) {
int tmploc;
long cnt;
cnt = 0;
while (!troom(item)) {
tmploc = item;
if (item == 0) return 0;
if (item == 1 || item == 1000) item = loc;
else item = it_loc(item);
if (item == tmploc || ++cnt >= 40000L) {
/* writeln("GAME ERROR: Loop in object tree."); */
return 0;
}
}
return item;
}
rbool player_has(int item) {
return is_within(item, 1, 0) || is_within(item, 1000, 0);
}
rbool in_scope(int item)
/* strictly speaking, visible actually checks scope; this routine
determines if an object would be in scope if there were no light
problems. */
{
int curloc;
int tmp;
long cnt;
if (it_isglobal(item)) return 1; /* Global objects always in scope. */
/* Flag objects in scope if their associated flag is set. */
tmp = it_flagnum(item);
if (tmp &&
(room[loc].flag_noun_bits & (1L << (tmp - 1)))) return 1;
curloc = it_loc(item); /* Should work for nouns or creatures */
cnt = 0;
while (curloc > maxroom && curloc != 1000 && it_open(curloc)) {
int tmploc;
tmploc = it_loc(curloc);
if (tmploc == curloc || ++cnt >= 40000L) {
/* writeln("GAME ERROR: Loop in the object tree."); */
return 0;
} else curloc = tmploc;
}
if (curloc == 1 || curloc == 1000 || curloc == loc + first_room) return 1;
else return 0;
}
static int good_light(int obj, int roomlight, rbool active)
/* obj is a noun number */
/* If active is false, we don't care if the light is actually turned
on is the valid light */
{
if (roomlight == 1 && !noun[obj].light)
return 0; /* obj is not a light source */
if (roomlight > 1) {
if (!matchclass(first_noun + obj, roomlight))
return 0; /* Not the correct light */
else return 1; /* The correct light _always_ illuminates the room */
}
if (!active) return 1;
/* Now need to determine if obj is actually providing light */
if (!noun[obj].on)
return 0; /* Light source is off or extinguished */
return 1;
}
int lightcheck(int parent, int roomlight, rbool active)
/* This checks to see if anything contained in parent is a valid
room light */
/* active=1 means that we only want active lights;
active=0 indicates that extinguished light sources are okay. */
{
int i;
contloop(i, parent) {
if (tnoun(i) && good_light(i - first_noun, roomlight, active)) return 1;
if (it_open(i) && lightcheck(i, roomlight, active))
return 1; /* Check children */
}
return 0;
/*
nounloop(i)
if (good_light(i,room[loc].light) && in_scope(i+first_noun))
return 1;
return 0;*/
}
rbool islit(void) {
if (room[loc].light == 0) return 1;
if (lightcheck(loc + first_room, room[loc].light, 1)) return 1;
if (lightcheck(1, room[loc].light, 1)) return 1; /* Player carried light */
if (lightcheck(1000, room[loc].light, 1)) return 1; /* Worn light */
return 0;
}
/* Is item visible to player? */
/* visible only works for "normal" items; if the object could
be a virtual object (i.e. with negative item number), then use
gen_visible() below */
rbool visible(int item) {
assert(item >= 0);
if (islit())
return in_scope(item);
else
return player_has(item);
}
rbool genvisible(parse_rec *dobj_) {
int i;
if (dobj_->obj > 0) return visible(dobj_->obj);
if (dobj_->info == D_INTERN) {
if (dobj_->obj != -ext_code[wdoor]) return 1;
return islit(); /* If item is a is a door */
}
if (dobj_->info == D_GLOBAL || dobj_->info == D_NUM) return 1;
if (dobj_->info == D_FLAG) {
for (i = 0; i < MAX_FLAG_NOUN; i++) /* Flag nouns */
if (flag_noun[i] != 0 && dobj_->obj == -flag_noun[i]
&& (room[loc].flag_noun_bits & (1L << i)) != 0)
return 1;
return 0;
}
if (dobj_->info == D_PIX) {
for (i = 0; i < MAX_PIX; i++) /* PIX names */
if (pix_name[i] != 0 && dobj_->obj == -pix_name[i] &&
(room[loc].PIX_bits & (1L << i)) != 0)
return 1;
return 0;
}
fatal("INTERNAL ERROR: Invalid gen_visible type.");
return 0;
}
/* Need to find a noun related to w */
/* If there is an object with name w in scope, it returns 0
(since the object will have already been added to the menu).
if there are none, it returns the first object with name w. */
static integer find_related(word w) {
int i;
int item;
if (w == 0) return 0;
item = 0;
nounloop(i)
if (noun[i].name == w) {
if (visible(i + first_noun)) return i + first_noun;
else if (item == 0) item = i + first_noun;
}
creatloop(i)
if (creature[i].name == w) {
if (visible(i + first_creat)) return i + first_creat;
else if (item == 0) item = i + first_creat;
}
return item;
}
static void add_to_scope(integer item) {
integer i;
if (tnoun(item)) {
noun[item - first_noun].scope = 1;
i = find_related(noun[item - first_noun].related_name);
if (i != 0) {
if (tnoun(i)) noun[i - first_noun].scope = 1;
else if (tcreat(i)) creature[i - first_creat].scope = 1;
}
} else if (tcreat(item)) creature[item - first_creat].scope = 1;
if (item == 1 || item == 1000 || troom(item) || it_open(item))
contloop(i, item)
add_to_scope(i);
}
void compute_scope(void) {
int i;
uint32 rflag;
nounloop(i) noun[i].scope = 0;
creatloop(i) creature[i].scope = 0;
add_to_scope(1);
add_to_scope(1000);
add_to_scope(loc + first_room);
rflag = room[loc].flag_noun_bits;
nounloop(i)
if (noun[i].isglobal ||
(noun[i].flagnum && (rflag & (1L << (noun[i].flagnum - 1)))))
add_to_scope(i + first_noun);
creatloop(i)
if (creature[i].isglobal ||
(creature[i].flagnum && (rflag & (1L << (creature[i].flagnum - 1)))))
add_to_scope(i + first_creat);
}
void compute_seen(void) {
int i;
compute_scope();
nounloop(i)
noun[i].seen = noun[i].seen || noun[i].scope;
creatloop(i)
creature[i].seen = creature[i].seen || creature[i].scope;
}
/*---------------------------------------------------------------------*/
/* Routines to compute the score */
void recompute_score(void) {
int obj;
tscore -= objscore;
objscore = 0;
nounloop(obj)
if (noun[obj].points && !noun[obj].unused &&
(visible(obj + first_noun)
|| is_within(obj + first_noun, treas_room, 0)))
objscore += noun[obj].points;
creatloop(obj)
if (!creature[obj].unused && creature[obj].points
&& visible(obj + first_creat))
objscore += creature[obj].points;
tscore += objscore;
}
/*---------------------------------------------------------------------*/
/* Menu Noun section: routines to get a list of 'relevant' nouns for */
/* the menuing system. They're here because they belong next to the */
/* scope routines, above */
static int *nlist, nleng; /* These are really local variables */
static void add_mnoun(int n) {
nlist = (int *)rrealloc(nlist, (nleng + 2) * sizeof(int));
nlist[nleng] = n;
nlist[++nleng] = 0;
}
/* This adds mitem and everything it contains */
static void add_mitem(int item) {
integer i;
if (tnoun(item) || tcreat(item)) add_mnoun(item);
if (item == 1 || item == 1000 || troom(item) || it_open(item))
contloop(i, item)
add_mitem(i);
/* Need to check related nouns */
if (tnoun(item)) {
i = find_related(noun[item - first_noun].related_name);
if (i != 0) add_mnoun(i);
}
}
static word getword(int item, int n)
/* Gets nth word associated with item */
{
if (n == 1) {
if (item < 0) return -item;
else if (tnoun(item)) return noun[item - first_noun].adj;
else if (tcreat(item)) return creature[item - first_creat].adj;
}
if (n == 2)
if (tnoun(item) || tcreat(item)) return it_name(item);
return 0;
}
static int cmp_nouns(const void *a, const void *b)
/* *a, *b are object numbers; need alphabetic sort.*/
{
word wa, wb;
int cmp;
wa = getword(*((const int *)a), 1);
wb = getword(*((const int *)b), 1);
cmp = strcmp(dict[wa], dict[wb]);
if (cmp != 0) return cmp;
wa = getword(*(const int *)a, 2);
wb = getword(*(const int *)b, 2);
return strcmp(dict[wa], dict[wb]);
}
int *get_nouns(void)
/* This returns the list of all objects that should show up on the menu */
/* The list should be 0 terminated and needs to be sorted */
{
int i;
uint32 rflag;
nlist = (int *)rmalloc(sizeof(int));
nlist[0] = 0;
nleng = 0;
for (i = 0; i < numglobal; i++)
add_mnoun(-globalnoun[i]);
for (i = 0; i < MAX_FLAG_NOUN; i++)
if (room[loc].flag_noun_bits & (1L << i))
add_mnoun(-flag_noun[i]);
add_mitem(1);
add_mitem(1000);
add_mitem(loc + first_room);
rflag = room[loc].flag_noun_bits;
nounloop(i)
if (noun[i].isglobal ||
(noun[i].flagnum && (rflag & (1L << (noun[i].flagnum - 1)))))
add_mitem(i + first_noun);
creatloop(i)
if (creature[i].isglobal ||
(creature[i].flagnum && (rflag & (1L << (creature[i].flagnum - 1)))))
add_mitem(i + first_creat);
qsort(nlist, nleng, sizeof(int), cmp_nouns);
return nlist;
}
/*---------------------------------------------------------------------*/
/* goto_room, the basic primitive used to move the player around */
void goto_room(int newroom) {
int i, j;
/* Move group members in old room to new room */
safecontloop(i, j, loc + first_room)
if (it_group(i))
it_move(i, newroom + first_room);
#if 0 /* -- this has been moved to v_go*/
if (loc != newroom)
oldloc = loc; /* Save old location for NO_BLOCK_HOSTILE purposes */
#endif
loc = newroom;
if (loc != newroom) oldloc = loc; /* No backtracking unless v_go allows it */
if (!room[loc].seen) {
room[loc].seen = 1;
tscore += room[loc].points;
first_visit_flag = 1;
room_firstdesc = 1;
v_look();
} else {
first_visit_flag = 0;
if (verboseflag)
v_look(); /* But see v_go() for a special case involving SPECIAL */
room_firstdesc = 0;
}
if (room[loc].end) endflag = 1;
if (room[loc].win) winflag = 1;
if (room[loc].killplayer) deadflag = 1;
do_autoverb = 1;
set_statline();
}
static void rundesc(int i, descr_ptr dp_[], const char *shortdesc, int msgid) {
if (dp_[i].size > 0)
print_descr(dp_[i], 1);
else if (!invischeck(shortdesc))
raw_lineout(shortdesc, 1, MSG_DESC, nullptr);
else sysmsg(msgid, "$You$ see nothing unexpected.");
}
void it_describe(int dobj_) {
if (troom(dobj_))
print_descr(room_ptr[dobj_ - first_room], 1);
else if (tnoun(dobj_))
rundesc(dobj_ - first_noun, noun_ptr, noun[dobj_ - first_noun].shortdesc, 194);
else if (tcreat(dobj_))
rundesc(dobj_ - first_creat, creat_ptr,
creature[dobj_ - first_creat].shortdesc, 195);
else if (dobj_ == -ext_code[wdoor]) { /* i.e. DOOR */
if (room[loc].locked_door)
sysmsg(21, "$You$ see a locked door.");
else sysmsg(22, "$You$ see a perfectly normal doorway.");
} else sysmsg(194, "$You$ see nothing unexpected.");
if (tnoun(dobj_) &&
(noun[dobj_ - first_noun].open || !noun[dobj_ - first_noun].closable) &&
!it_appears_empty(dobj_)) {
sysmsg(228, "Which contains:");
print_contents(dobj_, 1);
}
}
static char *build_position(word prep_, word name)
/* Return the malloc'd string '$prep_$ the $name$' */
{
int leng;
char *s;
leng = strlen(dict[prep_]) + strlen(dict[name]) + 6; /* includes final '\0' */
s = (char *)rmalloc(leng * sizeof(char));
Common::strcpy_s(s, leng, dict[prep_]);
Common::strcat_s(s, leng, " the ");
Common::strcat_s(s, leng, dict[name]);
assert((int)strlen(s) + 1 == leng);
return s;
}
static int print_obj(int obj, int ind_lev)
/* Prints out s on a line of its own if obj isn't INVISIBLE */
/* parent_descr is true if the parent has been described, false
otherwise (say if the parent is invisible). */
/* ind_lev=indentation level */
/* Return 1 if we actually printed something, 0 if obj is invisible */
{
int sdesc_flag; /* True if should print out as sdesc rather than
as adjective-noun */
int i, retval, parent;
const char *s;
char *t, *s0, *posstr;
if (tcreat(obj) && creature[obj - first_creat].initdesc != 0)
return 0; /* Don't print normal description if printing initdesc */
s0 = nullptr;
sdesc_flag = !player_has(obj); /* This should be tested. */
sdesc_flag = sdesc_flag || (ind_lev > 1); /* It seems that AGT uses the
sdesc for describing items
contained in other items */
/* Some code below relies on this, as well */
if (sdesc_flag)
s = it_sdesc(obj);
else if (it_name(obj) == 0 && it_adj(obj) == 0) /* Invisible */
return 0;
else {
s0 = objname(obj); /* Must remember to rfree s before exiting */
if (aver >= AGTME10) {
for (t = s0; isspace(*t); t++); /* Skip over initial whitespace... */
*t = toupper(*t); /* ...and upcase the first non-space character */
}
s = s0;
}
retval = 0;
if (sdesc_flag && tnoun(obj) && noun[obj - first_noun].initdesc != 0) {
retval = 1;
msgout(noun[obj - first_noun].initdesc, 1);
noun[obj - first_noun].initdesc = 0; /* Only show it once */
} else if (!invischeck(s)) {
retval = 1; /* We're actually going to print something */
for (i = 0; i < ind_lev; i++) writestr(" ");
raw_lineout(s, sdesc_flag, MSG_DESC, nullptr);
/* Do $word$ formatting if sdesc */
/* Need to output container */
parent = it_loc(obj);
if (tnoun(obj) && noun[obj - first_noun].pos_prep != 0) {
writestr(" (");
if (noun[obj - first_noun].pos_prep == -1)
writestr(noun[obj - first_noun].position);
else {
posstr = build_position(noun[obj - first_noun].pos_prep,
noun[obj - first_noun].pos_name);
writestr(posstr);
rfree(posstr);
}
writestr(")");
} else if (parent >= first_noun && it_invisible(parent, sdesc_flag)
&& (it_name(parent) != 0 || it_adj(parent) != 0)) {
/* If the parent object *isn't* invisible, we will already have
printed it out */
/* This also relies on sdesc_flag being the same for parent
and child objects */
if (parent >= first_creat && parent <= maxcreat)
sysmsg(221, "(Carried by");
else
sysmsg(222, " (Inside");
t = objname(parent);
writestr(t);
rfree(t);
sysmsg(223, ")");
}
if (tnoun(obj) && noun[obj - first_noun].light && noun[obj - first_noun].on
&& PURE_OBJ_DESC)
sysmsg(220, " (Providing light)");
writeln("");
}
if (!sdesc_flag)
rfree(s0);
return retval;
}
int print_contents(int obj, int ind_lev)
/* obj=object to list contents of; ind_lev=indentation level */
/* Returns number of objects contained in obj that were listed */
{
int i, cnt;
cnt = 0;
contloop(i, obj) {
if (print_obj(i, ind_lev)) cnt++;
if (it_open(i)) print_contents(i, ind_lev + 1);
}
return cnt;
}
/* ------------------------------------------------------------------- */
/* Routines for directly getting and setting object properties and */
/* attributes. */
#define NUM_WPROP 6
#define NUM_WATTR 6
static void *compute_addr(int obj, int prop, const prop_struct *ptable) {
int ofs;
void *base;
if (DIAG)
rprintf("(Accessing %s->%s)\n", dict[it_name(obj)], ptable[prop].name);
if (troom(obj)) {
base = (void *)(&room[obj - first_room]);
ofs = ptable[prop].room;
} else if (tnoun(obj)) {
base = (void *)(&noun[obj - first_noun]);
ofs = ptable[prop].noun;
} else if (tcreat(obj)) {
base = (void *)(&creature[obj - first_creat]);
ofs = ptable[prop].creature;
} else return nullptr;
if (ofs == -1) /* Field doesn't exist in this type of object */
return nullptr;
return (void *)(((char *)base) + ofs);
}
long getprop(int obj, int prop) {
integer *paddr;
if (prop >= NUM_PROP) return 0;
paddr = (integer *)compute_addr(obj, prop, proplist);
if (paddr == nullptr) return 0;
return *paddr;
}
void setprop(int obj, int prop, long val) {
integer *paddr;
if (prop >= NUM_WPROP) {
writeln("GAME ERROR: Read-only or non-existent property.");
return;
}
paddr = (integer *)compute_addr(obj, prop, proplist);
if (paddr == nullptr) {
writeln("GAME ERROR: Property-object mismatch.");
return;
}
*paddr = val;
}
rbool getattr(int obj, int prop) {
rbool *paddr;
if (prop >= NUM_ATTR) return 0;
paddr = (rbool *)compute_addr(obj, prop, attrlist);
if (paddr == nullptr) return 0;
return *paddr;
}
void setattr(int obj, int prop, rbool val) {
rbool *paddr;
if (prop >= NUM_WATTR && prop != 24) {
writeln("GAME ERROR: Read-only or non-existent attribute.");
return;
}
paddr = (rbool *)compute_addr(obj, prop, attrlist);
if (paddr == nullptr) {
writeln("GAME ERROR: Property-object mismatch.");
return;
}
*paddr = val;
}
/* ------------------------------------------------------------------- */
/* This sets up the creat_fix[] array, which is used to determine the */
/* scan ranges for addressed creatures in cases where there is more */
/* than one creature of the same name */
void init_creat_fix(void) {
int i, j;
creat_fix = (integer *)rmalloc(rangefix(maxcreat - first_creat + 1) * sizeof(integer));
for (i = 0; i < maxcreat - first_creat + 1; i++)
creat_fix[i] = i + first_creat;
for (i = 0; i < maxcreat - first_creat + 1; i++)
if (creat_fix[i] == i + first_creat) /* That is, it hasn't changed. */
for (j = i + 1; j < maxcreat - first_creat + 1; j++)
if (creature[i].name == creature[j].name &&
creature[i].adj == creature[j].adj)
creat_fix[j] = i + first_creat; /* That is, j --> i */
}
void free_creat_fix(void) {
rfree(creat_fix);
}
/* ------------------------------------------------------------------- */
#ifndef IT_MACRO
int it_contents(integer obj) {
if (tnoun(obj)) return noun[obj - first_noun].contents;
else if (troom(obj)) return room[obj - first_room].contents;
else if (tcreat(obj)) return creature[obj - first_creat].contents;
else if (obj == 1) return player_contents;
else if (obj == 1000) return player_worn;
else return 0;
}
rbool it_lockable(integer obj, word nword) {
if (tnoun(obj)) return noun[obj - first_noun].lockable;
else if (it_door(obj, nword)) return 1;
else return 0;
}
rbool it_locked(integer obj, word nword) {
if (tnoun(obj)) return noun[obj - first_noun].locked;
else if (it_door(obj, nword) && room[loc].locked_door) return 1;
else return 0;
}
#endif
} // End of namespace AGT
} // End of namespace Glk