1
/* object.c-- functions to manipulate rooms, nouns, and creatures */
2
/* Copyright (C) 1996-1999,2001 Robert Masenten */
3
/* This program may be redistributed under the terms of the
4
GNU General Public License, version 2; see agility.h for details. */
6
/* This is part of the source for AGiliTy */
8
/* This module contains most of the routines that implement the */
9
/* "physics" of the AGT world, including the scope routines. */
19
/* ------------------------------------------------------------------- */
20
/* Functions for manipulating parse_recs */
21
/* ------------------------------------------------------------------- */
23
/* Make artificial parse record for an object */
24
parse_rec *make_parserec(int obj,parse_rec *rec)
26
if (rec==NULL) rec=rmalloc(sizeof(parse_rec));
29
rec->noun=it_name(obj);
35
/* This is used by the parser to initialize a blank parse_rec */
36
void tmpobj(parse_rec *objrec)
40
objrec->noun=objrec->adj=0;
44
parse_rec *copy_parserec(parse_rec *rec)
47
if (rec==NULL) return NULL;
48
newrec=rmalloc(sizeof(parse_rec));
49
memcpy(newrec,rec,sizeof(parse_rec));
53
void free_all_parserec(void)
55
rfree(actor_rec);rfree(dobj_rec);rfree(iobj_rec);
59
/* ------------------------------------------------------------------- */
60
/* Functions for doing basic manipulation of items and extracting */
61
/* nformation about these items */
62
/* (often used to blackbox the difference between nouns and creatures) */
66
rbool matchclass(int obj, int oclass)
69
if (oclass==0) return 0;
70
for(i=obj;i!=oclass && i!=0;i=it_class(i));
75
/* Functions for getting bascic information about items */
77
static const char *it_sdesc(int item)
79
if (tnoun(item)) return noun[item-first_noun].shortdesc;
80
if (tcreat(item)) return creature[item-first_creat].shortdesc;
81
if (item<0) return dict[-item];
85
rbool it_possess(int item)
90
return (l==1 || l==1000);
93
rbool it_proper(int item)
96
return (!PURE_PROPER) || creature[item-first_creat].proper;
98
return noun[item-first_noun].proper;
103
static rbool invischeck(const char *s)
105
while(rspace(*s)) s++;
106
return strncasecmp(s,"INVISIBLE",9)==0;
110
static rbool it_invisible(int item, rbool sdesc_flag)
113
return invischeck(it_sdesc(item));
118
if (it_name(item)==0 && it_adj(item)==0) return 1;
119
s=objname(item); /* Must remember to rfree s before exiting */
127
static rbool it_appears_empty(int item)
132
if (item<0) return 1;
133
sdesc_flag=!player_has(item);
136
if (!it_invisible(i,sdesc_flag)) return 0;
140
/* We classify something as a weapon if it kills something. */
141
rbool it_isweapon(int objnum)
146
if (matchclass(objnum,creature[i].weapon)) return 1;
150
/* This used to be a macro like troom, tnoun, and tcreat, but it
151
got too complicated and isn't used in time-critical areas,
154
rbool it_door(int obj,word nword)
156
if (aver>=AGX00) return 0; /* No doors under Magx */
157
if (tdoor(obj)) return 1; /* The basic door */
158
if (it_loc(obj)==loc+first_room) return 0;
159
return (nword==ext_code[wdoor]);
163
/* ------------------------------------------------------------------- */
164
/* Routines that manipulate the linked lists representing containment */
167
static void set_contents(int p, int newval)
169
if (troom(p)) room[p-first_room].contents=newval;
170
else if (p==1) player_contents=newval;
171
else if (p==1000) player_worn=newval;
172
else if (tnoun(p)) noun[p-first_noun].contents=newval;
173
else if (tcreat(p)) creature[p-first_creat].contents=newval;
174
else {writeln("INT ERR: Invalid object heading chain.");return;}
177
static void set_next(int p, int newval)
179
if (tnoun(p)) noun[p-first_noun].next=newval;
180
else if (tcreat(p)) creature[p-first_creat].next=newval;
181
else {writeln("INT ERR: Invalid object in chain.");return;}
184
void add_object(int loc,int item)
193
if (p==0 || p>item) {
194
set_contents(loc,item);
196
} else { /* Figure out where to put the item */
200
} while (p!=0 && p<item);
208
static void set_location(int item, int newloc)
209
/* This routine assumes item is either a noun or a creature */
214
if (p!=0) { /* Fix .next values */
216
if (q==item) set_contents(p,it_next(item));
218
while(q!=item && q!=0) {p=q;q=it_next(p);}
219
assert(q!=0); /* This would mean the list structure was corrupted */
220
set_next(p,it_next(item));
223
/* We've unlinked it from the list at this point. */
226
noun[item-first_noun].location=newloc;
227
else if (tcreat(item))
228
creature[item-first_creat].location=newloc;
230
add_object(newloc,item);
234
void it_reposition(int item,int newloc,rbool save_pos)
239
if (player_has(item)) totwt-=noun[item-first_noun].weight;
240
if (it_loc(item)==1) totsize-=noun[item-first_noun].size;
242
/* Set position to NULL */
244
noun[item-first_noun].pos_prep=0;
245
noun[item-first_noun].pos_name=0;
246
noun[item-first_noun].nearby_noun=0;
247
noun[item-first_noun].position=NULL;
248
#if 0 /* I think this was wrong, so I'm commenting it out. */
249
noun[item-first_noun].initdesc=0;
253
set_location(item,newloc);
255
if (player_has(item))
257
totwt+=noun[item-first_noun].weight;
258
if (noun[item-first_noun].win)
261
if (it_loc(item)==1) /* only things you are carrying directly count vs.
263
totsize+=noun[item-first_noun].size;
265
else if (tcreat(item)) {
267
creature[item-first_creat].timecounter=0; /* Reset attack counter */
268
creature[item-first_creat].counter=0;
270
set_location(item,newloc);
274
if (noun[i].nearby_noun==item) {
275
noun[i].nearby_noun=0;
278
noun[i].position=NULL;
284
/* ------------------------------------------------------------------- */
285
/* Routines to deal with size and weight */
287
static long contsize(integer obj)
295
net+=noun[i-first_noun].size;
296
if (aver<AGX00) /* Under Magx, size isn't recursive */
302
static long contweight(integer obj)
310
net+=noun[i-first_noun].weight;
316
rbool is_within(integer obj1, integer obj2, rbool stop_if_closed)
317
/* True if obj1 is contained in obj2 */
323
i!=obj2 && i>=maxroom && i!=1000 && cnt<40000L;
326
if (stop_if_closed && !it_open(i)) break;
329
/* writeln("GAME ERROR: Loop in object tree.");*/
332
if (i==obj2) return 1;
338
int check_fit(int obj1, int obj2)
339
/* Does obj1 fit inside obj2? Return one of the FIT_... values
345
assert(tnoun(obj1)); /* This should have been checked earlier */
346
if (obj2==1000) obj2=1;
348
if (obj2==1) size=weight=100;
350
assert(tnoun(obj2)); /* check_fit shouldn't be called otherwise */
351
size=noun[obj2-first_noun].size;
352
weight=noun[obj2-first_noun].weight;
356
if (obj2==1 || (aver>AGTME15 && aver<AGX00) ) {
357
/* Pre-1.56 interpreters forgot to check this;
358
Magx deliberatly *doesn't* check this */
360
net=noun[obj1-first_noun].weight;
361
if (aver>=AGX00) net+=contweight(obj1);
362
if (net>weight) return FIT_WEIGHT;
365
if (is_within(obj1,1,0) || is_within(obj1,1000,0)) net=0;
368
net+=contweight(1000);
369
if (!PURE_SIZE) net=0;
371
if (is_within(obj1,obj2,0)) net=0; /* Avoid double-counting */
372
net+=contweight(obj2); /* Net size of contents of obj2 */
374
if (net>weight) return FIT_NETWEIGHT;
377
net=noun[obj1-first_noun].size;
378
if (net>size) return FIT_SIZE;
380
if (obj2==1 && !PURE_SIZE) return FIT_OK;
382
if (obj2==1 || aver>AGTME15) {
383
/* Pre-ME/1.56 interpreters didn't check this except for the player's
385
if (it_loc(obj1)==obj2
386
|| (aver<AGX00 && is_within(obj1,obj2,0)) )
387
net=0; /* Avoid double-counting */
388
net+=contsize(obj2); /* Net size of contents of obj2 */
389
if (net>size) return FIT_NETSIZE;
397
/* ------------------------------------------------------------------- */
398
/* Scope and visibility routines */
400
integer it_room(int item)
406
while(!troom(item)) {
408
if (item==0) return 0;
409
if (item==1 || item==1000) item=loc;
410
else item=it_loc(item);
411
if (item==tmploc || ++cnt>=40000L) {
412
/* writeln("GAME ERROR: Loop in object tree."); */
419
rbool player_has(int item)
421
return is_within(item,1,0) || is_within(item,1000,0);
425
rbool in_scope(int item)
426
/* strictly speaking, visible actually checks scope; this routine
427
determines if an object would be in scope if there were no light
434
if (it_isglobal(item)) return 1; /* Global objects always in scope. */
436
/* Flag objects in scope if their associated flag is set. */
437
tmp=it_flagnum(item);
439
(room[loc].flag_noun_bits & (1L<<(tmp-1)))) return 1;
441
curloc=it_loc(item); /* Should work for nouns or creatures */
443
while (curloc>maxroom && curloc!=1000 && it_open(curloc)) {
445
tmploc=it_loc(curloc);
446
if (tmploc==curloc || ++cnt>=40000L) {
447
/* writeln("GAME ERROR: Loop in the object tree."); */
452
if (curloc==1 || curloc==1000 || curloc==loc+first_room) return 1;
457
static int good_light(int obj,int roomlight,rbool active)
458
/* obj is a noun number */
459
/* If active is false, we don't care if the light is actually turned
460
on is the valid light */
462
if (roomlight==1 && !noun[obj].light)
463
return 0; /* obj is not a light source */
465
if (!matchclass(first_noun+obj,roomlight))
466
return 0; /* Not the correct light */
467
else return 1; /* The correct light _always_ illuminates the room */
469
if (!active) return 1;
470
/* Now need to determine if obj is actually providing light */
472
return 0; /* Light source is off or extinguished */
476
int lightcheck(int parent,int roomlight,rbool active)
477
/* This checks to see if anything contained in parent is a valid
479
/* active=1 means that we only want active lights;
480
active=0 indicates that extinguished light sources are okay. */
485
if (tnoun(i) && good_light(i-first_noun,roomlight,active)) return 1;
486
if (it_open(i) && lightcheck(i,roomlight,active))
487
return 1; /* Check children */
492
if (good_light(i,room[loc].light) && in_scope(i+first_noun))
500
if (room[loc].light==0) return 1;
501
if (lightcheck(loc+first_room,room[loc].light,1)) return 1;
502
if (lightcheck(1,room[loc].light,1)) return 1; /* Player carried light */
503
if (lightcheck(1000,room[loc].light,1)) return 1; /* Worn light */
507
/* Is item visible to player? */
508
/* visible only works for "normal" items; if the object could
509
be a virtual object (i.e. with negative item number), then use
510
gen_visible() below */
511
rbool visible(int item)
515
return in_scope(item);
517
return player_has(item);
520
rbool genvisible(parse_rec *dobj)
524
if (dobj->obj>0) return visible(dobj->obj);
526
if (dobj->info==D_INTERN) {
527
if (dobj->obj!=-ext_code[wdoor]) return 1;
528
return islit(); /* If item is a is a door */
530
if (dobj->info==D_GLOBAL || dobj->info==D_NUM) return 1;
531
if (dobj->info==D_FLAG) {
532
for(i=0;i<MAX_FLAG_NOUN;i++) /* Flag nouns */
533
if (flag_noun[i]!=0 && dobj->obj==-flag_noun[i]
534
&& (room[loc].flag_noun_bits & (1L<<i))!=0)
538
if (dobj->info==D_PIX) {
539
for(i=0;i<MAX_PIX;i++) /* PIX names */
540
if (pix_name[i]!=0 && dobj->obj==-pix_name[i] &&
541
(room[loc].PIX_bits & (1L<<i))!=0)
545
fatal("INTERNAL ERROR: Invalid gen_visible type.");
552
/* Need to find a noun related to w */
553
/* If there is an object with name w in scope, it returns 0
554
(since the object will have already been added to the menu).
555
if there are none, it returns the first object with name w. */
556
static integer find_related(word w)
564
if (noun[i].name==w) {
565
if (visible(i+first_noun)) return i+first_noun;
566
else if (item==0) item=i+first_noun;
569
if (creature[i].name==w) {
570
if (visible(i+first_creat)) return i+first_creat;
571
else if (item==0) item=i+first_creat;
577
static void add_to_scope(integer item)
582
noun[item-first_noun].scope=1;
583
i=find_related(noun[item-first_noun].related_name);
585
if (tnoun(i)) noun[i-first_noun].scope=1;
586
else if (tcreat(i)) creature[i-first_creat].scope=1;
589
else if (tcreat(item)) creature[item-first_creat].scope=1;
590
if (item==1 || item==1000 || troom(item) || it_open(item))
596
void compute_scope(void)
601
nounloop(i) noun[i].scope=0;
602
creatloop(i) creature[i].scope=0;
605
add_to_scope(loc+first_room);
606
rflag=room[loc].flag_noun_bits;
608
if (noun[i].isglobal ||
609
(noun[i].flagnum && (rflag & (1L<<(noun[i].flagnum-1)))))
610
add_to_scope(i+first_noun);
612
if (creature[i].isglobal ||
613
(creature[i].flagnum && (rflag & (1L<<(creature[i].flagnum-1)))))
614
add_to_scope(i+first_creat);
617
void compute_seen(void)
623
noun[i].seen=noun[i].seen || noun[i].scope;
625
creature[i].seen=creature[i].seen || creature[i].scope;
629
/*---------------------------------------------------------------------*/
630
/* Routines to compute the score */
632
void recompute_score(void)
639
if (noun[obj].points && !noun[obj].unused &&
640
( visible(obj+first_noun)
641
|| is_within(obj+first_noun,treas_room,0) ) )
642
objscore+=noun[obj].points;
644
if (!creature[obj].unused && creature[obj].points
645
&& visible(obj+first_creat))
646
objscore+=creature[obj].points;
652
/*---------------------------------------------------------------------*/
653
/* Menu Noun section: routines to get a list of 'relevant' nouns for */
654
/* the menuing system. They're here because they belong next to the */
655
/* scope routines, above */
657
static int *nlist, nleng; /* These are really local variables */
659
static void add_mnoun(int n)
661
nlist=rrealloc(nlist,(nleng+2)*sizeof(int));
667
/* This adds mitem and everything it contains */
668
static void add_mitem(int item)
672
if (tnoun(item) || tcreat(item)) add_mnoun(item);
673
if (item==1 || item==1000 || troom(item) || it_open(item))
676
/* Need to check related nouns */
678
i=find_related(noun[item-first_noun].related_name);
679
if (i!=0) add_mnoun(i);
684
static word getword(int item,int n)
685
/* Gets nth word associated with item */
688
if (item<0) return -item;
689
else if (tnoun(item)) return noun[item-first_noun].adj;
690
else if (tcreat(item)) return creature[item-first_creat].adj;
693
if (tnoun(item) || tcreat(item)) return it_name(item);
697
static int cmp_nouns(const void *a,const void *b)
698
/* *a, *b are object numbers; need alphabetic sort.*/
703
wa=getword(*((const int*)a),1);
704
wb=getword(*((const int*)b),1);
705
cmp=strcmp(dict[wa],dict[wb]);
706
if (cmp!=0) return cmp;
707
wa=getword(*(const int*)a,2);
708
wb=getword(*(const int*)b,2);
709
return strcmp(dict[wa],dict[wb]);
713
/* This returns the list of all objects that should show up on the menu */
714
/* The list should be 0 terminated and needs to be sorted */
719
nlist=rmalloc(sizeof(int));
723
for(i=0;i<numglobal;i++)
724
add_mnoun(-globalnoun[i]);
725
for(i=0;i<MAX_FLAG_NOUN;i++)
726
if (room[loc].flag_noun_bits & (1L<<i))
727
add_mnoun(-flag_noun[i]);
730
add_mitem(loc+first_room);
731
rflag=room[loc].flag_noun_bits;
733
if (noun[i].isglobal ||
734
(noun[i].flagnum && (rflag & (1L<<(noun[i].flagnum-1)))))
735
add_mitem(i+first_noun);
737
if (creature[i].isglobal ||
738
(creature[i].flagnum && (rflag & (1L<<(creature[i].flagnum-1)))))
739
add_mitem(i+first_creat);
740
qsort(nlist,nleng,sizeof(int),cmp_nouns);
746
/*---------------------------------------------------------------------*/
747
/* goto_room, the basic primitive used to move the player around */
749
void goto_room(int newroom)
753
/* Move group members in old room to new room */
754
safecontloop(i,j,loc+first_room)
756
it_move(i,newroom+first_room);
758
#if 0 /* -- this has been moved to v_go*/
760
oldloc=loc; /* Save old location for NO_BLOCK_HOSTILE purposes */
763
if (loc!=newroom) oldloc=loc; /* No backtracking unless v_go allows it */
764
if (!room[loc].seen) {
766
tscore+=room[loc].points;
773
v_look(); /* But see v_go() for a special case involving SPECIAL */
776
if (room[loc].end) endflag=1;
777
if (room[loc].win) winflag=1;
778
if (room[loc].killplayer) deadflag=1;
784
static void rundesc(int i, descr_ptr dp[],const char *shortdesc, int msgid)
787
print_descr(dp[i],1);
788
else if (!invischeck(shortdesc))
789
raw_lineout(shortdesc,1,MSG_DESC,NULL);
790
else sysmsg(msgid,"$You$ see nothing unexpected.");
793
void it_describe(int dobj)
796
print_descr(room_ptr[dobj-first_room],1);
797
else if (tnoun(dobj))
798
rundesc(dobj-first_noun,noun_ptr,noun[dobj-first_noun].shortdesc,194);
799
else if (tcreat(dobj))
800
rundesc(dobj-first_creat,creat_ptr,
801
creature[dobj-first_creat].shortdesc,195);
802
else if (dobj==-ext_code[wdoor]) /* i.e. DOOR */
804
if (room[loc].locked_door)
805
sysmsg(21,"$You$ see a locked door.");
806
else sysmsg(22,"$You$ see a perfectly normal doorway.");
808
else sysmsg(194,"$You$ see nothing unexpected.");
810
(noun[dobj-first_noun].open || !noun[dobj-first_noun].closable) &&
811
!it_appears_empty(dobj)) {
812
sysmsg(228,"Which contains:");
813
print_contents(dobj,1);
819
static char *build_position(word prep,word name)
820
/* Return the malloc'd string '$prep$ the $name$' */
825
leng=strlen(dict[prep])+strlen(dict[name])+6; /* includes final '\0' */
826
s=rmalloc(leng*sizeof(char));
828
strcpy(s,dict[prep]);
830
strcat(s,dict[name]);
831
assert(strlen(s)+1==leng);
840
static int print_obj(int obj,int ind_lev)
841
/* Prints out s on a line of its own if obj isn't INVISIBLE */
842
/* parent_descr is true if the parent has been described, false
843
otherwise (say if the parent is invisible). */
844
/* ind_lev=indentation level */
845
/* Return 1 if we actually printed something, 0 if obj is invisible */
847
int sdesc_flag; /* True if should print out as sdesc rather than
849
int i, retval, parent;
851
char *t, *s0, *posstr;
853
if (tcreat(obj) && creature[obj-first_creat].initdesc!=0)
854
return 0; /* Don't print normal description if printing initdesc */
857
sdesc_flag=!player_has(obj); /* This should be tested. */
858
sdesc_flag=sdesc_flag||(ind_lev>1); /* It seems that AGT uses the
859
sdesc for describing items
860
contained in other items */
861
/* Some code below relies on this, as well */
865
else if (it_name(obj)==0 && it_adj(obj)==0) /* Invisible */
868
s0=objname(obj); /* Must remember to rfree s before exiting */
870
for(t=s0;isspace(*t);t++); /* Skip over initial whitespace... */
871
*t=toupper(*t); /* ...and upcase the first non-space character */
877
if (sdesc_flag && tnoun(obj) && noun[obj-first_noun].initdesc!=0) {
879
msgout(noun[obj-first_noun].initdesc,1);
880
noun[obj-first_noun].initdesc=0; /* Only show it once */
882
else if (!invischeck(s)) {
883
retval=1; /* We're actually going to print something */
884
for(i=0;i<ind_lev;i++) writestr(" ");
885
raw_lineout(s,sdesc_flag,MSG_DESC,NULL);
886
/* Do $word$ formatting if sdesc */
887
/* Need to output container */
889
if (tnoun(obj) && noun[obj-first_noun].pos_prep!=0)
892
if (noun[obj-first_noun].pos_prep==-1)
893
writestr(noun[obj-first_noun].position);
895
posstr=build_position(noun[obj-first_noun].pos_prep,
896
noun[obj-first_noun].pos_name);
902
else if (parent>=first_noun && it_invisible(parent,sdesc_flag)
903
&& (it_name(parent)!=0 || it_adj(parent)!=0) ) {
904
/* If the parent object *isn't* invisible, we will already have
906
/* This also relies on sdesc_flag being the same for parent
909
if (parent>=first_creat && parent<=maxcreat)
910
sysmsg(221,"(Carried by");
912
sysmsg(222," (Inside");
914
writestr(t);rfree(t);
917
if (tnoun(obj) && noun[obj-first_noun].light && noun[obj-first_noun].on
919
sysmsg(220," (Providing light)");
928
int print_contents(int obj,int ind_lev)
929
/* obj=object to list contents of; ind_lev=indentation level */
930
/* Returns number of objects contained in obj that were listed */
937
if (print_obj(i,ind_lev)) cnt++;
938
if (it_open(i)) print_contents(i,ind_lev+1);
944
/* ------------------------------------------------------------------- */
945
/* Routines for directly getting and setting object properties and */
953
static void *compute_addr(int obj, int prop, const prop_struct *ptable)
959
rprintf("(Accessing %s->%s)\n",dict[it_name(obj)],ptable[prop].name);
961
base=(void*)(&room[obj-first_room]);
962
ofs=ptable[prop].room;
964
else if (tnoun(obj)) {
965
base=(void*)(&noun[obj-first_noun]);
966
ofs=ptable[prop].noun;
968
else if (tcreat(obj)) {
969
base=(void*)(&creature[obj-first_creat]);
970
ofs=ptable[prop].creature;
974
if (ofs==-1) /* Field doesn't exist in this type of object */
977
return (void*) ( ((char*)base) + ofs );
981
long getprop(int obj, int prop)
985
if (prop>=NUM_PROP) return 0;
986
paddr=(integer*)compute_addr(obj,prop,proplist);
987
if (paddr==NULL) return 0;
991
void setprop(int obj, int prop, long val)
995
if (prop>=NUM_WPROP) {
996
writeln("GAME ERROR: Read-only or non-existant property.");
1000
paddr=(integer*)compute_addr(obj,prop,proplist);
1002
writeln("GAME ERROR: Property-object mismatch.");
1008
rbool getattr(int obj, int prop)
1012
if (prop>=NUM_ATTR) return 0;
1013
paddr=(rbool*)compute_addr(obj,prop,attrlist);
1014
if (paddr==NULL) return 0;
1018
void setattr(int obj, int prop, rbool val)
1022
if (prop>=NUM_WATTR && prop!=24) {
1023
writeln("GAME ERROR: Read-only or non-existant attribute.");
1027
paddr=(rbool*)compute_addr(obj,prop,attrlist);
1029
writeln("GAME ERROR: Property-object mismatch.");
1037
/* ------------------------------------------------------------------- */
1038
/* This sets up the creat_fix[] array, which is used to determine the */
1039
/* scan ranges for addressed creatures in cases where there is more */
1040
/* than one creature of the same name */
1042
void init_creat_fix(void)
1046
creat_fix=rmalloc(rangefix(maxcreat-first_creat+1)*sizeof(integer));
1047
for(i=0;i<maxcreat-first_creat+1;i++)
1048
creat_fix[i]=i+first_creat;
1049
for(i=0;i<maxcreat-first_creat+1;i++)
1050
if (creat_fix[i]==i+first_creat) /* That is, it hasn't changed. */
1051
for(j=i+1;j<maxcreat-first_creat+1;j++)
1052
if (creature[i].name==creature[j].name &&
1053
creature[i].adj==creature[j].adj)
1054
creat_fix[j]=i+first_creat; /* That is, j --> i */
1057
void free_creat_fix(void)
1062
/* ------------------------------------------------------------------- */
1065
int it_contents(integer obj)
1067
if (tnoun(obj)) return noun[obj-first_noun].contents;
1068
else if (troom(obj)) return room[obj-first_room].contents;
1069
else if (tcreat(obj)) return creature[obj-first_creat].contents;
1070
else if (obj==1) return player_contents;
1071
else if (obj==1000) return player_worn;
1075
rbool it_lockable(integer obj, word nword)
1077
if (tnoun(obj)) return noun[obj-first_noun].lockable;
1078
else if (it_door(obj,nword)) return 1;
1082
rbool it_locked(integer obj, word nword)
1084
if (tnoun(obj)) return noun[obj-first_noun].locked;
1085
else if (it_door(obj,nword) && room[loc].locked_door) return 1;