xref: /dragonfly/games/hunt/huntd/execute.c (revision ffe53622)
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
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
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
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 	  case BOOT:
294 		pp->p_nboots++;
295 		for (newp = Boot; newp < &Boot[NBOOTS]; newp++) {
296 			if (newp->p_flying < 0)
297 				continue;
298 			if (newp->p_y == y && newp->p_x == x) {
299 				newp->p_flying = -1;
300 				if (newp->p_undershot)
301 					fixshots(y, x, newp->p_over);
302 			}
303 		}
304 		if (pp->p_nboots == 2)
305 			message(pp, "Wow!  A pair of boots!");
306 		else
307 			message(pp, "You can hobble around on one boot.");
308 		Maze[y][x] = SPACE;
309 		moved = TRUE;
310 		break;
311 	}
312 
313 	/* Can the player be moved? */
314 	if (moved) {
315 		/* Check the gun status: */
316 		if (pp->p_ncshot > 0)
317 			if (--pp->p_ncshot == conf_maxncshot)
318 				outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ok");
319 		/* Check for bullets flying past: */
320 		if (pp->p_undershot) {
321 			fixshots(pp->p_y, pp->p_x, pp->p_over);
322 			pp->p_undershot = FALSE;
323 		}
324 		/* Erase the player: */
325 		drawplayer(pp, FALSE);
326 		/* Save under: */
327 		pp->p_over = Maze[y][x];
328 		/* Move the player: */
329 		pp->p_y = y;
330 		pp->p_x = x;
331 		/* Draw the player in their new position */
332 		drawplayer(pp, TRUE);
333 	}
334 }
335 
336 /*
337  * face:
338  *	Change the direction the player is facing
339  */
340 static void
341 face(PLAYER *pp, int dir)
342 {
343 	if (pp->p_face != dir) {
344 		pp->p_face = dir;
345 		drawplayer(pp, TRUE);
346 	}
347 }
348 
349 /*
350  * fire:
351  *	Fire a shot of the given type in the given direction
352  */
353 static void
354 fire(PLAYER *pp, int req_index)
355 {
356 	if (pp == NULL)
357 		return;
358 
359 	/* Drop the shot type down until we can afford it: */
360 	while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
361 		req_index--;
362 
363 	/* Can we shoot at all? */
364 	if (req_index < 0) {
365 		message(pp, "Not enough charges.");
366 		return;
367 	}
368 
369 	/* Check if the gun is too hot: */
370 	if (pp->p_ncshot > conf_maxncshot)
371 		return;
372 
373 	/* Heat up the gun: */
374 	if (pp->p_ncshot++ == conf_maxncshot) {
375 		/* The gun has overheated: */
376 		outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, "   ");
377 	}
378 
379 	/* Use up some ammo: */
380 	pp->p_ammo -= shot_req[req_index];
381 	ammo_update(pp);
382 
383 	/* Start the bullet moving: */
384 	add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
385 		shot_req[req_index], pp, FALSE, pp->p_face);
386 	pp->p_undershot = TRUE;
387 
388 	/* Show the bullet to everyone: */
389 	showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
390 	sendcom(ALL_PLAYERS, REFRESH);
391 }
392 
393 /*
394  * fire_slime:
395  *	Fire a slime shot in the given direction
396  */
397 static void
398 fire_slime(PLAYER *pp, int req_index)
399 {
400 	if (pp == NULL)
401 		return;
402 
403 	/* Check configuration: */
404 	if (!conf_ooze)
405 		return;
406 
407 	/* Drop the slime type back util we can afford it: */
408 	while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
409 		req_index--;
410 
411 	/* Can we afford to slime at all? */
412 	if (req_index < 0) {
413 		message(pp, "Not enough charges.");
414 		return;
415 	}
416 
417 	/* Is the gun too hot? */
418 	if (pp->p_ncshot > conf_maxncshot)
419 		return;
420 
421 	/* Heat up the gun: */
422 	if (pp->p_ncshot++ == conf_maxncshot) {
423 		/* The gun has overheated: */
424 		outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, "   ");
425 	}
426 
427 	/* Use up some ammo: */
428 	pp->p_ammo -= slime_req[req_index];
429 	ammo_update(pp);
430 
431 	/* Start the slime moving: */
432 	add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
433 		slime_req[req_index] * conf_slimefactor, pp, FALSE, pp->p_face);
434 	pp->p_undershot = TRUE;
435 
436 	/* Show the object to everyone: */
437 	showexpl(pp->p_y, pp->p_x, SLIME);
438 	sendcom(ALL_PLAYERS, REFRESH);
439 }
440 
441 /*
442  * add_shot:
443  *	Create a shot with the given properties
444  */
445 void
446 add_shot(int type, int y, int x, char wface, int charge, PLAYER *owner,
447     int expl, char over)
448 {
449 	BULLET	*bp;
450 	int	size;
451 
452 	/* Determine the bullet's size based on its type and charge: */
453 	switch (type) {
454 	  case SHOT:
455 	  case MINE:
456 		size = 1;
457 		break;
458 	  case GRENADE:
459 	  case GMINE:
460 		size = 2;
461 		break;
462 	  case SATCHEL:
463 		size = 3;
464 		break;
465 	  case BOMB:
466 		for (size = 3; size < MAXBOMB; size++)
467 			if (shot_req[size] >= charge)
468 				break;
469 		size++;
470 		break;
471 	  default:
472 		size = 0;
473 		break;
474 	}
475 
476 	/* Create the bullet: */
477 	bp = create_shot(type, y, x, wface, charge, size, owner,
478 		(owner == NULL) ? NULL : owner->p_ident, expl, over);
479 
480 	/* Insert the bullet into the front of the bullet list: */
481 	bp->b_next = Bullets;
482 	Bullets = bp;
483 }
484 
485 /*
486  * create_shot:
487  *	allocate storage for an (unlinked) bullet structure;
488  *	initialize and return it
489  */
490 BULLET *
491 create_shot(int type, int y, int x, char wface, int charge, int size,
492     PLAYER *owner, IDENT *score, int expl, char over)
493 {
494 	BULLET	*bp;
495 
496 	bp = (BULLET *) malloc(sizeof (BULLET));	/* NOSTRICT */
497 	if (bp == NULL) {
498 		logit(LOG_ERR, "malloc");
499 		if (owner != NULL)
500 			message(owner, "Out of memory");
501 		return NULL;
502 	}
503 
504 	bp->b_face = wface;
505 	bp->b_x = x;
506 	bp->b_y = y;
507 	bp->b_charge = charge;
508 	bp->b_owner = owner;
509 	bp->b_score = score;
510 	bp->b_type = type;
511 	bp->b_size = size;
512 	bp->b_expl = expl;
513 	bp->b_over = over;
514 	bp->b_next = NULL;
515 
516 	return bp;
517 }
518 
519 /*
520  * cloak:
521  *	Turn on or increase length of a cloak
522  */
523 static void
524 cloak(PLAYER *pp)
525 {
526 	/* Check configuration: */
527 	if (!conf_cloak)
528 		return;
529 
530 	/* Can we afford it?: */
531 	if (pp->p_ammo <= 0) {
532 		message(pp, "No more charges");
533 		return;
534 	}
535 
536 	/* Can't cloak with boots: */
537 	if (pp->p_nboots > 0) {
538 		message(pp, "Boots are too noisy to cloak!");
539 		return;
540 	}
541 
542 	/* Consume a unit of ammo: */
543 	pp->p_ammo--;
544 	ammo_update(pp);
545 
546 	/* Add to the duration of a cloak: */
547 	pp->p_cloak += conf_cloaklen;
548 
549 	/* Disable scan, if enabled: */
550 	if (pp->p_scan >= 0)
551 		pp->p_scan = -1;
552 
553 	/* Re-draw the player's scan/cloak status: */
554 	showstat(pp);
555 }
556 
557 /*
558  * scan:
559  *	Turn on or increase length of a scan
560  */
561 static void
562 scan(PLAYER *pp)
563 {
564 	/* Check configuration: */
565 	if (!conf_scan)
566 		return;
567 
568 	/* Can we afford it?: */
569 	if (pp->p_ammo <= 0) {
570 		message(pp, "No more charges");
571 		return;
572 	}
573 
574 	/* Consume one unit of ammo: */
575 	pp->p_ammo--;
576 	ammo_update(pp);
577 
578 	/* Increase the scan time: */
579 	pp->p_scan += Nplayer * conf_scanlen;
580 
581 	/* Disable cloak, if enabled: */
582 	if (pp->p_cloak >= 0)
583 		pp->p_cloak = -1;
584 
585 	/* Re-draw the player's scan/cloak status: */
586 	showstat(pp);
587 }
588 
589 /*
590  * pickup:
591  *	pick up a mine or grenade, with some probability of it exploding
592  */
593 static void
594 pickup(PLAYER *pp, int y, int x, int prob, int obj)
595 {
596 	int	req;
597 
598 	/* Figure out how much ammo the player is trying to pick up: */
599 	switch (obj) {
600 	  case MINE:
601 		req = BULREQ;
602 		break;
603 	  case GMINE:
604 		req = GRENREQ;
605 		break;
606 	  default:
607 #ifdef DIAGNOSTIC
608 		abort();
609 #endif
610 		return;
611 	}
612 
613 	/* Does it explode? */
614 	if (rand_num(100) < prob)
615 		/* Ooooh, unlucky: (Boom) */
616 		add_shot(obj, y, x, LEFTS, req, NULL, TRUE, pp->p_face);
617 	else {
618 		/* Safely picked it up. Add to player's ammo: */
619 		pp->p_ammo += req;
620 		ammo_update(pp);
621 	}
622 }
623 
624 void
625 ammo_update(PLAYER *pp)
626 {
627 	outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo);
628 }
629