1/* $OpenBSD: execute.c,v 1.14 2017/01/21 08:22:57 krw 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 <sys/select.h> 35#include <stdlib.h> 36#include <string.h> 37#include <syslog.h> 38 39#include "conf.h" 40#include "hunt.h" 41#include "server.h" 42 43static void cloak(PLAYER *); 44static void face(PLAYER *, int); 45static void fire(PLAYER *, int); 46static void fire_slime(PLAYER *, int); 47static void move_player(PLAYER *, int); 48static void pickup(PLAYER *, int, int, int, int); 49static void scan(PLAYER *); 50 51 52/* 53 * mon_execute: 54 * Execute a single monitor command 55 */ 56void 57mon_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 */ 82void 83execute(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 */ 197static void 198move_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 */ 341static void 342face(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 */ 354static void 355fire(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 */ 398static void 399fire_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 */ 446void 447add_shot(int type, int y, int x, char face, 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, face, 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 */ 491BULLET * 492create_shot(int type, int y, int x, char face, int charge, int size, 493 PLAYER *owner, IDENT *score, int expl, char over) 494{ 495 BULLET *bp; 496 497 bp = malloc(sizeof (BULLET)); 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 = face; 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 */ 524static void 525cloak(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 */ 562static void 563scan(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 */ 594static void 595pickup(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, (PLAYER *) NULL, 618 TRUE, pp->p_face); 619 else { 620 /* Safely picked it up. Add to player's ammo: */ 621 pp->p_ammo += req; 622 ammo_update(pp); 623 } 624} 625 626void 627ammo_update(PLAYER *pp) 628{ 629 outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo); 630} 631