1 /*        $NetBSD: execute.c,v 1.13 2021/05/02 12:50:45 rillig Exp $  */
2 /*
3  * Copyright (c) 1983-2003, Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * + Redistributions of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  * + Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  * + Neither the name of the University of California, San Francisco nor
16  *   the names of its contributors may be used to endorse or promote
17  *   products derived from this software without specific prior written
18  *   permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __RCSID("$NetBSD: execute.c,v 1.13 2021/05/02 12:50:45 rillig Exp $");
36 #endif /* not lint */
37 
38 #include <stdlib.h>
39 #include "hunt.h"
40 
41 static void cloak(PLAYER *);
42 static void turn_player(PLAYER *, int);
43 static void fire(PLAYER *, int);
44 static void fire_slime(PLAYER *, int);
45 static void move_player(PLAYER *, int);
46 static void pickup(PLAYER *, int, int, int, int);
47 static void scan(PLAYER *);
48 
49 
50 #ifdef MONITOR
51 /*
52  * mon_execute:
53  *        Execute a single monitor command
54  */
55 void
mon_execute(PLAYER * pp)56 mon_execute(PLAYER *pp)
57 {
58           char ch;
59 
60           ch = pp->p_cbuf[pp->p_ncount++];
61           switch (ch) {
62             case CTRL('L'):
63                     sendcom(pp, REDRAW);
64                     break;
65             case 'q':
66                     (void) strcpy(pp->p_death, "| Quit |");
67                     break;
68           }
69 }
70 #endif
71 
72 /*
73  * execute:
74  *        Execute a single command
75  */
76 void
execute(PLAYER * pp)77 execute(PLAYER *pp)
78 {
79           char ch;
80 
81           ch = pp->p_cbuf[pp->p_ncount++];
82 
83 #ifdef FLY
84           if (pp->p_flying >= 0) {
85                     switch (ch) {
86                       case CTRL('L'):
87                               sendcom(pp, REDRAW);
88                               break;
89                       case 'q':
90                               (void) strcpy(pp->p_death, "| Quit |");
91                               break;
92                     }
93                     return;
94           }
95 #endif
96 
97           switch (ch) {
98             case CTRL('L'):
99                     sendcom(pp, REDRAW);
100                     break;
101             case 'h':
102                     move_player(pp, LEFTS);
103                     break;
104             case 'H':
105                     turn_player(pp, LEFTS);
106                     break;
107             case 'j':
108                     move_player(pp, BELOW);
109                     break;
110             case 'J':
111                     turn_player(pp, BELOW);
112                     break;
113             case 'k':
114                     move_player(pp, ABOVE);
115                     break;
116             case 'K':
117                     turn_player(pp, ABOVE);
118                     break;
119             case 'l':
120                     move_player(pp, RIGHT);
121                     break;
122             case 'L':
123                     turn_player(pp, RIGHT);
124                     break;
125             case 'f':
126             case '1':
127                     fire(pp, 0);                  /* SHOT */
128                     break;
129             case 'g':
130             case '2':
131                     fire(pp, 1);                  /* GRENADE */
132                     break;
133             case 'F':
134             case '3':
135                     fire(pp, 2);                  /* SATCHEL */
136                     break;
137             case 'G':
138             case '4':
139                     fire(pp, 3);                  /* 7x7 BOMB */
140                     break;
141             case '5':
142                     fire(pp, 4);                  /* 9x9 BOMB */
143                     break;
144             case '6':
145                     fire(pp, 5);                  /* 11x11 BOMB */
146                     break;
147             case '7':
148                     fire(pp, 6);                  /* 13x13 BOMB */
149                     break;
150             case '8':
151                     fire(pp, 7);                  /* 15x15 BOMB */
152                     break;
153             case '9':
154                     fire(pp, 8);                  /* 17x17 BOMB */
155                     break;
156             case '0':
157                     fire(pp, 9);                  /* 19x19 BOMB */
158                     break;
159             case '@':
160                     fire(pp, 10);                 /* 21x21 BOMB */
161                     break;
162 #ifdef OOZE
163             case 'o':
164                     fire_slime(pp, 0);  /* SLIME */
165                     break;
166             case 'O':
167                     fire_slime(pp, 1);  /* SSLIME */
168                     break;
169             case 'p':
170                     fire_slime(pp, 2);
171                     break;
172             case 'P':
173                     fire_slime(pp, 3);
174                     break;
175 #endif
176             case 's':
177                     scan(pp);
178                     break;
179             case 'c':
180                     cloak(pp);
181                     break;
182             case 'q':
183                     (void) strcpy(pp->p_death, "| Quit |");
184                     break;
185           }
186 }
187 
188 /*
189  * move_player:
190  *        Execute a move in the given direction
191  */
192 static void
move_player(PLAYER * pp,int dir)193 move_player(PLAYER *pp, int dir)
194 {
195           PLAYER *newp;
196           int x, y;
197           bool moved;
198           BULLET *bp;
199 
200           y = pp->p_y;
201           x = pp->p_x;
202 
203           switch (dir) {
204             case LEFTS:
205                     x--;
206                     break;
207             case RIGHT:
208                     x++;
209                     break;
210             case ABOVE:
211                     y--;
212                     break;
213             case BELOW:
214                     y++;
215                     break;
216           }
217 
218           moved = false;
219           switch (Maze[y][x]) {
220             case SPACE:
221 #ifdef RANDOM
222             case DOOR:
223 #endif
224                     moved = true;
225                     break;
226             case WALL1:
227             case WALL2:
228             case WALL3:
229 #ifdef REFLECT
230             case WALL4:
231             case WALL5:
232 #endif
233                     break;
234             case MINE:
235             case GMINE:
236                     if (dir == pp->p_face)
237                               pickup(pp, y, x, 2, Maze[y][x]);
238                     else if (opposite(dir, pp->p_face))
239                               pickup(pp, y, x, 95, Maze[y][x]);
240                     else
241                               pickup(pp, y, x, 50, Maze[y][x]);
242                     Maze[y][x] = SPACE;
243                     moved = true;
244                     break;
245             case SHOT:
246             case GRENADE:
247             case SATCHEL:
248             case BOMB:
249 #ifdef OOZE
250             case SLIME:
251 #endif
252 #ifdef DRONE
253             case DSHOT:
254 #endif
255                     bp = is_bullet(y, x);
256                     if (bp != NULL)
257                               bp->b_expl = true;
258                     Maze[y][x] = SPACE;
259                     moved = true;
260                     break;
261             case LEFTS:
262             case RIGHT:
263             case ABOVE:
264             case BELOW:
265                     if (dir != pp->p_face)
266                               sendcom(pp, BELL);
267                     else {
268                               newp = play_at(y, x);
269                               checkdam(newp, pp, pp->p_ident, STABDAM, KNIFE);
270                     }
271                     break;
272 #ifdef FLY
273             case FLYER:
274                     newp = play_at(y, x);
275                     message(newp, "Oooh, there's a short guy waving at you!");
276                     message(pp, "You couldn't quite reach him!");
277                     break;
278 #endif
279 #ifdef BOOTS
280             case BOOT:
281             case BOOT_PAIR:
282                     if (Maze[y][x] == BOOT)
283                               pp->p_nboots++;
284                     else
285                               pp->p_nboots += 2;
286                     for (newp = Boot; newp < &Boot[NBOOTS]; newp++) {
287                               if (newp->p_flying < 0)
288                                         continue;
289                               if (newp->p_y == y && newp->p_x == x) {
290                                         newp->p_flying = -1;
291                                         if (newp->p_undershot)
292                                                   fixshots(y, x, newp->p_over);
293                               }
294                     }
295                     if (pp->p_nboots == 2)
296                               message(pp, "Wow!  A pair of boots!");
297                     else
298                               message(pp, "You can hobble around on one boot.");
299                     Maze[y][x] = SPACE;
300                     moved = true;
301                     break;
302 #endif
303           }
304           if (moved) {
305                     if (pp->p_ncshot > 0)
306                               if (--pp->p_ncshot == MAXNCSHOT) {
307                                         cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
308                                         outstr(pp, " ok", 3);
309                               }
310                     if (pp->p_undershot) {
311                               fixshots(pp->p_y, pp->p_x, pp->p_over);
312                               pp->p_undershot = false;
313                     }
314                     drawplayer(pp, false);
315                     pp->p_over = Maze[y][x];
316                     pp->p_y = y;
317                     pp->p_x = x;
318                     drawplayer(pp, true);
319           }
320 }
321 
322 /*
323  * turn_player:
324  *        Change the direction the player is facing
325  */
326 static void
turn_player(PLAYER * pp,int dir)327 turn_player(PLAYER *pp, int dir)
328 {
329           if (pp->p_face != dir) {
330                     pp->p_face = dir;
331                     drawplayer(pp, true);
332           }
333 }
334 
335 /*
336  * fire:
337  *        Fire a shot of the given type in the given direction
338  */
339 static void
fire(PLAYER * pp,int req_index)340 fire(PLAYER *pp, int req_index)
341 {
342           if (pp == NULL)
343                     return;
344 #ifdef DEBUG
345           if (req_index < 0 || req_index >= MAXBOMB)
346                     message(pp, "What you do?");
347 #endif
348           while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
349                     req_index--;
350           if (req_index < 0) {
351                     message(pp, "Not enough charges.");
352                     return;
353           }
354           if (pp->p_ncshot > MAXNCSHOT)
355                     return;
356           if (pp->p_ncshot++ == MAXNCSHOT) {
357                     cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
358                     outstr(pp, "   ", 3);
359           }
360           pp->p_ammo -= shot_req[req_index];
361           (void) snprintf(Buf, sizeof(Buf), "%3d", pp->p_ammo);
362           cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
363           outstr(pp, Buf, 3);
364 
365           add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
366                     shot_req[req_index], pp, false, pp->p_face);
367           pp->p_undershot = true;
368 
369           /*
370            * Show the object to everyone
371            */
372           showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
373           for (pp = Player; pp < End_player; pp++)
374                     sendcom(pp, REFRESH);
375 #ifdef MONITOR
376           for (pp = Monitor; pp < End_monitor; pp++)
377                     sendcom(pp, REFRESH);
378 #endif
379 }
380 
381 #ifdef OOZE
382 /*
383  * fire_slime:
384  *        Fire a slime shot in the given direction
385  */
386 static void
fire_slime(PLAYER * pp,int req_index)387 fire_slime(PLAYER *pp, int req_index)
388 {
389           if (pp == NULL)
390                     return;
391 #ifdef DEBUG
392           if (req_index < 0 || req_index >= MAXSLIME)
393                     message(pp, "What you do?");
394 #endif
395           while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
396                     req_index--;
397           if (req_index < 0) {
398                     message(pp, "Not enough charges.");
399                     return;
400           }
401           if (pp->p_ncshot > MAXNCSHOT)
402                     return;
403           if (pp->p_ncshot++ == MAXNCSHOT) {
404                     cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
405                     outstr(pp, "   ", 3);
406           }
407           pp->p_ammo -= slime_req[req_index];
408           (void) snprintf(Buf, sizeof(Buf), "%3d", pp->p_ammo);
409           cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
410           outstr(pp, Buf, 3);
411 
412           add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
413                     slime_req[req_index] * SLIME_FACTOR, pp, false, pp->p_face);
414           pp->p_undershot = true;
415 
416           /*
417            * Show the object to everyone
418            */
419           showexpl(pp->p_y, pp->p_x, SLIME);
420           for (pp = Player; pp < End_player; pp++)
421                     sendcom(pp, REFRESH);
422 #ifdef MONITOR
423           for (pp = Monitor; pp < End_monitor; pp++)
424                     sendcom(pp, REFRESH);
425 #endif
426 }
427 #endif
428 
429 /*
430  * add_shot:
431  *        Create a shot with the given properties
432  */
433 void
add_shot(int type,int y,int x,char face,int charge,PLAYER * owner,int expl,char over)434 add_shot(int type, int y, int x, char face, int charge,
435            PLAYER *owner, int expl, char over)
436 {
437           BULLET *bp;
438           int size;
439 
440           switch (type) {
441             case SHOT:
442             case MINE:
443                     size = 1;
444                     break;
445             case GRENADE:
446             case GMINE:
447                     size = 2;
448                     break;
449             case SATCHEL:
450                     size = 3;
451                     break;
452             case BOMB:
453                     for (size = 3; size < MAXBOMB; size++)
454                               if (shot_req[size] >= charge)
455                                         break;
456                     size++;
457                     break;
458             default:
459                     size = 0;
460                     break;
461           }
462 
463           bp = create_shot(type, y, x, face, charge, size, owner,
464                     (owner == NULL) ? NULL : owner->p_ident, expl, over);
465           bp->b_next = Bullets;
466           Bullets = bp;
467 }
468 
469 BULLET *
create_shot(int type,int y,int x,char face,int charge,int size,PLAYER * owner,IDENT * score,int expl,char over)470 create_shot(int type, int y, int x, char face, int charge,
471               int size, PLAYER *owner, IDENT *score, int expl, char over)
472 {
473           BULLET *bp;
474 
475           bp = malloc(sizeof(*bp));
476           if (bp == NULL) {
477                     if (owner != NULL)
478                               message(owner, "Out of memory");
479                     return NULL;
480           }
481 
482           bp->b_face = face;
483           bp->b_x = x;
484           bp->b_y = y;
485           bp->b_charge = charge;
486           bp->b_owner = owner;
487           bp->b_score = score;
488           bp->b_type = type;
489           bp->b_size = size;
490           bp->b_expl = expl;
491           bp->b_over = over;
492           bp->b_next = NULL;
493 
494           return bp;
495 }
496 
497 /*
498  * cloak:
499  *        Turn on or increase length of a cloak
500  */
501 static void
cloak(PLAYER * pp)502 cloak(PLAYER *pp)
503 {
504           if (pp->p_ammo <= 0) {
505                     message(pp, "No more charges");
506                     return;
507           }
508 #ifdef BOOTS
509           if (pp->p_nboots > 0) {
510                     message(pp, "Boots are too noisy to cloak!");
511                     return;
512           }
513 #endif
514           (void) snprintf(Buf, sizeof(Buf), "%3d", --pp->p_ammo);
515           cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
516           outstr(pp, Buf, 3);
517 
518           pp->p_cloak += CLOAKLEN;
519 
520           if (pp->p_scan >= 0)
521                     pp->p_scan = -1;
522 
523           showstat(pp);
524 }
525 
526 /*
527  * scan:
528  *        Turn on or increase length of a scan
529  */
530 static void
scan(PLAYER * pp)531 scan(PLAYER *pp)
532 {
533           if (pp->p_ammo <= 0) {
534                     message(pp, "No more charges");
535                     return;
536           }
537           (void) snprintf(Buf, sizeof(Buf), "%3d", --pp->p_ammo);
538           cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
539           outstr(pp, Buf, 3);
540 
541           pp->p_scan += SCANLEN;
542 
543           if (pp->p_cloak >= 0)
544                     pp->p_cloak = -1;
545 
546           showstat(pp);
547 }
548 
549 /*
550  * pickup:
551  *        check whether the object blew up or whether he picked it up
552  */
553 static void
pickup(PLAYER * pp,int y,int x,int prob,int obj)554 pickup(PLAYER *pp, int y, int x, int prob, int obj)
555 {
556           int req;
557 
558           switch (obj) {
559             case MINE:
560                     req = BULREQ;
561                     break;
562             case GMINE:
563                     req = GRENREQ;
564                     break;
565             default:
566                     abort();
567           }
568           if (rand_num(100) < prob)
569                     add_shot(obj, y, x, LEFTS, req, NULL, true, pp->p_face);
570           else {
571                     pp->p_ammo += req;
572                     (void) snprintf(Buf, sizeof(Buf), "%3d", pp->p_ammo);
573                     cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
574                     outstr(pp, Buf, 3);
575           }
576 }
577