xref: /dragonfly/games/hunt/huntd/execute.c (revision 36a3d1d6)
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  * $DragonFly: src/games/hunt/huntd/execute.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
34  */
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include "hunt.h"
40 #include "conf.h"
41 #include "server.h"
42 
43 static void	cloak(PLAYER *);
44 static void	face(PLAYER *, int);
45 static void	fire(PLAYER *, int);
46 static void	fire_slime(PLAYER *, int);
47 static void	move_player(PLAYER *, int);
48 static void	pickup(PLAYER *, int, int, int, int);
49 static void	scan(PLAYER *);
50 
51 
52 /*
53  * mon_execute:
54  *	Execute a single monitor command
55  */
56 void
57 mon_execute(PLAYER *pp)
58 {
59 	char	ch;
60 
61 	ch = pp->p_cbuf[pp->p_ncount++];
62 
63 	switch (ch) {
64 	  case CTRL('L'):
65 		/* Redraw messed-up screen */
66 		sendcom(pp, REDRAW);
67 		break;
68 	  case 'q':
69 		/* Quit client */
70 		(void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
71 		break;
72 	  default:
73 		/* Ignore everything else */
74 		;
75 	}
76 }
77 
78 /*
79  * execute:
80  *	Execute a single command from a player
81  */
82 void
83 execute(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(PLAYER *pp, int dir)
199 {
200 	PLAYER	*newp;
201 	int	x, y;
202 	FLAG	moved;
203 	BULLET	*bp;
204 
205 	y = pp->p_y;
206 	x = pp->p_x;
207 
208 	switch (dir) {
209 	  case LEFTS:
210 		x--;
211 		break;
212 	  case RIGHT:
213 		x++;
214 		break;
215 	  case ABOVE:
216 		y--;
217 		break;
218 	  case BELOW:
219 		y++;
220 		break;
221 	}
222 
223 	moved = FALSE;
224 
225 	/* What would the player move over: */
226 	switch (Maze[y][x]) {
227 	  /* Players can move through spaces and doors, no problem: */
228 	  case SPACE:
229 	  case DOOR:
230 		moved = TRUE;
231 		break;
232 	  /* Can't move through walls: */
233 	  case WALL1:
234 	  case WALL2:
235 	  case WALL3:
236 	  case WALL4:
237 	  case WALL5:
238 		break;
239 	  /* Moving over a mine - try to pick it up: */
240 	  case MINE:
241 	  case GMINE:
242 		if (dir == pp->p_face)
243 			/* facing it: 2% chance of trip */
244 			pickup(pp, y, x, conf_ptrip_face, Maze[y][x]);
245 		else if (opposite(dir, pp->p_face))
246 			/* facing away: 95% chance of trip */
247 			pickup(pp, y, x, conf_ptrip_back, Maze[y][x]);
248 		else
249 			/* facing sideways: 50% chance of trip */
250 			pickup(pp, y, x, conf_ptrip_side, Maze[y][x]);
251 		/* Remove the mine: */
252 		Maze[y][x] = SPACE;
253 		moved = TRUE;
254 		break;
255 	  /* Moving into a bullet: */
256 	  case SHOT:
257 	  case GRENADE:
258 	  case SATCHEL:
259 	  case BOMB:
260 	  case SLIME:
261 	  case DSHOT:
262 		/* Find which bullet: */
263 		bp = is_bullet(y, x);
264 		if (bp != NULL)
265 			/* Detonate it: */
266 			bp->b_expl = TRUE;
267 		/* Remove it: */
268 		Maze[y][x] = SPACE;
269 		moved = TRUE;
270 		break;
271 	  /* Moving into another player: */
272 	  case LEFTS:
273 	  case RIGHT:
274 	  case ABOVE:
275 	  case BELOW:
276 		if (dir != pp->p_face)
277 			/* Can't walk backwards/sideways into another player: */
278 			sendcom(pp, BELL);
279 		else {
280 			/* Stab the other player */
281 			newp = play_at(y, x);
282 			checkdam(newp, pp, pp->p_ident, conf_stabdam, KNIFE);
283 		}
284 		break;
285 	  /* Moving into a player flying overhead: */
286 	  case FLYER:
287 		newp = play_at(y, x);
288 		message(newp, "Oooh, there's a short guy waving at you!");
289 		message(pp, "You couldn't quite reach him!");
290 		break;
291 	  /* Picking up a boot, or two: */
292 	  case BOOT_PAIR:
293 		pp->p_nboots++;
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
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
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
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
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 *
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
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
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
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
626 ammo_update(PLAYER *pp)
627 {
628 	outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo);
629 }
630