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