xref: /openbsd/games/battlestar/cypher.c (revision f3c23159)
1 /*	$OpenBSD: cypher.c,v 1.21 2022/08/08 17:57:05 op 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 #include <ctype.h>
34 #include <err.h>
35 #include <limits.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "extern.h"
42 #include "pathnames.h"
43 
44 static void verb_with_all(unsigned int *, int, int (*)(void), const char *);
45 
46 /*
47  * Prompt user to input an integer, which is stored in *value.
48  * On failure prints a warning, leaves *value untouched, and returns -1.
49  */
50 int
getnum(int * value,const char * fmt,...)51 getnum(int *value, const char *fmt, ...)
52 {
53 	char buffer[BUFSIZ];
54 	va_list ap;
55 	const char *errstr;
56 	int n;
57 
58 	va_start(ap, fmt);
59 	vprintf(fmt, ap);
60 	fflush(stdout);
61 	va_end(ap);
62 
63 	if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
64 		warnx("error reading input");
65 		return (-1);
66 	}
67 	buffer[strcspn(buffer, "\n")] = '\0';
68 
69 	n = strtonum(buffer, INT_MIN, INT_MAX, &errstr);
70 	if (errstr) {
71 		warnx("number %s: %s", errstr, buffer);
72 		return (-1);
73 	}
74 	*value = n;
75 	return (0);
76 }
77 
78 /* returns 0 if error or no more commands to do,
79  *         1 if there are more commands remaining on the current input line
80  */
81 int
cypher(void)82 cypher(void)
83 {
84 	int     n;
85 	int     junk;
86 	int     lflag = -1;
87 	char   *line = NULL, *filename;
88 	size_t  linesize = 0;
89 	ssize_t linelen;
90 
91 	while (wordnumber <= wordcount) {
92 		if (wordtype[wordnumber] != VERB &&
93 		    !(wordtype[wordnumber] == OBJECT &&
94 		    wordvalue[wordnumber] == KNIFE)) {
95 			printf("%s: How's that?\n",
96 			    (wordnumber == wordcount) ? words[wordnumber - 1] : words[wordnumber]);
97 			return (0);
98 		}
99 
100 		switch (wordvalue[wordnumber]) {
101 
102 		case AUXVERB:
103 			/* Take the following word as the verb */
104 			wordnumber++;
105 			return(cypher());
106 			break;
107 
108 		case UP:
109 			if (location[position].access || tempwiz) {
110 				if (!location[position].access)
111 					puts("Zap!  A gust of wind lifts you up.");
112 				if (!moveplayer(location[position].up, AHEAD))
113 					return (0);
114 			} else {
115 				puts("There is no way up.");
116 				return (0);
117 			}
118 			lflag = 0;
119 			break;
120 
121 		case DOWN:
122 			if (!moveplayer(location[position].down, AHEAD))
123 				return (0);
124 			lflag = 0;
125 			break;
126 
127 		case LEFT:
128 			if (!moveplayer(left, LEFT))
129 				return (0);
130 			lflag = 0;
131 			break;
132 
133 		case RIGHT:
134 			if (!moveplayer(right, RIGHT))
135 				return (0);
136 			lflag = 0;
137 			break;
138 
139 		case AHEAD:
140 			if (!moveplayer(ahead, AHEAD))
141 				return (0);
142 			lflag = 0;
143 			break;
144 
145 		case BACK:
146 			if (!moveplayer(back, BACK))
147 				return (0);
148 			lflag = 0;
149 			break;
150 
151 		case SHOOT:
152 			verb_with_all(location[position].objects, OBJ_PERSON,
153 			    shoot, "shoot at");
154 			break;
155 
156 		case TAKE:
157 			if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
158 				int things;
159 				things = 0;
160 				for (n = 0; n < NUMOFOBJECTS; n++)
161 					if (TestBit(location[position].objects, n) && objsht[n]) {
162 						things++;
163 						wordvalue[wordnumber + 1] = n;
164 /* Some objects (type NOUNS) have special treatment in take().  For these
165  * we must set the type to NOUNS.  However for SWORD and BODY all it does
166  * is find which of many objects is meant, so we need do nothing here.
167  * BATHGOD must become NORMGOD as well.  NOUNS with no special case must be
168  * included here to get the right error.  DOOR cannot occur as an object so
169  * need not be included.
170  */
171 						switch (n) {
172 						case BATHGOD:
173 							wordvalue[wordnumber + 1] = NORMGOD;
174 							/* FALLTHROUGH */
175 						case NORMGOD:
176 						case AMULET:
177 						case MEDALION:
178 						case TALISMAN:
179 						case MAN:
180 						case TIMER:
181 						case NATIVE:
182 							wordtype[wordnumber + 1] = NOUNS;
183 							break;
184 						default:
185 							wordtype[wordnumber + 1] = OBJECT;
186 						}
187 						wordnumber = take(location[position].objects);
188 						wordnumber += 2;
189 					}
190 				if (!things)
191 					puts("Nothing to take!");
192 			} else
193 				take(location[position].objects);
194 			break;
195 
196 		case DROP:
197 			if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
198 				int things;
199 				things = 0;
200 				for (n = 0; n < NUMOFOBJECTS; n++)
201 					if (TestBit(inven, n)) {
202 						things++;
203 						wordvalue[wordnumber + 1] = n;
204 						wordnumber = drop("Dropped");
205 					}
206 				wordnumber++;
207 				wordnumber++;
208 				if (!things)
209 					puts("Nothing to drop!");
210 			} else
211 				drop("Dropped");
212 			break;
213 
214 
215 		case KICK:
216 		case THROW:
217 			if (wordnumber < wordcount && wordvalue[wordnumber + 1] == EVERYTHING) {
218 				int things, wv;
219 				things = 0;
220 				wv = wordvalue[wordnumber];
221 				for (n = 0; n < NUMOFOBJECTS; n++)
222 					if (TestBit(inven, n) ||
223 					  (TestBit(location[position].objects, n) && objsht[n])) {
224 						things++;
225 						wordvalue[wordnumber + 1] = n;
226 						wordnumber = throw(wordvalue[wordnumber] == KICK ? "Kicked" : "Thrown");
227 					}
228 				wordnumber += 2;
229 				if (!things)
230 					printf("Nothing to %s!\n", wv == KICK ? "kick" : "throw");
231 			} else
232 				throw(wordvalue[wordnumber] == KICK ? "Kicked" : "Thrown");
233 			break;
234 
235 		case TAKEOFF:
236 			verb_with_all(wear, 0, takeoff, "take off");
237 			break;
238 
239 		case DRAW:
240 			verb_with_all(wear, 0, draw, "draw");
241 			break;
242 
243 		case PUTON:
244 			verb_with_all(location[position].objects, 0, puton, "put on");
245 			break;
246 
247 		case WEARIT:
248 			verb_with_all(inven, 0, wearit, "wear");
249 			break;
250 
251 		case EAT:
252 			verb_with_all(inven, 0, eat, "eat");
253 			break;
254 
255 		case PUT:
256 			put();
257 			break;
258 
259 		case INVEN:
260 			if (ucard(inven)) {
261 				puts("You are holding:\n");
262 				for (n = 0; n < NUMOFOBJECTS; n++)
263 					if (TestBit(inven, n))
264 						printf("\t%s\n", objsht[n]);
265 				printf("\n= %d kilogram%s ", carrying,
266 				    (carrying == 1 ?  "." : "s."));
267 				if (WEIGHT)
268 					printf("(%d%%)\n", carrying * 100 / WEIGHT);
269 				else
270 					printf("(can't lift any weight%s)\n",
271 					    (carrying ? " or move with what you have" : ""));
272 				if (CUMBER)
273 					printf("Your arms are %d%% full.\n",
274 					    encumber * 100 / CUMBER);
275 				else
276 					printf("You can't pick anything up.\n");
277 			} else
278 				puts("You aren't carrying anything.");
279 
280 			if (ucard(wear)) {
281 				puts("\nYou are wearing:\n");
282 				for (n = 0; n < NUMOFOBJECTS; n++)
283 					if (TestBit(wear, n))
284 						printf("\t%s\n", objsht[n]);
285 			} else
286 				puts("\nYou are stark naked.");
287 			if (card(injuries, NUMOFINJURIES)) {
288 				puts("\nYou have suffered:\n");
289 				for (n = 0; n < NUMOFINJURIES; n++)
290 					if (injuries[n])
291 						printf("\t%s\n", ouch[n]);
292 				printf("\nYou can still carry up to %d kilogram%s\n", WEIGHT, (WEIGHT == 1 ? "." : "s."));
293 			} else
294 				puts("\nYou are in perfect health.");
295 			break;
296 
297 		case USE:
298 			lflag = use();
299 			break;
300 
301 		case OPEN:
302 			dooropen();
303 			break;
304 
305 		case LOOK:
306 			if (!notes[CANTSEE] || TestBit(inven, LAMPON) ||
307 			    TestBit(location[position].objects, LAMPON)
308 			    || matchlight) {
309 				beenthere[position] = 2;
310 				writedes();
311 				printobjs();
312 				if (matchlight) {
313 					puts("\nYour match splutters out.");
314 					matchlight = 0;
315 				}
316 			} else
317 				puts("I can't see anything.");
318 			return (0);	/* No commands after a look */
319 			break;
320 
321 		case SU:
322 			if (tempwiz) {
323 				getnum(&position, "\nRoom (was %d) = ", position);
324 				getnum(&ourtime, "Time (was %d) = ", ourtime);
325 				getnum(&fuel, "Fuel (was %d) = ", fuel);
326 				getnum(&torps, "Torps (was %d) = ", torps);
327 				getnum(&CUMBER, "CUMBER (was %d) = ", CUMBER);
328 				getnum(&WEIGHT, "WEIGHT (was %d) = ", WEIGHT);
329 				getnum(&ourclock, "Clock (was %d) = ", ourclock);
330 				if (getnum(&junk, "Wizard (was %d) = ", tempwiz) != -1 && !junk)
331 					tempwiz = 0;
332 				printf("\nDONE.\n");
333 				return (0);	/* No commands after a SU */
334 			} else
335 				puts("You aren't a wizard.");
336 			break;
337 
338 		case SCORE:
339 			printf("\tPLEASURE\tPOWER\t\tEGO\n");
340 			printf("\t%3d\t\t%3d\t\t%3d\n\n", pleasure, power, ego);
341 			printf("This gives you the rating of %s in %d turns.\n", rate(), ourtime);
342 			printf("You have visited %d out of %d rooms this run (%d%%).\n", card(beenthere, NUMOFROOMS), NUMOFROOMS, card(beenthere, NUMOFROOMS) * 100 / NUMOFROOMS);
343 			break;
344 
345 		/* case KNIFE: */
346 		case KILL:
347 			murder();
348 			break;
349 
350 		case UNDRESS:
351 			undress();
352 			break;
353 
354 		case RAVAGE:
355 			ravage();
356 			break;
357 
358 		case SAVE:
359 			printf("\nSave file name (default %s):  ",
360 			    DEFAULT_SAVE_FILE);
361 			linelen = getline(&line, &linesize, stdin);
362 			if (linelen == -1 || *line == '\n')
363 				filename = save_file_name(DEFAULT_SAVE_FILE);
364 			else {
365 				if (line[linelen - 1] == '\n')
366 					line[linelen - 1] = '\0';
367 				filename = save_file_name(line);
368 			}
369 			save(filename);
370 			free(filename);
371 			break;
372 
373 		case VERBOSE:
374 			verbose = 1;
375 			printf("[Maximum verbosity]\n");
376 			break;
377 
378 		case BRIEF:
379 			verbose = 0;
380 			printf("[Standard verbosity]\n");
381 			break;
382 
383 		case FOLLOW:
384 			lflag = follow();
385 			break;
386 
387 		case GIVE:
388 			give();
389 			break;
390 
391 		case KISS:
392 			kiss();
393 			break;
394 
395 		case LOVE:
396 			love();
397 			break;
398 
399 		case RIDE:
400 			lflag = ride();
401 			break;
402 
403 		case DRIVE:
404 			lflag = drive();
405 			break;
406 
407 		case LIGHT:
408 			light();
409 			break;
410 
411 		case LAUNCH:
412 			if (!launch())
413 				return (0);
414 			else
415 				lflag = 0;
416 			break;
417 
418 		case LANDIT:
419 			if (!land())
420 				return (0);
421 			else
422 				lflag = 0;
423 			break;
424 
425 		case TIME:
426 			chime();
427 			break;
428 
429 		case SLEEP:
430 			zzz();
431 			break;
432 
433 		case DIG:
434 			dig();
435 			break;
436 
437 		case JUMP:
438 			lflag = jump();
439 			break;
440 
441 		case BURY:
442 			bury();
443 			break;
444 
445 		case SWIM:
446 			puts("Surf's up!");
447 			break;
448 
449 		case DRINK:
450 			drink();
451 			break;
452 
453 		case QUIT:
454 			die(0);
455 
456 		default:
457 			puts("How's that?");
458 			return (0);
459 			break;
460 
461 		}
462 		if (!lflag)
463 			newlocation();
464 
465 		free(line);
466 
467 		if (wordnumber < wordcount && !stop_cypher &&
468 		    (*words[wordnumber] == ',' || *words[wordnumber] == '.')) {
469 			wordnumber++;
470 			return (1);
471 		} else
472 			return (0);
473 	}
474 	return (0);
475 }
476 
477 int
inc_wordnumber(const char * v,const char * adv)478 inc_wordnumber(const char *v, const char *adv)
479 {
480 	wordnumber++;
481 	if (wordnumber >= wordcount) {
482 		printf("%c%s %s?\n",
483 		    toupper((unsigned char)v[0]), v + 1, adv);
484 		return(-1);
485 	}
486 	return(0);
487 }
488 
489 static void
verb_with_all(unsigned int * testarray,int objflg,int (* verbfunc)(void),const char * verbname)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