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