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