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/alan2/alan2.h"
24 #include "glk/alan2/types.h"
25 #include "glk/alan2/exe.h"
26 #include "glk/alan2/glkio.h"
27 #include "glk/alan2/inter.h"
28 #include "glk/alan2/main.h"
29 #include "glk/alan2/parse.h"
30 #include "glk/alan2/stack.h"
31 #include "glk/alan2/decode.h"
32 
33 namespace Glk {
34 namespace Alan2 {
35 
36 #define WIDTH 80
37 
38 #define N_EVTS 100
39 
40 
41 /* PUBLIC DATA */
42 
43 /* The event queue */
44 EvtqElem eventq[N_EVTS];        /* Event queue */
45 int etop = 0;                   /* Event queue top pointer */
46 
47 Boolean looking = FALSE;        /* LOOKING? flag */
48 
49 int dscrstkp = 0;               /* Describe-stack pointer */
50 
51 
52 void dscrobjs();
53 void dscracts();
54 
55 
print(Aword fpos,Aword len)56 void print(Aword fpos, Aword len) {
57 	char str[2 * WIDTH];              // String buffer
58 	int outlen = 0;                   // Current output length
59 	int ch = 0;
60 	int i;
61 	long savfp = 0;                   // Temporary saved text file position
62 	static Boolean printFlag = FALSE; // Printing already?
63 	Boolean savedPrintFlag = printFlag;
64 	void *info = nullptr;             // Saved decoding info
65 
66 
67 	if (len == 0) return;
68 
69 	if (isHere(HERO)) {           /* Check if the player will see it */
70 		if (printFlag) {            /* Already printing? */
71 			/* Save current text file position and/or decoding info */
72 			if (header->pack)
73 				info = pushDecode();
74 			else
75 				savfp = ftell(txtfil);
76 		}
77 		printFlag = TRUE;           /* We're printing now! */
78 		fseek(txtfil, fpos, 0);     /* Position to start of text */
79 		if (header->pack)
80 			startDecoding();
81 		for (outlen = 0; outlen != (int)len; outlen = outlen + strlen(str)) {
82 			/* Fill the buffer from the beginning */
83 			for (i = 0; i <= WIDTH || (i > WIDTH && ch != ' '); i++) {
84 				if (outlen + i == (int)len)  /* No more characters? */
85 					break;
86 				if (header->pack)
87 					ch = decodeChar();
88 				else
89 					ch = getc(txtfil);
90 				if (ch == EOFChar)      /* Or end of text? */
91 					break;
92 				str[i] = ch;
93 			}
94 			str[i] = '\0';
95 			output(str);
96 		}
97 		/* And restore */
98 		printFlag = savedPrintFlag;
99 		if (printFlag) {
100 			if (header->pack)
101 				popDecode(info);
102 			else
103 				fseek(txtfil, savfp, 0);
104 		}
105 	}
106 }
107 
sys(Aword fpos,Aword len)108 void sys(Aword fpos, Aword len) {
109 #ifdef GLK
110 	::error("system calls aren't supported");
111 #else
112 	char *command;
113 
114 	getstr(fpos, len);            /* Returns address to string on stack */
115 	command = (char *)pop();
116 	int tmp = system(command);
117 	free(command);
118 #endif
119 }
120 
getstr(Aword fpos,Aword len)121 void getstr(Aword fpos, Aword len) {
122 	char *buf = (char *)allocate(len + 1);
123 
124 	push((Aptr) buf);            /* Push the address to the string */
125 	fseek(txtfil, fpos, 0);       /* Position to start of text */
126 	if (header->pack)
127 		startDecoding();
128 	while (len--)
129 		if (header->pack)
130 			*(buf++) = decodeChar();
131 		else
132 			*(buf++) = getc(txtfil);
133 	*buf = '\0';
134 }
135 
score(Aword sc)136 void score(Aword sc) {
137 	char buf[80];
138 
139 	if (sc == 0) {
140 		prmsg(M_SCORE1);
141 		sprintf(buf, "%d", cur.score);
142 		output(buf);
143 		prmsg(M_SCORE2);
144 		sprintf(buf, "%ld.", (unsigned long) header->maxscore);
145 		output(buf);
146 	} else {
147 		cur.score += scores[sc - 1];
148 		scores[sc - 1] = 0;
149 	}
150 }
151 
visits(Aword v)152 void visits(Aword v) {
153 	cur.visits = v;
154 }
155 
confirm(MsgKind msgno)156 Boolean confirm(MsgKind msgno) {
157 	char buf[80];
158 
159 	/* This is a bit of a hack since we really want to compare the input,
160 	   it could be affirmative, but for now any input is NOT! */
161 	prmsg(msgno);
162 
163 	if (!readline(buf)) return TRUE;
164 	col = 1;
165 
166 	return (buf[0] == '\0');
167 }
168 
quit(CONTEXT)169 void quit(CONTEXT) {
170 	char buf[80];
171 
172 	para();
173 	while (!g_vm->shouldQuit()) {
174 		col = 1;
175 		statusline();
176 		prmsg(M_QUITACTION);
177 		if (!readline(buf)) {
178 			CALL1(terminate, 0)
179 		}
180 
181 		if (scumm_stricmp(buf, "restart") == 0) {
182 			g_vm->setRestart(true);
183 			LONG_JUMP
184 		} else if (scumm_stricmp(buf, "restore") == 0) {
185 			restore();
186 			LONG_JUMP
187 		} else if (scumm_stricmp(buf, "quit") == 0) {
188 			CALL1(terminate, 0)
189 		}
190 	}
191 }
192 
restart()193 void restart() {
194 	para();
195 	if (confirm(M_REALLY)) {
196 		//longjmp(restart_label, TRUE);
197 		::error("TODO: restart");
198 	} else
199 		return;
200 	syserr("Fallthrough in RESTART");
201 }
202 
cancl(Aword evt)203 void cancl(Aword evt) {
204 	int i;
205 
206 	for (i = etop - 1; i >= 0; i--)
207 		if (eventq[i].event == (int)evt) {
208 			while (i < etop - 1) {
209 				eventq[i].event = eventq[i + 1].event;
210 				eventq[i].time = eventq[i + 1].time;
211 				eventq[i].where = eventq[i + 1].where;
212 				i++;
213 			}
214 			etop--;
215 			return;
216 		}
217 }
218 
schedule(Aword evt,Aword whr,Aword aft)219 void schedule(Aword evt, Aword whr, Aword aft) {
220 	int i;
221 	int time;
222 
223 	cancl(evt);
224 	/* Check for overflow */
225 	if (etop == N_EVTS) syserr("Out of event space.");
226 
227 	time = cur.tick + aft;
228 
229 	/* Bubble this event down */
230 	for (i = etop; i >= 1 && eventq[i - 1].time <= time; i--) {
231 		eventq[i].event = eventq[i - 1].event;
232 		eventq[i].time = eventq[i - 1].time;
233 		eventq[i].where = eventq[i - 1].where;
234 	}
235 
236 	eventq[i].time = time;
237 	eventq[i].where = whr;
238 	eventq[i].event = evt;
239 	etop++;
240 }
241 
242 
243 /*----------------------------------------------------------------------
244 
245   getatr()
246 
247   Get an attribute value from an attribute list
248 
249  */
getatr(Aaddr atradr,Aaddr atr)250 static Aptr getatr(
251     Aaddr atradr,              /* IN - ACODE address to attribute table */
252     Aaddr atr                  /* IN - The attribute to read */
253 ) {
254 	AtrElem *at;
255 
256 	at = (AtrElem *) addrTo(atradr);
257 	return at[atr - 1].val;
258 }
259 
260 
261 /*----------------------------------------------------------------------
262 
263   setatr()
264 
265   Set a particular attribute to a value.
266 
267  */
setatr(Aaddr atradr,Aword atr,Aword val)268 static void setatr(
269 	Aaddr atradr,              /* IN - ACODE address to attribute table */
270 	Aword atr,                 /* IN - attribute code */
271 	Aword val                  /* IN - new value */
272 ) {
273 	AtrElem *at;
274 
275 	at = (AtrElem *) addrTo(atradr);
276 	at[atr - 1].val = val;
277 }
278 
279 
280 /*----------------------------------------------------------------------
281 
282   make()
283 
284   */
285 
makloc(Aword loc,Aword atr,Aword val)286 static void makloc(Aword loc, Aword atr, Aword val) {
287 	setatr(locs[loc - LOCMIN].atrs, atr, val);
288 }
289 
makobj(Aword obj,Aword atr,Aword val)290 static void makobj(Aword obj, Aword atr, Aword val) {
291 	setatr(objs[obj - OBJMIN].atrs, atr, val);
292 }
293 
makact(Aword act,Aword atr,Aword val)294 static void makact(Aword act, Aword atr, Aword val) {
295 	setatr(acts[act - ACTMIN].atrs, atr, val);
296 }
297 
make(Aword id,Aword atr,Aword val)298 void make(Aword id, Aword atr, Aword val) {
299 	char str[80];
300 
301 	if (isObj(id))
302 		makobj(id, atr, val);
303 	else if (isLoc(id))
304 		makloc(id, atr, val);
305 	else if (isAct(id))
306 		makact(id, atr, val);
307 	else {
308 		sprintf(str, "Can't MAKE item (%ld).", (unsigned long) id);
309 		syserr(str);
310 	}
311 }
312 
313 
314 /*----------------------------------------------------------------------------
315 
316   set()
317 
318  */
319 
setloc(Aword loc,Aword atr,Aword val)320 static void setloc(Aword loc, Aword atr, Aword val) {
321 	setatr(locs[loc - LOCMIN].atrs, atr, val);
322 	locs[loc - LOCMIN].describe = 0;
323 }
324 
setobj(Aword obj,Aword atr,Aword val)325 static void setobj(Aword obj, Aword atr, Aword val) {
326 	setatr(objs[obj - OBJMIN].atrs, atr, val);
327 }
328 
setact(Aword act,Aword atr,Aword val)329 static void setact(Aword act, Aword atr, Aword val) {
330 	setatr(acts[act - ACTMIN].atrs, atr, val);
331 }
332 
set(Aword id,Aword atr,Aword val)333 void set(Aword id, Aword atr, Aword val) {
334 	char str[80];
335 
336 	if (isObj(id))
337 		setobj(id, atr, val);
338 	else if (isLoc(id))
339 		setloc(id, atr, val);
340 	else if (isAct(id))
341 		setact(id, atr, val);
342 	else {
343 		sprintf(str, "Can't SET item (%ld).", (unsigned long) id);
344 		syserr(str);
345 	}
346 }
347 
setstr(Aword id,Aword atr,Aword str)348 void setstr(Aword id, Aword atr, Aword str) {
349 	free((char *)attribute(id, atr));
350 	set(id, atr, str);
351 }
352 
353 
354 
355 /*-----------------------------------------------------------------------------
356 
357   incr/decr
358 
359   */
360 
361 /*----------------------------------------------------------------------
362 
363   incratr()
364 
365   Increment a particular attribute by a value.
366 
367  */
incratr(Aaddr atradr,Aword atr,Aword step)368 static void incratr(
369     Aaddr atradr,           /* IN - ACODE address to attribute table */
370     Aword atr,              /* IN - attribute code */
371     Aword step              /* IN - step to increment by */
372 ) {
373 	AtrElem *at;
374 
375 	at = (AtrElem *) addrTo(atradr);
376 	at[atr - 1].val += step;
377 }
378 
incrloc(Aword loc,Aword atr,Aword step)379 static void incrloc(Aword loc, Aword atr, Aword step) {
380 	incratr(locs[loc - LOCMIN].atrs, atr, step);
381 	locs[loc - LOCMIN].describe = 0;
382 }
383 
incrobj(Aword obj,Aword atr,Aword step)384 static void incrobj(Aword obj, Aword atr, Aword step) {
385 	incratr(objs[obj - OBJMIN].atrs, atr, step);
386 }
387 
incract(Aword act,Aword atr,Aword step)388 static void incract(Aword act, Aword atr, Aword step) {
389 	incratr(acts[act - ACTMIN].atrs, atr, step);
390 }
391 
incr(Aword id,Aword atr,Aword step)392 void incr(Aword id, Aword atr, Aword step) {
393 	char str[80];
394 
395 	if (isObj(id))
396 		incrobj(id, atr, step);
397 	else if (isLoc(id))
398 		incrloc(id, atr, step);
399 	else if (isAct(id))
400 		incract(id, atr, step);
401 	else {
402 		sprintf(str, "Can't INCR item (%ld).", (unsigned long) id);
403 		syserr(str);
404 	}
405 }
406 
decr(Aword id,Aword atr,Aword step)407 void decr(Aword id, Aword atr, Aword step) {
408 	char str[80];
409 
410 	// TODO: Original did explicit negation on an unsigned value. Make sure that the
411 	// casts added to ignore the warnings are okay
412 	if (isObj(id))
413 		incrobj(id, atr, static_cast<uint>(-(int)step));
414 	else if (isLoc(id))
415 		incrloc(id, atr, static_cast<uint>(-(int)step));
416 	else if (isAct(id))
417 		incract(id, atr, static_cast<uint>(-(int)step));
418 	else {
419 		sprintf(str, "Can't DECR item (%ld).", (unsigned long) id);
420 		syserr(str);
421 	}
422 }
423 
424 
425 /*----------------------------------------------------------------------
426 
427   attribute()
428 
429   */
430 
locatr(Aword loc,Aword atr)431 static Aptr locatr(Aword loc, Aword atr) {
432 	return getatr(locs[loc - LOCMIN].atrs, atr);
433 }
434 
objatr(Aword obj,Aword atr)435 static Aptr objatr(Aword obj, Aword atr) {
436 	return getatr(objs[obj - OBJMIN].atrs, atr);
437 }
438 
actatr(Aword act,Aword atr)439 static Aptr actatr(Aword act, Aword atr) {
440 	return getatr(acts[act - ACTMIN].atrs, atr);
441 }
442 
litatr(Aword lit,Aword atr)443 static Aptr litatr(Aword lit, Aword atr) {
444 	char str[80];
445 
446 	if (atr == 1)
447 		return litValues[lit - LITMIN].value;
448 	else {
449 		sprintf(str, "Unknown attribute for literal (%ld).", (unsigned long) atr);
450 		syserr(str);
451 	}
452 	return (Aptr)EOD;
453 }
454 
attribute(Aword id,Aword atr)455 Aptr attribute(Aword id, Aword atr) {
456 	char str[80];
457 
458 	if (isObj(id))
459 		return objatr(id, atr);
460 	else if (isLoc(id))
461 		return locatr(id, atr);
462 	else if (isAct(id))
463 		return actatr(id, atr);
464 	else if (isLit(id))
465 		return litatr(id, atr);
466 	else {
467 		sprintf(str, "Can't ATTRIBUTE item (%ld).", (unsigned long) id);
468 		syserr(str);
469 	}
470 	return (Aptr)EOD;
471 }
472 
strattr(Aword id,Aword atr)473 Aptr strattr(Aword id, Aword atr) {
474 	return (Aptr) strdup((char *)attribute(id, atr));
475 }
476 
477 
478 /*----------------------------------------------------------------------
479 
480   where()
481 
482   */
483 
objloc(Aword obj)484 static Aword objloc(Aword obj) {
485 	if (isCnt(objs[obj - OBJMIN].loc)) /* In something ? */
486 		if (isObj(objs[obj - OBJMIN].loc) || isAct(objs[obj - OBJMIN].loc))
487 			return (where(objs[obj - OBJMIN].loc));
488 		else /* Containers not anywhere is where the hero is! */
489 			return (where(HERO));
490 	else
491 		return (objs[obj - OBJMIN].loc);
492 }
493 
actloc(Aword act)494 static Aword actloc(Aword act) {
495 	return (acts[act - ACTMIN].loc);
496 }
497 
where(Aword id)498 Aword where(Aword id) {
499 	char str[80];
500 
501 	if (isObj(id))
502 		return objloc(id);
503 	else if (isAct(id))
504 		return actloc(id);
505 	else {
506 		sprintf(str, "Can't WHERE item (%ld).", (unsigned long) id);
507 		syserr(str);
508 	}
509 	return (Aptr)EOD;
510 }
511 
512 
513 /*----------------------------------------------------------------------
514 
515   aggregates
516 
517   */
518 
agrmax(Aword atr,Aword whr)519 Aint agrmax(Aword atr, Aword whr) {
520 	Aword i;
521 	Aint max = 0;
522 
523 	for (i = OBJMIN; i <= OBJMAX; i++) {
524 		if (isLoc(whr)) {
525 			if (where(i) == whr && (int)attribute(i, atr) > max)
526 				max = attribute(i, atr);
527 		} else if (objs[i - OBJMIN].loc == whr && (int)attribute(i, atr) > max)
528 			max = attribute(i, atr);
529 	}
530 	return (max);
531 }
532 
agrsum(Aword atr,Aword whr)533 Aint agrsum(Aword atr, Aword whr) {
534 	Aword i;
535 	Aint sum = 0;
536 
537 	for (i = OBJMIN; i <= OBJMAX; i++) {
538 		if (isLoc(whr)) {
539 			if (where(i) == whr)
540 				sum += attribute(i, atr);
541 		} else if (objs[i - OBJMIN].loc == whr)
542 			sum += attribute(i, atr);
543 	}
544 	return (sum);
545 }
546 
agrcount(Aword whr)547 Aint agrcount(Aword whr) {
548 	Aword i;
549 	Aword count = 0;
550 
551 	for (i = OBJMIN; i <= OBJMAX; i++) {
552 		if (isLoc(whr)) {
553 			if (where(i) == whr)
554 				count++;
555 		} else if (objs[i - OBJMIN].loc == whr)
556 			count++;
557 	}
558 	return (count);
559 }
560 
561 
562 /*----------------------------------------------------------------------
563 
564   locate()
565 
566   */
567 
locobj(Aword obj,Aword whr)568 static void locobj(Aword obj, Aword whr) {
569 	if (isCnt(whr)) { /* Into a container */
570 		if (whr == obj)
571 			syserr("Locating something inside itself.");
572 		if (checklim(whr, obj))
573 			return;
574 		else
575 			objs[obj - OBJMIN].loc = whr;
576 	} else {
577 		objs[obj - OBJMIN].loc = whr;
578 		/* Make sure the location is described since it's changed */
579 		locs[whr - LOCMIN].describe = 0;
580 	}
581 }
582 
locact(Aword act,Aword whr)583 static void locact(Aword act, Aword whr) {
584 	Aword prevact = cur.act;
585 	Aword prevloc = cur.loc;
586 
587 	cur.loc = whr;
588 	acts[act - ACTMIN].loc = whr;
589 	if (act == HERO) {
590 		if (locs[acts[act - ACTMIN].loc - LOCMIN].describe % (cur.visits + 1) == 0)
591 			look();
592 		else {
593 			if (anyOutput)
594 				para();
595 			say(where(HERO));
596 			prmsg(M_AGAIN);
597 			newline();
598 			dscrobjs();
599 			dscracts();
600 		}
601 		locs[where(HERO) - LOCMIN].describe++;
602 		locs[where(HERO) - LOCMIN].describe %= (cur.visits + 1);
603 	} else
604 		locs[whr - LOCMIN].describe = 0;
605 	if (locs[cur.loc - LOCMIN].does != 0) {
606 		cur.act = act;
607 		interpret(locs[cur.loc - LOCMIN].does);
608 		cur.act = prevact;
609 	}
610 
611 	if (cur.act != (int)act)
612 		cur.loc = prevloc;
613 }
614 
locate(Aword id,Aword whr)615 void locate(Aword id, Aword whr) {
616 	char str[80];
617 
618 	if (isObj(id))
619 		locobj(id, whr);
620 	else if (isAct(id))
621 		locact(id, whr);
622 	else {
623 		sprintf(str, "Can't LOCATE item (%ld).", (unsigned long) id);
624 		syserr(str);
625 	}
626 }
627 
628 
629 /*----------------------------------------------------------------------
630 
631   isHere()
632 
633   */
634 
objhere(Aword obj)635 static Abool objhere(Aword obj) {
636 	if (isCnt(objs[obj - OBJMIN].loc)) {  /* In something? */
637 		if (isObj(objs[obj - OBJMIN].loc) || isAct(objs[obj - OBJMIN].loc))
638 			return (isHere(objs[obj - OBJMIN].loc));
639 		else /* If the container wasn't anywhere, assume where HERO is! */
640 			return ((int)where(HERO) == cur.loc);
641 	} else {
642 		return (int)(objs[obj - OBJMIN].loc) == cur.loc;
643 	}
644 }
645 
acthere(Aword act)646 static Aword acthere(Aword act) {
647 	return (int)(acts[act - ACTMIN].loc) == cur.loc;
648 }
649 
isHere(Aword id)650 Abool isHere(Aword id) {
651 	char str[80];
652 
653 	if (isObj(id))
654 		return objhere(id);
655 	else if (isAct(id))
656 		return acthere(id);
657 	else {
658 		sprintf(str, "Can't HERE item (%ld).", (unsigned long) id);
659 		syserr(str);
660 	}
661 	return (Abool)EOD;
662 }
663 
664 /*----------------------------------------------------------------------
665 
666   isNear()
667 
668   */
669 
objnear(Aword obj)670 static Aword objnear(Aword obj) {
671 	if (isCnt(objs[obj - OBJMIN].loc)) {  /* In something? */
672 		if (isObj(objs[obj - OBJMIN].loc) || isAct(objs[obj - OBJMIN].loc))
673 			return (isNear(objs[obj - OBJMIN].loc));
674 		else  /* If the container wasn't anywhere, assume here, so not nearby! */
675 			return (FALSE);
676 	} else
677 		return (exitto(where(obj), cur.loc));
678 }
679 
actnear(Aword act)680 static Aword actnear(Aword act) {
681 	return (exitto(where(act), cur.loc));
682 }
683 
isNear(Aword id)684 Abool isNear(Aword id) {
685 	char str[80];
686 
687 	if (isObj(id))
688 		return objnear(id);
689 	else if (isAct(id))
690 		return actnear(id);
691 	else {
692 		sprintf(str, "Can't NEAR item (%ld).", (unsigned long) id);
693 		syserr(str);
694 	}
695 	return (Abool)EOD;
696 }
697 
698 
699 /*----------------------------------------------------------------------
700 
701   in()
702 
703   */
704 
in(Aword obj,Aword cnt)705 Abool in(Aword obj, Aword cnt) {
706 	if (!isObj(obj))
707 		return (FALSE);
708 	if (!isCnt(cnt))
709 		syserr("IN in a non-container.");
710 
711 	return (objs[obj - OBJMIN].loc == cnt);
712 }
713 
714 
715 /*----------------------------------------------------------------------
716 
717   say()
718 
719   */
720 
sayloc(Aword loc)721 static void sayloc(Aword loc) {
722 	interpret(locs[loc - LOCMIN].nams);
723 }
724 
sayobj(Aword obj)725 static void sayobj(Aword obj) {
726 	interpret(objs[obj - OBJMIN].dscr2);
727 }
728 
sayact(Aword act)729 static void sayact(Aword act) {
730 	interpret(acts[act - ACTMIN].nam);
731 }
732 
sayint(Aword val)733 void sayint(Aword val) {
734 	char buf[25];
735 
736 	if (isHere(HERO)) {
737 		sprintf(buf, "%ld", (unsigned long) val);
738 		output(buf);
739 	}
740 }
741 
saystr(char * str)742 void saystr(char *str) {
743 	if (isHere(HERO))
744 		output(str);
745 	free(str);
746 }
747 
saylit(Aword lit)748 static void saylit(Aword lit) {
749 	char *str;
750 
751 	if (isNum(lit))
752 		sayint(litValues[lit - LITMIN].value);
753 	else {
754 		str = (char *)strdup((char *)litValues[lit - LITMIN].value);
755 		saystr(str);
756 	}
757 }
758 
sayarticle(Aword id)759 void sayarticle(Aword id) {
760 	if (!isObj(id))
761 		syserr("Trying to say article of something *not* an object.");
762 	if (objs[id - OBJMIN].art != 0)
763 		interpret(objs[id - OBJMIN].art);
764 	else
765 		prmsg(M_ARTICLE);
766 }
767 
say(Aword id)768 void say(Aword id) {
769 	char str[80];
770 
771 	if (isHere(HERO)) {
772 		if (isObj(id))
773 			sayobj(id);
774 		else if (isLoc(id))
775 			sayloc(id);
776 		else if (isAct(id))
777 			sayact(id);
778 		else if (isLit(id))
779 			saylit(id);
780 		else {
781 			sprintf(str, "Can't SAY item (%ld).", (unsigned long) id);
782 			syserr(str);
783 		}
784 	}
785 }
786 
787 
788 /*----------------------------------------------------------------------
789 
790   describe()
791 
792   */
793 
dscrloc(Aword loc)794 static void dscrloc(Aword loc) {
795 	if (locs[loc - LOCMIN].dscr != 0)
796 		interpret(locs[loc - LOCMIN].dscr);
797 }
798 
dscrobj(Aword obj)799 static void dscrobj(Aword obj) {
800 	objs[obj - OBJMIN].describe = FALSE;
801 	if (objs[obj - OBJMIN].dscr1 != 0)
802 		interpret(objs[obj - OBJMIN].dscr1);
803 	else {
804 		prmsg(M_SEEOBJ1);
805 		sayarticle(obj);
806 		say(obj);
807 		prmsg(M_SEEOBJ4);
808 		if (objs[obj - OBJMIN].cont != 0)
809 			list(obj);
810 	}
811 }
812 
dscract(Aword act)813 static void dscract(Aword act) {
814 	ScrElem *scr = NULL;
815 
816 	if (acts[act - ACTMIN].script != 0) {
817 		for (scr = (ScrElem *) addrTo(acts[act - ACTMIN].scradr); !endOfTable(scr); scr++)
818 			if (scr->code == acts[act - ACTMIN].script)
819 				break;
820 		if (endOfTable(scr)) scr = NULL;
821 	}
822 	if (scr != NULL && scr->dscr != 0)
823 		interpret(scr->dscr);
824 	else if (acts[act - ACTMIN].dscr != 0)
825 		interpret(acts[act - ACTMIN].dscr);
826 	else {
827 		interpret(acts[act - ACTMIN].nam);
828 		prmsg(M_SEEACT);
829 	}
830 	acts[act - ACTMIN].describe = FALSE;
831 }
832 
833 
834 static Aword dscrstk[255];
835 
describe(Aword id)836 void describe(Aword id) {
837 	int i;
838 	char str[80];
839 
840 	for (i = 0; i < dscrstkp; i++)
841 		if (dscrstk[i] == id)
842 			syserr("Recursive DESCRIBE.");
843 	dscrstk[dscrstkp++] = id;
844 
845 	if (isObj(id))
846 		dscrobj(id);
847 	else if (isLoc(id))
848 		dscrloc(id);
849 	else if (isAct(id))
850 		dscract(id);
851 	else {
852 		sprintf(str, "Can't DESCRIBE item (%ld).", (unsigned long) id);
853 		syserr(str);
854 	}
855 
856 	dscrstkp--;
857 }
858 
859 
860 /*----------------------------------------------------------------------
861 
862   use()
863 
864   */
865 
use(Aword act,Aword scr)866 void use(Aword act, Aword scr) {
867 	char str[80];
868 
869 	if (!isAct(act)) {
870 		sprintf(str, "Item is not an Actor (%ld).", (unsigned long) act);
871 		syserr(str);
872 	}
873 
874 	acts[act - ACTMIN].script = scr;
875 	acts[act - ACTMIN].step = 0;
876 }
877 
878 
879 /*----------------------------------------------------------------------
880 
881   list()
882 
883   */
884 
list(Aword cnt)885 void list(Aword cnt) {
886 	uint i;
887 	Aword props;
888 	Aword prevobj = 0;
889 	Boolean found = FALSE;
890 	Boolean multiple = FALSE;
891 
892 	/* Find container properties */
893 	if (isObj(cnt))
894 		props = objs[cnt - OBJMIN].cont;
895 	else if (isAct(cnt))
896 		props = acts[cnt - ACTMIN].cont;
897 	else
898 		props = cnt;
899 
900 	for (i = OBJMIN; i <= OBJMAX; i++) {
901 		if (in(i, cnt)) { /* Yes, it's in this container */
902 			if (!found) {
903 				found = TRUE;
904 				if (cnts[props - CNTMIN].header != 0)
905 					interpret(cnts[props - CNTMIN].header);
906 				else {
907 					prmsg(M_CONTAINS1);
908 					if (cnts[props - CNTMIN].nam != 0) /* It has it's own name */
909 						interpret(cnts[props - CNTMIN].nam);
910 					else
911 						say(cnts[props - CNTMIN].parent); /* It is actually an object or actor */
912 					prmsg(M_CONTAINS2);
913 				}
914 			} else {
915 				if (multiple) {
916 					needsp = FALSE;
917 					prmsg(M_CONTAINS3);
918 				}
919 				multiple = TRUE;
920 				sayarticle(prevobj);
921 				say(prevobj);
922 			}
923 			prevobj = i;
924 		}
925 	}
926 
927 	if (found) {
928 		if (multiple)
929 			prmsg(M_CONTAINS4);
930 		sayarticle(prevobj);
931 		say(prevobj);
932 		prmsg(M_CONTAINS5);
933 	} else {
934 		if (cnts[props - CNTMIN].empty != 0)
935 			interpret(cnts[props - CNTMIN].empty);
936 		else {
937 			prmsg(M_EMPTY1);
938 			if (cnts[props - CNTMIN].nam != 0) /* It has it's own name */
939 				interpret(cnts[props - CNTMIN].nam);
940 			else
941 				say(cnts[props - CNTMIN].parent); /* It is actually an actor or object */
942 			prmsg(M_EMPTY2);
943 		}
944 	}
945 	needsp = TRUE;
946 }
947 
948 
949 /*----------------------------------------------------------------------
950 
951   empty()
952 
953   */
954 
empty(Aword cnt,Aword whr)955 void empty(Aword cnt, Aword whr) {
956 	for (uint i = OBJMIN; i <= OBJMAX; i++)
957 		if (in(i, cnt))
958 			locate(i, whr);
959 }
960 
961 
962 /*----------------------------------------------------------------------*\
963 
964   Description of current location
965 
966   dscrobjs()
967   dscracts()
968   look()
969 
970 \*----------------------------------------------------------------------*/
971 
dscrobjs()972 void dscrobjs() {
973 	uint i;
974 	int prevobj = 0;
975 	Boolean found = FALSE;
976 	Boolean multiple = FALSE;
977 
978 	/* First describe everything here with its own description */
979 	for (i = OBJMIN; i <= OBJMAX; i++)
980 		if ((int)objs[i - OBJMIN].loc == cur.loc &&
981 		        objs[i - OBJMIN].describe &&
982 		        objs[i - OBJMIN].dscr1)
983 			describe(i);
984 
985 	/* Then list everything else here */
986 	for (i = OBJMIN; i <= OBJMAX; i++)
987 		if ((int)objs[i - OBJMIN].loc == cur.loc && objs[i - OBJMIN].describe) {
988 			if (!found) {
989 				prmsg(M_SEEOBJ1);
990 				sayarticle(i);
991 				say(i);
992 				found = TRUE;
993 			} else {
994 				if (multiple) {
995 					needsp = FALSE;
996 					prmsg(M_SEEOBJ2);
997 					sayarticle(prevobj);
998 					say(prevobj);
999 				}
1000 				multiple = TRUE;
1001 			}
1002 			prevobj = i;
1003 		}
1004 
1005 	if (found) {
1006 		if (multiple) {
1007 			prmsg(M_SEEOBJ3);
1008 			sayarticle(prevobj);
1009 			say(prevobj);
1010 		}
1011 		prmsg(M_SEEOBJ4);
1012 	}
1013 
1014 	/* Set describe flag for all objects */
1015 	for (i = OBJMIN; i <= OBJMAX; i++)
1016 		objs[i - OBJMIN].describe = TRUE;
1017 }
1018 
dscracts()1019 void dscracts() {
1020 	uint i;
1021 
1022 	for (i = HERO + 1; i <= ACTMAX; i++)
1023 		if ((int)acts[i - ACTMIN].loc == cur.loc && acts[i - ACTMIN].describe)
1024 			describe(i);
1025 
1026 	/* Set describe flag for all actors */
1027 	for (i = HERO; i <= ACTMAX; i++)
1028 		acts[i - ACTMIN].describe = TRUE;
1029 }
1030 
look()1031 void look() {
1032 	uint i;
1033 
1034 	if (looking)
1035 		syserr("Recursive LOOK.");
1036 
1037 	looking = TRUE;
1038 	/* Set describe flag for all objects and actors */
1039 	for (i = OBJMIN; i <= OBJMAX; i++)
1040 		objs[i - OBJMIN].describe = TRUE;
1041 	for (i = ACTMIN; i <= ACTMAX; i++)
1042 		acts[i - ACTMIN].describe = TRUE;
1043 
1044 	if (anyOutput)
1045 		para();
1046 
1047 	g_vm->glk_set_style(style_Subheader);
1048 	needsp = FALSE;
1049 	say(cur.loc);
1050 	needsp = FALSE;
1051 	output(".");
1052 	g_vm->glk_set_style(style_Normal);
1053 	newline();
1054 	needsp = FALSE;
1055 	describe(cur.loc);
1056 	dscrobjs();
1057 	dscracts();
1058 	looking = FALSE;
1059 }
1060 
1061 
1062 /*----------------------------------------------------------------------
1063 
1064   save()
1065 
1066   */
1067 
save()1068 void save() {
1069 	(void)g_vm->saveGame();
1070 }
1071 
1072 
1073 /*----------------------------------------------------------------------
1074 
1075   restore()
1076 
1077   */
1078 
restore()1079 void restore() {
1080 	(void)g_vm->loadGame();
1081 }
1082 
1083 
1084 /*----------------------------------------------------------------------
1085 
1086   rnd()
1087 
1088   */
1089 
rnd(Aword from,Aword to)1090 Aword rnd(Aword from, Aword to) {
1091 	if (to == from)
1092 		return to;
1093 	else if (to > from)
1094 		return (rand() / 10) % (to - from + 1) + from;
1095 	else
1096 		return (rand() / 10) % (from - to + 1) + to;
1097 }
1098 
1099 /*----------------------------------------------------------------------
1100 
1101   btw()
1102 
1103   BETWEEN
1104 
1105   */
1106 
btw(Aint val,Aint low,Aint high)1107 Abool btw(Aint val, Aint low, Aint high) {
1108 	if (high > low)
1109 		return low <= val && val <= high;
1110 	else
1111 		return high <= val && val <= low;
1112 }
1113 
1114 
1115 
1116 /*----------------------------------------------------------------------
1117 
1118   contains()
1119 
1120   */
1121 
contains(Aptr string,Aptr substring)1122 Aword contains(Aptr string, Aptr substring) {
1123 	Abool found;
1124 
1125 	strlow((char *)string);
1126 	strlow((char *)substring);
1127 
1128 	found = (strstr((char *)string, (char *)substring) != 0);
1129 
1130 	free((char *)string);
1131 	free((char *)substring);
1132 
1133 	return (found);
1134 }
1135 
1136 
1137 /*----------------------------------------------------------------------
1138 
1139   streq()
1140 
1141   Compare two strings approximately, ignore case
1142 
1143   */
streq(char a[],char b[])1144 Abool streq(char a[], char b[]) {
1145 	Boolean eq;
1146 
1147 	strlow(a);
1148 	strlow(b);
1149 
1150 	eq = (strcmp(a, b) == 0);
1151 
1152 	free(a);
1153 	free(b);
1154 
1155 	return (eq);
1156 }
1157 
1158 } // End of namespace Alan2
1159 } // End of namespace Glk
1160