/* $OpenBSD: execute.c,v 1.12 2016/01/07 21:37:53 mestre Exp $ */ /* $NetBSD: execute.c,v 1.2 1997/10/10 16:33:13 lukem Exp $ */ /* * Copyright (c) 1983-2003, Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * + Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * + Neither the name of the University of California, San Francisco nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "conf.h" #include "hunt.h" #include "server.h" static void cloak(PLAYER *); static void face(PLAYER *, int); static void fire(PLAYER *, int); static void fire_slime(PLAYER *, int); static void move_player(PLAYER *, int); static void pickup(PLAYER *, int, int, int, int); static void scan(PLAYER *); /* * mon_execute: * Execute a single monitor command */ void mon_execute(PLAYER *pp) { char ch; ch = pp->p_cbuf[pp->p_ncount++]; switch (ch) { case CTRL('L'): /* Redraw messed-up screen */ sendcom(pp, REDRAW); break; case 'q': /* Quit client */ (void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death); break; default: /* Ignore everything else */ ; } } /* * execute: * Execute a single command from a player */ void execute(PLAYER *pp) { char ch; ch = pp->p_cbuf[pp->p_ncount++]; /* When flying, only allow refresh and quit. */ if (pp->p_flying >= 0) { switch (ch) { case CTRL('L'): sendcom(pp, REDRAW); break; case 'q': (void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death); break; } return; } /* Decode the command character: */ switch (ch) { case CTRL('L'): sendcom(pp, REDRAW); /* Refresh */ break; case 'h': move_player(pp, LEFTS); /* Move left */ break; case 'H': face(pp, LEFTS); /* Face left */ break; case 'j': move_player(pp, BELOW); /* Move down */ break; case 'J': face(pp, BELOW); /* Face down */ break; case 'k': move_player(pp, ABOVE); /* Move up */ break; case 'K': face(pp, ABOVE); /* Face up */ break; case 'l': move_player(pp, RIGHT); /* Move right */ break; case 'L': face(pp, RIGHT); /* Face right */ break; case 'f': case '1': fire(pp, 0); /* SHOT */ break; case 'g': case '2': fire(pp, 1); /* GRENADE */ break; case 'F': case '3': fire(pp, 2); /* SATCHEL */ break; case 'G': case '4': fire(pp, 3); /* 7x7 BOMB */ break; case '5': fire(pp, 4); /* 9x9 BOMB */ break; case '6': fire(pp, 5); /* 11x11 BOMB */ break; case '7': fire(pp, 6); /* 13x13 BOMB */ break; case '8': fire(pp, 7); /* 15x15 BOMB */ break; case '9': fire(pp, 8); /* 17x17 BOMB */ break; case '0': fire(pp, 9); /* 19x19 BOMB */ break; case '@': fire(pp, 10); /* 21x21 BOMB */ break; case 'o': fire_slime(pp, 0); /* SLIME */ break; case 'O': fire_slime(pp, 1); /* SSLIME */ break; case 'p': fire_slime(pp, 2); /* large slime */ break; case 'P': fire_slime(pp, 3); /* very large slime */ break; case 's': /* start scanning */ scan(pp); break; case 'c': /* start cloaking */ cloak(pp); break; case 'q': /* quit */ (void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death); break; } } /* * move_player: * Try to move player 'pp' in direction 'dir'. */ static void move_player(PLAYER *pp, int dir) { PLAYER *newp; int x, y; FLAG moved; BULLET *bp; y = pp->p_y; x = pp->p_x; switch (dir) { case LEFTS: x--; break; case RIGHT: x++; break; case ABOVE: y--; break; case BELOW: y++; break; } moved = FALSE; /* What would the player move over: */ switch (Maze[y][x]) { /* Players can move through spaces and doors, no problem: */ case SPACE: case DOOR: moved = TRUE; break; /* Can't move through walls: */ case WALL1: case WALL2: case WALL3: case WALL4: case WALL5: break; /* Moving over a mine - try to pick it up: */ case MINE: case GMINE: if (dir == pp->p_face) /* facing it: 2% chance of trip */ pickup(pp, y, x, conf_ptrip_face, Maze[y][x]); else if (opposite(dir, pp->p_face)) /* facing away: 95% chance of trip */ pickup(pp, y, x, conf_ptrip_back, Maze[y][x]); else /* facing sideways: 50% chance of trip */ pickup(pp, y, x, conf_ptrip_side, Maze[y][x]); /* Remove the mine: */ Maze[y][x] = SPACE; moved = TRUE; break; /* Moving into a bullet: */ case SHOT: case GRENADE: case SATCHEL: case BOMB: case SLIME: case DSHOT: /* Find which bullet: */ bp = is_bullet(y, x); if (bp != NULL) /* Detonate it: */ bp->b_expl = TRUE; /* Remove it: */ Maze[y][x] = SPACE; moved = TRUE; break; /* Moving into another player: */ case LEFTS: case RIGHT: case ABOVE: case BELOW: if (dir != pp->p_face) /* Can't walk backwards/sideways into another player: */ sendcom(pp, BELL); else { /* Stab the other player */ newp = play_at(y, x); checkdam(newp, pp, pp->p_ident, conf_stabdam, KNIFE); } break; /* Moving into a player flying overhead: */ case FLYER: newp = play_at(y, x); message(newp, "Oooh, there's a short guy waving at you!"); message(pp, "You couldn't quite reach him!"); break; /* Picking up a boot, or two: */ case BOOT_PAIR: pp->p_nboots++; case BOOT: pp->p_nboots++; for (newp = Boot; newp < &Boot[NBOOTS]; newp++) { if (newp->p_flying < 0) continue; if (newp->p_y == y && newp->p_x == x) { newp->p_flying = -1; if (newp->p_undershot) fixshots(y, x, newp->p_over); } } if (pp->p_nboots == 2) message(pp, "Wow! A pair of boots!"); else message(pp, "You can hobble around on one boot."); Maze[y][x] = SPACE; moved = TRUE; break; } /* Can the player be moved? */ if (moved) { /* Check the gun status: */ if (pp->p_ncshot > 0) if (--pp->p_ncshot == conf_maxncshot) outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ok"); /* Check for bullets flying past: */ if (pp->p_undershot) { fixshots(pp->p_y, pp->p_x, pp->p_over); pp->p_undershot = FALSE; } /* Erase the player: */ drawplayer(pp, FALSE); /* Save under: */ pp->p_over = Maze[y][x]; /* Move the player: */ pp->p_y = y; pp->p_x = x; /* Draw the player in their new position */ drawplayer(pp, TRUE); } } /* * face: * Change the direction the player is facing */ static void face(PLAYER *pp, int dir) { if (pp->p_face != dir) { pp->p_face = dir; drawplayer(pp, TRUE); } } /* * fire: * Fire a shot of the given type in the given direction */ static void fire(PLAYER *pp, int req_index) { if (pp == NULL) return; /* Drop the shot type down until we can afford it: */ while (req_index >= 0 && pp->p_ammo < shot_req[req_index]) req_index--; /* Can we shoot at all? */ if (req_index < 0) { message(pp, "Not enough charges."); return; } /* Check if the gun is too hot: */ if (pp->p_ncshot > conf_maxncshot) return; /* Heat up the gun: */ if (pp->p_ncshot++ == conf_maxncshot) { /* The gun has overheated: */ outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " "); } /* Use up some ammo: */ pp->p_ammo -= shot_req[req_index]; ammo_update(pp); /* Start the bullet moving: */ add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face, shot_req[req_index], pp, FALSE, pp->p_face); pp->p_undershot = TRUE; /* Show the bullet to everyone: */ showexpl(pp->p_y, pp->p_x, shot_type[req_index]); sendcom(ALL_PLAYERS, REFRESH); } /* * fire_slime: * Fire a slime shot in the given direction */ static void fire_slime(PLAYER *pp, int req_index) { if (pp == NULL) return; /* Check configuration: */ if (!conf_ooze) return; /* Drop the slime type back util we can afford it: */ while (req_index >= 0 && pp->p_ammo < slime_req[req_index]) req_index--; /* Can we afford to slime at all? */ if (req_index < 0) { message(pp, "Not enough charges."); return; } /* Is the gun too hot? */ if (pp->p_ncshot > conf_maxncshot) return; /* Heat up the gun: */ if (pp->p_ncshot++ == conf_maxncshot) { /* The gun has overheated: */ outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " "); } /* Use up some ammo: */ pp->p_ammo -= slime_req[req_index]; ammo_update(pp); /* Start the slime moving: */ add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face, slime_req[req_index] * conf_slimefactor, pp, FALSE, pp->p_face); pp->p_undershot = TRUE; /* Show the object to everyone: */ showexpl(pp->p_y, pp->p_x, SLIME); sendcom(ALL_PLAYERS, REFRESH); } /* * add_shot: * Create a shot with the given properties */ void add_shot(int type, int y, int x, char face, int charge, PLAYER *owner, int expl, char over) { BULLET *bp; int size; /* Determine the bullet's size based on its type and charge: */ switch (type) { case SHOT: case MINE: size = 1; break; case GRENADE: case GMINE: size = 2; break; case SATCHEL: size = 3; break; case BOMB: for (size = 3; size < MAXBOMB; size++) if (shot_req[size] >= charge) break; size++; break; default: size = 0; break; } /* Create the bullet: */ bp = create_shot(type, y, x, face, charge, size, owner, (owner == NULL) ? NULL : owner->p_ident, expl, over); /* Insert the bullet into the front of the bullet list: */ bp->b_next = Bullets; Bullets = bp; } /* * create_shot: * allocate storage for an (unlinked) bullet structure; * initialize and return it */ BULLET * create_shot(int type, int y, int x, char face, int charge, int size, PLAYER *owner, IDENT *score, int expl, char over) { BULLET *bp; bp = malloc(sizeof (BULLET)); if (bp == NULL) { logit(LOG_ERR, "malloc"); if (owner != NULL) message(owner, "Out of memory"); return NULL; } bp->b_face = face; bp->b_x = x; bp->b_y = y; bp->b_charge = charge; bp->b_owner = owner; bp->b_score = score; bp->b_type = type; bp->b_size = size; bp->b_expl = expl; bp->b_over = over; bp->b_next = NULL; return bp; } /* * cloak: * Turn on or increase length of a cloak */ static void cloak(PLAYER *pp) { /* Check configuration: */ if (!conf_cloak) return; /* Can we afford it?: */ if (pp->p_ammo <= 0) { message(pp, "No more charges"); return; } /* Can't cloak with boots: */ if (pp->p_nboots > 0) { message(pp, "Boots are too noisy to cloak!"); return; } /* Consume a unit of ammo: */ pp->p_ammo--; ammo_update(pp); /* Add to the duration of a cloak: */ pp->p_cloak += conf_cloaklen; /* Disable scan, if enabled: */ if (pp->p_scan >= 0) pp->p_scan = -1; /* Re-draw the player's scan/cloak status: */ showstat(pp); } /* * scan: * Turn on or increase length of a scan */ static void scan(PLAYER *pp) { /* Check configuration: */ if (!conf_scan) return; /* Can we afford it?: */ if (pp->p_ammo <= 0) { message(pp, "No more charges"); return; } /* Consume one unit of ammo: */ pp->p_ammo--; ammo_update(pp); /* Increase the scan time: */ pp->p_scan += Nplayer * conf_scanlen; /* Disable cloak, if enabled: */ if (pp->p_cloak >= 0) pp->p_cloak = -1; /* Re-draw the player's scan/cloak status: */ showstat(pp); } /* * pickup: * pick up a mine or grenade, with some probability of it exploding */ static void pickup(PLAYER *pp, int y, int x, int prob, int obj) { int req; /* Figure out how much ammo the player is trying to pick up: */ switch (obj) { case MINE: req = BULREQ; break; case GMINE: req = GRENREQ; break; default: #ifdef DIAGNOSTIC abort(); #endif return; } /* Does it explode? */ if (rand_num(100) < prob) /* Ooooh, unlucky: (Boom) */ add_shot(obj, y, x, LEFTS, req, (PLAYER *) NULL, TRUE, pp->p_face); else { /* Safely picked it up. Add to player's ammo: */ pp->p_ammo += req; ammo_update(pp); } } void ammo_update(PLAYER *pp) { outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo); }