1 /*-
2 * Copyright (c) 1983-2003, Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University of California, San Francisco nor
15 * the names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $OpenBSD: execute.c,v 1.8 2004/01/16 00:13:19 espie Exp $
32 * $NetBSD: execute.c,v 1.2 1997/10/10 16:33:13 lukem Exp $
33 */
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include "hunt.h"
39 #include "conf.h"
40 #include "server.h"
41
42 static void cloak(PLAYER *);
43 static void face(PLAYER *, int);
44 static void fire(PLAYER *, int);
45 static void fire_slime(PLAYER *, int);
46 static void move_player(PLAYER *, int);
47 static void pickup(PLAYER *, int, int, int, int);
48 static void scan(PLAYER *);
49
50
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
62 switch (ch) {
63 case CTRL('L'):
64 /* Redraw messed-up screen */
65 sendcom(pp, REDRAW);
66 break;
67 case 'q':
68 /* Quit client */
69 strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
70 break;
71 default:
72 /* Ignore everything else */
73 ;
74 }
75 }
76
77 /*
78 * execute:
79 * Execute a single command from a player
80 */
81 void
execute(PLAYER * pp)82 execute(PLAYER *pp)
83 {
84 char ch;
85
86 ch = pp->p_cbuf[pp->p_ncount++];
87
88 /* When flying, only allow refresh and quit. */
89 if (pp->p_flying >= 0) {
90 switch (ch) {
91 case CTRL('L'):
92 sendcom(pp, REDRAW);
93 break;
94 case 'q':
95 strlcpy(pp->p_death, "| Quit |",
96 sizeof pp->p_death);
97 break;
98 }
99 return;
100 }
101
102 /* Decode the command character: */
103 switch (ch) {
104 case CTRL('L'):
105 sendcom(pp, REDRAW); /* Refresh */
106 break;
107 case 'h':
108 move_player(pp, LEFTS); /* Move left */
109 break;
110 case 'H':
111 face(pp, LEFTS); /* Face left */
112 break;
113 case 'j':
114 move_player(pp, BELOW); /* Move down */
115 break;
116 case 'J':
117 face(pp, BELOW); /* Face down */
118 break;
119 case 'k':
120 move_player(pp, ABOVE); /* Move up */
121 break;
122 case 'K':
123 face(pp, ABOVE); /* Face up */
124 break;
125 case 'l':
126 move_player(pp, RIGHT); /* Move right */
127 break;
128 case 'L':
129 face(pp, RIGHT); /* Face right */
130 break;
131 case 'f':
132 case '1':
133 fire(pp, 0); /* SHOT */
134 break;
135 case 'g':
136 case '2':
137 fire(pp, 1); /* GRENADE */
138 break;
139 case 'F':
140 case '3':
141 fire(pp, 2); /* SATCHEL */
142 break;
143 case 'G':
144 case '4':
145 fire(pp, 3); /* 7x7 BOMB */
146 break;
147 case '5':
148 fire(pp, 4); /* 9x9 BOMB */
149 break;
150 case '6':
151 fire(pp, 5); /* 11x11 BOMB */
152 break;
153 case '7':
154 fire(pp, 6); /* 13x13 BOMB */
155 break;
156 case '8':
157 fire(pp, 7); /* 15x15 BOMB */
158 break;
159 case '9':
160 fire(pp, 8); /* 17x17 BOMB */
161 break;
162 case '0':
163 fire(pp, 9); /* 19x19 BOMB */
164 break;
165 case '@':
166 fire(pp, 10); /* 21x21 BOMB */
167 break;
168 case 'o':
169 fire_slime(pp, 0); /* SLIME */
170 break;
171 case 'O':
172 fire_slime(pp, 1); /* SSLIME */
173 break;
174 case 'p':
175 fire_slime(pp, 2); /* large slime */
176 break;
177 case 'P':
178 fire_slime(pp, 3); /* very large slime */
179 break;
180 case 's': /* start scanning */
181 scan(pp);
182 break;
183 case 'c': /* start cloaking */
184 cloak(pp);
185 break;
186 case 'q': /* quit */
187 strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
188 break;
189 }
190 }
191
192 /*
193 * move_player:
194 * Try to move player 'pp' in direction 'dir'.
195 */
196 static void
move_player(PLAYER * pp,int dir)197 move_player(PLAYER *pp, int dir)
198 {
199 PLAYER *newp;
200 int x, y;
201 FLAG moved;
202 BULLET *bp;
203
204 y = pp->p_y;
205 x = pp->p_x;
206
207 switch (dir) {
208 case LEFTS:
209 x--;
210 break;
211 case RIGHT:
212 x++;
213 break;
214 case ABOVE:
215 y--;
216 break;
217 case BELOW:
218 y++;
219 break;
220 }
221
222 moved = FALSE;
223
224 /* What would the player move over: */
225 switch (Maze[y][x]) {
226 /* Players can move through spaces and doors, no problem: */
227 case SPACE:
228 case DOOR:
229 moved = TRUE;
230 break;
231 /* Can't move through walls: */
232 case WALL1:
233 case WALL2:
234 case WALL3:
235 case WALL4:
236 case WALL5:
237 break;
238 /* Moving over a mine - try to pick it up: */
239 case MINE:
240 case GMINE:
241 if (dir == pp->p_face)
242 /* facing it: 2% chance of trip */
243 pickup(pp, y, x, conf_ptrip_face, Maze[y][x]);
244 else if (opposite(dir, pp->p_face))
245 /* facing away: 95% chance of trip */
246 pickup(pp, y, x, conf_ptrip_back, Maze[y][x]);
247 else
248 /* facing sideways: 50% chance of trip */
249 pickup(pp, y, x, conf_ptrip_side, Maze[y][x]);
250 /* Remove the mine: */
251 Maze[y][x] = SPACE;
252 moved = TRUE;
253 break;
254 /* Moving into a bullet: */
255 case SHOT:
256 case GRENADE:
257 case SATCHEL:
258 case BOMB:
259 case SLIME:
260 case DSHOT:
261 /* Find which bullet: */
262 bp = is_bullet(y, x);
263 if (bp != NULL)
264 /* Detonate it: */
265 bp->b_expl = TRUE;
266 /* Remove it: */
267 Maze[y][x] = SPACE;
268 moved = TRUE;
269 break;
270 /* Moving into another player: */
271 case LEFTS:
272 case RIGHT:
273 case ABOVE:
274 case BELOW:
275 if (dir != pp->p_face)
276 /* Can't walk backwards/sideways into another player: */
277 sendcom(pp, BELL);
278 else {
279 /* Stab the other player */
280 newp = play_at(y, x);
281 checkdam(newp, pp, pp->p_ident, conf_stabdam, KNIFE);
282 }
283 break;
284 /* Moving into a player flying overhead: */
285 case FLYER:
286 newp = play_at(y, x);
287 message(newp, "Oooh, there's a short guy waving at you!");
288 message(pp, "You couldn't quite reach him!");
289 break;
290 /* Picking up a boot, or two: */
291 case BOOT_PAIR:
292 pp->p_nboots++;
293 /* FALLTHROUGH */
294 case BOOT:
295 pp->p_nboots++;
296 for (newp = Boot; newp < &Boot[NBOOTS]; newp++) {
297 if (newp->p_flying < 0)
298 continue;
299 if (newp->p_y == y && newp->p_x == x) {
300 newp->p_flying = -1;
301 if (newp->p_undershot)
302 fixshots(y, x, newp->p_over);
303 }
304 }
305 if (pp->p_nboots == 2)
306 message(pp, "Wow! A pair of boots!");
307 else
308 message(pp, "You can hobble around on one boot.");
309 Maze[y][x] = SPACE;
310 moved = TRUE;
311 break;
312 }
313
314 /* Can the player be moved? */
315 if (moved) {
316 /* Check the gun status: */
317 if (pp->p_ncshot > 0)
318 if (--pp->p_ncshot == conf_maxncshot)
319 outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ok");
320 /* Check for bullets flying past: */
321 if (pp->p_undershot) {
322 fixshots(pp->p_y, pp->p_x, pp->p_over);
323 pp->p_undershot = FALSE;
324 }
325 /* Erase the player: */
326 drawplayer(pp, FALSE);
327 /* Save under: */
328 pp->p_over = Maze[y][x];
329 /* Move the player: */
330 pp->p_y = y;
331 pp->p_x = x;
332 /* Draw the player in their new position */
333 drawplayer(pp, TRUE);
334 }
335 }
336
337 /*
338 * face:
339 * Change the direction the player is facing
340 */
341 static void
face(PLAYER * pp,int dir)342 face(PLAYER *pp, int dir)
343 {
344 if (pp->p_face != dir) {
345 pp->p_face = dir;
346 drawplayer(pp, TRUE);
347 }
348 }
349
350 /*
351 * fire:
352 * Fire a shot of the given type in the given direction
353 */
354 static void
fire(PLAYER * pp,int req_index)355 fire(PLAYER *pp, int req_index)
356 {
357 if (pp == NULL)
358 return;
359
360 /* Drop the shot type down until we can afford it: */
361 while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
362 req_index--;
363
364 /* Can we shoot at all? */
365 if (req_index < 0) {
366 message(pp, "Not enough charges.");
367 return;
368 }
369
370 /* Check if the gun is too hot: */
371 if (pp->p_ncshot > conf_maxncshot)
372 return;
373
374 /* Heat up the gun: */
375 if (pp->p_ncshot++ == conf_maxncshot) {
376 /* The gun has overheated: */
377 outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ");
378 }
379
380 /* Use up some ammo: */
381 pp->p_ammo -= shot_req[req_index];
382 ammo_update(pp);
383
384 /* Start the bullet moving: */
385 add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
386 shot_req[req_index], pp, FALSE, pp->p_face);
387 pp->p_undershot = TRUE;
388
389 /* Show the bullet to everyone: */
390 showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
391 sendcom(ALL_PLAYERS, REFRESH);
392 }
393
394 /*
395 * fire_slime:
396 * Fire a slime shot in the given direction
397 */
398 static void
fire_slime(PLAYER * pp,int req_index)399 fire_slime(PLAYER *pp, int req_index)
400 {
401 if (pp == NULL)
402 return;
403
404 /* Check configuration: */
405 if (!conf_ooze)
406 return;
407
408 /* Drop the slime type back util we can afford it: */
409 while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
410 req_index--;
411
412 /* Can we afford to slime at all? */
413 if (req_index < 0) {
414 message(pp, "Not enough charges.");
415 return;
416 }
417
418 /* Is the gun too hot? */
419 if (pp->p_ncshot > conf_maxncshot)
420 return;
421
422 /* Heat up the gun: */
423 if (pp->p_ncshot++ == conf_maxncshot) {
424 /* The gun has overheated: */
425 outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ");
426 }
427
428 /* Use up some ammo: */
429 pp->p_ammo -= slime_req[req_index];
430 ammo_update(pp);
431
432 /* Start the slime moving: */
433 add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
434 slime_req[req_index] * conf_slimefactor, pp, FALSE, pp->p_face);
435 pp->p_undershot = TRUE;
436
437 /* Show the object to everyone: */
438 showexpl(pp->p_y, pp->p_x, SLIME);
439 sendcom(ALL_PLAYERS, REFRESH);
440 }
441
442 /*
443 * add_shot:
444 * Create a shot with the given properties
445 */
446 void
add_shot(int type,int y,int x,char wface,int charge,PLAYER * owner,int expl,char over)447 add_shot(int type, int y, int x, char wface, int charge, PLAYER *owner,
448 int expl, char over)
449 {
450 BULLET *bp;
451 int size;
452
453 /* Determine the bullet's size based on its type and charge: */
454 switch (type) {
455 case SHOT:
456 case MINE:
457 size = 1;
458 break;
459 case GRENADE:
460 case GMINE:
461 size = 2;
462 break;
463 case SATCHEL:
464 size = 3;
465 break;
466 case BOMB:
467 for (size = 3; size < MAXBOMB; size++)
468 if (shot_req[size] >= charge)
469 break;
470 size++;
471 break;
472 default:
473 size = 0;
474 break;
475 }
476
477 /* Create the bullet: */
478 bp = create_shot(type, y, x, wface, charge, size, owner,
479 (owner == NULL) ? NULL : owner->p_ident, expl, over);
480
481 /* Insert the bullet into the front of the bullet list: */
482 bp->b_next = Bullets;
483 Bullets = bp;
484 }
485
486 /*
487 * create_shot:
488 * allocate storage for an (unlinked) bullet structure;
489 * initialize and return it
490 */
491 BULLET *
create_shot(int type,int y,int x,char wface,int charge,int size,PLAYER * owner,IDENT * score,int expl,char over)492 create_shot(int type, int y, int x, char wface, int charge, int size,
493 PLAYER *owner, IDENT *score, int expl, char over)
494 {
495 BULLET *bp;
496
497 bp = (BULLET *) malloc(sizeof (BULLET)); /* NOSTRICT */
498 if (bp == NULL) {
499 logit(LOG_ERR, "malloc");
500 if (owner != NULL)
501 message(owner, "Out of memory");
502 return NULL;
503 }
504
505 bp->b_face = wface;
506 bp->b_x = x;
507 bp->b_y = y;
508 bp->b_charge = charge;
509 bp->b_owner = owner;
510 bp->b_score = score;
511 bp->b_type = type;
512 bp->b_size = size;
513 bp->b_expl = expl;
514 bp->b_over = over;
515 bp->b_next = NULL;
516
517 return bp;
518 }
519
520 /*
521 * cloak:
522 * Turn on or increase length of a cloak
523 */
524 static void
cloak(PLAYER * pp)525 cloak(PLAYER *pp)
526 {
527 /* Check configuration: */
528 if (!conf_cloak)
529 return;
530
531 /* Can we afford it?: */
532 if (pp->p_ammo <= 0) {
533 message(pp, "No more charges");
534 return;
535 }
536
537 /* Can't cloak with boots: */
538 if (pp->p_nboots > 0) {
539 message(pp, "Boots are too noisy to cloak!");
540 return;
541 }
542
543 /* Consume a unit of ammo: */
544 pp->p_ammo--;
545 ammo_update(pp);
546
547 /* Add to the duration of a cloak: */
548 pp->p_cloak += conf_cloaklen;
549
550 /* Disable scan, if enabled: */
551 if (pp->p_scan >= 0)
552 pp->p_scan = -1;
553
554 /* Re-draw the player's scan/cloak status: */
555 showstat(pp);
556 }
557
558 /*
559 * scan:
560 * Turn on or increase length of a scan
561 */
562 static void
scan(PLAYER * pp)563 scan(PLAYER *pp)
564 {
565 /* Check configuration: */
566 if (!conf_scan)
567 return;
568
569 /* Can we afford it?: */
570 if (pp->p_ammo <= 0) {
571 message(pp, "No more charges");
572 return;
573 }
574
575 /* Consume one unit of ammo: */
576 pp->p_ammo--;
577 ammo_update(pp);
578
579 /* Increase the scan time: */
580 pp->p_scan += Nplayer * conf_scanlen;
581
582 /* Disable cloak, if enabled: */
583 if (pp->p_cloak >= 0)
584 pp->p_cloak = -1;
585
586 /* Re-draw the player's scan/cloak status: */
587 showstat(pp);
588 }
589
590 /*
591 * pickup:
592 * pick up a mine or grenade, with some probability of it exploding
593 */
594 static void
pickup(PLAYER * pp,int y,int x,int prob,int obj)595 pickup(PLAYER *pp, int y, int x, int prob, int obj)
596 {
597 int req;
598
599 /* Figure out how much ammo the player is trying to pick up: */
600 switch (obj) {
601 case MINE:
602 req = BULREQ;
603 break;
604 case GMINE:
605 req = GRENREQ;
606 break;
607 default:
608 #ifdef DIAGNOSTIC
609 abort();
610 #endif
611 return;
612 }
613
614 /* Does it explode? */
615 if (rand_num(100) < prob)
616 /* Ooooh, unlucky: (Boom) */
617 add_shot(obj, y, x, LEFTS, req, NULL, TRUE, pp->p_face);
618 else {
619 /* Safely picked it up. Add to player's ammo: */
620 pp->p_ammo += req;
621 ammo_update(pp);
622 }
623 }
624
625 void
ammo_update(PLAYER * pp)626 ammo_update(PLAYER *pp)
627 {
628 outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo);
629 }
630