1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/agt/agility.h"
24 #include "glk/agt/interp.h"
25 #include "glk/agt/exec.h"
26
27 namespace Glk {
28 namespace AGT {
29
30 /* */
31 /* This module contains most of the routines that implement the */
32 /* "physics" of the AGT world, including the scope routines. */
33
34
35 /* ------------------------------------------------------------------- */
36 /* Functions for manipulating parse_recs */
37 /* ------------------------------------------------------------------- */
38
39 /* Make artificial parse record for an object */
make_parserec(int obj,parse_rec * rec)40 parse_rec *make_parserec(int obj, parse_rec *rec) {
41 if (rec == NULL) rec = (parse_rec *)rmalloc(sizeof(parse_rec));
42 rec->obj = obj;
43 rec->info = D_NOUN;
44 rec->noun = it_name(obj);
45 rec->adj = it_adj(obj);
46 rec->num = 0;
47 return rec;
48 }
49
50 /* This is used by the parser to initialize a blank parse_rec */
tmpobj(parse_rec * objrec)51 void tmpobj(parse_rec *objrec) {
52 objrec->info = D_NOUN;
53 objrec->num = 0;
54 objrec->noun = objrec->adj = 0;
55 objrec->obj = 0;
56 }
57
copy_parserec(parse_rec * rec)58 parse_rec *copy_parserec(parse_rec *rec) {
59 parse_rec *newrec;
60 if (rec == NULL) return NULL;
61 newrec = (parse_rec *)rmalloc(sizeof(parse_rec));
62 memcpy(newrec, rec, sizeof(parse_rec));
63 return newrec;
64 }
65
free_all_parserec(void)66 void free_all_parserec(void) {
67 rfree(actor_rec);
68 rfree(dobj_rec);
69 rfree(iobj_rec);
70 }
71
72
73 /* ------------------------------------------------------------------- */
74 /* Functions for doing basic manipulation of items and extracting */
75 /* nformation about these items */
76 /* (often used to blackbox the difference between nouns and creatures) */
77
78
79
matchclass(int obj,int oclass)80 rbool matchclass(int obj, int oclass) {
81 int i;
82 if (oclass == 0) return 0;
83 for (i = obj; i != oclass && i != 0; i = it_class(i));
84 return i == oclass;
85 }
86
87
88 /* Functions for getting bascic information about items */
89
it_sdesc(int item)90 static const char *it_sdesc(int item) {
91 if (tnoun(item)) return noun[item - first_noun].shortdesc;
92 if (tcreat(item)) return creature[item - first_creat].shortdesc;
93 if (item < 0) return dict[-item];
94 return NULL;
95 }
96
it_possess(int item)97 rbool it_possess(int item) {
98 int l;
99
100 l = it_loc(item);
101 return (l == 1 || l == 1000);
102 }
103
it_proper(int item)104 rbool it_proper(int item) {
105 if (tcreat(item))
106 return (!PURE_PROPER) || creature[item - first_creat].proper;
107 if (tnoun(item))
108 return noun[item - first_noun].proper;
109 return 0;
110 }
111
112
invischeck(const char * s)113 static rbool invischeck(const char *s) {
114 while (rspace(*s)) s++;
115 return strncasecmp(s, "INVISIBLE", 9) == 0;
116 }
117
118
it_invisible(int item,rbool sdesc_flag)119 static rbool it_invisible(int item, rbool sdesc_flag) {
120 if (sdesc_flag)
121 return invischeck(it_sdesc(item));
122 else {
123 char *s;
124 rbool tmp;
125
126 if (it_name(item) == 0 && it_adj(item) == 0) return 1;
127 s = objname(item); /* Must remember to rfree s before exiting */
128 tmp = invischeck(s);
129 rfree(s);
130 return tmp;
131 }
132 }
133
134
it_appears_empty(int item)135 static rbool it_appears_empty(int item) {
136 int i;
137 int sdesc_flag;
138
139 if (item < 0) return 1;
140 sdesc_flag = !player_has(item);
141
142 contloop(i, item)
143 if (!it_invisible(i, sdesc_flag)) return 0;
144 return 1;
145 }
146
147 /* We classify something as a weapon if it kills something. */
it_isweapon(int objnum)148 rbool it_isweapon(int objnum) {
149 int i;
150
151 creatloop(i)
152 if (matchclass(objnum, creature[i].weapon)) return 1;
153 return 0;
154 }
155
156 /* This used to be a macro like troom, tnoun, and tcreat, but it
157 got too complicated and isn't used in time-critical areas,
158 anyhow */
159
it_door(int obj,word nword)160 rbool it_door(int obj, word nword) {
161 if (aver >= AGX00) return 0; /* No doors under Magx */
162 if (tdoor(obj)) return 1; /* The basic door */
163 if (it_loc(obj) == loc + first_room) return 0;
164 return (nword == ext_code[wdoor]);
165 }
166
167
168 /* ------------------------------------------------------------------- */
169 /* Routines that manipulate the linked lists representing containment */
170 /* information */
171
set_contents(int p,int newval)172 static void set_contents(int p, int newval) {
173 if (troom(p)) room[p - first_room].contents = newval;
174 else if (p == 1) player_contents = newval;
175 else if (p == 1000) player_worn = newval;
176 else if (tnoun(p)) noun[p - first_noun].contents = newval;
177 else if (tcreat(p)) creature[p - first_creat].contents = newval;
178 else {
179 writeln("INT ERR: Invalid object heading chain.");
180 return;
181 }
182 }
183
set_next(int p,int newval)184 static void set_next(int p, int newval) {
185 if (tnoun(p)) noun[p - first_noun].next = newval;
186 else if (tcreat(p)) creature[p - first_creat].next = newval;
187 else {
188 writeln("INT ERR: Invalid object in chain.");
189 return;
190 }
191 }
192
add_object(int loc_,int item)193 void add_object(int loc_, int item) {
194 int p, q;
195
196 set_next(item, 0);
197
198 if (loc_ == 0) return;
199 p = it_contents(loc_);
200
201 if (p == 0 || p > item) {
202 set_contents(loc_, item);
203 set_next(item, p);
204 } else { /* Figure out where to put the item */
205 do {
206 q = p;
207 p = it_next(p);
208 } while (p != 0 && p < item);
209
210 set_next(q, item);
211 set_next(item, p);
212 }
213 }
214
215
set_location(int item,int newloc)216 static void set_location(int item, int newloc)
217 /* This routine assumes item is either a noun or a creature */
218 {
219 int p, q;
220
221 p = it_loc(item);
222 if (p != 0) { /* Fix .next values */
223 q = it_contents(p);
224 if (q == item) set_contents(p, it_next(item));
225 else {
226 while (q != item && q != 0) {
227 p = q;
228 q = it_next(p);
229 }
230 assert(q != 0); /* This would mean the list structure was corrupted */
231 set_next(p, it_next(item));
232 }
233 }
234 /* We've unlinked it from the list at this point. */
235
236 if (tnoun(item))
237 noun[item - first_noun].location = newloc;
238 else if (tcreat(item))
239 creature[item - first_creat].location = newloc;
240
241 add_object(newloc, item);
242 }
243
244
it_reposition(int item,int newloc,rbool save_pos)245 void it_reposition(int item, int newloc, rbool save_pos) {
246 integer i;
247
248 if (tnoun(item)) {
249 if (player_has(item)) totwt -= noun[item - first_noun].weight;
250 if (it_loc(item) == 1) totsize -= noun[item - first_noun].size;
251
252 /* Set position to NULL */
253 if (!save_pos) {
254 noun[item - first_noun].pos_prep = 0;
255 noun[item - first_noun].pos_name = 0;
256 noun[item - first_noun].nearby_noun = 0;
257 noun[item - first_noun].position = NULL;
258 #if 0 /* I think this was wrong, so I'm commenting it out. */
259 noun[item - first_noun].initdesc = 0;
260 #endif
261 }
262
263 set_location(item, newloc);
264
265 if (player_has(item)) {
266 totwt += noun[item - first_noun].weight;
267 if (noun[item - first_noun].win)
268 winflag = 1;
269 }
270 if (it_loc(item) == 1) /* only things you are carrying directly count vs.
271 size limit. */
272 totsize += noun[item - first_noun].size;
273 } else if (tcreat(item)) {
274 if (newloc == 0) {
275 creature[item - first_creat].timecounter = 0; /* Reset attack counter */
276 creature[item - first_creat].counter = 0;
277 }
278 set_location(item, newloc);
279 }
280
281 nounloop(i)
282 if (noun[i].nearby_noun == item) {
283 noun[i].nearby_noun = 0;
284 noun[i].pos_prep = 0;
285 noun[i].pos_name = 0;
286 noun[i].position = NULL;
287 }
288 }
289
290
291
292 /* ------------------------------------------------------------------- */
293 /* Routines to deal with size and weight */
294
contsize(integer obj)295 static long contsize(integer obj) {
296 int i;
297 long net;
298
299 net = 0;
300 contloop(i, obj) {
301 if (tnoun(i))
302 net += noun[i - first_noun].size;
303 if (aver < AGX00) /* Under Magx, size isn't recursive */
304 net += contsize(i);
305 }
306 return net;
307 }
308
contweight(integer obj)309 static long contweight(integer obj) {
310 int i;
311 long net;
312
313 net = 0;
314 contloop(i, obj) {
315 if (tnoun(i))
316 net += noun[i - first_noun].weight;
317 net += contweight(i);
318 }
319 return net;
320 }
321
is_within(integer obj1,integer obj2,rbool stop_if_closed)322 rbool is_within(integer obj1, integer obj2, rbool stop_if_closed)
323 /* True if obj1 is contained in obj2 */
324 {
325 int i;
326 long cnt;
327
328 for (i = obj1, cnt = 0;
329 i != obj2 && i >= maxroom && i != 1000 && cnt < 40000L;
330 cnt++) {
331 i = it_loc(i);
332 if (stop_if_closed && !it_open(i)) break;
333 }
334 if (cnt >= 40000L) {
335 /* writeln("GAME ERROR: Loop in object tree.");*/
336 return 0;
337 }
338 if (i == obj2) return 1;
339 return 0;
340 }
341
342
343
check_fit(int obj1,int obj2)344 int check_fit(int obj1, int obj2)
345 /* Does obj1 fit inside obj2? Return one of the FIT_... values
346 defined in exec.h */
347 {
348 int size, weight;
349 long net;
350
351 assert(tnoun(obj1)); /* This should have been checked earlier */
352 if (obj2 == 1000) obj2 = 1;
353
354 if (obj2 == 1) size = weight = 100;
355 else {
356 assert(tnoun(obj2)); /* check_fit shouldn't be called otherwise */
357 size = noun[obj2 - first_noun].size;
358 weight = noun[obj2 - first_noun].weight;
359 }
360
361 /* Weight */
362 if (obj2 == 1 || (aver > AGTME15 && aver < AGX00)) {
363 /* Pre-1.56 interpreters forgot to check this;
364 Magx deliberatly *doesn't* check this */
365
366 net = noun[obj1 - first_noun].weight;
367 if (aver >= AGX00) net += contweight(obj1);
368 if (net > weight) return FIT_WEIGHT;
369
370 if (obj2 == 1) {
371 if (is_within(obj1, 1, 0) || is_within(obj1, 1000, 0)) net = 0;
372 net += contweight(1);
373 if (aver >= AGX00)
374 net += contweight(1000);
375 if (!PURE_SIZE) net = 0;
376 } else {
377 if (is_within(obj1, obj2, 0)) net = 0; /* Avoid double-counting */
378 net += contweight(obj2); /* Net size of contents of obj2 */
379 }
380 if (net > weight) return FIT_NETWEIGHT;
381 }
382
383 net = noun[obj1 - first_noun].size;
384 if (net > size) return FIT_SIZE;
385
386 if (obj2 == 1 && !PURE_SIZE) return FIT_OK;
387
388 if (obj2 == 1 || aver > AGTME15) {
389 /* Pre-ME/1.56 interpreters didn't check this except for the player's
390 inventory */
391 if (it_loc(obj1) == obj2
392 || (aver < AGX00 && is_within(obj1, obj2, 0)))
393 net = 0; /* Avoid double-counting */
394 net += contsize(obj2); /* Net size of contents of obj2 */
395 if (net > size) return FIT_NETSIZE;
396 }
397
398 return FIT_OK;
399 }
400
401
402
403 /* ------------------------------------------------------------------- */
404 /* Scope and visibility routines */
405
it_room(int item)406 integer it_room(int item) {
407 int tmploc;
408 long cnt;
409
410 cnt = 0;
411 while (!troom(item)) {
412 tmploc = item;
413 if (item == 0) return 0;
414 if (item == 1 || item == 1000) item = loc;
415 else item = it_loc(item);
416 if (item == tmploc || ++cnt >= 40000L) {
417 /* writeln("GAME ERROR: Loop in object tree."); */
418 return 0;
419 }
420 }
421 return item;
422 }
423
player_has(int item)424 rbool player_has(int item) {
425 return is_within(item, 1, 0) || is_within(item, 1000, 0);
426 }
427
428
in_scope(int item)429 rbool in_scope(int item)
430 /* strictly speaking, visible actually checks scope; this routine
431 determines if an object would be in scope if there were no light
432 problems. */
433 {
434 int curloc;
435 int tmp;
436 long cnt;
437
438 if (it_isglobal(item)) return 1; /* Global objects always in scope. */
439
440 /* Flag objects in scope if their associated flag is set. */
441 tmp = it_flagnum(item);
442 if (tmp &&
443 (room[loc].flag_noun_bits & (1L << (tmp - 1)))) return 1;
444
445 curloc = it_loc(item); /* Should work for nouns or creatures */
446 cnt = 0;
447 while (curloc > maxroom && curloc != 1000 && it_open(curloc)) {
448 int tmploc;
449 tmploc = it_loc(curloc);
450 if (tmploc == curloc || ++cnt >= 40000L) {
451 /* writeln("GAME ERROR: Loop in the object tree."); */
452 return 0;
453 } else curloc = tmploc;
454 }
455 if (curloc == 1 || curloc == 1000 || curloc == loc + first_room) return 1;
456 else return 0;
457 }
458
459
good_light(int obj,int roomlight,rbool active)460 static int good_light(int obj, int roomlight, rbool active)
461 /* obj is a noun number */
462 /* If active is false, we don't care if the light is actually turned
463 on is the valid light */
464 {
465 if (roomlight == 1 && !noun[obj].light)
466 return 0; /* obj is not a light source */
467 if (roomlight > 1) {
468 if (!matchclass(first_noun + obj, roomlight))
469 return 0; /* Not the correct light */
470 else return 1; /* The correct light _always_ illuminates the room */
471 }
472 if (!active) return 1;
473 /* Now need to determine if obj is actually providing light */
474 if (!noun[obj].on)
475 return 0; /* Light source is off or extinguished */
476 return 1;
477 }
478
lightcheck(int parent,int roomlight,rbool active)479 int lightcheck(int parent, int roomlight, rbool active)
480 /* This checks to see if anything contained in parent is a valid
481 room light */
482 /* active=1 means that we only want active lights;
483 active=0 indicates that extinguished light sources are okay. */
484 {
485 int i;
486
487 contloop(i, parent) {
488 if (tnoun(i) && good_light(i - first_noun, roomlight, active)) return 1;
489 if (it_open(i) && lightcheck(i, roomlight, active))
490 return 1; /* Check children */
491 }
492 return 0;
493 /*
494 nounloop(i)
495 if (good_light(i,room[loc].light) && in_scope(i+first_noun))
496 return 1;
497 return 0;*/
498 }
499
500
islit(void)501 rbool islit(void) {
502 if (room[loc].light == 0) return 1;
503 if (lightcheck(loc + first_room, room[loc].light, 1)) return 1;
504 if (lightcheck(1, room[loc].light, 1)) return 1; /* Player carried light */
505 if (lightcheck(1000, room[loc].light, 1)) return 1; /* Worn light */
506 return 0;
507 }
508
509 /* Is item visible to player? */
510 /* visible only works for "normal" items; if the object could
511 be a virtual object (i.e. with negative item number), then use
512 gen_visible() below */
visible(int item)513 rbool visible(int item) {
514 assert(item >= 0);
515 if (islit())
516 return in_scope(item);
517 else
518 return player_has(item);
519 }
520
genvisible(parse_rec * dobj_)521 rbool genvisible(parse_rec *dobj_) {
522 int i;
523
524 if (dobj_->obj > 0) return visible(dobj_->obj);
525
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 */
529 }
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)
535 return 1;
536 return 0;
537 }
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)
542 return 1;
543 return 0;
544 }
545 fatal("INTERNAL ERROR: Invalid gen_visible type.");
546 return 0;
547 }
548
549
550
551
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. */
find_related(word w)556 static integer find_related(word w) {
557 int i;
558 int item;
559
560 if (w == 0) return 0;
561 item = 0;
562 nounloop(i)
563 if (noun[i].name == w) {
564 if (visible(i + first_noun)) return i + first_noun;
565 else if (item == 0) item = i + first_noun;
566 }
567 creatloop(i)
568 if (creature[i].name == w) {
569 if (visible(i + first_creat)) return i + first_creat;
570 else if (item == 0) item = i + first_creat;
571 }
572 return item;
573 }
574
575
add_to_scope(integer item)576 static void add_to_scope(integer item) {
577 integer i;
578
579 if (tnoun(item)) {
580 noun[item - first_noun].scope = 1;
581 i = find_related(noun[item - first_noun].related_name);
582 if (i != 0) {
583 if (tnoun(i)) noun[i - first_noun].scope = 1;
584 else if (tcreat(i)) creature[i - first_creat].scope = 1;
585 }
586 } else if (tcreat(item)) creature[item - first_creat].scope = 1;
587 if (item == 1 || item == 1000 || troom(item) || it_open(item))
588 contloop(i, item)
589 add_to_scope(i);
590 }
591
592
compute_scope(void)593 void compute_scope(void) {
594 int i;
595 uint32 rflag;
596
597 nounloop(i) noun[i].scope = 0;
598 creatloop(i) creature[i].scope = 0;
599 add_to_scope(1);
600 add_to_scope(1000);
601 add_to_scope(loc + first_room);
602 rflag = room[loc].flag_noun_bits;
603 nounloop(i)
604 if (noun[i].isglobal ||
605 (noun[i].flagnum && (rflag & (1L << (noun[i].flagnum - 1)))))
606 add_to_scope(i + first_noun);
607 creatloop(i)
608 if (creature[i].isglobal ||
609 (creature[i].flagnum && (rflag & (1L << (creature[i].flagnum - 1)))))
610 add_to_scope(i + first_creat);
611 }
612
compute_seen(void)613 void compute_seen(void) {
614 int i;
615
616 compute_scope();
617 nounloop(i)
618 noun[i].seen = noun[i].seen || noun[i].scope;
619 creatloop(i)
620 creature[i].seen = creature[i].seen || creature[i].scope;
621 }
622
623
624 /*---------------------------------------------------------------------*/
625 /* Routines to compute the score */
626
recompute_score(void)627 void recompute_score(void) {
628 int obj;
629
630 tscore -= objscore;
631 objscore = 0;
632 nounloop(obj)
633 if (noun[obj].points && !noun[obj].unused &&
634 (visible(obj + first_noun)
635 || is_within(obj + first_noun, treas_room, 0)))
636 objscore += noun[obj].points;
637 creatloop(obj)
638 if (!creature[obj].unused && creature[obj].points
639 && visible(obj + first_creat))
640 objscore += creature[obj].points;
641 tscore += objscore;
642 }
643
644
645
646 /*---------------------------------------------------------------------*/
647 /* Menu Noun section: routines to get a list of 'relevant' nouns for */
648 /* the menuing system. They're here because they belong next to the */
649 /* scope routines, above */
650
651 static int *nlist, nleng; /* These are really local variables */
652
add_mnoun(int n)653 static void add_mnoun(int n) {
654 nlist = (int *)rrealloc(nlist, (nleng + 2) * sizeof(int));
655 nlist[nleng] = n;
656 nlist[++nleng] = 0;
657 }
658
659
660 /* This adds mitem and everything it contains */
add_mitem(int item)661 static void add_mitem(int item) {
662 integer i;
663
664 if (tnoun(item) || tcreat(item)) add_mnoun(item);
665 if (item == 1 || item == 1000 || troom(item) || it_open(item))
666 contloop(i, item)
667 add_mitem(i);
668 /* Need to check related nouns */
669 if (tnoun(item)) {
670 i = find_related(noun[item - first_noun].related_name);
671 if (i != 0) add_mnoun(i);
672 }
673 }
674
675
getword(int item,int n)676 static word getword(int item, int n)
677 /* Gets nth word associated with item */
678 {
679 if (n == 1) {
680 if (item < 0) return -item;
681 else if (tnoun(item)) return noun[item - first_noun].adj;
682 else if (tcreat(item)) return creature[item - first_creat].adj;
683 }
684 if (n == 2)
685 if (tnoun(item) || tcreat(item)) return it_name(item);
686 return 0;
687 }
688
cmp_nouns(const void * a,const void * b)689 static int cmp_nouns(const void *a, const void *b)
690 /* *a, *b are object numbers; need alphabetic sort.*/
691 {
692 word wa, wb;
693 int cmp;
694
695 wa = getword(*((const int *)a), 1);
696 wb = getword(*((const int *)b), 1);
697 cmp = strcmp(dict[wa], dict[wb]);
698 if (cmp != 0) return cmp;
699 wa = getword(*(const int *)a, 2);
700 wb = getword(*(const int *)b, 2);
701 return strcmp(dict[wa], dict[wb]);
702 }
703
get_nouns(void)704 int *get_nouns(void)
705 /* This returns the list of all objects that should show up on the menu */
706 /* The list should be 0 terminated and needs to be sorted */
707 {
708 int i;
709 uint32 rflag;
710
711 nlist = (int *)rmalloc(sizeof(int));
712 nlist[0] = 0;
713 nleng = 0;
714
715 for (i = 0; i < numglobal; i++)
716 add_mnoun(-globalnoun[i]);
717 for (i = 0; i < MAX_FLAG_NOUN; i++)
718 if (room[loc].flag_noun_bits & (1L << i))
719 add_mnoun(-flag_noun[i]);
720 add_mitem(1);
721 add_mitem(1000);
722 add_mitem(loc + first_room);
723 rflag = room[loc].flag_noun_bits;
724 nounloop(i)
725 if (noun[i].isglobal ||
726 (noun[i].flagnum && (rflag & (1L << (noun[i].flagnum - 1)))))
727 add_mitem(i + first_noun);
728 creatloop(i)
729 if (creature[i].isglobal ||
730 (creature[i].flagnum && (rflag & (1L << (creature[i].flagnum - 1)))))
731 add_mitem(i + first_creat);
732 qsort(nlist, nleng, sizeof(int), cmp_nouns);
733 return nlist;
734 }
735
736
737
738 /*---------------------------------------------------------------------*/
739 /* goto_room, the basic primitive used to move the player around */
740
goto_room(int newroom)741 void goto_room(int newroom) {
742 int i, j;
743
744 /* Move group members in old room to new room */
745 safecontloop(i, j, loc + first_room)
746 if (it_group(i))
747 it_move(i, newroom + first_room);
748
749 #if 0 /* -- this has been moved to v_go*/
750 if (loc != newroom)
751 oldloc = loc; /* Save old location for NO_BLOCK_HOSTILE purposes */
752 #endif
753 loc = newroom;
754 if (loc != newroom) oldloc = loc; /* No backtracking unless v_go allows it */
755 if (!room[loc].seen) {
756 room[loc].seen = 1;
757 tscore += room[loc].points;
758 first_visit_flag = 1;
759 room_firstdesc = 1;
760 v_look();
761 } else {
762 first_visit_flag = 0;
763 if (verboseflag)
764 v_look(); /* But see v_go() for a special case involving SPECIAL */
765 room_firstdesc = 0;
766 }
767 if (room[loc].end) endflag = 1;
768 if (room[loc].win) winflag = 1;
769 if (room[loc].killplayer) deadflag = 1;
770 do_autoverb = 1;
771 set_statline();
772 }
773
774
rundesc(int i,descr_ptr dp_[],const char * shortdesc,int msgid)775 static void rundesc(int i, descr_ptr dp_[], const char *shortdesc, int msgid) {
776 if (dp_[i].size > 0)
777 print_descr(dp_[i], 1);
778 else if (!invischeck(shortdesc))
779 raw_lineout(shortdesc, 1, MSG_DESC, NULL);
780 else sysmsg(msgid, "$You$ see nothing unexpected.");
781 }
782
it_describe(int dobj_)783 void it_describe(int dobj_) {
784 if (troom(dobj_))
785 print_descr(room_ptr[dobj_ - first_room], 1);
786 else if (tnoun(dobj_))
787 rundesc(dobj_ - first_noun, noun_ptr, noun[dobj_ - first_noun].shortdesc, 194);
788 else if (tcreat(dobj_))
789 rundesc(dobj_ - first_creat, creat_ptr,
790 creature[dobj_ - first_creat].shortdesc, 195);
791 else if (dobj_ == -ext_code[wdoor]) { /* i.e. DOOR */
792 if (room[loc].locked_door)
793 sysmsg(21, "$You$ see a locked door.");
794 else sysmsg(22, "$You$ see a perfectly normal doorway.");
795 } else sysmsg(194, "$You$ see nothing unexpected.");
796 if (tnoun(dobj_) &&
797 (noun[dobj_ - first_noun].open || !noun[dobj_ - first_noun].closable) &&
798 !it_appears_empty(dobj_)) {
799 sysmsg(228, "Which contains:");
800 print_contents(dobj_, 1);
801 }
802 }
803
804
805
build_position(word prep_,word name)806 static char *build_position(word prep_, word name)
807 /* Return the malloc'd string '$prep_$ the $name$' */
808 {
809 int leng;
810 char *s;
811
812 leng = strlen(dict[prep_]) + strlen(dict[name]) + 6; /* includes final '\0' */
813 s = (char *)rmalloc(leng * sizeof(char));
814
815 strcpy(s, dict[prep_]);
816 strcat(s, " the ");
817 strcat(s, dict[name]);
818 assert((int)strlen(s) + 1 == leng);
819 return s;
820 }
821
822
823
824
825
826
print_obj(int obj,int ind_lev)827 static int print_obj(int obj, int ind_lev)
828 /* Prints out s on a line of its own if obj isn't INVISIBLE */
829 /* parent_descr is true if the parent has been described, false
830 otherwise (say if the parent is invisible). */
831 /* ind_lev=indentation level */
832 /* Return 1 if we actually printed something, 0 if obj is invisible */
833 {
834 int sdesc_flag; /* True if should print out as sdesc rather than
835 as adjective-noun */
836 int i, retval, parent;
837 const char *s;
838 char *t, *s0, *posstr;
839
840 if (tcreat(obj) && creature[obj - first_creat].initdesc != 0)
841 return 0; /* Don't print normal description if printing initdesc */
842
843 s0 = NULL;
844 sdesc_flag = !player_has(obj); /* This should be tested. */
845 sdesc_flag = sdesc_flag || (ind_lev > 1); /* It seems that AGT uses the
846 sdesc for describing items
847 contained in other items */
848 /* Some code below relies on this, as well */
849
850 if (sdesc_flag)
851 s = it_sdesc(obj);
852 else if (it_name(obj) == 0 && it_adj(obj) == 0) /* Invisible */
853 return 0;
854 else {
855 s0 = objname(obj); /* Must remember to rfree s before exiting */
856 if (aver >= AGTME10) {
857 for (t = s0; isspace(*t); t++); /* Skip over initial whitespace... */
858 *t = toupper(*t); /* ...and upcase the first non-space character */
859 }
860 s = s0;
861 }
862
863 retval = 0;
864 if (sdesc_flag && tnoun(obj) && noun[obj - first_noun].initdesc != 0) {
865 retval = 1;
866 msgout(noun[obj - first_noun].initdesc, 1);
867 noun[obj - first_noun].initdesc = 0; /* Only show it once */
868 } else if (!invischeck(s)) {
869 retval = 1; /* We're actually going to print something */
870 for (i = 0; i < ind_lev; i++) writestr(" ");
871 raw_lineout(s, sdesc_flag, MSG_DESC, NULL);
872 /* Do $word$ formatting if sdesc */
873 /* Need to output container */
874 parent = it_loc(obj);
875 if (tnoun(obj) && noun[obj - first_noun].pos_prep != 0) {
876 writestr(" (");
877 if (noun[obj - first_noun].pos_prep == -1)
878 writestr(noun[obj - first_noun].position);
879 else {
880 posstr = build_position(noun[obj - first_noun].pos_prep,
881 noun[obj - first_noun].pos_name);
882 writestr(posstr);
883 rfree(posstr);
884 }
885 writestr(")");
886 } else if (parent >= first_noun && it_invisible(parent, sdesc_flag)
887 && (it_name(parent) != 0 || it_adj(parent) != 0)) {
888 /* If the parent object *isn't* invisible, we will already have
889 printed it out */
890 /* This also relies on sdesc_flag being the same for parent
891 and child objects */
892
893 if (parent >= first_creat && parent <= maxcreat)
894 sysmsg(221, "(Carried by");
895 else
896 sysmsg(222, " (Inside");
897 t = objname(parent);
898 writestr(t);
899 rfree(t);
900 sysmsg(223, ")");
901 }
902 if (tnoun(obj) && noun[obj - first_noun].light && noun[obj - first_noun].on
903 && PURE_OBJ_DESC)
904 sysmsg(220, " (Providing light)");
905 writeln("");
906 }
907 if (!sdesc_flag)
908 rfree(s0);
909 return retval;
910 }
911
912
print_contents(int obj,int ind_lev)913 int print_contents(int obj, int ind_lev)
914 /* obj=object to list contents of; ind_lev=indentation level */
915 /* Returns number of objects contained in obj that were listed */
916 {
917 int i, cnt;
918
919 cnt = 0;
920
921 contloop(i, obj) {
922 if (print_obj(i, ind_lev)) cnt++;
923 if (it_open(i)) print_contents(i, ind_lev + 1);
924 }
925 return cnt;
926 }
927
928
929 /* ------------------------------------------------------------------- */
930 /* Routines for directly getting and setting object properties and */
931 /* attributes. */
932
933
934 #define NUM_WPROP 6
935 #define NUM_WATTR 6
936
937
compute_addr(int obj,int prop,const prop_struct * ptable)938 static void *compute_addr(int obj, int prop, const prop_struct *ptable) {
939 int ofs;
940 void *base;
941
942 if (DIAG)
943 rprintf("(Accessing %s->%s)\n", dict[it_name(obj)], ptable[prop].name);
944 if (troom(obj)) {
945 base = (void *)(&room[obj - first_room]);
946 ofs = ptable[prop].room;
947 } else if (tnoun(obj)) {
948 base = (void *)(&noun[obj - first_noun]);
949 ofs = ptable[prop].noun;
950 } else if (tcreat(obj)) {
951 base = (void *)(&creature[obj - first_creat]);
952 ofs = ptable[prop].creature;
953 } else return NULL;
954
955 if (ofs == -1) /* Field doesn't exist in this type of object */
956 return NULL;
957
958 return (void *)(((char *)base) + ofs);
959 }
960
961
getprop(int obj,int prop)962 long getprop(int obj, int prop) {
963 integer *paddr;
964
965 if (prop >= NUM_PROP) return 0;
966 paddr = (integer *)compute_addr(obj, prop, proplist);
967 if (paddr == NULL) return 0;
968 return *paddr;
969 }
970
setprop(int obj,int prop,long val)971 void setprop(int obj, int prop, long val) {
972 integer *paddr;
973
974 if (prop >= NUM_WPROP) {
975 writeln("GAME ERROR: Read-only or non-existant property.");
976 return;
977 }
978
979 paddr = (integer *)compute_addr(obj, prop, proplist);
980 if (paddr == NULL) {
981 writeln("GAME ERROR: Property-object mismatch.");
982 return;
983 }
984 *paddr = val;
985 }
986
getattr(int obj,int prop)987 rbool getattr(int obj, int prop) {
988 rbool *paddr;
989
990 if (prop >= NUM_ATTR) return 0;
991 paddr = (rbool *)compute_addr(obj, prop, attrlist);
992 if (paddr == NULL) return 0;
993 return *paddr;
994 }
995
setattr(int obj,int prop,rbool val)996 void setattr(int obj, int prop, rbool val) {
997 rbool *paddr;
998
999 if (prop >= NUM_WATTR && prop != 24) {
1000 writeln("GAME ERROR: Read-only or non-existant attribute.");
1001 return;
1002 }
1003
1004 paddr = (rbool *)compute_addr(obj, prop, attrlist);
1005 if (paddr == NULL) {
1006 writeln("GAME ERROR: Property-object mismatch.");
1007 return;
1008 }
1009 *paddr = val;
1010 }
1011
1012
1013
1014 /* ------------------------------------------------------------------- */
1015 /* This sets up the creat_fix[] array, which is used to determine the */
1016 /* scan ranges for addressed creatures in cases where there is more */
1017 /* than one creature of the same name */
1018
init_creat_fix(void)1019 void init_creat_fix(void) {
1020 int i, j;
1021
1022 creat_fix = (integer *)rmalloc(rangefix(maxcreat - first_creat + 1) * sizeof(integer));
1023 for (i = 0; i < maxcreat - first_creat + 1; i++)
1024 creat_fix[i] = i + first_creat;
1025 for (i = 0; i < maxcreat - first_creat + 1; i++)
1026 if (creat_fix[i] == i + first_creat) /* That is, it hasn't changed. */
1027 for (j = i + 1; j < maxcreat - first_creat + 1; j++)
1028 if (creature[i].name == creature[j].name &&
1029 creature[i].adj == creature[j].adj)
1030 creat_fix[j] = i + first_creat; /* That is, j --> i */
1031 }
1032
free_creat_fix(void)1033 void free_creat_fix(void) {
1034 rfree(creat_fix);
1035 }
1036
1037 /* ------------------------------------------------------------------- */
1038
1039 #ifndef IT_MACRO
it_contents(integer obj)1040 int it_contents(integer obj) {
1041 if (tnoun(obj)) return noun[obj - first_noun].contents;
1042 else if (troom(obj)) return room[obj - first_room].contents;
1043 else if (tcreat(obj)) return creature[obj - first_creat].contents;
1044 else if (obj == 1) return player_contents;
1045 else if (obj == 1000) return player_worn;
1046 else return 0;
1047 }
1048
it_lockable(integer obj,word nword)1049 rbool it_lockable(integer obj, word nword) {
1050 if (tnoun(obj)) return noun[obj - first_noun].lockable;
1051 else if (it_door(obj, nword)) return 1;
1052 else return 0;
1053 }
1054
it_locked(integer obj,word nword)1055 rbool it_locked(integer obj, word nword) {
1056 if (tnoun(obj)) return noun[obj - first_noun].locked;
1057 else if (it_door(obj, nword) && room[loc].locked_door) return 1;
1058 else return 0;
1059 }
1060
1061 #endif
1062
1063 } // End of namespace AGT
1064 } // End of namespace Glk
1065