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 /* FALLTHROUGH */ 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