1 /* $OpenBSD: execute.c,v 1.8 2004/01/16 00:13:19 espie 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 <stdlib.h> 35 #include <string.h> 36 #include <syslog.h> 37 #include "hunt.h" 38 #include "conf.h" 39 #include "server.h" 40 41 static void cloak(PLAYER *); 42 static void face(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 /* 51 * mon_execute: 52 * Execute a single monitor command 53 */ 54 void 55 mon_execute(pp) 56 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 (void) 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(pp) 83 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(pp, dir) 199 PLAYER *pp; 200 int dir; 201 { 202 PLAYER *newp; 203 int x, y; 204 FLAG moved; 205 BULLET *bp; 206 207 y = pp->p_y; 208 x = pp->p_x; 209 210 switch (dir) { 211 case LEFTS: 212 x--; 213 break; 214 case RIGHT: 215 x++; 216 break; 217 case ABOVE: 218 y--; 219 break; 220 case BELOW: 221 y++; 222 break; 223 } 224 225 moved = FALSE; 226 227 /* What would the player move over: */ 228 switch (Maze[y][x]) { 229 /* Players can move through spaces and doors, no problem: */ 230 case SPACE: 231 case DOOR: 232 moved = TRUE; 233 break; 234 /* Can't move through walls: */ 235 case WALL1: 236 case WALL2: 237 case WALL3: 238 case WALL4: 239 case WALL5: 240 break; 241 /* Moving over a mine - try to pick it up: */ 242 case MINE: 243 case GMINE: 244 if (dir == pp->p_face) 245 /* facing it: 2% chance of trip */ 246 pickup(pp, y, x, conf_ptrip_face, Maze[y][x]); 247 else if (opposite(dir, pp->p_face)) 248 /* facing away: 95% chance of trip */ 249 pickup(pp, y, x, conf_ptrip_back, Maze[y][x]); 250 else 251 /* facing sideways: 50% chance of trip */ 252 pickup(pp, y, x, conf_ptrip_side, Maze[y][x]); 253 /* Remove the mine: */ 254 Maze[y][x] = SPACE; 255 moved = TRUE; 256 break; 257 /* Moving into a bullet: */ 258 case SHOT: 259 case GRENADE: 260 case SATCHEL: 261 case BOMB: 262 case SLIME: 263 case DSHOT: 264 /* Find which bullet: */ 265 bp = is_bullet(y, x); 266 if (bp != NULL) 267 /* Detonate it: */ 268 bp->b_expl = TRUE; 269 /* Remove it: */ 270 Maze[y][x] = SPACE; 271 moved = TRUE; 272 break; 273 /* Moving into another player: */ 274 case LEFTS: 275 case RIGHT: 276 case ABOVE: 277 case BELOW: 278 if (dir != pp->p_face) 279 /* Can't walk backwards/sideways into another player: */ 280 sendcom(pp, BELL); 281 else { 282 /* Stab the other player */ 283 newp = play_at(y, x); 284 checkdam(newp, pp, pp->p_ident, conf_stabdam, KNIFE); 285 } 286 break; 287 /* Moving into a player flying overhead: */ 288 case FLYER: 289 newp = play_at(y, x); 290 message(newp, "Oooh, there's a short guy waving at you!"); 291 message(pp, "You couldn't quite reach him!"); 292 break; 293 /* Picking up a boot, or two: */ 294 case BOOT_PAIR: 295 pp->p_nboots++; 296 case BOOT: 297 pp->p_nboots++; 298 for (newp = Boot; newp < &Boot[NBOOTS]; newp++) { 299 if (newp->p_flying < 0) 300 continue; 301 if (newp->p_y == y && newp->p_x == x) { 302 newp->p_flying = -1; 303 if (newp->p_undershot) 304 fixshots(y, x, newp->p_over); 305 } 306 } 307 if (pp->p_nboots == 2) 308 message(pp, "Wow! A pair of boots!"); 309 else 310 message(pp, "You can hobble around on one boot."); 311 Maze[y][x] = SPACE; 312 moved = TRUE; 313 break; 314 } 315 316 /* Can the player be moved? */ 317 if (moved) { 318 /* Check the gun status: */ 319 if (pp->p_ncshot > 0) 320 if (--pp->p_ncshot == conf_maxncshot) 321 outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ok"); 322 /* Check for bullets flying past: */ 323 if (pp->p_undershot) { 324 fixshots(pp->p_y, pp->p_x, pp->p_over); 325 pp->p_undershot = FALSE; 326 } 327 /* Erase the player: */ 328 drawplayer(pp, FALSE); 329 /* Save under: */ 330 pp->p_over = Maze[y][x]; 331 /* Move the player: */ 332 pp->p_y = y; 333 pp->p_x = x; 334 /* Draw the player in their new position */ 335 drawplayer(pp, TRUE); 336 } 337 } 338 339 /* 340 * face: 341 * Change the direction the player is facing 342 */ 343 static void 344 face(pp, dir) 345 PLAYER *pp; 346 int dir; 347 { 348 if (pp->p_face != dir) { 349 pp->p_face = dir; 350 drawplayer(pp, TRUE); 351 } 352 } 353 354 /* 355 * fire: 356 * Fire a shot of the given type in the given direction 357 */ 358 static void 359 fire(pp, req_index) 360 PLAYER *pp; 361 int req_index; 362 { 363 if (pp == NULL) 364 return; 365 366 /* Drop the shot type down until we can afford it: */ 367 while (req_index >= 0 && pp->p_ammo < shot_req[req_index]) 368 req_index--; 369 370 /* Can we shoot at all? */ 371 if (req_index < 0) { 372 message(pp, "Not enough charges."); 373 return; 374 } 375 376 /* Check if the gun is too hot: */ 377 if (pp->p_ncshot > conf_maxncshot) 378 return; 379 380 /* Heat up the gun: */ 381 if (pp->p_ncshot++ == conf_maxncshot) { 382 /* The gun has overheated: */ 383 outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " "); 384 } 385 386 /* Use up some ammo: */ 387 pp->p_ammo -= shot_req[req_index]; 388 ammo_update(pp); 389 390 /* Start the bullet moving: */ 391 add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face, 392 shot_req[req_index], pp, FALSE, pp->p_face); 393 pp->p_undershot = TRUE; 394 395 /* Show the bullet to everyone: */ 396 showexpl(pp->p_y, pp->p_x, shot_type[req_index]); 397 sendcom(ALL_PLAYERS, REFRESH); 398 } 399 400 /* 401 * fire_slime: 402 * Fire a slime shot in the given direction 403 */ 404 static void 405 fire_slime(pp, req_index) 406 PLAYER *pp; 407 int req_index; 408 { 409 if (pp == NULL) 410 return; 411 412 /* Check configuration: */ 413 if (!conf_ooze) 414 return; 415 416 /* Drop the slime type back util we can afford it: */ 417 while (req_index >= 0 && pp->p_ammo < slime_req[req_index]) 418 req_index--; 419 420 /* Can we afford to slime at all? */ 421 if (req_index < 0) { 422 message(pp, "Not enough charges."); 423 return; 424 } 425 426 /* Is the gun too hot? */ 427 if (pp->p_ncshot > conf_maxncshot) 428 return; 429 430 /* Heat up the gun: */ 431 if (pp->p_ncshot++ == conf_maxncshot) { 432 /* The gun has overheated: */ 433 outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " "); 434 } 435 436 /* Use up some ammo: */ 437 pp->p_ammo -= slime_req[req_index]; 438 ammo_update(pp); 439 440 /* Start the slime moving: */ 441 add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face, 442 slime_req[req_index] * conf_slimefactor, pp, FALSE, pp->p_face); 443 pp->p_undershot = TRUE; 444 445 /* Show the object to everyone: */ 446 showexpl(pp->p_y, pp->p_x, SLIME); 447 sendcom(ALL_PLAYERS, REFRESH); 448 } 449 450 /* 451 * add_shot: 452 * Create a shot with the given properties 453 */ 454 void 455 add_shot(type, y, x, face, charge, owner, expl, over) 456 int type; 457 int y, x; 458 char face; 459 int charge; 460 PLAYER *owner; 461 int expl; 462 char over; 463 { 464 BULLET *bp; 465 int size; 466 467 /* Determine the bullet's size based on its type and charge: */ 468 switch (type) { 469 case SHOT: 470 case MINE: 471 size = 1; 472 break; 473 case GRENADE: 474 case GMINE: 475 size = 2; 476 break; 477 case SATCHEL: 478 size = 3; 479 break; 480 case BOMB: 481 for (size = 3; size < MAXBOMB; size++) 482 if (shot_req[size] >= charge) 483 break; 484 size++; 485 break; 486 default: 487 size = 0; 488 break; 489 } 490 491 /* Create the bullet: */ 492 bp = create_shot(type, y, x, face, charge, size, owner, 493 (owner == NULL) ? NULL : owner->p_ident, expl, over); 494 495 /* Insert the bullet into the front of the bullet list: */ 496 bp->b_next = Bullets; 497 Bullets = bp; 498 } 499 500 /* 501 * create_shot: 502 * allocate storage for an (unlinked) bullet structure; 503 * initialize and return it 504 */ 505 BULLET * 506 create_shot(type, y, x, face, charge, size, owner, score, expl, over) 507 int type; 508 int y, x; 509 char face; 510 int charge; 511 int size; 512 PLAYER *owner; 513 IDENT *score; 514 int expl; 515 char over; 516 { 517 BULLET *bp; 518 519 bp = (BULLET *) malloc(sizeof (BULLET)); /* NOSTRICT */ 520 if (bp == NULL) { 521 logit(LOG_ERR, "malloc"); 522 if (owner != NULL) 523 message(owner, "Out of memory"); 524 return NULL; 525 } 526 527 bp->b_face = face; 528 bp->b_x = x; 529 bp->b_y = y; 530 bp->b_charge = charge; 531 bp->b_owner = owner; 532 bp->b_score = score; 533 bp->b_type = type; 534 bp->b_size = size; 535 bp->b_expl = expl; 536 bp->b_over = over; 537 bp->b_next = NULL; 538 539 return bp; 540 } 541 542 /* 543 * cloak: 544 * Turn on or increase length of a cloak 545 */ 546 static void 547 cloak(pp) 548 PLAYER *pp; 549 { 550 /* Check configuration: */ 551 if (!conf_cloak) 552 return; 553 554 /* Can we afford it?: */ 555 if (pp->p_ammo <= 0) { 556 message(pp, "No more charges"); 557 return; 558 } 559 560 /* Can't cloak with boots: */ 561 if (pp->p_nboots > 0) { 562 message(pp, "Boots are too noisy to cloak!"); 563 return; 564 } 565 566 /* Consume a unit of ammo: */ 567 pp->p_ammo--; 568 ammo_update(pp); 569 570 /* Add to the duration of a cloak: */ 571 pp->p_cloak += conf_cloaklen; 572 573 /* Disable scan, if enabled: */ 574 if (pp->p_scan >= 0) 575 pp->p_scan = -1; 576 577 /* Re-draw the player's scan/cloak status: */ 578 showstat(pp); 579 } 580 581 /* 582 * scan: 583 * Turn on or increase length of a scan 584 */ 585 static void 586 scan(pp) 587 PLAYER *pp; 588 { 589 /* Check configuration: */ 590 if (!conf_scan) 591 return; 592 593 /* Can we afford it?: */ 594 if (pp->p_ammo <= 0) { 595 message(pp, "No more charges"); 596 return; 597 } 598 599 /* Consume one unit of ammo: */ 600 pp->p_ammo--; 601 ammo_update(pp); 602 603 /* Increase the scan time: */ 604 pp->p_scan += Nplayer * conf_scanlen; 605 606 /* Disable cloak, if enabled: */ 607 if (pp->p_cloak >= 0) 608 pp->p_cloak = -1; 609 610 /* Re-draw the player's scan/cloak status: */ 611 showstat(pp); 612 } 613 614 /* 615 * pickup: 616 * pick up a mine or grenade, with some probability of it exploding 617 */ 618 static void 619 pickup(pp, y, x, prob, obj) 620 PLAYER *pp; 621 int y, x; 622 int prob; 623 int obj; 624 { 625 int req; 626 627 /* Figure out how much ammo the player is trying to pick up: */ 628 switch (obj) { 629 case MINE: 630 req = BULREQ; 631 break; 632 case GMINE: 633 req = GRENREQ; 634 break; 635 default: 636 #ifdef DIAGNOSTIC 637 abort(); 638 #endif 639 return; 640 } 641 642 /* Does it explode? */ 643 if (rand_num(100) < prob) 644 /* Ooooh, unlucky: (Boom) */ 645 add_shot(obj, y, x, LEFTS, req, (PLAYER *) NULL, 646 TRUE, pp->p_face); 647 else { 648 /* Safely picked it up. Add to player's ammo: */ 649 pp->p_ammo += req; 650 ammo_update(pp); 651 } 652 } 653 654 void 655 ammo_update(pp) 656 PLAYER *pp; 657 { 658 outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo); 659 } 660