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