xref: /openbsd-src/games/hunt/huntd/execute.c (revision ac1fa6a8a418bc90e3475167b0e6680f850eeb92)
1*ac1fa6a8Skrw /*	$OpenBSD: execute.c,v 1.14 2017/01/21 08:22:57 krw Exp $	*/
23faf6791Sd /*	$NetBSD: execute.c,v 1.2 1997/10/10 16:33:13 lukem Exp $	*/
33faf6791Sd /*
4598075eaSpjanzen  * Copyright (c) 1983-2003, Regents of the University of California.
5598075eaSpjanzen  * All rights reserved.
6598075eaSpjanzen  *
7598075eaSpjanzen  * Redistribution and use in source and binary forms, with or without
8598075eaSpjanzen  * modification, are permitted provided that the following conditions are
9598075eaSpjanzen  * met:
10598075eaSpjanzen  *
11598075eaSpjanzen  * + Redistributions of source code must retain the above copyright
12598075eaSpjanzen  *   notice, this list of conditions and the following disclaimer.
13598075eaSpjanzen  * + Redistributions in binary form must reproduce the above copyright
14598075eaSpjanzen  *   notice, this list of conditions and the following disclaimer in the
15598075eaSpjanzen  *   documentation and/or other materials provided with the distribution.
16598075eaSpjanzen  * + Neither the name of the University of California, San Francisco nor
17598075eaSpjanzen  *   the names of its contributors may be used to endorse or promote
18598075eaSpjanzen  *   products derived from this software without specific prior written
19598075eaSpjanzen  *   permission.
20598075eaSpjanzen  *
21598075eaSpjanzen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22598075eaSpjanzen  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23598075eaSpjanzen  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24598075eaSpjanzen  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25598075eaSpjanzen  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26598075eaSpjanzen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27598075eaSpjanzen  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28598075eaSpjanzen  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29598075eaSpjanzen  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30598075eaSpjanzen  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31598075eaSpjanzen  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
323faf6791Sd  */
333faf6791Sd 
34e29f8a1bSguenther #include <sys/select.h>
353faf6791Sd #include <stdlib.h>
36fab1dce0Sd #include <string.h>
37fab1dce0Sd #include <syslog.h>
389ef48543Smestre 
39fab1dce0Sd #include "conf.h"
409ef48543Smestre #include "hunt.h"
41fab1dce0Sd #include "server.h"
423faf6791Sd 
43c72b5b24Smillert static void	cloak(PLAYER *);
44c72b5b24Smillert static void	face(PLAYER *, int);
45c72b5b24Smillert static void	fire(PLAYER *, int);
46c72b5b24Smillert static void	fire_slime(PLAYER *, int);
47c72b5b24Smillert static void	move_player(PLAYER *, int);
48c72b5b24Smillert static void	pickup(PLAYER *, int, int, int, int);
49c72b5b24Smillert static void	scan(PLAYER *);
503faf6791Sd 
513faf6791Sd 
523faf6791Sd /*
533faf6791Sd  * mon_execute:
543faf6791Sd  *	Execute a single monitor command
553faf6791Sd  */
563faf6791Sd void
mon_execute(PLAYER * pp)570f16a76cSmestre mon_execute(PLAYER *pp)
583faf6791Sd {
593faf6791Sd 	char	ch;
603faf6791Sd 
613faf6791Sd 	ch = pp->p_cbuf[pp->p_ncount++];
62fab1dce0Sd 
633faf6791Sd 	switch (ch) {
643faf6791Sd 	  case CTRL('L'):
65fab1dce0Sd 		/* Redraw messed-up screen */
663faf6791Sd 		sendcom(pp, REDRAW);
673faf6791Sd 		break;
683faf6791Sd 	  case 'q':
69fab1dce0Sd 		/* Quit client */
70fab1dce0Sd 		(void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
713faf6791Sd 		break;
72fab1dce0Sd 	  default:
73fab1dce0Sd 		/* Ignore everything else */
740194b367Sd 		;
753faf6791Sd 	}
763faf6791Sd }
773faf6791Sd 
783faf6791Sd /*
793faf6791Sd  * execute:
80fab1dce0Sd  *	Execute a single command from a player
813faf6791Sd  */
823faf6791Sd void
execute(PLAYER * pp)830f16a76cSmestre execute(PLAYER *pp)
843faf6791Sd {
853faf6791Sd 	char	ch;
863faf6791Sd 
873faf6791Sd 	ch = pp->p_cbuf[pp->p_ncount++];
883faf6791Sd 
89fab1dce0Sd 	/* When flying, only allow refresh and quit. */
903faf6791Sd 	if (pp->p_flying >= 0) {
913faf6791Sd 		switch (ch) {
923faf6791Sd 		  case CTRL('L'):
933faf6791Sd 			sendcom(pp, REDRAW);
943faf6791Sd 			break;
953faf6791Sd 		  case 'q':
96fab1dce0Sd 			(void) strlcpy(pp->p_death, "| Quit |",
97fab1dce0Sd 			    sizeof pp->p_death);
983faf6791Sd 			break;
993faf6791Sd 		}
1003faf6791Sd 		return;
1013faf6791Sd 	}
1023faf6791Sd 
103fab1dce0Sd 	/* Decode the command character: */
1043faf6791Sd 	switch (ch) {
1053faf6791Sd 	  case CTRL('L'):
106fab1dce0Sd 		sendcom(pp, REDRAW);	/* Refresh */
1073faf6791Sd 		break;
1083faf6791Sd 	  case 'h':
109fab1dce0Sd 		move_player(pp, LEFTS); /* Move left */
1103faf6791Sd 		break;
1113faf6791Sd 	  case 'H':
112fab1dce0Sd 		face(pp, LEFTS);	/* Face left */
1133faf6791Sd 		break;
1143faf6791Sd 	  case 'j':
115fab1dce0Sd 		move_player(pp, BELOW); /* Move down */
1163faf6791Sd 		break;
1173faf6791Sd 	  case 'J':
118fab1dce0Sd 		face(pp, BELOW);	/* Face down */
1193faf6791Sd 		break;
1203faf6791Sd 	  case 'k':
121fab1dce0Sd 		move_player(pp, ABOVE); /* Move up */
1223faf6791Sd 		break;
1233faf6791Sd 	  case 'K':
124fab1dce0Sd 		face(pp, ABOVE);	/* Face up */
1253faf6791Sd 		break;
1263faf6791Sd 	  case 'l':
127fab1dce0Sd 		move_player(pp, RIGHT);	/* Move right */
1283faf6791Sd 		break;
1293faf6791Sd 	  case 'L':
130fab1dce0Sd 		face(pp, RIGHT);	/* Face right */
1313faf6791Sd 		break;
1323faf6791Sd 	  case 'f':
1333faf6791Sd 	  case '1':
1343faf6791Sd 		fire(pp, 0);		/* SHOT */
1353faf6791Sd 		break;
1363faf6791Sd 	  case 'g':
1373faf6791Sd 	  case '2':
1383faf6791Sd 		fire(pp, 1);		/* GRENADE */
1393faf6791Sd 		break;
1403faf6791Sd 	  case 'F':
1413faf6791Sd 	  case '3':
1423faf6791Sd 		fire(pp, 2);		/* SATCHEL */
1433faf6791Sd 		break;
1443faf6791Sd 	  case 'G':
1453faf6791Sd 	  case '4':
1463faf6791Sd 		fire(pp, 3);		/* 7x7 BOMB */
1473faf6791Sd 		break;
1483faf6791Sd 	  case '5':
1493faf6791Sd 		fire(pp, 4);		/* 9x9 BOMB */
1503faf6791Sd 		break;
1513faf6791Sd 	  case '6':
1523faf6791Sd 		fire(pp, 5);		/* 11x11 BOMB */
1533faf6791Sd 		break;
1543faf6791Sd 	  case '7':
1553faf6791Sd 		fire(pp, 6);		/* 13x13 BOMB */
1563faf6791Sd 		break;
1573faf6791Sd 	  case '8':
1583faf6791Sd 		fire(pp, 7);		/* 15x15 BOMB */
1593faf6791Sd 		break;
1603faf6791Sd 	  case '9':
1613faf6791Sd 		fire(pp, 8);		/* 17x17 BOMB */
1623faf6791Sd 		break;
1633faf6791Sd 	  case '0':
1643faf6791Sd 		fire(pp, 9);		/* 19x19 BOMB */
1653faf6791Sd 		break;
1663faf6791Sd 	  case '@':
1673faf6791Sd 		fire(pp, 10);		/* 21x21 BOMB */
1683faf6791Sd 		break;
1693faf6791Sd 	  case 'o':
1703faf6791Sd 		fire_slime(pp, 0);	/* SLIME */
1713faf6791Sd 		break;
1723faf6791Sd 	  case 'O':
1733faf6791Sd 		fire_slime(pp, 1);	/* SSLIME */
1743faf6791Sd 		break;
1753faf6791Sd 	  case 'p':
176fab1dce0Sd 		fire_slime(pp, 2);	/* large slime */
1773faf6791Sd 		break;
1783faf6791Sd 	  case 'P':
179fab1dce0Sd 		fire_slime(pp, 3);	/* very large slime */
1803faf6791Sd 		break;
181fab1dce0Sd 	  case 's':			/* start scanning */
1823faf6791Sd 		scan(pp);
1833faf6791Sd 		break;
184fab1dce0Sd 	  case 'c':			/* start cloaking */
1853faf6791Sd 		cloak(pp);
1863faf6791Sd 		break;
187fab1dce0Sd 	  case 'q':			/* quit */
188fab1dce0Sd 		(void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
1893faf6791Sd 		break;
1903faf6791Sd 	}
1913faf6791Sd }
1923faf6791Sd 
1933faf6791Sd /*
1943faf6791Sd  * move_player:
195fab1dce0Sd  *	Try to move player 'pp' in direction 'dir'.
1963faf6791Sd  */
1973faf6791Sd static void
move_player(PLAYER * pp,int dir)1980f16a76cSmestre move_player(PLAYER *pp, int dir)
1993faf6791Sd {
2003faf6791Sd 	PLAYER	*newp;
2013faf6791Sd 	int	x, y;
2023faf6791Sd 	FLAG	moved;
2033faf6791Sd 	BULLET	*bp;
2043faf6791Sd 
2053faf6791Sd 	y = pp->p_y;
2063faf6791Sd 	x = pp->p_x;
2073faf6791Sd 
2083faf6791Sd 	switch (dir) {
2093faf6791Sd 	  case LEFTS:
2103faf6791Sd 		x--;
2113faf6791Sd 		break;
2123faf6791Sd 	  case RIGHT:
2133faf6791Sd 		x++;
2143faf6791Sd 		break;
2153faf6791Sd 	  case ABOVE:
2163faf6791Sd 		y--;
2173faf6791Sd 		break;
2183faf6791Sd 	  case BELOW:
2193faf6791Sd 		y++;
2203faf6791Sd 		break;
2213faf6791Sd 	}
2223faf6791Sd 
2233faf6791Sd 	moved = FALSE;
224fab1dce0Sd 
225fab1dce0Sd 	/* What would the player move over: */
2263faf6791Sd 	switch (Maze[y][x]) {
227fab1dce0Sd 	  /* Players can move through spaces and doors, no problem: */
2283faf6791Sd 	  case SPACE:
2293faf6791Sd 	  case DOOR:
2303faf6791Sd 		moved = TRUE;
2313faf6791Sd 		break;
232fab1dce0Sd 	  /* Can't move through walls: */
2333faf6791Sd 	  case WALL1:
2343faf6791Sd 	  case WALL2:
2353faf6791Sd 	  case WALL3:
2363faf6791Sd 	  case WALL4:
2373faf6791Sd 	  case WALL5:
2383faf6791Sd 		break;
239fab1dce0Sd 	  /* Moving over a mine - try to pick it up: */
2403faf6791Sd 	  case MINE:
2413faf6791Sd 	  case GMINE:
2423faf6791Sd 		if (dir == pp->p_face)
243fab1dce0Sd 			/* facing it: 2% chance of trip */
244fab1dce0Sd 			pickup(pp, y, x, conf_ptrip_face, Maze[y][x]);
2453faf6791Sd 		else if (opposite(dir, pp->p_face))
246fab1dce0Sd 			/* facing away: 95% chance of trip */
247fab1dce0Sd 			pickup(pp, y, x, conf_ptrip_back, Maze[y][x]);
2483faf6791Sd 		else
249fab1dce0Sd 			/* facing sideways: 50% chance of trip */
250fab1dce0Sd 			pickup(pp, y, x, conf_ptrip_side, Maze[y][x]);
251fab1dce0Sd 		/* Remove the mine: */
2523faf6791Sd 		Maze[y][x] = SPACE;
2533faf6791Sd 		moved = TRUE;
2543faf6791Sd 		break;
255fab1dce0Sd 	  /* Moving into a bullet: */
2563faf6791Sd 	  case SHOT:
2573faf6791Sd 	  case GRENADE:
2583faf6791Sd 	  case SATCHEL:
2593faf6791Sd 	  case BOMB:
2603faf6791Sd 	  case SLIME:
2613faf6791Sd 	  case DSHOT:
262fab1dce0Sd 		/* Find which bullet: */
2633faf6791Sd 		bp = is_bullet(y, x);
2643faf6791Sd 		if (bp != NULL)
265fab1dce0Sd 			/* Detonate it: */
2663faf6791Sd 			bp->b_expl = TRUE;
267fab1dce0Sd 		/* Remove it: */
2683faf6791Sd 		Maze[y][x] = SPACE;
2693faf6791Sd 		moved = TRUE;
2703faf6791Sd 		break;
271fab1dce0Sd 	  /* Moving into another player: */
2723faf6791Sd 	  case LEFTS:
2733faf6791Sd 	  case RIGHT:
2743faf6791Sd 	  case ABOVE:
2753faf6791Sd 	  case BELOW:
2763faf6791Sd 		if (dir != pp->p_face)
277fab1dce0Sd 			/* Can't walk backwards/sideways into another player: */
2783faf6791Sd 			sendcom(pp, BELL);
2793faf6791Sd 		else {
280fab1dce0Sd 			/* Stab the other player */
2813faf6791Sd 			newp = play_at(y, x);
282fab1dce0Sd 			checkdam(newp, pp, pp->p_ident, conf_stabdam, KNIFE);
2833faf6791Sd 		}
2843faf6791Sd 		break;
285fab1dce0Sd 	  /* Moving into a player flying overhead: */
2863faf6791Sd 	  case FLYER:
2873faf6791Sd 		newp = play_at(y, x);
2883faf6791Sd 		message(newp, "Oooh, there's a short guy waving at you!");
2893faf6791Sd 		message(pp, "You couldn't quite reach him!");
2903faf6791Sd 		break;
291fab1dce0Sd 	  /* Picking up a boot, or two: */
2923faf6791Sd 	  case BOOT_PAIR:
2933faf6791Sd 		pp->p_nboots++;
294fab1dce0Sd 	  case BOOT:
295fab1dce0Sd 		pp->p_nboots++;
2963faf6791Sd 		for (newp = Boot; newp < &Boot[NBOOTS]; newp++) {
2973faf6791Sd 			if (newp->p_flying < 0)
2983faf6791Sd 				continue;
2993faf6791Sd 			if (newp->p_y == y && newp->p_x == x) {
3003faf6791Sd 				newp->p_flying = -1;
3013faf6791Sd 				if (newp->p_undershot)
3023faf6791Sd 					fixshots(y, x, newp->p_over);
3033faf6791Sd 			}
3043faf6791Sd 		}
3053faf6791Sd 		if (pp->p_nboots == 2)
3063faf6791Sd 			message(pp, "Wow!  A pair of boots!");
3073faf6791Sd 		else
3083faf6791Sd 			message(pp, "You can hobble around on one boot.");
3093faf6791Sd 		Maze[y][x] = SPACE;
3103faf6791Sd 		moved = TRUE;
3113faf6791Sd 		break;
3123faf6791Sd 	}
313fab1dce0Sd 
314fab1dce0Sd 	/* Can the player be moved? */
3153faf6791Sd 	if (moved) {
316fab1dce0Sd 		/* Check the gun status: */
3173faf6791Sd 		if (pp->p_ncshot > 0)
318fab1dce0Sd 			if (--pp->p_ncshot == conf_maxncshot)
319fab1dce0Sd 				outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ok");
320fab1dce0Sd 		/* Check for bullets flying past: */
3213faf6791Sd 		if (pp->p_undershot) {
3223faf6791Sd 			fixshots(pp->p_y, pp->p_x, pp->p_over);
3233faf6791Sd 			pp->p_undershot = FALSE;
3243faf6791Sd 		}
325fab1dce0Sd 		/* Erase the player: */
3263faf6791Sd 		drawplayer(pp, FALSE);
327fab1dce0Sd 		/* Save under: */
3283faf6791Sd 		pp->p_over = Maze[y][x];
329fab1dce0Sd 		/* Move the player: */
3303faf6791Sd 		pp->p_y = y;
3313faf6791Sd 		pp->p_x = x;
332fab1dce0Sd 		/* Draw the player in their new position */
3333faf6791Sd 		drawplayer(pp, TRUE);
3343faf6791Sd 	}
3353faf6791Sd }
3363faf6791Sd 
3373faf6791Sd /*
3383faf6791Sd  * face:
3393faf6791Sd  *	Change the direction the player is facing
3403faf6791Sd  */
3413faf6791Sd static void
face(PLAYER * pp,int dir)3420f16a76cSmestre face(PLAYER *pp, int dir)
3433faf6791Sd {
3443faf6791Sd 	if (pp->p_face != dir) {
3453faf6791Sd 		pp->p_face = dir;
3463faf6791Sd 		drawplayer(pp, TRUE);
3473faf6791Sd 	}
3483faf6791Sd }
3493faf6791Sd 
3503faf6791Sd /*
3513faf6791Sd  * fire:
3523faf6791Sd  *	Fire a shot of the given type in the given direction
3533faf6791Sd  */
3543faf6791Sd static void
fire(PLAYER * pp,int req_index)3550f16a76cSmestre fire(PLAYER *pp, int req_index)
3563faf6791Sd {
3573faf6791Sd 	if (pp == NULL)
3583faf6791Sd 		return;
359fab1dce0Sd 
360fab1dce0Sd 	/* Drop the shot type down until we can afford it: */
3613faf6791Sd 	while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
3623faf6791Sd 		req_index--;
363fab1dce0Sd 
364fab1dce0Sd 	/* Can we shoot at all? */
3653faf6791Sd 	if (req_index < 0) {
3663faf6791Sd 		message(pp, "Not enough charges.");
3673faf6791Sd 		return;
3683faf6791Sd 	}
3693faf6791Sd 
370fab1dce0Sd 	/* Check if the gun is too hot: */
371fab1dce0Sd 	if (pp->p_ncshot > conf_maxncshot)
372fab1dce0Sd 		return;
373fab1dce0Sd 
374fab1dce0Sd 	/* Heat up the gun: */
375fab1dce0Sd 	if (pp->p_ncshot++ == conf_maxncshot) {
376fab1dce0Sd 		/* The gun has overheated: */
377fab1dce0Sd 		outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, "   ");
378fab1dce0Sd 	}
379fab1dce0Sd 
380fab1dce0Sd 	/* Use up some ammo: */
381fab1dce0Sd 	pp->p_ammo -= shot_req[req_index];
382fab1dce0Sd 	ammo_update(pp);
383fab1dce0Sd 
384fab1dce0Sd 	/* Start the bullet moving: */
3853faf6791Sd 	add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
3863faf6791Sd 		shot_req[req_index], pp, FALSE, pp->p_face);
3873faf6791Sd 	pp->p_undershot = TRUE;
3883faf6791Sd 
389fab1dce0Sd 	/* Show the bullet to everyone: */
3903faf6791Sd 	showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
391fab1dce0Sd 	sendcom(ALL_PLAYERS, REFRESH);
3923faf6791Sd }
3933faf6791Sd 
3943faf6791Sd /*
3953faf6791Sd  * fire_slime:
3963faf6791Sd  *	Fire a slime shot in the given direction
3973faf6791Sd  */
3983faf6791Sd static void
fire_slime(PLAYER * pp,int req_index)3990f16a76cSmestre fire_slime(PLAYER *pp, int req_index)
4003faf6791Sd {
4013faf6791Sd 	if (pp == NULL)
4023faf6791Sd 		return;
403fab1dce0Sd 
404fab1dce0Sd 	/* Check configuration: */
405fab1dce0Sd 	if (!conf_ooze)
406fab1dce0Sd 		return;
407fab1dce0Sd 
408fab1dce0Sd 	/* Drop the slime type back util we can afford it: */
4093faf6791Sd 	while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
4103faf6791Sd 		req_index--;
411fab1dce0Sd 
412fab1dce0Sd 	/* Can we afford to slime at all? */
4133faf6791Sd 	if (req_index < 0) {
4143faf6791Sd 		message(pp, "Not enough charges.");
4153faf6791Sd 		return;
4163faf6791Sd 	}
4173faf6791Sd 
418fab1dce0Sd 	/* Is the gun too hot? */
419fab1dce0Sd 	if (pp->p_ncshot > conf_maxncshot)
420fab1dce0Sd 		return;
421fab1dce0Sd 
422fab1dce0Sd 	/* Heat up the gun: */
423fab1dce0Sd 	if (pp->p_ncshot++ == conf_maxncshot) {
424fab1dce0Sd 		/* The gun has overheated: */
425fab1dce0Sd 		outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, "   ");
426fab1dce0Sd 	}
427fab1dce0Sd 
428fab1dce0Sd 	/* Use up some ammo: */
429fab1dce0Sd 	pp->p_ammo -= slime_req[req_index];
430fab1dce0Sd 	ammo_update(pp);
431fab1dce0Sd 
432fab1dce0Sd 	/* Start the slime moving: */
4333faf6791Sd 	add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
434fab1dce0Sd 		slime_req[req_index] * conf_slimefactor, pp, FALSE, pp->p_face);
4353faf6791Sd 	pp->p_undershot = TRUE;
4363faf6791Sd 
437fab1dce0Sd 	/* Show the object to everyone: */
4383faf6791Sd 	showexpl(pp->p_y, pp->p_x, SLIME);
439fab1dce0Sd 	sendcom(ALL_PLAYERS, REFRESH);
4403faf6791Sd }
4413faf6791Sd 
4423faf6791Sd /*
4433faf6791Sd  * add_shot:
4443faf6791Sd  *	Create a shot with the given properties
4453faf6791Sd  */
4463faf6791Sd void
add_shot(int type,int y,int x,char face,int charge,PLAYER * owner,int expl,char over)4470f16a76cSmestre add_shot(int type, int y, int x, char face, int charge, PLAYER *owner,
4480f16a76cSmestre     int expl, char over)
4493faf6791Sd {
4503faf6791Sd 	BULLET	*bp;
4513faf6791Sd 	int	size;
4523faf6791Sd 
453fab1dce0Sd 	/* Determine the bullet's size based on its type and charge: */
4543faf6791Sd 	switch (type) {
4553faf6791Sd 	  case SHOT:
4563faf6791Sd 	  case MINE:
4573faf6791Sd 		size = 1;
4583faf6791Sd 		break;
4593faf6791Sd 	  case GRENADE:
4603faf6791Sd 	  case GMINE:
4613faf6791Sd 		size = 2;
4623faf6791Sd 		break;
4633faf6791Sd 	  case SATCHEL:
4643faf6791Sd 		size = 3;
4653faf6791Sd 		break;
4663faf6791Sd 	  case BOMB:
4673faf6791Sd 		for (size = 3; size < MAXBOMB; size++)
4683faf6791Sd 			if (shot_req[size] >= charge)
4693faf6791Sd 				break;
4703faf6791Sd 		size++;
4713faf6791Sd 		break;
4723faf6791Sd 	  default:
4733faf6791Sd 		size = 0;
4743faf6791Sd 		break;
4753faf6791Sd 	}
4763faf6791Sd 
477fab1dce0Sd 	/* Create the bullet: */
4783faf6791Sd 	bp = create_shot(type, y, x, face, charge, size, owner,
4793faf6791Sd 		(owner == NULL) ? NULL : owner->p_ident, expl, over);
480fab1dce0Sd 
481fab1dce0Sd 	/* Insert the bullet into the front of the bullet list: */
4823faf6791Sd 	bp->b_next = Bullets;
4833faf6791Sd 	Bullets = bp;
4843faf6791Sd }
4853faf6791Sd 
486fab1dce0Sd /*
487fab1dce0Sd  * create_shot:
488fab1dce0Sd  *	allocate storage for an (unlinked) bullet structure;
489fab1dce0Sd  *	initialize and return it
490fab1dce0Sd  */
4913faf6791Sd BULLET *
create_shot(int type,int y,int x,char face,int charge,int size,PLAYER * owner,IDENT * score,int expl,char over)4920f16a76cSmestre create_shot(int type, int y, int x, char face, int charge, int size,
4930f16a76cSmestre     PLAYER *owner, IDENT *score, int expl, char over)
4943faf6791Sd {
4953faf6791Sd 	BULLET	*bp;
4963faf6791Sd 
497f06752bbSguenther 	bp = malloc(sizeof (BULLET));
4983faf6791Sd 	if (bp == NULL) {
4993fc386a2Sespie 		logit(LOG_ERR, "malloc");
5003faf6791Sd 		if (owner != NULL)
5013faf6791Sd 			message(owner, "Out of memory");
5023faf6791Sd 		return NULL;
5033faf6791Sd 	}
5043faf6791Sd 
5053faf6791Sd 	bp->b_face = face;
5063faf6791Sd 	bp->b_x = x;
5073faf6791Sd 	bp->b_y = y;
5083faf6791Sd 	bp->b_charge = charge;
5093faf6791Sd 	bp->b_owner = owner;
5103faf6791Sd 	bp->b_score = score;
5113faf6791Sd 	bp->b_type = type;
5123faf6791Sd 	bp->b_size = size;
5133faf6791Sd 	bp->b_expl = expl;
5143faf6791Sd 	bp->b_over = over;
5153faf6791Sd 	bp->b_next = NULL;
5163faf6791Sd 
5173faf6791Sd 	return bp;
5183faf6791Sd }
5193faf6791Sd 
5203faf6791Sd /*
5213faf6791Sd  * cloak:
5223faf6791Sd  *	Turn on or increase length of a cloak
5233faf6791Sd  */
5243faf6791Sd static void
cloak(PLAYER * pp)5250f16a76cSmestre cloak(PLAYER *pp)
5263faf6791Sd {
527fab1dce0Sd 	/* Check configuration: */
528fab1dce0Sd 	if (!conf_cloak)
529fab1dce0Sd 		return;
530fab1dce0Sd 
531fab1dce0Sd 	/* Can we afford it?: */
5323faf6791Sd 	if (pp->p_ammo <= 0) {
5333faf6791Sd 		message(pp, "No more charges");
5343faf6791Sd 		return;
5353faf6791Sd 	}
536fab1dce0Sd 
537fab1dce0Sd 	/* Can't cloak with boots: */
5383faf6791Sd 	if (pp->p_nboots > 0) {
5393faf6791Sd 		message(pp, "Boots are too noisy to cloak!");
5403faf6791Sd 		return;
5413faf6791Sd 	}
5423faf6791Sd 
543fab1dce0Sd 	/* Consume a unit of ammo: */
544fab1dce0Sd 	pp->p_ammo--;
545fab1dce0Sd 	ammo_update(pp);
5463faf6791Sd 
547fab1dce0Sd 	/* Add to the duration of a cloak: */
548fab1dce0Sd 	pp->p_cloak += conf_cloaklen;
549fab1dce0Sd 
550fab1dce0Sd 	/* Disable scan, if enabled: */
5513faf6791Sd 	if (pp->p_scan >= 0)
5523faf6791Sd 		pp->p_scan = -1;
5533faf6791Sd 
554fab1dce0Sd 	/* Re-draw the player's scan/cloak status: */
5553faf6791Sd 	showstat(pp);
5563faf6791Sd }
5573faf6791Sd 
5583faf6791Sd /*
5593faf6791Sd  * scan:
5603faf6791Sd  *	Turn on or increase length of a scan
5613faf6791Sd  */
5623faf6791Sd static void
scan(PLAYER * pp)5630f16a76cSmestre scan(PLAYER *pp)
5643faf6791Sd {
565fab1dce0Sd 	/* Check configuration: */
566fab1dce0Sd 	if (!conf_scan)
567fab1dce0Sd 		return;
568fab1dce0Sd 
569fab1dce0Sd 	/* Can we afford it?: */
5703faf6791Sd 	if (pp->p_ammo <= 0) {
5713faf6791Sd 		message(pp, "No more charges");
5723faf6791Sd 		return;
5733faf6791Sd 	}
5743faf6791Sd 
575fab1dce0Sd 	/* Consume one unit of ammo: */
576fab1dce0Sd 	pp->p_ammo--;
577fab1dce0Sd 	ammo_update(pp);
5783faf6791Sd 
579fab1dce0Sd 	/* Increase the scan time: */
580fab1dce0Sd 	pp->p_scan += Nplayer * conf_scanlen;
581fab1dce0Sd 
582fab1dce0Sd 	/* Disable cloak, if enabled: */
5833faf6791Sd 	if (pp->p_cloak >= 0)
5843faf6791Sd 		pp->p_cloak = -1;
5853faf6791Sd 
586fab1dce0Sd 	/* Re-draw the player's scan/cloak status: */
5873faf6791Sd 	showstat(pp);
5883faf6791Sd }
5893faf6791Sd 
5903faf6791Sd /*
5913faf6791Sd  * pickup:
592fab1dce0Sd  *	pick up a mine or grenade, with some probability of it exploding
5933faf6791Sd  */
594fab1dce0Sd static void
pickup(PLAYER * pp,int y,int x,int prob,int obj)5950f16a76cSmestre pickup(PLAYER *pp, int y, int x, int prob, int obj)
5963faf6791Sd {
5973faf6791Sd 	int	req;
5983faf6791Sd 
599fab1dce0Sd 	/* Figure out how much ammo the player is trying to pick up: */
6003faf6791Sd 	switch (obj) {
6013faf6791Sd 	  case MINE:
6023faf6791Sd 		req = BULREQ;
6033faf6791Sd 		break;
6043faf6791Sd 	  case GMINE:
6053faf6791Sd 		req = GRENREQ;
6063faf6791Sd 		break;
6073faf6791Sd 	  default:
608fab1dce0Sd #ifdef DIAGNOSTIC
6093faf6791Sd 		abort();
610fab1dce0Sd #endif
611fab1dce0Sd 		return;
6123faf6791Sd 	}
613fab1dce0Sd 
614fab1dce0Sd 	/* Does it explode? */
6153faf6791Sd 	if (rand_num(100) < prob)
616fab1dce0Sd 		/* Ooooh, unlucky: (Boom) */
6173faf6791Sd 		add_shot(obj, y, x, LEFTS, req, (PLAYER *) NULL,
6183faf6791Sd 			TRUE, pp->p_face);
6193faf6791Sd 	else {
620fab1dce0Sd 		/* Safely picked it up. Add to player's ammo: */
6213faf6791Sd 		pp->p_ammo += req;
622fab1dce0Sd 		ammo_update(pp);
6233faf6791Sd 	}
6243faf6791Sd }
625fab1dce0Sd 
626fab1dce0Sd void
ammo_update(PLAYER * pp)6270f16a76cSmestre ammo_update(PLAYER *pp)
628fab1dce0Sd {
629fab1dce0Sd 	outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo);
630fab1dce0Sd }
631