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 
32    This file contains several things:
33    i) The code for each of the built-in verbs, all prefixed with 'v_'
34 	  (so, for example, the code for DROP is in v_drop()).
35    ii) The main routine for checking and running player commands.
36    iii) The main routine for doing intelligent disambiguation.
37 
38    */
39 
40 
41 /* ------------------------------------------------------------------- */
42 /* VERBS: Functions that implement the predefined verbs.   */
43 /* ------------------------------------------------------------------- */
44 
v_look()45 void v_look() {
46 	do_look = 1;
47 }
48 
49 
50 /* 1=N, etc. */
v_go(int dir)51 static void v_go(int dir) {
52 	int newloc, tmploc;
53 	int i;
54 	/*  rbool has_seen;*/
55 
56 	dir--;
57 
58 	tmploc = loc;
59 	newloc = room[loc].path[dir];
60 	if (newloc > exitmsg_base) { /* Customized error messages */
61 		msgout(newloc - exitmsg_base, 1);
62 		return;
63 	}
64 	if (newloc < 0) { /* Run autoverb */
65 		int v0;
66 
67 		v0 = verb_code(-newloc);
68 		if (v0 == 0) {
69 			if (!PURE_ERROR)
70 				writeln("GAME ERROR: Invalid verb.");
71 			return;
72 		}
73 		clear_stack();
74 		(void)scan_metacommand(0, v0, 0, 0, 0, NULL);
75 		return;
76 	}
77 	if (newloc < first_room) {
78 		if (dir == 12) /* Special */
79 			sysmsg(182, "Nothing happens.");
80 		else if (dir == 10) /* ENTER */
81 			sysmsg(197, "$You$ can't enter anything here.");
82 		else if (dir == 11) /* EXIT */
83 			sysmsg(198, "$You're$ not inside anything that $you$ can exit.");
84 		else
85 			sysmsg(13, "$You$ can't go that way.");
86 		return;
87 	}
88 	if (newloc > maxroom) {
89 		if (!PURE_ERROR)
90 			writeln("GAME ERROR: Invalid room number.");
91 		return;
92 	}
93 
94 	/* Then need to check for hostile creatures */
95 	/* (If we are going back to the room we came from and not PURE_HOSTILE
96 	   is set, then we don't need to check this) */
97 
98 	if (dir != 12 && (PURE_HOSTILE || newloc != oldloc + first_room))
99 		creatloop(i)
100 		if (creature[i].location == loc + first_room &&
101 		        creature[i].hostile) {
102 			parse_rec tmpcreat;
103 			curr_creat_rec = &tmpcreat;
104 			make_parserec(i + first_creat, &tmpcreat);
105 			sysmsg(14, "$The_c$$c_name$ blocks $your$ way.");
106 			curr_creat_rec = NULL;
107 			return;
108 		}
109 
110 	/*  has_seen=room[newloc-first_room].has_seen;*/
111 	goto_room(newloc - first_room);
112 	if (dir != 12 && newloc != tmploc + first_room) /* SPECIAL */
113 		oldloc = tmploc; /* Can backtrack as long as not from special */
114 	if (dir == 12 && special_ptr[loc].size > 0)
115 		/* need to print special of NEW room */
116 		runptr(loc, special_ptr, "INTERNAL ERROR: Invalid special ptr", 0, NULL, NULL);
117 
118 	if (tmploc == loc && dir == 12) /* SPECIAL that sends us nowhere */
119 		do_look = 0;
120 }
121 
122 
123 /* PUSH, PULL, TURN, PLAY, CHANGE_LOCATIONS */
v_noun(int vc,parse_rec * nounrec)124 static void v_noun(int vc, parse_rec *nounrec) {
125 	int dobj_;
126 
127 	dobj_ = p_obj(nounrec);
128 
129 	if (vc == 0 && !it_pushable(dobj_)) {
130 		int msgnum;
131 		if (!tcreat(dobj_)) msgnum = 172;
132 		else if (creature[dobj_ - first_creat].gender == 0)
133 			if (creature[dobj_ - first_creat].hostile) msgnum = 167;
134 			else msgnum = 168;
135 		else if (creature[dobj_ - first_creat].hostile) msgnum = 169;
136 		else msgnum = 170;
137 		sysmsgd(msgnum, "$You$ can't $verb$ $the_n$$noun$.", nounrec); /* Push */
138 		return;
139 	}
140 	if (vc == 1 && !it_pullable(dobj_)) { /* Pull */
141 		sysmsgd(tcreat(dobj_) ? 173 : 175, "$You$ can't $verb$ $the_n$$noun$.",
142 		        nounrec);
143 		return;
144 	}
145 	if (vc == 2 && !it_turnable(dobj_)) { /* Turn */
146 		sysmsgd(tcreat(dobj_) ? 164 : 166, "$You$ can't $verb$ $the_n$$noun$.",
147 		        nounrec);
148 		return;
149 	}
150 	if (vc == 3 && !it_playable(dobj_)) { /* Play */
151 		sysmsgd(tcreat(dobj_) ? 176 : 178, "$You$ can't $verb$ $the_n$$noun$.",
152 		        nounrec);
153 		return;
154 	}
155 	if (matchclass(dobj_, room[loc].key)) { /* SPECIAL triggered */
156 		v_go(13);
157 		return;
158 	}
159 	if (vc == 4) {
160 		sysmsgd(tcreat(dobj_) ? 180 : 181, "Nothing happens.", nounrec);
161 		return;
162 	}
163 	/* otherwise, print out relevent description. */
164 	if (vc == 0) /* Push */
165 		runptr(dobj_ - first_noun, push_ptr,
166 		       "$You$ $verb$ $the_n$$noun$ for a while, but nothing happens.",
167 		       171, nounrec, NULL);
168 	if (vc == 1) /* Pull */
169 		runptr(dobj_ - first_noun, pull_ptr,
170 		       "$You$ $verb$ $the_n$$noun$ a bit, but nothing happens.", 174,
171 		       nounrec, NULL);
172 	if (vc == 2) /* Turn */
173 		runptr(dobj_ - first_noun, turn_ptr,
174 		       "$You$ $verb$ $the_n$$noun$, but nothing happens.", 165,
175 		       nounrec, NULL);
176 	if (vc == 3) /* Play */
177 		runptr(dobj_ - first_noun, play_ptr,
178 		       "$You$ $verb$ $the_n$$noun$ for a bit, but nothing happens.", 177,
179 		       nounrec, NULL);
180 }
181 
182 /* vc==1 if ASK, 0 if TALK TO */
v_talk(int vc,parse_rec * nounrec,parse_rec * objrec)183 static void v_talk(int vc, parse_rec *nounrec, parse_rec *objrec) {
184 	int dobj_, iobj_;
185 
186 	dobj_ = p_obj(nounrec);
187 	iobj_ = p_obj(objrec);
188 
189 	if (nounrec->info == D_END || nounrec->info == D_AND) {
190 		alt_sysmsg(211, "Who $are$ $you$ addressing?", nounrec, objrec);
191 		return;
192 	}
193 	if (!genvisible(nounrec)) {
194 		alt_sysmsg(212, "Who $are$ $you$ addressing?", nounrec, objrec);
195 		return;
196 	}
197 	if (!tcreat(dobj_)) {
198 		alt_sysmsg(vc ? 161 : 156, "That isn't animate.", nounrec, objrec);
199 		return;
200 	}
201 	if (vc == 0)
202 		runptr(dobj_ - first_creat,
203 		       talk_ptr, "$Your$ conversational gambit is ignored.",
204 		       creature[dobj_ - first_creat].gender == 0 ? 157 : (iobj_ == 0 ? 159 : 158),
205 		       nounrec, objrec);
206 	if (vc == 1)
207 		runptr(dobj_ - first_creat, ask_ptr, "$You$ get no answer.",
208 		       iobj_ == 0 ? 162 : 163, nounrec, objrec);
209 }
210 
v_examine(parse_rec * nounrec)211 static void v_examine(parse_rec *nounrec) {
212 	if (!islit()) {
213 		sysmsgd(room[loc].light == 1 ? 19 : 20, "It's too dark to see anything.",
214 		        nounrec);
215 	}
216 	it_describe(nounrec->obj);
217 }
218 
v_view(parse_rec * nounrec)219 static void v_view(parse_rec *nounrec) { /* VIEW a picture */
220 	int i;
221 	int dobj_;
222 	dobj_ = p_obj(nounrec);
223 
224 	if (tnoun(dobj_) && noun[dobj_ - first_noun].pict != 0)
225 		pictcmd(1, pictable[noun[dobj_ - first_noun].pict - 1]);
226 	else if (tcreat(dobj_) && creature[dobj_ - first_creat].pict != 0)
227 		pictcmd(1, pictable[creature[dobj_ - first_creat].pict - 1]);
228 	else if (dobj_ == -ext_code[wscene] && room[loc].pict != 0)
229 		/* View the room picture */
230 		pictcmd(1, pictable[room[loc].pict - 1]);
231 	else {  /* room.PIX_bits */
232 		if (dobj_ < 0)
233 			for (i = 0; i < maxpix; i++) /* Check them all */
234 				if (dobj_ == -pix_name[i] &&
235 				        (room[loc].PIX_bits & (1L << i))) {
236 					pictcmd(2, i);
237 					return;
238 				}
239 		sysmsgd(217, "That can't be VIEWed here.", nounrec);
240 	}
241 }
242 
243 
v_read(parse_rec * nounrec)244 static void v_read(parse_rec *nounrec) {
245 	int dobj_;
246 	dobj_ = p_obj(nounrec);
247 
248 	if (!tnoun(dobj_) || !noun[dobj_ - first_noun].readable) {
249 		sysmsg(134,
250 		       "$You$ can't read $the_n$$noun$, "
251 		       "so instead $you$ just examine $n_indir$.");
252 		it_describe(dobj_);
253 		return;
254 	}
255 	if (text_ptr[dobj_ - first_noun].size > 0)
256 		runptr(dobj_ - first_noun, text_ptr,
257 		       "INTERNAL ERROR: Invalid read pointer", 0, NULL, NULL);
258 	else
259 		runptr(dobj_ - first_noun, noun_ptr, "$You$ learn nothing new.",
260 		       193, nounrec, NULL);
261 }
262 
263 
v_eat(int vc,parse_rec * nounrec)264 static void v_eat(int vc, parse_rec *nounrec) {
265 	int dobj_;
266 	dobj_ = p_obj(nounrec);
267 
268 	if (!tnoun(dobj_)) {
269 		sysmsgd(124, "That can't be consumed.", nounrec);
270 		return;
271 	}
272 	if (vc == 0 && !noun[dobj_ - first_noun].edible) {
273 		sysmsgd(124, "$You$ can't eat that.", nounrec);
274 		return;
275 	}
276 	if (vc == 1 && !noun[dobj_ - first_noun].drinkable) {
277 		sysmsgd(127, "$You$ can't drink that.", nounrec);
278 		return;
279 	}
280 
281 	sysmsgd(128, "$You$ $verb$ $the_n$$adjective$ $noun$.", nounrec);
282 
283 	if (noun[dobj_ - first_noun].movable) it_destroy(dobj_);
284 	if (noun[dobj_ - first_noun].poisonous) {
285 		sysmsgd(129, "Unfortunatly, $n_pro$ $n_was$ poisonous.", nounrec);
286 		deadflag = 1;
287 	}
288 }
289 
290 
291 /* assumes objrec is in the noun range */
can_wear(parse_rec * objrec)292 static int can_wear(parse_rec *objrec) {
293 	static const char *errs[] = {
294 		"$The_n$$noun$ $n_is$ far too heavy to wear.",
295 		"$You're$ already loaded down with too much weight as it is.",
296 		"$The_n$$noun$ $n_is$ too big and bulky to wear.",
297 		"$You're$ wearing too much to also wear $the_n$$noun$."
298 	};
299 	int n;
300 
301 	if (!it_canmove(objrec->obj)) {
302 		sysmsgd(202, "$You$ can't move $the_n$$noun$.", objrec);
303 	}
304 	n = check_fit(objrec->obj, 1000);
305 	if (n == FIT_OK /* || n>=FIT_SIZE */)  return 1;
306 	sysmsgd(37 + n, errs[n - 1], objrec);
307 	return 0;
308 }
309 
310 
311 /* assumes objrec is in the noun range */
can_carry(parse_rec * objrec)312 static int can_carry(parse_rec *objrec) {
313 	static const char *errs[] = {
314 		"$The_n$$noun$ $n_is$ far too heavy to carry.",
315 		"$You're$ already carrying too much weight as it is.",
316 		"$The_n$$noun$ $n_is$ too big and bulky to pick up.",
317 		"$You're$ carrying too much to also carry $the_n$$noun$."
318 	};
319 	int n;
320 
321 	n = check_fit(objrec->obj, 1);
322 	if (n == FIT_OK) return 1;
323 	sysmsgd(30 + n - 1, errs[n - 1], objrec);
324 	return 0;
325 }
326 
v_get(parse_rec * objrec)327 static int v_get(parse_rec *objrec) {
328 	int cnt, i;
329 	int obj;
330 
331 	obj = objrec->obj;
332 
333 	/* If there is a hostile creature in the room and PURE_GETHOSTILE isn't
334 	   set, then don't let the player pick up anything */
335 	if (!PURE_GETHOSTILE)
336 		creatloop(i)
337 		if (creature[i].location == loc + first_room &&
338 		        creature[i].hostile) {
339 			parse_rec tmpcreat;
340 			make_parserec(i + first_creat, &tmpcreat);
341 			curr_creat_rec = &tmpcreat;
342 			sysmsgd(14, "$The_c$$c_name$ blocks $your$ way.", objrec);
343 			return 0;
344 		}
345 
346 	if (objrec->info == D_ALL) {
347 		cnt = 0;
348 		nounloop(i)
349 		if (noun[i].location == loc + first_room && noun[i].movable) {
350 			/* Need to add weight/size check */
351 			parse_rec tmpnoun;
352 			make_parserec(i + first_noun, &tmpnoun);
353 			if (can_carry(&tmpnoun)) {
354 				get_obj(i + first_noun);
355 				sysmsgd(8, "$You$ pick up $the_n$$adjective$ $noun$.", &tmpnoun);
356 			}
357 			cnt++;
358 		}
359 		if (cnt == 0) {
360 			sysmsgd(24, "There doesn't seem to be anything here to take.", objrec);
361 			return 0;
362 		} else return 1;
363 	}
364 	if (it_door(obj, objrec->noun)) {
365 		if (room[loc].locked_door)
366 			sysmsgd(25, "You can't pick up the door.", objrec);
367 		else
368 			sysmsgd(26, "You can't pick up the doorway.", objrec);
369 		return 0;
370 	}
371 	if (!tnoun(obj) || !noun[obj - first_noun].movable) {
372 		sysmsgd(tcreat(obj) ? (creature[obj - first_creat].hostile ? 34 : 35) : 29,
373 		        "$You$ can't pick $the_n$$noun$ up.", objrec);
374 		return 0;
375 	}
376 	if (it_loc(obj) == 1) {
377 		sysmsgd(27, "$You$ already have $the_n$$noun$.", objrec);
378 		return 1;
379 	}
380 	if (!can_carry(objrec)) return 0;
381 	get_obj(obj);
382 	sysmsgd(8, "$You$ pick up $the_n$$adjective$ $noun$.", objrec);
383 	return 1;
384 }
385 
v_remove(parse_rec * objrec)386 static int v_remove(parse_rec *objrec) {
387 	int i, j;
388 	integer obj;
389 
390 	obj = objrec->obj;
391 	if (objrec->info == D_ALL) {
392 		if (player_worn == 0) {
393 			sysmsgd(46, "$You're$ not wearing anything.", objrec);
394 			return 0;
395 		}
396 		safecontloop(i, j, 1000)
397 		if (it_canmove(i)) {
398 			parse_rec tmp;
399 			if (PURE_WEAR) drop_obj(i);
400 			else it_move(i, 1);  /* Really need to check to make sure
401 				   we haven't exceeded weight requirement
402 				 here */
403 			make_parserec(i, &tmp);
404 			sysmsgd(9, "$You$ take off $the_n$$noun$.", &tmp);
405 		}
406 		return 1;
407 	}
408 	if (it_loc(obj) != 1000) {
409 		sysmsgd(213, "$You're$ not wearing that.", objrec);
410 		return 0;
411 	}
412 	if (!it_canmove(obj)) {
413 		sysmsgd(201, "$You're$ not able to remove $the_n$$noun$.", objrec);
414 		return 0;
415 	}
416 	sysmsgd(9, "$You$ take off $the_n$$noun$.", objrec);
417 	if (PURE_WEAR) drop_obj(obj);  /* Required to be consistent w/ AGT */
418 	else v_get(objrec);  /* (trap can_carry problems) */
419 	return 1;
420 }
421 
v_drop(parse_rec * objrec)422 static void v_drop(parse_rec *objrec) {
423 	int i, j;
424 	int obj;
425 	obj = objrec->obj;
426 
427 	if (obj == ALL_MARK) {
428 		if (player_contents == 0)
429 			sysmsgd(45, "$You$ don't have anything to drop.", objrec);
430 		else safecontloop(i, j, 1) {
431 			parse_rec tmp;
432 			make_parserec(i, &tmp);
433 			drop_obj(i);
434 			sysmsgd(9, "$You$ $verb$ $the_n$$noun$.", &tmp);
435 		}
436 		return;
437 	}
438 	if (!it_possess(obj)) {
439 		sysmsgd(47, "$You$ don't have that.", objrec);
440 		return;
441 	}
442 	if (!it_canmove(obj)) {
443 		sysmsgd(200, "$You're$ not able to $verb$ $the_n$$noun$.", objrec);
444 		return;
445 	}
446 	if (it_loc(obj) == 1000) {
447 		sysmsgd(216, "(Taking it off first)", objrec);
448 	}
449 	sysmsgd(9, "$You$ $verb$ $the_n$$noun$.", objrec);
450 	drop_obj(obj);
451 }
452 
v_wear(parse_rec * objrec)453 static void v_wear(parse_rec *objrec) {
454 	int i, cnt;
455 	int obj;
456 
457 	obj = objrec->obj;
458 	if (objrec->info == D_ALL) {
459 		cnt = 0;
460 		nounloop(i)
461 		if (noun[i].location != 1000 && visible(i + first_noun) &&
462 		        noun[i].wearable) {
463 			parse_rec tmp;
464 			make_parserec(i + first_noun, &tmp);
465 			if (can_wear(&tmp)) {
466 				it_move(i + first_noun, 1000);
467 				sysmsgd(42, "$You$ put on $the_n$$adjective$ $noun$.", &tmp);
468 			}
469 			cnt++;
470 		}
471 		if (cnt == 0)
472 			sysmsgd(36, "There doesn't seem to be anything $you$ can wear here.",
473 			        objrec);
474 		return;
475 	}
476 	if (!tnoun(obj) || !noun[obj - first_noun].wearable) {
477 		sysmsgd(tcreat(obj) ? (creature[obj - first_creat].hostile ? 43 : 44) : 203,
478 		        "$You$ can't wear that.", objrec);
479 		return;
480 	}
481 	if (it_loc(obj) == 1000) {
482 		sysmsgd(37, "$You$ $are$ already wearing $the_n$$noun$.", objrec);
483 		return;
484 	}
485 	if (!can_wear(objrec)) return;
486 	sysmsgd(42, "$You$ put on $the_n$$noun$.", objrec);
487 	it_move(obj, 1000);
488 }
489 
490 /* l_or_u: 0=lock, 1=unlock */
do_lock(uchar l_or_u,parse_rec * nounrec,parse_rec * objrec)491 static int do_lock(uchar l_or_u, parse_rec *nounrec, parse_rec *objrec) {
492 	int dnoun;
493 	int dobj_, iobj_;
494 	word dobj_word;
495 
496 	dobj_ = p_obj(nounrec);
497 	iobj_ = p_obj(objrec);
498 	dobj_word = nounrec->noun;
499 
500 	if (it_door(dobj_, dobj_word) && l_or_u != room[loc].locked_door) {
501 		/* That is, trying to unlock an unlocked door, or lock a locked one. */
502 		if (l_or_u == 0)
503 			alt_sysmsg(114, "The door is already locked.", nounrec, objrec);
504 		else
505 			alt_sysmsg(105,
506 			           "There doesn't seem to be any door here that need unlocking.",
507 			           nounrec, objrec);
508 		return 0;
509 	}
510 	if (!it_lockable(dobj_, dobj_word)) {
511 		alt_sysmsg((l_or_u ? 108 : 118), "$The_n$$noun$ can't be $verb$ed.",
512 		           nounrec, objrec);
513 		return 0;
514 	}
515 	if (tnoun(dobj_) && noun[dobj_ - first_noun].closable && it_open(dobj_)) {
516 		if (l_or_u == 0) {
517 			alt_sysmsg(120, "$You$ will need to close $the_n$$noun$ first.",
518 			           nounrec, objrec);
519 			return 0;
520 		} else { /* l_or_u==1 */
521 			alt_sysmsg(110, "$The_n$$noun$ $n_is$ already open!",
522 			           nounrec, objrec);
523 			return 0;
524 		}
525 	}
526 	if (it_locked(dobj_, dobj_word) != l_or_u) {
527 		alt_sysmsg((l_or_u ? 109 : 119), "$The_n$$noun$ $n_is$ already $verb$ed",
528 		           nounrec, objrec);
529 		return 0;
530 	}
531 	if (it_door(dobj_, dobj_word) || dobj_ < 0) { /* i.e. a door */
532 		alt_sysmsg((l_or_u ? 104 : 115),
533 		           "$You$ try to $verb$ $the_n$$noun$, but fail.",
534 		           nounrec, objrec);
535 		return 0;
536 	}
537 	dnoun = dobj_ - first_noun;
538 	if (iobj_ == 0) {
539 		alt_sysmsg((l_or_u ? 106 : 208),
540 		           "$You$ will need to use something to do that.",
541 		           nounrec, objrec);
542 		return 0;
543 	}
544 	if (!player_has(iobj_)) {
545 		alt_sysmsg((l_or_u ? 107 : 117), "$You$ don't have $the_o$$object$.",
546 		           nounrec, objrec);
547 		return 0;
548 	}
549 	if (!matchclass(iobj_, noun[dnoun].key)) {
550 		alt_sysmsg(l_or_u ? (vb == 15 ? 80 : 111) : 121, /* vb 15 is OPEN */
551 		           "$The_o$$object$ doesn't fit.", nounrec, objrec);
552 		return 0;
553 	}
554 	noun[dnoun].locked = !l_or_u;
555 	return 1;
556 }
557 
558 /* First argument indicates lock or unlock-- 0=lock, 1=unlock */
v_lock(uchar l_or_u,parse_rec * nounrec,parse_rec * objrec)559 static void v_lock(uchar l_or_u, parse_rec *nounrec, parse_rec *objrec) {
560 	if (!do_lock(l_or_u, nounrec, objrec)) return;
561 	/* Need to fix these messages: */
562 	alt_sysmsg((l_or_u ? 112 : 122),
563 	           "$You$ $verb$ $the_n$$noun$ with $the_o$$object$.",
564 	           nounrec, objrec);
565 }
566 
567 /* OPEN ... WITH ... */
v_open(parse_rec * nounrec,parse_rec * objrec)568 static void v_open(parse_rec *nounrec, parse_rec *objrec) {
569 	int dnoun;
570 	int dobj_, iobj_;
571 
572 	dobj_ = p_obj(nounrec);
573 	iobj_ = p_obj(objrec);
574 
575 	dnoun = dobj_ - first_noun;
576 	if (it_door(dobj_, nounrec->noun)) {
577 		if (room[loc].locked_door)
578 			alt_sysmsg(71, "$The_n$$noun$ $n_is$ locked.",
579 			           nounrec, objrec);
580 		else
581 			alt_sysmsg(72, "$The_n$$noun$ $n_is$ already open.",
582 			           nounrec, objrec);
583 		return;
584 	}
585 	if (it_open(dobj_)) {
586 		alt_sysmsg(78, "$The_n$$noun$ $n_is$ already open.", nounrec, objrec);
587 		return;
588 	}
589 	if (!tnoun(dobj_) || !noun[dnoun].closable) {
590 		alt_sysmsg(77, "$You$ can't open $the_n$$noun$.", nounrec, objrec);
591 		return;
592 	}
593 	if (iobj_ != 0) { /* Need to do unlock action */
594 		if (!do_lock(1, nounrec, objrec)) return;
595 		/* If something goes wrong, return */
596 	}
597 	if (noun[dnoun].lockable && noun[dnoun].locked) {
598 		alt_sysmsg(79, "It is locked.", nounrec, objrec);
599 		return;
600 	}
601 	noun[dnoun].open = 1;
602 	if (iobj_ != 0) /* Obviously these messages need improvement */
603 		alt_sysmsg(81, "$You$ have opened $the_n$$noun$ with $the_o$$object$.",
604 		           nounrec, objrec);
605 	else alt_sysmsg(82, "$You$ have opened $the_n$$noun$.", nounrec, objrec);
606 	if (noun[dnoun].contents != 0)
607 		alt_sysmsg(187, "Inside, $you$ see the following:", nounrec, objrec);
608 	print_contents(dobj_, 1);
609 }
610 
v_close(parse_rec * nounrec)611 static void v_close(parse_rec *nounrec) {
612 	int dobj_;
613 	dobj_ = nounrec->obj;
614 
615 	if (it_door(dobj_, nounrec->noun)) {
616 		if (room[loc].locked_door)
617 			sysmsgd(84, "The door is already closed.", nounrec);
618 		else
619 			sysmsgd(85, "That apparently can't be closed.", nounrec);
620 		return;
621 	}
622 	if (!it_open(dobj_)) {
623 		sysmsgd(88, "$The_n$$noun$ $n_is$ already closed.", nounrec);
624 		return;
625 	}
626 	if (!tnoun(dobj_) || !noun[dobj_ - first_noun].closable) {
627 		sysmsgd(87, "$You$ can't close $the_n$$noun$.", nounrec);
628 		return;
629 	}
630 	noun[dobj_ - first_noun].open = 0;
631 	sysmsgd(89, "$You$ have closed $the_n$$noun$.", nounrec);
632 }
633 
634 
v_light(int newstate,parse_rec * nounrec)635 static void v_light(int newstate, parse_rec *nounrec) {
636 	int dobj_;
637 	dobj_ = p_obj(nounrec);
638 
639 	if (!tnoun(dobj_) || !noun[dobj_ - first_noun].light) {
640 		sysmsgd(newstate ? 135 : 140, "$You$ can't $verb$ $the_n$$noun$.", nounrec);
641 		return;
642 	}
643 	dobj_ -= first_noun;
644 	if (noun[dobj_].on == newstate) {
645 		if (newstate)
646 			sysmsgd(136, "$The_n$$noun$ $n_is$ already lit.", nounrec);
647 		else sysmsgd(141,
648 			             "$The_n$$noun$ $n_is$n't lit, so $you$ can't extinguish $n_indir$",
649 			             nounrec);
650 		return;
651 	}
652 	noun[dobj_].on = newstate;
653 	if (newstate) sysmsgd(138, "$The_n$$noun$ $n_is$ now lit.", nounrec);
654 	else sysmsgd(143, "$The_n$$noun$ $n_is$ no longer lit.", nounrec);
655 }
656 
v_turn(word prep_,parse_rec * nounrec)657 static void v_turn(word prep_, parse_rec *nounrec) {
658 	int newstate;  /* 1=on, 0=off */
659 	int dobj_;
660 	dobj_ = p_obj(nounrec);
661 
662 	newstate = (prep_ == ext_code[won]); /* ON or OFF ? */
663 	if (!it_turnable(dobj_) && !nounattr(dobj_, light)) {
664 		sysmsgd(newstate ? 209 : 210,
665 		        "$You$ can't turn $the_n$$noun$ $prep_$.", nounrec);
666 		return;
667 	}
668 	if (matchclass(dobj_, room[loc].key)) { /* SPECIAL triggered */
669 		v_go(13);
670 		return;
671 	}
672 	if (!tnoun(dobj_)) { /* This should be redundant */
673 		writeln("INTERNAL ERROR: Non-noun turn on/off not supported");
674 		return;
675 	}
676 	dobj_ -= first_noun;
677 	if (noun[dobj_].on == newstate) {
678 		sysmsgd(newstate ? 137 : 142, "$The_n$$noun$ $n_is$ already $prep_$.",
679 		        nounrec);
680 		return;
681 	}
682 	noun[dobj_].on = newstate;
683 	sysmsgd(newstate ? 139 : 144, "$The_n$$noun$ $n_is$ now $prep_$.", nounrec);
684 }
685 
686 
687 
688 /* Missile=1 if actually firing a weapon. */
v_attack(uchar missile,parse_rec * targrec,parse_rec * weprec)689 static void v_attack(uchar missile, parse_rec *targrec, parse_rec *weprec) {
690 	int targ, wep;
691 	targ = targrec->obj;
692 	wep = weprec->obj;
693 
694 	/* The following fix really belongs in the parser, but it might
695 	   break some games to do this translation before running metacommands */
696 	if (missile && targ == 0) /* SHOOT <target> */
697 		if (!tnoun(wep) || !noun[wep - first_noun].shootable) {
698 			targ = wep;
699 			targrec = weprec;
700 			wep = 0;
701 		}
702 
703 	curr_creat_rec = targrec; /* So error messages will print properly */
704 	if (wep > 0 && !player_has(wep)) {
705 		alt_sysmsg(98, "(Getting $the_o$$object$ first)", targrec, weprec);
706 		if (!v_get(weprec)) return;
707 	}
708 	if ((targ > 0 && !tcreat(targ)) || targ < 0) {
709 		alt_sysmsg(missile ? 90 : 93,
710 		           "It only makes sense to attack living things.",
711 		           targrec, weprec);
712 		return;
713 	}
714 	if (missile) {
715 		if (wep == 0) {
716 			sysmsgd(94, "It's not clear what $you$ want to $verb$ with.", targrec);
717 			return;
718 		} else if (!tnoun(wep) || !noun[wep - first_noun].shootable) {
719 			alt_sysmsg(it_isweapon(wep) ? 96 : 95,
720 			           "$The_o$$object$ doesn't seem to be able to fire.",
721 			           targrec, weprec);
722 			return;
723 		} else if (noun[wep - first_noun].num_shots <= 0) {
724 			alt_sysmsg(97, "$The_o$$object$ $o_is$ out of ammunition.",
725 			           targrec, weprec);
726 			return;
727 		} else noun[wep - first_noun].num_shots--;
728 	}
729 
730 	if (targ == 0) {
731 		if (!missile) {
732 			alt_sysmsg(206, "Attack what???", NULL, weprec);
733 			return;
734 		} else {
735 			alt_sysmsg(188, "$You$ fire a shot into the air.", NULL, weprec);
736 			return;
737 		}
738 	}
739 
740 	if (wep == 0) { /* and !missile, but that's taken care of above */
741 		sysmsgd(creature[targ - first_creat].hostile ? 91 : 92,
742 		        "$You$ attack $the_n$$noun$ with $your$ bare hands, but $n_pro$ "
743 		        "evades $your$ attack.", targrec);
744 		return;
745 	}
746 
747 	if (matchclass(wep, creature[targ - first_creat].weapon)) {
748 		if (missile)
749 			alt_sysmsg(creature[targ - first_creat].hostile ? 99 : 101,
750 			           "$You$ shoot $the_n$$noun$; "
751 			           "$n_pro$ vanishes in a cloud of red smoke."
752 			           , targrec, weprec);
753 		else
754 			alt_sysmsg(creature[targ - first_creat].hostile ? 49 : 53,
755 			           "$You$ kill $the_o$$object$; "
756 			           "$o_pro$ vanishes in a cloud of red smoke.",
757 			           weprec, targrec);
758 		it_destroy(targ);
759 		if (!missile) drop_obj(wep);
760 		return;
761 	} else {
762 		if (!missile) {
763 			int msgnum;
764 			if (creature[targ - first_creat].hostile) {
765 				alt_sysmsg(50, NULL, weprec, targrec); /* Preliminary message */
766 				msgnum = 51;
767 			} else msgnum = 54;
768 			if (noun[wep - first_noun].drinkable) { /* i.e. a liquid */
769 				alt_sysmsg(msgnum + 1, "$You$ splash $the_o$$object$ with "
770 				           "$the_n$$noun$, but the liquid quickly evaporates "
771 				           "without noticable effect.", weprec, targrec);
772 				it_destroy(wep);
773 			} else {
774 				alt_sysmsg(msgnum,
775 				           "$You$ strike at $the_o$$object$ with $the_n$$noun$, "
776 				           "but $your$ weapon bounces off of $o_indir$ harmlessly",
777 				           weprec, targrec);
778 				drop_obj(wep);
779 			}
780 		} else
781 			alt_sysmsg(creature[targ - first_creat].hostile ? 100 : 102 ,
782 			           "$You$ fire at $the_n$$noun$ with $the_o$$object$, but $your$ "
783 			           "shots don't seem to have any effect.", targrec, weprec);
784 
785 		if (creature[targ - first_creat].hostile &&
786 		        ++creature[targ - first_creat].counter >=
787 		        creature[targ - first_creat].threshold) {
788 			alt_sysmsg(204, "$The_n$$noun$ counterattacks! $N_pro$ fights "
789 			           "viciously and $you$ $are$ unable to defend $your$self "
790 			           "against $n_indir$.", targrec, weprec);
791 			deadflag = 1;
792 		}
793 	}
794 }
795 
796 /* child_proc is true if v_put is being called by v_put, and so
797    shouldn't print success messages */
v_put(parse_rec * nounrec,word prep_,parse_rec * objrec,rbool child_proc)798 static rbool v_put(parse_rec *nounrec, word prep_,
799 				   parse_rec *objrec, rbool child_proc) {
800 	rbool in_prep;
801 	int dobj_, iobj_;
802 
803 	dobj_ = p_obj(nounrec);
804 	iobj_ = p_obj(objrec);
805 
806 	in_prep = (prep_ == ext_code[win] || prep_ == ext_code[winto]
807 	           || prep_ == ext_code[winside]);
808 
809 	if (prep_ == 0 || iobj_ == 0) {
810 		v_drop(nounrec);
811 		return 1;
812 	}
813 	if (!tnoun(dobj_)) {
814 		alt_sysmsg(tcreat(dobj_) ? 11 : 10,
815 		           "$You$ can't do that with $the_n$$noun$.",
816 		           nounrec, objrec);
817 		return 0;
818 	}
819 	if (!noun[dobj_ - first_noun].movable) {
820 		alt_sysmsg(61, "$You$ can't move $the_n$$adjective$ $noun$.",
821 		           nounrec, objrec);
822 		return 0;
823 	}
824 	if (tcreat(iobj_)) {
825 		alt_sysmsg(189, "$The_o$$object$ doesn't want $n_indir$.",
826 		           nounrec, objrec);
827 		return 0;
828 	}
829 	if (!tnoun(iobj_)) {
830 		alt_sysmsg(tcreat(iobj_) ? 12 : 64,
831 		           "$You$ can't put something $prep_$ $the_o$$object$.",
832 		           nounrec, objrec);
833 		return 0;
834 	}
835 	if (dobj_ == iobj_) {
836 		alt_sysmsg(62, "$You$ can't put something $prep_$ $n_indir$self.",
837 		           nounrec, objrec);
838 		return 0;
839 	}
840 	if (!it_open(iobj_) && in_prep) {
841 		alt_sysmsg(65, "$The_o$$object$ $o_is$n't open.", nounrec, objrec);
842 		return 0;
843 	}
844 	if (player_has(iobj_) && !in_prep) {
845 		alt_sysmsg(is_within(iobj_, 1, 0) ? 68 : 69,
846 		           "$You$ can't put $the_n$$noun$ $prep_$ something that $you$ "
847 		           "$are$ carrying.", nounrec, objrec);
848 		return 0;
849 	}
850 
851 	if (in_prep) { /* PUT IN */
852 		if (check_fit(dobj_, iobj_) != FIT_OK) {
853 			alt_sysmsg(66, "$You$ can't fit $the_n$$noun$ into $the_o$$object$.",
854 			           nounrec, objrec);
855 			return 0;
856 		}
857 		if (it_loc(dobj_) == 1000)
858 			alt_sysmsg(216, "(Taking $n_indir$ off first)", nounrec, objrec);
859 		it_move(dobj_, iobj_);
860 	} else { /* PUT <prep_> with a preposition other than IN */
861 		int parent;
862 
863 		parent = it_loc(iobj_);
864 		if (!troom(parent)) {
865 			parse_rec parent_rec;
866 			make_parserec(parent, &parent_rec);
867 			if (!v_put(nounrec, ext_code[win], &parent_rec, 1)) return 0;
868 		} else {
869 			if (it_loc(dobj_) == 1000)
870 				alt_sysmsg(216, "(Taking $n_indir$ off first)", nounrec, objrec);
871 			drop_obj(dobj_);
872 		}
873 		dobj_ -= first_noun;
874 		assert(noun[dobj_].pos_prep == 0); /* v_put should have ensured this */
875 		noun[dobj_].pos_prep = prep_;
876 		noun[dobj_].pos_name = it_name(iobj_);
877 		if (iobj_ > 0) noun[dobj_].nearby_noun = iobj_;
878 	}
879 	if (!child_proc)
880 		alt_sysmsg(67, "$You$ place $the_n$$noun$ $prep_$ $the_o$$object$.",
881 		           nounrec, objrec);
882 	return 1;
883 }
884 
885 
886 /* at, to, in, into, across, inside */
v_throw(parse_rec * nounrec,word prep_,parse_rec * objrec)887 static void v_throw(parse_rec *nounrec, word prep_, parse_rec *objrec) {
888 	int dobj_, iobj_;
889 	dobj_ = p_obj(nounrec);
890 	iobj_ = p_obj(objrec);
891 
892 	/* Need to check to see what the preposition is-- if it is AT
893 	   then we should send it to attack routine. */
894 	if (!player_has(nounrec->obj)) {
895 		alt_sysmsg(47, "$You$ don't have $the_n$$noun$.", nounrec, objrec);
896 		return;
897 	}
898 	if (prep_ == 0) {
899 		v_drop(nounrec);
900 		return;
901 	}
902 	if (prep_ != ext_code[wat])
903 		v_put(nounrec, prep_, objrec, 0);
904 	else /* prep_ is AT */
905 		if (!noun[dobj_ - first_noun].movable) {
906 			alt_sysmsg(215, "$You$ can't move $the_n$$adjective$ $noun$.",
907 			           nounrec, objrec);
908 			return;
909 		}
910 	if (tcreat(iobj_))  /* If a creature, treat as an attack */
911 		v_attack(0, objrec, nounrec);
912 	else {  /* THROW AT somethin inanimate */
913 		if (dobj_ == iobj_) {
914 			alt_sysmsg(56, "$You$ can't $verb$ $the_n$$noun$ $prep_$ $n_indir$self.",
915 			           nounrec, objrec);
916 			return;
917 		}
918 		if (it_loc(dobj_) == 1000)
919 			alt_sysmsg(216, "(Taking it off first)", nounrec, objrec);
920 
921 		if (tnoun(dobj_) && noun[dobj_ - first_noun].drinkable) {
922 			/* A liquid */
923 			if (tnoun(iobj_) && noun[iobj_ - first_noun].open)
924 				alt_sysmsg(58, "$You$ throw $the_n$$noun$ into $the_o$$object$, "
925 				           "but $n_pro$ quickly evaporates.",
926 				           nounrec, objrec);
927 			else
928 				alt_sysmsg(57, "$The_n$$noun$ splashes on $the_o$$object$ but "
929 				           "quickly evaporates.", nounrec, objrec);
930 			it_destroy(dobj_);
931 		} else { /* _Not_ a liquid: */
932 			if (tnoun(iobj_) && noun[iobj_ - first_noun].open)
933 				if (check_fit(dobj_, iobj_)) {
934 					alt_sysmsg(60, "$The_n$$noun$ lands inside $the_o$$object$.",
935 					           nounrec, objrec);
936 					it_move(dobj_, iobj_);
937 					return;
938 				} else {
939 					alt_sysmsg(205, "You $verb$ $the_n$$noun$ into $the_o$$object$, "
940 					           "but there isn't enough room and $n_pro$ falls out.",
941 					           nounrec, objrec);
942 				}
943 			else
944 				alt_sysmsg(59, "$The_n$$noun$ bounces off $the_o$$object$.",
945 				           nounrec, objrec);
946 			/* At this point, either the object is closed or doesn't have enough
947 			room */
948 			it_move(dobj_, first_room + loc);
949 		}
950 	}
951 }
952 
953 
954 
v_inventory(void)955 void v_inventory(void) {
956 	if (player_contents != 0) {
957 		sysmsg(130, "$You're$ carrying:");
958 		print_contents(1, 1);  /* obj=1=self, ind_lev=1 */
959 	} else sysmsg(131, "$You$ $are$ empty-handed.");
960 	if (player_worn != 0) {
961 		sysmsg(132, "$You're$ wearing:");
962 		print_contents(1000, 1);
963 	}
964 }
965 
966 
967 
968 
969 
v_quit(void)970 static void v_quit(void) {
971 	sysmsg(145, "Are you sure you want to quit?");
972 	if (yesno("")) {
973 		sysmsg(146, NULL);
974 		quitflag = 1;
975 	}
976 }
977 
978 const char dirname[12][10] = {"north", "south", "east", "west",
979 							  "northeast", "northwest", "southeast", "southwest",
980 							  "up", "down", "in", "out"
981 							 };
982 
v_listexit(void)983 void v_listexit(void) {
984 	int i, j, k;
985 
986 	if (!islit()) {
987 		sysmsg(23, "It is too dark to see anything.");
988 		return;
989 	}
990 	j = k = 0;
991 	for (i = 0; i < 12; i++)
992 		if (room[loc].path[i] != 0) k++;
993 	if (k == 0)
994 		sysmsg(224, "There are no immediately visible exits.");
995 	else {
996 		sysmsg(225, "There are exits to");
997 		for (i = 0; i < 12; i++)
998 			if (room[loc].path[i] != 0) {
999 				j++;
1000 				if (j > 1) writestr(", ");
1001 				if (j > 1 && j == k) writestr("or ");
1002 				if (i < 8) writestr("the ");
1003 				writestr(dirname[i]);
1004 			}
1005 		writeln(".");
1006 	}
1007 }
1008 
1009 
v_yell(void)1010 static void v_yell(void) {
1011 	sysmsg(150, "YAAAAEEEEEEEEOOOOOOUUUUUAAAAHHHHHH!!!!!");
1012 }
1013 
1014 
1015 
1016 /* ------------------------------------------------------------------- */
1017 /*  VERB EXECUTION AND GRAMMER CHECKING */
1018 
1019 
checkgram(int vb_,int dobj_,word prep_,int iobj_,rbool redir_flag)1020 static int checkgram(int vb_, int dobj_, word prep_, int iobj_, rbool redir_flag) {
1021 	int i;
1022 	int msgnum;
1023 
1024 	/* We turn off certain sorts of grammar checking if either PURE_GRAMMAR
1025 	   is set or there has been signicant redirection. */
1026 	if (redir_flag < 2) redir_flag = 0;
1027 	if (PURE_GRAMMAR) redir_flag = 1;
1028 
1029 	/* First of all, no constraints on dummy_verb grammer */
1030 	if (vb_ >= BASE_VERB && vb_ < TOTAL_VERB) return 0;
1031 
1032 	if (!(verbflag[vb_]&VERB_TAKEOBJ)
1033 	        && (dobj_ != 0 || iobj_ != 0 || prep_ > 0)
1034 	        && vb_ != OLD_VERB + 11) {
1035 		if (redir_flag) return 0; /* Original AGT doesn't check this. */
1036 		sysmsg(190, "$Verb$ doesn't take an object.");
1037 		return -1;
1038 	}
1039 
1040 	/* Now verify prepositons. If PURE_GRAMMAR is set, we don't
1041 	   check prepositions unless the verb actually accepts at least one.
1042 	   (this reflects the behavior of the original AGT interpreters). */
1043 	if (prep_ > 0 && !(redir_flag && syntbl[preplist[vb_]] == 0)) {
1044 		for (i = preplist[vb_]; syntbl[i] != 0 && syntbl[i] != prep_; i++);
1045 		if (syntbl[i] != prep_) {
1046 			msgnum = 191;
1047 			if (vb_ == 15) msgnum = 74; /* Open */
1048 			if (vb_ == 17) msgnum = 116; /* Lock */
1049 			if (vb_ == 14) msgnum = 48; /* Throw */
1050 			sysmsg(msgnum, "$Verb$ doesn't take $prep_$ as a preposition.");
1051 			return -1;
1052 		}
1053 	}
1054 	if (iobj_ == ALL_MARK) {
1055 		sysmsg(199, "You can't use ALL as an indirect object");
1056 		return -1;
1057 	}
1058 	if (dobj_ == ALL_MARK && vb_ != 33 && vb_ != 41 && vb_ != 51 && vb_ != 52) {
1059 		/* i.e. verb is not GET,DROP,WEAR,REMOVE */
1060 		msgnum = 5;
1061 		if (vb_ == 31) msgnum = 155; /* Talk */
1062 		if (vb_ == 34) msgnum = 160; /* Ask */
1063 		sysmsg(5, "You can't use ALL with '$verb$'.");
1064 		return -1;
1065 	}
1066 	return 0;
1067 }
1068 
1069 
1070 /* This checks to make sure that all of the objects are present */
verify_scope(int vb_,parse_rec * nounrec,word prep_,parse_rec * objrec)1071 static rbool verify_scope(int vb_, parse_rec *nounrec,
1072 						  word prep_, parse_rec *objrec) {
1073 	int msgnum;
1074 	int dobj_, iobj_;
1075 	dobj_ = nounrec->obj;
1076 	iobj_ = objrec->obj;
1077 
1078 	if (!(verbflag[vb_]&VERB_TAKEOBJ)) return 1;
1079 	/* No objects (and we've already checked the grammar in
1080 	a previous routine) */
1081 
1082 	if (vb_ == 31 || vb_ == 34) /* TELL, ASK */
1083 		return 1;  /* These verbs handle this themselves */
1084 
1085 	if (dobj_ == 0) {
1086 		sysmsg(184, "What do $you$ want to $verb$?");
1087 		return 0;
1088 	}
1089 	if (dobj_ != ALL_MARK && !genvisible(nounrec)
1090 	        && !(it_door(dobj_, nounrec->noun) && /* DOOR object handling */
1091 	             (vb_ == 33 || vb_ == 15 || vb_ == 16 || vb_ == 17 || vb_ == 18
1092 	              || vb_ == 29 || vb_ == 24 || vb_ == 22 || vb_ == 21))) {
1093 		msgnum = 3;
1094 		if (vb_ == 33) msgnum = 28; /* Get */
1095 		if (vb_ == 29) msgnum = 63; /* Put */
1096 		if (vb_ == 15) msgnum = 75; /* Open */
1097 		if (vb_ == 16) msgnum = 86; /* Close */
1098 		if (vb_ == 24) msgnum = 126; /* Drink */
1099 		if (vb_ == 22) msgnum = 133; /* Read */
1100 		if (vb_ == 21) msgnum = 179; /* Change_Locations */
1101 		sysmsg(msgnum, "$You$ don't see any $noun$ here.");
1102 		return 0;
1103 	}
1104 
1105 	if (prep_ != 0 && vb_ != 35) { /* verb 35 is TURN e.g. ON|OFF */
1106 		if (iobj_ == 0) {
1107 			msgnum = 214;
1108 			if (vb_ == 29) msgnum = 70; /* Put */
1109 			sysmsg(msgnum, "What do $you$ want to $verb$ $the_n$$noun$ $prep_$?");
1110 			return 0;
1111 		}
1112 		if (iobj_ == -ext_code[wdoor]) {
1113 			sysmsg(183, "You can't $verb$ $prep_$ $the_o$$object$.");
1114 			return 0;
1115 		}
1116 		if (iobj_ != ALL_MARK && !genvisible(objrec)) {
1117 			msgnum = 4;
1118 			if (vb_ == 15) msgnum = 76; /* Open */
1119 			if (vb_ == 18) msgnum = 207; /* Unlock */
1120 			sysmsg(msgnum, "$You$ don't see any $object$ here.");
1121 			return 0;
1122 		}
1123 	}
1124 	return 1;
1125 }
1126 
1127 
exec_verb_info(void)1128 static void exec_verb_info(void) {
1129 	char *a, *b, *c;
1130 	char buff[200];
1131 
1132 	a = objname(dobj);
1133 	b = objname(iobj);
1134 	c = objname(actor);
1135 	sprintf(buff, "\t\t]]%s, %s %s(%ld) %s %s(%ld)", c, dict[ syntbl[auxsyn[vb]] ],
1136 	        a, dobj_rec->num, prep == 0 ? "->" : dict[prep], b, iobj_rec->num);
1137 	writeln(buff);
1138 	rfree(a);
1139 	rfree(b);
1140 	rfree(c);
1141 }
1142 
1143 
1144 /* Returns true if the turn is done. */
metacommand_cycle(int save_vb,int * p_redir_flag)1145 rbool metacommand_cycle(int save_vb, int *p_redir_flag) {
1146 	if (!have_meta) return 0;
1147 
1148 
1149 	/* Now check metacommands */
1150 	if (DEBUG_AGT_CMD)
1151 		debugout("*** Scanning: ANY metacommands ****\n");
1152 	/* ANY metacommands: */
1153 	supress_debug = !debug_any;
1154 	clear_stack();
1155 	if ((PURE_METAVERB || !was_metaverb)
1156 	        && 2 == scan_metacommand(0, 0, 0, 0, 0, NULL))
1157 		return 1;
1158 
1159 	supress_debug = 0;
1160 
1161 	vb = save_vb;
1162 	actor_in_scope |= visible(actor); /* Set up for ActorWasPresent */
1163 
1164 	clear_stack();
1165 	if (actor != 0 && aver < AGX00) {
1166 		if (DEBUG_AGT_CMD)
1167 			debugout("*** Scanning: ANYBODY metacommands ****\n");
1168 		if (2 == scan_metacommand(2, vb, dobj, prep, iobj, NULL))
1169 			return 1;
1170 	}
1171 
1172 	clear_stack();
1173 	if (DEBUG_AGT_CMD)
1174 		debugout("*** Scanning: VERB metacommands ****\n");
1175 	/* Normal treatment */
1176 	if (2 == scan_metacommand(actor, vb, dobj, prep, iobj, p_redir_flag))
1177 		return 1;
1178 	/* Note that scan_metacommand will change the -global- copy of vb if a
1179 	RedirectTo occurs. */
1180 
1181 	return 0;
1182 }
1183 
1184 
1185 
1186 /* Execute both meta-commands and more normal commands */
1187 /* May need tweaking for AGAIN and UNDO */
exec_verb(void)1188 void exec_verb(void) {
1189 	int objswap;  /* 1=if iobj has been moved to dobj */
1190 	/* (Done for metacommands when there is an iobj but no dobj) */
1191 	rbool turndone;
1192 	int save_vb;
1193 	int redir_flag;
1194 
1195 	if (DEBUG_EXEC_VERB) exec_verb_info();
1196 
1197 	do_disambig = 0; /* We're doing this for real */
1198 
1199 	save_vb = vb;
1200 	cmd_saveable = 1;
1201 	redir_flag = 0;
1202 
1203 	was_metaverb = (verbflag[vb] & VERB_META)
1204 	               && actor == 0 && dobj == 0 && prep == 0 && iobj == 0;
1205 
1206 	/* The following is purely for metacommands */
1207 	if (dobj == 0 && dobj_rec->info != D_NUM && iobj != 0) {
1208 		dobj = iobj;
1209 		rfree(dobj_rec);
1210 		dobj_rec = copy_parserec(iobj_rec);
1211 		objswap = 1;
1212 	} else objswap = 0;
1213 
1214 	beforecmd = 1; /* This is for 1.8x support */
1215 
1216 	turndone = metacommand_cycle(save_vb, &redir_flag) || deadflag;
1217 
1218 	if (!turndone && DEBUG_AGT_CMD)
1219 		debugout("*** Executing Built-in Verbs ****\n");
1220 
1221 	if (actor > 0 && !turndone) {
1222 		if (!actor_in_scope)
1223 			sysmsg(196, "I don't see whom $you$ $are$ trying to address here.");
1224 		else
1225 			sysmsg(192, "$The_name$$name$ doesn't want to.");
1226 	} else if (vb == 19 && dobj == 0 && prep == 0 && iobj == 0)
1227 		/* LOOK: Doesn't matter if turn is done. */
1228 		v_look();
1229 	else if (!turndone) {
1230 		/* Execute normal verbs: check grammer and then call */
1231 		if (!objswap) {
1232 			if (checkgram(vb, dobj, prep, iobj, redir_flag) == -1) return;
1233 		} else if (checkgram(vb, 0, prep, iobj, redir_flag) == -1) return;
1234 
1235 		if (!verify_scope(vb, dobj_rec, prep, iobj_rec)) return;
1236 
1237 		if (vb < 13 && vb > 0) v_go(vb);
1238 		else switch (vb) {
1239 
1240 			case 14:
1241 				v_throw(dobj_rec, prep, iobj_rec);
1242 				break;
1243 			case 29:
1244 				v_put(dobj_rec, prep, iobj_rec, 0);
1245 				break;
1246 
1247 			/* _with_ verbs */
1248 			case 15:
1249 				v_open(dobj_rec, iobj_rec);
1250 				break;
1251 			case 16:
1252 				v_close(dobj_rec);
1253 				break;
1254 			case 17:
1255 				v_lock(0, dobj_rec, iobj_rec);
1256 				break; /* LOCK */
1257 			case 18:
1258 				v_lock(1, dobj_rec, iobj_rec);
1259 				break; /* UNLOCK */
1260 			case 36:
1261 				v_noun(0, dobj_rec);
1262 				break; /* PUSH (WITH);Ignore indir object*/
1263 
1264 			case 26:
1265 				v_attack(0, dobj_rec, iobj_rec);
1266 				break;
1267 			case 49:
1268 				if (prep == ext_code[wwith])
1269 					v_attack(1, dobj_rec, iobj_rec); /* SHOOT WITH */
1270 				else
1271 					v_attack(1, iobj_rec, dobj_rec); /* SHOOT AT */
1272 				break;
1273 
1274 			/* _about_ verbs */
1275 			case 31:
1276 				v_talk(0, dobj_rec, iobj_rec);
1277 				break; /* TELL */
1278 			case 34:
1279 				v_talk(1, dobj_rec, iobj_rec);
1280 				break; /* ASK */
1281 
1282 			case 28:
1283 				v_yell();
1284 				break;
1285 			case 27:
1286 				sysmsg(149, "Time passes...");
1287 				break;            /* wait */
1288 			case 55:
1289 				v_go(13);
1290 				break;  /* magic_word */
1291 
1292 			/* case 19: v_look();break;  -- this is moved up above */
1293 
1294 			case 50:
1295 				runptr(loc, help_ptr, "Sorry, you're on your own here.",
1296 				       2, NULL, NULL);
1297 				break;   /* HELP */
1298 			case 32:
1299 				v_inventory();
1300 				break;
1301 			case 56:
1302 				v_view(dobj_rec);
1303 				break;  /* VIEW */
1304 			case 35:
1305 				if (prep > 0)
1306 					v_turn(prep, dobj_rec); /* TURN ON|OFF */
1307 				else
1308 					v_noun(2, dobj_rec); /* TURN */
1309 				break;
1310 			case 20:
1311 				v_examine(dobj_rec);
1312 				break;
1313 			case 22:
1314 				v_read(dobj_rec);
1315 				break;
1316 			case 23:
1317 				v_eat(0, dobj_rec);
1318 				break;  /* EAT */
1319 			case 24:
1320 				v_eat(1, dobj_rec);
1321 				break;  /* DRINK */
1322 			case 37:
1323 				v_noun(1, dobj_rec);
1324 				break;   /* PULL  */
1325 			case 38:
1326 				v_noun(3, dobj_rec);
1327 				break;  /* PLAY */
1328 			case 47:
1329 				v_light(1, dobj_rec);
1330 				break;   /* LIGHT */
1331 			case 48:
1332 				v_light(0, dobj_rec);
1333 				break;   /* EXTINGUISH */
1334 			case 21:
1335 				v_noun(4, dobj_rec);
1336 				break; /* Change Location */
1337 
1338 			case 51:
1339 				v_wear(dobj_rec);
1340 				break;
1341 			case 33:
1342 				v_get(dobj_rec);
1343 				break;    /* ? */
1344 			case 52:
1345 				v_remove(dobj_rec);
1346 				break;
1347 			case 41:
1348 				v_drop(dobj_rec);
1349 				break;
1350 
1351 			case 19:
1352 				v_look();
1353 				break;
1354 			case 25:
1355 				print_score();
1356 				break;
1357 			case 30:
1358 				cmd_saveable = 0;
1359 				v_quit();
1360 				break;
1361 			/* case 40:  SHOW --> default message */
1362 			case 39:
1363 			case 42:
1364 				v_listexit();
1365 				break;
1366 			case 43:
1367 				cmd_saveable = 0;
1368 				verboseflag = 0; /* BRIEF */
1369 				writeln(
1370 				    "[Now in BRIEF mode (room descriptions will only be printed"
1371 				    " when they are entered the first time)]");
1372 				break;
1373 			case 44:
1374 				cmd_saveable = 0;
1375 				verboseflag = 1;
1376 				v_look();  /* VERBOSE */
1377 				writeln("[Now in VERBOSE mode (room descriptions will be"
1378 				        " printed every time you enter a room)]");
1379 				break;
1380 			case 45:
1381 				cmd_saveable = 0;
1382 				g_vm->saveGame();
1383 				break;
1384 			case 46:
1385 				cmd_saveable = 0;
1386 				doing_restore = 1;
1387 				return;
1388 				break;
1389 			case 53:
1390 				cmd_saveable = 0;
1391 				script(1);
1392 				break;
1393 			case 54:
1394 				cmd_saveable = 0;
1395 				script(0);
1396 				break;
1397 			case 58:         /* INSTRUCTIONS */
1398 				agt_clrscr();
1399 				print_instructions(hold_fc);
1400 				close_ins_file();
1401 				break;
1402 			case (OLD_VERB+1):
1403 				cmd_saveable = 0; /* RESTART */
1404 				if (restart_state == NULL)
1405 					writeln("Sorry, too little memory to support RESTART.");
1406 				else {
1407 					doing_restore = 2;
1408 					return;
1409 				}
1410 				break;
1411 			case (OLD_VERB+4):
1412 				cmd_saveable = 0;  /* NOTIFY */
1413 				notify_flag = !notify_flag;
1414 				if (notify_flag) writeln("Score notification is now on.");
1415 				else writeln("Score notification is now off.");
1416 				break;
1417 			case (OLD_VERB+5):
1418 				listexit_flag = 1;
1419 				writeln("[LISTEXIT mode on: room exits will be listed.]");
1420 				break; /* LISTEXIT ON */
1421 			case (OLD_VERB+6):
1422 				listexit_flag = 0;
1423 				writeln("[LISTEXIT mode off: room exits will not be listed.]");
1424 				break;
1425 			case (OLD_VERB+7):  /* AGILDEBUG */
1426 				if (debug_mode) get_debugcmd();
1427 				else writeln("Nice try.");
1428 				break;
1429 			case (OLD_VERB+8): /* LOG, LOG ON */
1430 				logon();
1431 				break;
1432 			case (OLD_VERB+9): /* LOG OFF */
1433 				if (logflag & 2) break; /* We're replaying; ignore. */
1434 				if (logflag & 1) close_pfile(log_out, 5);
1435 				logflag = 0;
1436 				break;
1437 			case (OLD_VERB+10): /* REPLAY n */
1438 				fast_replay = 0;
1439 				replay(dobj_rec->num);
1440 				break;
1441 			case (OLD_VERB+11): /* REPLAY STEP */
1442 				fast_replay = 0;
1443 				replay(-1);
1444 				break;
1445 			case (OLD_VERB+13): /* REPLAY FAST */
1446 				fast_replay = 1;
1447 				replay(0);
1448 				break;
1449 			case (OLD_VERB+12): /* MENU */
1450 				if (verbmenu == NULL) {
1451 					writeln("Sorry, but menus are not supported by this game.");
1452 					menu_mode = 0;
1453 					break;
1454 				}
1455 				if (freeze_mode) {
1456 					writeln("Sorry, but that is not allowed.");
1457 					break;
1458 				}
1459 				menu_mode = !menu_mode;
1460 				break;
1461 			case 57: /* AFTER ?!? */
1462 				writeln("INTERNAL ERROR: Invalid execution of AFTER");
1463 				break;
1464 			case (OLD_VERB+14): /* SOUND ON */
1465 				musiccmd(8, 0);
1466 				break;
1467 			case (OLD_VERB+15): /* SOUND OFF */
1468 				musiccmd(9, 0);
1469 				break;
1470 			case (OLD_VERB+16):  /* INTRO */
1471 				agt_clrscr();
1472 				print_descr(intro_ptr, 1);
1473 				break;
1474 			default:
1475 				sysmsg(185, "Don't know how to $verb$ here...");
1476 				return;
1477 			}
1478 	}
1479 
1480 	compute_seen();
1481 
1482 	if (!PURE_AFTER && !doing_restore && end_of_turn)
1483 		increment_turn();
1484 
1485 	beforecmd = 0;
1486 
1487 	/* In AGT 1.8x, run aftercommand verb metacommands. */
1488 	/* (This is the most serious flaw in 1.82/1.83; it drastically changes the
1489 	   semantics of metacommand execution from the earlier formats) */
1490 	if (TWO_CYCLE && !quitflag && !turndone && !deadflag) {
1491 		if (DEBUG_AGT_CMD)
1492 			debugout("*** Scanning (after) metacommands ****\n");
1493 		/* Normal treatment */
1494 		turndone = turndone || metacommand_cycle(save_vb, &redir_flag);
1495 	}
1496 
1497 	if (aver >= AGT15 && !quitflag && !endflag && !deadflag) {
1498 		if (DEBUG_AGT_CMD)
1499 			debugout("*** Scanning: AFTER metacommands ****\n");
1500 		/* AFTER metacommands: */
1501 		supress_debug = !debug_any;
1502 		clear_stack();
1503 		if ((PURE_METAVERB || !was_metaverb) &&
1504 		        2 == scan_metacommand(0, 57, 0, 0, 0, NULL))
1505 			turndone = 1;
1506 		supress_debug = 0;
1507 	}
1508 
1509 	/* If the player really typed 'q' and we generated an "EndGame"
1510 	   metacommand, then really quit. (usually it just gives the
1511 	   "restart, restore, undo, quit..." message */
1512 	if (save_vb == 30 && endflag) quitflag = 1;
1513 }
1514 
1515 
1516 
1517 
1518 
1519 
1520 
1521 /* We need to be able to handle both NOUN and OBJECT searches */
1522 /* If obj==0, then we are doing a noun search, otherwise we are doing
1523   an object search */
1524 /* Return the disambiguation score;
1525 	 0 if the object doesn't trigger anything
1526 	 1000 if it runs an action token or built in verb.
1527 	 Other values may be returned if an ErrMessage token is encountered.
1528 	 500 is the cutoff for ALL expansion.
1529   */
1530 
1531 
objcheck_cycle(rbool * success,parse_rec * act,int verbid,parse_rec * dorec,word prep_,parse_rec * iorec)1532 int objcheck_cycle(rbool *success, parse_rec *act, int verbid,
1533 				   parse_rec *dorec, word prep_, parse_rec *iorec) {
1534 	int result;
1535 
1536 	actor = act->obj;
1537 	actor_rec = copy_parserec(act);
1538 	/* The xobj_rec don't really matter */
1539 	dobj = dorec->obj;
1540 	dobj_rec = copy_parserec(dorec);
1541 	if (iorec == NULL) {
1542 		iobj_rec = make_parserec(0, NULL);
1543 		iobj = 0;
1544 	} else {
1545 		iobj = iorec->obj;
1546 		iobj_rec = copy_parserec(iorec);
1547 	}
1548 
1549 	clear_stack();
1550 	*success = 1;
1551 	supress_debug = !debug_disambig;
1552 	if (actor != 0 && aver < AGX00) {
1553 		result = scan_metacommand(2, verbid, dobj, prep_, iobj, NULL);
1554 		if (result == 2) {
1555 			free_all_parserec();
1556 			return disambig_score;
1557 		}
1558 		if (result == -2) {
1559 			free_all_parserec();
1560 			return DISAMBIG_SUCC;
1561 		}
1562 	}
1563 	clear_stack();
1564 	result = scan_metacommand(actor, verbid, dobj, prep_, iobj, NULL);
1565 	supress_debug = 0;
1566 	switch (result) {
1567 	case -2:
1568 		free_all_parserec();
1569 		return DISAMBIG_SUCC; /* We matched with something */
1570 	case 0:
1571 	case 1:
1572 		break;  /* Nothing matched, but we still need to check
1573 			  built-in verbs */
1574 	case 2:
1575 		free_all_parserec();
1576 		return disambig_score; /* End of turn, no match */
1577 	default:
1578 		writeln("INTERNAL ERROR: Invalid scan_metacommand return value.");
1579 	}
1580 	*success = 0;
1581 	free_all_parserec();
1582 	return 0;
1583 }
1584 
1585 
1586 
check_obj(parse_rec * act,int verbid,parse_rec * dorec,word prep_,parse_rec * iorec)1587 int check_obj(parse_rec *act, int verbid,
1588 			  parse_rec *dorec, word prep_, parse_rec *iorec) {
1589 	int result;
1590 	rbool success;
1591 
1592 	if (iorec == NULL)
1593 		do_disambig = 1; /* Disambiguating dobj */
1594 	else
1595 		do_disambig = 2; /* Disambiguating iobj */
1596 
1597 	disambig_score = 0;
1598 	if (have_meta) {
1599 		beforecmd = 1;
1600 		result = objcheck_cycle(&success, act, verbid, dorec, prep_, iorec);
1601 		if (success) return result;
1602 	}
1603 
1604 	/* Check built-in verbs here */
1605 	if (verbid < BASE_VERB)
1606 		switch (verbid) {
1607 		case 14: /* THROW dobj prep_ iobj */
1608 		case 29: /* PUT dobj prep_ iobj */
1609 			if (do_disambig == 2 && genvisible(iorec)) return DISAMBIG_SUCC;
1610 		// fallthrough
1611 		case 41: /* DROP */
1612 			if (do_disambig == 1 && it_possess(dobj)) return DISAMBIG_SUCC;
1613 			break;
1614 
1615 		case 49: /* SHOOT ... AT or WITH ... */
1616 			if (prep_ == ext_code[wwith]) {
1617 				if (do_disambig == 1 && tcreat(dobj)) return DISAMBIG_SUCC;
1618 				else if (do_disambig == 2 && it_possess(iobj) && tnoun(iobj)
1619 				         && noun[iobj - first_noun].shootable)
1620 					return DISAMBIG_SUCC;
1621 			} else {         /* prep_!=wwith */
1622 				if (do_disambig == 2 && tcreat(iobj)) return DISAMBIG_SUCC;
1623 				else if (do_disambig == 1 && it_possess(dobj) && tnoun(dobj)
1624 				         && noun[dobj - first_noun].shootable)
1625 					return DISAMBIG_SUCC;
1626 			}
1627 			break;
1628 
1629 		case 26: /* ATTACK ... WITH ... */
1630 			if (do_disambig == 2 && it_possess(iobj)) return DISAMBIG_SUCC;
1631 			if (do_disambig == 1 && tcreat(dobj) && visible(dobj))
1632 				return DISAMBIG_SUCC;
1633 			break;
1634 
1635 		case 51:  /* WEAR */
1636 			if (do_disambig == 1)
1637 				if (tnoun(dobj) && visible(dobj) && noun[dobj - first_noun].wearable
1638 				        && it_loc(dobj) != 1000)
1639 					return DISAMBIG_SUCC;
1640 			break;
1641 		case 33: /* GET */
1642 			if (do_disambig == 1 && tnoun(dobj)
1643 			        && visible(dobj)
1644 			        && noun[dobj - first_noun].location != 1
1645 			        && noun[dobj - first_noun].movable)
1646 				return (player_has(dobj)) ? 499 : DISAMBIG_SUCC;
1647 			break;
1648 		case 52:  /* REMOVE */
1649 			if (do_disambig == 1 && it_loc(dobj) == 1000) return DISAMBIG_SUCC;
1650 			break;
1651 
1652 		/* The following could be better, but I don't want to give
1653 		away puzzles by accident */
1654 		case 15: /* OPEN */
1655 		case 17: /* LOCK */
1656 		case 18: /* UNLOCK */
1657 			if (do_disambig == 2 && it_possess(iobj)) return DISAMBIG_SUCC;
1658 		/* ... fall through ... */
1659 		default: /* All other verbs just use visibility check */
1660 			if (do_disambig == 1 && genvisible(dorec)) return DISAMBIG_SUCC;
1661 			if (do_disambig == 2 && genvisible(iorec)) return DISAMBIG_SUCC;
1662 		}
1663 
1664 	if (have_meta && TWO_CYCLE) {
1665 		beforecmd = 0;
1666 		result = objcheck_cycle(&success, act, verbid, dorec, prep_, iorec);
1667 		if (success) return result;
1668 	}
1669 
1670 	return disambig_score; /* Failed to find a match */
1671 }
1672 
1673 } // End of namespace AGT
1674 } // End of namespace Glk
1675