Home | History | Annotate | Line # | Download | only in huntd
      1 /*	$NetBSD: execute.c,v 1.13 2021/05/02 12:50:45 rillig Exp $	*/
      2 /*
      3  * Copyright (c) 1983-2003, Regents of the University of California.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are
      8  * met:
      9  *
     10  * + Redistributions of source code must retain the above copyright
     11  *   notice, this list of conditions and the following disclaimer.
     12  * + Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  * + Neither the name of the University of California, San Francisco nor
     16  *   the names of its contributors may be used to endorse or promote
     17  *   products derived from this software without specific prior written
     18  *   permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     23  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 #ifndef lint
     35 __RCSID("$NetBSD: execute.c,v 1.13 2021/05/02 12:50:45 rillig Exp $");
     36 #endif /* not lint */
     37 
     38 #include <stdlib.h>
     39 #include "hunt.h"
     40 
     41 static void cloak(PLAYER *);
     42 static void turn_player(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 #ifdef MONITOR
     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 	switch (ch) {
     62 	  case CTRL('L'):
     63 		sendcom(pp, REDRAW);
     64 		break;
     65 	  case 'q':
     66 		(void) strcpy(pp->p_death, "| Quit |");
     67 		break;
     68 	}
     69 }
     70 #endif
     71 
     72 /*
     73  * execute:
     74  *	Execute a single command
     75  */
     76 void
     77 execute(PLAYER *pp)
     78 {
     79 	char ch;
     80 
     81 	ch = pp->p_cbuf[pp->p_ncount++];
     82 
     83 #ifdef FLY
     84 	if (pp->p_flying >= 0) {
     85 		switch (ch) {
     86 		  case CTRL('L'):
     87 			sendcom(pp, REDRAW);
     88 			break;
     89 		  case 'q':
     90 			(void) strcpy(pp->p_death, "| Quit |");
     91 			break;
     92 		}
     93 		return;
     94 	}
     95 #endif
     96 
     97 	switch (ch) {
     98 	  case CTRL('L'):
     99 		sendcom(pp, REDRAW);
    100 		break;
    101 	  case 'h':
    102 		move_player(pp, LEFTS);
    103 		break;
    104 	  case 'H':
    105 		turn_player(pp, LEFTS);
    106 		break;
    107 	  case 'j':
    108 		move_player(pp, BELOW);
    109 		break;
    110 	  case 'J':
    111 		turn_player(pp, BELOW);
    112 		break;
    113 	  case 'k':
    114 		move_player(pp, ABOVE);
    115 		break;
    116 	  case 'K':
    117 		turn_player(pp, ABOVE);
    118 		break;
    119 	  case 'l':
    120 		move_player(pp, RIGHT);
    121 		break;
    122 	  case 'L':
    123 		turn_player(pp, RIGHT);
    124 		break;
    125 	  case 'f':
    126 	  case '1':
    127 		fire(pp, 0);		/* SHOT */
    128 		break;
    129 	  case 'g':
    130 	  case '2':
    131 		fire(pp, 1);		/* GRENADE */
    132 		break;
    133 	  case 'F':
    134 	  case '3':
    135 		fire(pp, 2);		/* SATCHEL */
    136 		break;
    137 	  case 'G':
    138 	  case '4':
    139 		fire(pp, 3);		/* 7x7 BOMB */
    140 		break;
    141 	  case '5':
    142 		fire(pp, 4);		/* 9x9 BOMB */
    143 		break;
    144 	  case '6':
    145 		fire(pp, 5);		/* 11x11 BOMB */
    146 		break;
    147 	  case '7':
    148 		fire(pp, 6);		/* 13x13 BOMB */
    149 		break;
    150 	  case '8':
    151 		fire(pp, 7);		/* 15x15 BOMB */
    152 		break;
    153 	  case '9':
    154 		fire(pp, 8);		/* 17x17 BOMB */
    155 		break;
    156 	  case '0':
    157 		fire(pp, 9);		/* 19x19 BOMB */
    158 		break;
    159 	  case '@':
    160 		fire(pp, 10);		/* 21x21 BOMB */
    161 		break;
    162 #ifdef OOZE
    163 	  case 'o':
    164 		fire_slime(pp, 0);	/* SLIME */
    165 		break;
    166 	  case 'O':
    167 		fire_slime(pp, 1);	/* SSLIME */
    168 		break;
    169 	  case 'p':
    170 		fire_slime(pp, 2);
    171 		break;
    172 	  case 'P':
    173 		fire_slime(pp, 3);
    174 		break;
    175 #endif
    176 	  case 's':
    177 		scan(pp);
    178 		break;
    179 	  case 'c':
    180 		cloak(pp);
    181 		break;
    182 	  case 'q':
    183 		(void) strcpy(pp->p_death, "| Quit |");
    184 		break;
    185 	}
    186 }
    187 
    188 /*
    189  * move_player:
    190  *	Execute a move in the given direction
    191  */
    192 static void
    193 move_player(PLAYER *pp, int dir)
    194 {
    195 	PLAYER *newp;
    196 	int x, y;
    197 	bool moved;
    198 	BULLET *bp;
    199 
    200 	y = pp->p_y;
    201 	x = pp->p_x;
    202 
    203 	switch (dir) {
    204 	  case LEFTS:
    205 		x--;
    206 		break;
    207 	  case RIGHT:
    208 		x++;
    209 		break;
    210 	  case ABOVE:
    211 		y--;
    212 		break;
    213 	  case BELOW:
    214 		y++;
    215 		break;
    216 	}
    217 
    218 	moved = false;
    219 	switch (Maze[y][x]) {
    220 	  case SPACE:
    221 #ifdef RANDOM
    222 	  case DOOR:
    223 #endif
    224 		moved = true;
    225 		break;
    226 	  case WALL1:
    227 	  case WALL2:
    228 	  case WALL3:
    229 #ifdef REFLECT
    230 	  case WALL4:
    231 	  case WALL5:
    232 #endif
    233 		break;
    234 	  case MINE:
    235 	  case GMINE:
    236 		if (dir == pp->p_face)
    237 			pickup(pp, y, x, 2, Maze[y][x]);
    238 		else if (opposite(dir, pp->p_face))
    239 			pickup(pp, y, x, 95, Maze[y][x]);
    240 		else
    241 			pickup(pp, y, x, 50, Maze[y][x]);
    242 		Maze[y][x] = SPACE;
    243 		moved = true;
    244 		break;
    245 	  case SHOT:
    246 	  case GRENADE:
    247 	  case SATCHEL:
    248 	  case BOMB:
    249 #ifdef OOZE
    250 	  case SLIME:
    251 #endif
    252 #ifdef DRONE
    253 	  case DSHOT:
    254 #endif
    255 		bp = is_bullet(y, x);
    256 		if (bp != NULL)
    257 			bp->b_expl = true;
    258 		Maze[y][x] = SPACE;
    259 		moved = true;
    260 		break;
    261 	  case LEFTS:
    262 	  case RIGHT:
    263 	  case ABOVE:
    264 	  case BELOW:
    265 		if (dir != pp->p_face)
    266 			sendcom(pp, BELL);
    267 		else {
    268 			newp = play_at(y, x);
    269 			checkdam(newp, pp, pp->p_ident, STABDAM, KNIFE);
    270 		}
    271 		break;
    272 #ifdef FLY
    273 	  case FLYER:
    274 		newp = play_at(y, x);
    275 		message(newp, "Oooh, there's a short guy waving at you!");
    276 		message(pp, "You couldn't quite reach him!");
    277 		break;
    278 #endif
    279 #ifdef BOOTS
    280 	  case BOOT:
    281 	  case BOOT_PAIR:
    282 		if (Maze[y][x] == BOOT)
    283 			pp->p_nboots++;
    284 		else
    285 			pp->p_nboots += 2;
    286 		for (newp = Boot; newp < &Boot[NBOOTS]; newp++) {
    287 			if (newp->p_flying < 0)
    288 				continue;
    289 			if (newp->p_y == y && newp->p_x == x) {
    290 				newp->p_flying = -1;
    291 				if (newp->p_undershot)
    292 					fixshots(y, x, newp->p_over);
    293 			}
    294 		}
    295 		if (pp->p_nboots == 2)
    296 			message(pp, "Wow!  A pair of boots!");
    297 		else
    298 			message(pp, "You can hobble around on one boot.");
    299 		Maze[y][x] = SPACE;
    300 		moved = true;
    301 		break;
    302 #endif
    303 	}
    304 	if (moved) {
    305 		if (pp->p_ncshot > 0)
    306 			if (--pp->p_ncshot == MAXNCSHOT) {
    307 				cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
    308 				outstr(pp, " ok", 3);
    309 			}
    310 		if (pp->p_undershot) {
    311 			fixshots(pp->p_y, pp->p_x, pp->p_over);
    312 			pp->p_undershot = false;
    313 		}
    314 		drawplayer(pp, false);
    315 		pp->p_over = Maze[y][x];
    316 		pp->p_y = y;
    317 		pp->p_x = x;
    318 		drawplayer(pp, true);
    319 	}
    320 }
    321 
    322 /*
    323  * turn_player:
    324  *	Change the direction the player is facing
    325  */
    326 static void
    327 turn_player(PLAYER *pp, int dir)
    328 {
    329 	if (pp->p_face != dir) {
    330 		pp->p_face = dir;
    331 		drawplayer(pp, true);
    332 	}
    333 }
    334 
    335 /*
    336  * fire:
    337  *	Fire a shot of the given type in the given direction
    338  */
    339 static void
    340 fire(PLAYER *pp, int req_index)
    341 {
    342 	if (pp == NULL)
    343 		return;
    344 #ifdef DEBUG
    345 	if (req_index < 0 || req_index >= MAXBOMB)
    346 		message(pp, "What you do?");
    347 #endif
    348 	while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
    349 		req_index--;
    350 	if (req_index < 0) {
    351 		message(pp, "Not enough charges.");
    352 		return;
    353 	}
    354 	if (pp->p_ncshot > MAXNCSHOT)
    355 		return;
    356 	if (pp->p_ncshot++ == MAXNCSHOT) {
    357 		cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
    358 		outstr(pp, "   ", 3);
    359 	}
    360 	pp->p_ammo -= shot_req[req_index];
    361 	(void) snprintf(Buf, sizeof(Buf), "%3d", pp->p_ammo);
    362 	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    363 	outstr(pp, Buf, 3);
    364 
    365 	add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
    366 		shot_req[req_index], pp, false, pp->p_face);
    367 	pp->p_undershot = true;
    368 
    369 	/*
    370 	 * Show the object to everyone
    371 	 */
    372 	showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
    373 	for (pp = Player; pp < End_player; pp++)
    374 		sendcom(pp, REFRESH);
    375 #ifdef MONITOR
    376 	for (pp = Monitor; pp < End_monitor; pp++)
    377 		sendcom(pp, REFRESH);
    378 #endif
    379 }
    380 
    381 #ifdef OOZE
    382 /*
    383  * fire_slime:
    384  *	Fire a slime shot in the given direction
    385  */
    386 static void
    387 fire_slime(PLAYER *pp, int req_index)
    388 {
    389 	if (pp == NULL)
    390 		return;
    391 #ifdef DEBUG
    392 	if (req_index < 0 || req_index >= MAXSLIME)
    393 		message(pp, "What you do?");
    394 #endif
    395 	while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
    396 		req_index--;
    397 	if (req_index < 0) {
    398 		message(pp, "Not enough charges.");
    399 		return;
    400 	}
    401 	if (pp->p_ncshot > MAXNCSHOT)
    402 		return;
    403 	if (pp->p_ncshot++ == MAXNCSHOT) {
    404 		cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
    405 		outstr(pp, "   ", 3);
    406 	}
    407 	pp->p_ammo -= slime_req[req_index];
    408 	(void) snprintf(Buf, sizeof(Buf), "%3d", pp->p_ammo);
    409 	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    410 	outstr(pp, Buf, 3);
    411 
    412 	add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
    413 		slime_req[req_index] * SLIME_FACTOR, pp, false, pp->p_face);
    414 	pp->p_undershot = true;
    415 
    416 	/*
    417 	 * Show the object to everyone
    418 	 */
    419 	showexpl(pp->p_y, pp->p_x, SLIME);
    420 	for (pp = Player; pp < End_player; pp++)
    421 		sendcom(pp, REFRESH);
    422 #ifdef MONITOR
    423 	for (pp = Monitor; pp < End_monitor; pp++)
    424 		sendcom(pp, REFRESH);
    425 #endif
    426 }
    427 #endif
    428 
    429 /*
    430  * add_shot:
    431  *	Create a shot with the given properties
    432  */
    433 void
    434 add_shot(int type, int y, int x, char face, int charge,
    435 	 PLAYER *owner, int expl, char over)
    436 {
    437 	BULLET *bp;
    438 	int size;
    439 
    440 	switch (type) {
    441 	  case SHOT:
    442 	  case MINE:
    443 		size = 1;
    444 		break;
    445 	  case GRENADE:
    446 	  case GMINE:
    447 		size = 2;
    448 		break;
    449 	  case SATCHEL:
    450 		size = 3;
    451 		break;
    452 	  case BOMB:
    453 		for (size = 3; size < MAXBOMB; size++)
    454 			if (shot_req[size] >= charge)
    455 				break;
    456 		size++;
    457 		break;
    458 	  default:
    459 		size = 0;
    460 		break;
    461 	}
    462 
    463 	bp = create_shot(type, y, x, face, charge, size, owner,
    464 		(owner == NULL) ? NULL : owner->p_ident, expl, over);
    465 	bp->b_next = Bullets;
    466 	Bullets = bp;
    467 }
    468 
    469 BULLET *
    470 create_shot(int type, int y, int x, char face, int charge,
    471 	    int size, PLAYER *owner, IDENT *score, int expl, char over)
    472 {
    473 	BULLET *bp;
    474 
    475 	bp = malloc(sizeof(*bp));
    476 	if (bp == NULL) {
    477 		if (owner != NULL)
    478 			message(owner, "Out of memory");
    479 		return NULL;
    480 	}
    481 
    482 	bp->b_face = face;
    483 	bp->b_x = x;
    484 	bp->b_y = y;
    485 	bp->b_charge = charge;
    486 	bp->b_owner = owner;
    487 	bp->b_score = score;
    488 	bp->b_type = type;
    489 	bp->b_size = size;
    490 	bp->b_expl = expl;
    491 	bp->b_over = over;
    492 	bp->b_next = NULL;
    493 
    494 	return bp;
    495 }
    496 
    497 /*
    498  * cloak:
    499  *	Turn on or increase length of a cloak
    500  */
    501 static void
    502 cloak(PLAYER *pp)
    503 {
    504 	if (pp->p_ammo <= 0) {
    505 		message(pp, "No more charges");
    506 		return;
    507 	}
    508 #ifdef BOOTS
    509 	if (pp->p_nboots > 0) {
    510 		message(pp, "Boots are too noisy to cloak!");
    511 		return;
    512 	}
    513 #endif
    514 	(void) snprintf(Buf, sizeof(Buf), "%3d", --pp->p_ammo);
    515 	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    516 	outstr(pp, Buf, 3);
    517 
    518 	pp->p_cloak += CLOAKLEN;
    519 
    520 	if (pp->p_scan >= 0)
    521 		pp->p_scan = -1;
    522 
    523 	showstat(pp);
    524 }
    525 
    526 /*
    527  * scan:
    528  *	Turn on or increase length of a scan
    529  */
    530 static void
    531 scan(PLAYER *pp)
    532 {
    533 	if (pp->p_ammo <= 0) {
    534 		message(pp, "No more charges");
    535 		return;
    536 	}
    537 	(void) snprintf(Buf, sizeof(Buf), "%3d", --pp->p_ammo);
    538 	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    539 	outstr(pp, Buf, 3);
    540 
    541 	pp->p_scan += SCANLEN;
    542 
    543 	if (pp->p_cloak >= 0)
    544 		pp->p_cloak = -1;
    545 
    546 	showstat(pp);
    547 }
    548 
    549 /*
    550  * pickup:
    551  *	check whether the object blew up or whether he picked it up
    552  */
    553 static void
    554 pickup(PLAYER *pp, int y, int x, int prob, int obj)
    555 {
    556 	int req;
    557 
    558 	switch (obj) {
    559 	  case MINE:
    560 		req = BULREQ;
    561 		break;
    562 	  case GMINE:
    563 		req = GRENREQ;
    564 		break;
    565 	  default:
    566 		abort();
    567 	}
    568 	if (rand_num(100) < prob)
    569 		add_shot(obj, y, x, LEFTS, req, NULL, true, pp->p_face);
    570 	else {
    571 		pp->p_ammo += req;
    572 		(void) snprintf(Buf, sizeof(Buf), "%3d", pp->p_ammo);
    573 		cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    574 		outstr(pp, Buf, 3);
    575 	}
    576 }
    577