xref: /openbsd/games/battlestar/cypher.c (revision 7b36286a)
1 /*	$OpenBSD: cypher.c,v 1.16 2007/09/09 17:10:02 ray Exp $	*/
2 /*	$NetBSD: cypher.c,v 1.3 1995/03/21 15:07:15 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cypher.c	8.2 (Berkeley) 4/28/95";
36 #else
37 static char rcsid[] = "$OpenBSD: cypher.c,v 1.16 2007/09/09 17:10:02 ray Exp $";
38 #endif
39 #endif /* not lint */
40 
41 #include <stdarg.h>
42 
43 #include "extern.h"
44 #include "pathnames.h"
45 
46 static void verb_with_all(unsigned int *, int, int (*)(void), const char *);
47 
48 /*
49  * Prompt user to input an integer, which is stored in *value.
50  * On failure prints a warning, leaves *value untouched, and returns -1.
51  */
52 int
53 getnum(int *value, const char *fmt, ...)
54 {
55 	char buffer[BUFSIZ];
56 	va_list ap;
57 	const char *errstr;
58 	int n;
59 
60 	va_start(ap, fmt);
61 	vprintf(fmt, ap);
62 	fflush(stdout);
63 	va_end(ap);
64 
65 	if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
66 		warnx("error reading input");
67 		return (-1);
68 	}
69 	buffer[strcspn(buffer, "\n")] = '\0';
70 
71 	n = strtonum(buffer, INT_MIN, INT_MAX, &errstr);
72 	if (errstr) {
73 		warnx("number %s: %s", errstr, buffer);
74 		return (-1);
75 	}
76 	*value = n;
77 	return (0);
78 }
79 
80 /* returns 0 if error or no more commands to do,
81  *         1 if there are more commands remaining on the current input line
82  */
83 int
84 cypher(void)
85 {
86 	int     n;
87 	int     junk;
88 	int     lflag = -1;
89 	char   *filename, *rfilename;
90 	size_t  filename_len;
91 
92 	while (wordnumber <= wordcount) {
93 		if (wordtype[wordnumber] != VERB &&
94 		    !(wordtype[wordnumber] == OBJECT &&
95 		    wordvalue[wordnumber] == KNIFE)) {
96 			printf("%s: How's that?\n",
97 			    (wordnumber == wordcount) ? words[wordnumber - 1] : words[wordnumber]);
98 			return (0);
99 		}
100 
101 		switch (wordvalue[wordnumber]) {
102 
103 		case AUXVERB:
104 			/* Take the following word as the verb */
105 			wordnumber++;
106 			return(cypher());
107 			break;
108 
109 		case UP:
110 			if (location[position].access || wiz || tempwiz) {
111 				if (!location[position].access)
112 					puts("Zap!  A gust of wind lifts you up.");
113 				if (!moveplayer(location[position].up, AHEAD))
114 					return (0);
115 			} else {
116 				puts("There is no way up.");
117 				return (0);
118 			}
119 			lflag = 0;
120 			break;
121 
122 		case DOWN:
123 			if (!moveplayer(location[position].down, AHEAD))
124 				return (0);
125 			lflag = 0;
126 			break;
127 
128 		case LEFT:
129 			if (!moveplayer(left, LEFT))
130 				return (0);
131 			lflag = 0;
132 			break;
133 
134 		case RIGHT:
135 			if (!moveplayer(right, RIGHT))
136 				return (0);
137 			lflag = 0;
138 			break;
139 
140 		case AHEAD:
141 			if (!moveplayer(ahead, AHEAD))
142 				return (0);
143 			lflag = 0;
144 			break;
145 
146 		case BACK:
147 			if (!moveplayer(back, BACK))
148 				return (0);
149 			lflag = 0;
150 			break;
151 
152 		case SHOOT:
153 			verb_with_all(location[position].objects, OBJ_PERSON,
154 			    shoot, "shoot at");
155 			break;
156 
157 		case TAKE:
158 			if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
159 				int things;
160 				things = 0;
161 				for (n = 0; n < NUMOFOBJECTS; n++)
162 					if (TestBit(location[position].objects, n) && objsht[n]) {
163 						things++;
164 						wordvalue[wordnumber + 1] = n;
165 /* Some objects (type NOUNS) have special treatment in take().  For these
166  * we must set the type to NOUNS.  However for SWORD and BODY all it does
167  * is find which of many objects is meant, so we need do nothing here.
168  * BATHGOD must become NORMGOD as well.  NOUNS with no special case must be
169  * included here to get the right error.  DOOR cannot occur as an object so
170  * need not be included.
171  */
172 						switch (n) {
173 						case BATHGOD:
174 							wordvalue[wordnumber + 1] = NORMGOD;
175 							/* FALLTHROUGH */
176 						case NORMGOD:
177 						case AMULET:
178 						case MEDALION:
179 						case TALISMAN:
180 						case MAN:
181 						case TIMER:
182 						case NATIVE:
183 							wordtype[wordnumber + 1] = NOUNS;
184 							break;
185 						default:
186 							wordtype[wordnumber + 1] = OBJECT;
187 						}
188 						wordnumber = take(location[position].objects);
189 						wordnumber += 2;
190 					}
191 				if (!things)
192 					puts("Nothing to take!");
193 			} else
194 				take(location[position].objects);
195 			break;
196 
197 		case DROP:
198 			if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
199 				int things;
200 				things = 0;
201 				for (n = 0; n < NUMOFOBJECTS; n++)
202 					if (TestBit(inven, n)) {
203 						things++;
204 						wordvalue[wordnumber + 1] = n;
205 						wordnumber = drop("Dropped");
206 					}
207 				wordnumber++;
208 				wordnumber++;
209 				if (!things)
210 					puts("Nothing to drop!");
211 			} else
212 				drop("Dropped");
213 			break;
214 
215 
216 		case KICK:
217 		case THROW:
218 			if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
219 				int things, wv;
220 				things = 0;
221 				wv = wordvalue[wordnumber];
222 				for (n = 0; n < NUMOFOBJECTS; n++)
223 					if (TestBit(inven, n) ||
224 					  (TestBit(location[position].objects, n) && objsht[n])) {
225 						things++;
226 						wordvalue[wordnumber + 1] = n;
227 						wordnumber = throw(wordvalue[wordnumber] == KICK ? "Kicked" : "Thrown");
228 					}
229 				wordnumber += 2;
230 				if (!things)
231 					printf("Nothing to %s!\n", wv == KICK ? "kick" : "throw");
232 			} else
233 				throw(wordvalue[wordnumber] == KICK ? "Kicked" : "Thrown");
234 			break;
235 
236 		case TAKEOFF:
237 			verb_with_all(wear, 0, takeoff, "take off");
238 			break;
239 
240 		case DRAW:
241 			verb_with_all(wear, 0, draw, "draw");
242 			break;
243 
244 		case PUTON:
245 			verb_with_all(location[position].objects, 0, puton, "put on");
246 			break;
247 
248 		case WEARIT:
249 			verb_with_all(inven, 0, wearit, "wear");
250 			break;
251 
252 		case EAT:
253 			verb_with_all(inven, 0, eat, "eat");
254 			break;
255 
256 		case PUT:
257 			put();
258 			break;
259 
260 		case INVEN:
261 			if (ucard(inven)) {
262 				puts("You are holding:\n");
263 				for (n = 0; n < NUMOFOBJECTS; n++)
264 					if (TestBit(inven, n))
265 						printf("\t%s\n", objsht[n]);
266 				printf("\n= %d kilogram%s ", carrying,
267 				    (carrying == 1 ?  "." : "s."));
268 				if (WEIGHT)
269 					printf("(%d%%)\n", carrying * 100 / WEIGHT);
270 				else
271 					printf("(can't lift any weight%s)\n",
272 					    (carrying ? " or move with what you have" : ""));
273 				if (CUMBER)
274 					printf("Your arms are %d%% full.\n",
275 					    encumber * 100 / CUMBER);
276 				else
277 					printf("You can't pick anything up.\n");
278 			} else
279 				puts("You aren't carrying anything.");
280 
281 			if (ucard(wear)) {
282 				puts("\nYou are wearing:\n");
283 				for (n = 0; n < NUMOFOBJECTS; n++)
284 					if (TestBit(wear, n))
285 						printf("\t%s\n", objsht[n]);
286 			} else
287 				puts("\nYou are stark naked.");
288 			if (card(injuries, NUMOFINJURIES)) {
289 				puts("\nYou have suffered:\n");
290 				for (n = 0; n < NUMOFINJURIES; n++)
291 					if (injuries[n])
292 						printf("\t%s\n", ouch[n]);
293 				printf("\nYou can still carry up to %d kilogram%s\n", WEIGHT, (WEIGHT == 1 ? "." : "s."));
294 			} else
295 				puts("\nYou are in perfect health.");
296 			break;
297 
298 		case USE:
299 			lflag = use();
300 			break;
301 
302 		case OPEN:
303 			dooropen();
304 			break;
305 
306 		case LOOK:
307 			if (!notes[CANTSEE] || TestBit(inven, LAMPON) ||
308 			    TestBit(location[position].objects, LAMPON)
309 			    || matchlight) {
310 				beenthere[position] = 2;
311 				writedes();
312 				printobjs();
313 				if (matchlight) {
314 					puts("\nYour match splutters out.");
315 					matchlight = 0;
316 				}
317 			} else
318 				puts("I can't see anything.");
319 			return (0);	/* No commands after a look */
320 			break;
321 
322 		case SU:
323 			if (wiz || tempwiz) {
324 				getnum(&position, "\nRoom (was %d) = ", position);
325 				getnum(&ourtime, "Time (was %d) = ", ourtime);
326 				getnum(&fuel, "Fuel (was %d) = ", fuel);
327 				getnum(&torps, "Torps (was %d) = ", torps);
328 				getnum(&CUMBER, "CUMBER (was %d) = ", CUMBER);
329 				getnum(&WEIGHT, "WEIGHT (was %d) = ", WEIGHT);
330 				getnum(&ourclock, "Clock (was %d) = ", ourclock);
331 				if (getnum(&junk, "Wizard (was %d, %d) = ", wiz, tempwiz) != -1 && !junk)
332 					tempwiz = wiz = 0;
333 				printf("\nDONE.\n");
334 				return (0);	/* No commands after a SU */
335 			} else
336 				puts("You aren't a wizard.");
337 			break;
338 
339 		case SCORE:
340 			printf("\tPLEASURE\tPOWER\t\tEGO\n");
341 			printf("\t%3d\t\t%3d\t\t%3d\n\n", pleasure, power, ego);
342 			printf("This gives you the rating of %s in %d turns.\n", rate(), ourtime);
343 			printf("You have visited %d out of %d rooms this run (%d%%).\n", card(beenthere, NUMOFROOMS), NUMOFROOMS, card(beenthere, NUMOFROOMS) * 100 / NUMOFROOMS);
344 			break;
345 
346 		/* case KNIFE: */
347 		case KILL:
348 			murder();
349 			break;
350 
351 		case UNDRESS:
352 			undress();
353 			break;
354 
355 		case RAVAGE:
356 			ravage();
357 			break;
358 
359 		case SAVE:
360 			printf("\nSave file name (default %s):  ",
361 			    DEFAULT_SAVE_FILE);
362 			filename = fgetln(stdin, &filename_len);
363 			if (filename_len == 0
364 			    || (filename_len == 1 && filename[0] == '\n'))
365 				rfilename = save_file_name(DEFAULT_SAVE_FILE,
366 				    strlen(DEFAULT_SAVE_FILE));
367 			else {
368 				if (filename[filename_len - 1] == '\n')
369 					filename_len--;
370 				rfilename = save_file_name(filename,
371 				    filename_len);
372 			}
373 			save(rfilename);
374 			free(rfilename);
375 			break;
376 
377 		case VERBOSE:
378 			verbose = 1;
379 			printf("[Maximum verbosity]\n");
380 			break;
381 
382 		case BRIEF:
383 			verbose = 0;
384 			printf("[Standard verbosity]\n");
385 			break;
386 
387 		case FOLLOW:
388 			lflag = follow();
389 			break;
390 
391 		case GIVE:
392 			give();
393 			break;
394 
395 		case KISS:
396 			kiss();
397 			break;
398 
399 		case LOVE:
400 			love();
401 			break;
402 
403 		case RIDE:
404 			lflag = ride();
405 			break;
406 
407 		case DRIVE:
408 			lflag = drive();
409 			break;
410 
411 		case LIGHT:
412 			light();
413 			break;
414 
415 		case LAUNCH:
416 			if (!launch())
417 				return (0);
418 			else
419 				lflag = 0;
420 			break;
421 
422 		case LANDIT:
423 			if (!land())
424 				return (0);
425 			else
426 				lflag = 0;
427 			break;
428 
429 		case TIME:
430 			chime();
431 			break;
432 
433 		case SLEEP:
434 			zzz();
435 			break;
436 
437 		case DIG:
438 			dig();
439 			break;
440 
441 		case JUMP:
442 			lflag = jump();
443 			break;
444 
445 		case BURY:
446 			bury();
447 			break;
448 
449 		case SWIM:
450 			puts("Surf's up!");
451 			break;
452 
453 		case DRINK:
454 			drink();
455 			break;
456 
457 		case QUIT:
458 			die(0);
459 
460 		default:
461 			puts("How's that?");
462 			return (0);
463 			break;
464 
465 		}
466 		if (!lflag)
467 			newlocation();
468 		if (wordnumber < wordcount && !stop_cypher &&
469 		    (*words[wordnumber] == ',' || *words[wordnumber] == '.')) {
470 			wordnumber++;
471 			return (1);
472 		} else
473 			return (0);
474 	}
475 	return (0);
476 }
477 
478 int
479 inc_wordnumber(const char *v, const char *adv)
480 {
481 	wordnumber++;
482 	if (wordnumber >= wordcount) {
483 		printf("%c%s %s?\n", toupper(v[0]), v + 1, adv);
484 		return(-1);
485 	}
486 	return(0);
487 }
488 
489 static void
490 verb_with_all(unsigned int *testarray, int objflg, int (*verbfunc)(void),
491               const char *verbname)
492 {
493 	int things, n;
494 
495 	things = 0;
496 	if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
497 		for (n = 0; n < NUMOFOBJECTS; n++)
498 			if (TestBit(testarray, n) &&
499 			    (objsht[n] || (objflg & objflags[n]))) {
500 				things++;
501 				wordvalue[wordnumber + 1] = n;
502 				/* Assume it's a NOUN if no short description */
503 				if (objsht[n])
504 					wordtype[wordnumber + 1] = OBJECT;
505 				else
506 					wordtype[wordnumber + 1] = NOUNS;
507 				wordnumber = verbfunc();
508 			}
509 		wordnumber += 2;
510 		if (!things)
511 			printf("Nothing to %s!\n", verbname);
512 	} else
513 		verbfunc();
514 }
515