/* * move_slime: * move the given slime shot speed times and add it back if * it hasn't fizzled yet */ static void move_slime(BULLET *bp, int speed, BULLET *next) { int i, j, dirmask, count; PLAYER *pp; BULLET *nbp; if (speed == 0) { if (bp->b_charge <= 0) free(bp); else save_bullet(bp); return; } /* Draw it: */ showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*'); switch (Maze[bp->b_y][bp->b_x]) { /* Someone got hit by slime or lava: */ case LEFTS: case RIGHT: case ABOVE: case BELOW: case FLYER: pp = play_at(bp->b_y, bp->b_x); message(pp, "You've been slimed."); checkdam(pp, bp->b_owner, bp->b_score, conf_mindam, bp->b_type); break; /* Bullets detonate in slime and lava: */ case SHOT: case GRENADE: case SATCHEL: case BOMB: case DSHOT: explshot(next, bp->b_y, bp->b_x); explshot(Bullets, bp->b_y, bp->b_x); break; } /* Drain the slime/lava of some energy: */ if (--bp->b_charge <= 0) { /* It fizzled: */ free(bp); return; } /* Figure out which way the slime should flow: */ dirmask = 0; count = 0; switch (bp->b_face) { case LEFTS: if (!iswall(bp->b_y, bp->b_x - 1)) dirmask |= WEST, count++; if (!iswall(bp->b_y - 1, bp->b_x)) dirmask |= NORTH, count++; if (!iswall(bp->b_y + 1, bp->b_x)) dirmask |= SOUTH, count++; if (dirmask == 0) if (!iswall(bp->b_y, bp->b_x + 1)) dirmask |= EAST, count++; break; case RIGHT: if (!iswall(bp->b_y, bp->b_x + 1)) dirmask |= EAST, count++; if (!iswall(bp->b_y - 1, bp->b_x)) dirmask |= NORTH, count++; if (!iswall(bp->b_y + 1, bp->b_x)) dirmask |= SOUTH, count++; if (dirmask == 0) if (!iswall(bp->b_y, bp->b_x - 1)) dirmask |= WEST, count++; break; case ABOVE: if (!iswall(bp->b_y - 1, bp->b_x)) dirmask |= NORTH, count++; if (!iswall(bp->b_y, bp->b_x - 1)) dirmask |= WEST, count++; if (!iswall(bp->b_y, bp->b_x + 1)) dirmask |= EAST, count++; if (dirmask == 0) if (!iswall(bp->b_y + 1, bp->b_x)) dirmask |= SOUTH, count++; break; case BELOW: if (!iswall(bp->b_y + 1, bp->b_x)) dirmask |= SOUTH, count++; if (!iswall(bp->b_y, bp->b_x - 1)) dirmask |= WEST, count++; if (!iswall(bp->b_y, bp->b_x + 1)) dirmask |= EAST, count++; if (dirmask == 0) if (!iswall(bp->b_y - 1, bp->b_x)) dirmask |= NORTH, count++; break; } if (count == 0) { /* * No place to go. Just sit here for a while and wait * for adjacent squares to clear out. */ save_bullet(bp); return; } if (bp->b_charge < count) { /* Only bp->b_charge paths may be taken */ while (count > bp->b_charge) { if (dirmask & WEST) dirmask &= ~WEST; else if (dirmask & EAST) dirmask &= ~EAST; else if (dirmask & NORTH) dirmask &= ~NORTH; else if (dirmask & SOUTH) dirmask &= ~SOUTH; count--; } } /* Spawn little slimes off in every possible direction: */ i = bp->b_charge / count; j = bp->b_charge % count; if (dirmask & WEST) { count--; nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS, i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE); move_slime(nbp, speed - 1, next); } if (dirmask & EAST) { count--; nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT, (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE); move_slime(nbp, speed - 1, next); } if (dirmask & NORTH) { count--; nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE, (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE); move_slime(nbp, speed - 1, next); } if (dirmask & SOUTH) { count--; nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW, (count < j) ? i + 1 : i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE); move_slime(nbp, speed - 1, next); } free(bp); }
/* * chkshot * Handle explosions */ static void chkshot(BULLET *bp, BULLET *next) { int y, x; int dy, dx, absdy; int delta, damage; char expl; PLAYER *pp; delta = 0; switch (bp->b_type) { case SHOT: case MINE: case GRENADE: case GMINE: case SATCHEL: case BOMB: delta = bp->b_size - 1; break; case SLIME: case LAVA: chkslime(bp, next); return; case DSHOT: bp->b_type = SLIME; chkslime(bp, next); return; } /* Draw the explosion square: */ for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) { if (y < 0 || y >= HEIGHT) continue; dy = y - bp->b_y; absdy = (dy < 0) ? -dy : dy; for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) { /* Draw a part of the explosion cloud: */ if (x < 0 || x >= WIDTH) continue; dx = x - bp->b_x; if (dx == 0) expl = (dy == 0) ? '*' : '|'; else if (dy == 0) expl = '-'; else if (dx == dy) expl = '\\'; else if (dx == -dy) expl = '/'; else expl = '*'; showexpl(y, x, expl); /* Check what poor bastard was in the explosion: */ switch (Maze[y][x]) { case LEFTS: case RIGHT: case ABOVE: case BELOW: case FLYER: if (dx < 0) dx = -dx; if (absdy > dx) damage = bp->b_size - absdy; else damage = bp->b_size - dx; /* Everybody hurts, sometimes. */ pp = play_at(y, x); checkdam(pp, bp->b_owner, bp->b_score, damage * conf_mindam, bp->b_type); break; case GMINE: case MINE: /* Mines detonate in a chain reaction: */ add_shot((Maze[y][x] == GMINE) ? GRENADE : SHOT, y, x, LEFTS, (Maze[y][x] == GMINE) ? GRENREQ : BULREQ, NULL, TRUE, SPACE); Maze[y][x] = SPACE; break; } } } }
/* * move_player: * Execute a move in the given direction */ static void move_player(PLAYER *pp, int dir) { PLAYER *newp; int x, y; bool 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; switch (Maze[y][x]) { case SPACE: #ifdef RANDOM case DOOR: #endif moved = true; break; case WALL1: case WALL2: case WALL3: #ifdef REFLECT case WALL4: case WALL5: #endif break; case MINE: case GMINE: if (dir == pp->p_face) pickup(pp, y, x, 2, Maze[y][x]); else if (opposite(dir, pp->p_face)) pickup(pp, y, x, 95, Maze[y][x]); else pickup(pp, y, x, 50, Maze[y][x]); Maze[y][x] = SPACE; moved = true; break; case SHOT: case GRENADE: case SATCHEL: case BOMB: #ifdef OOZE case SLIME: #endif #ifdef DRONE case DSHOT: #endif bp = is_bullet(y, x); if (bp != NULL) bp->b_expl = true; Maze[y][x] = SPACE; moved = true; break; case LEFTS: case RIGHT: case ABOVE: case BELOW: if (dir != pp->p_face) sendcom(pp, BELL); else { newp = play_at(y, x); checkdam(newp, pp, pp->p_ident, STABDAM, KNIFE); } break; #ifdef FLY 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; #endif #ifdef BOOTS case BOOT: case BOOT_PAIR: if (Maze[y][x] == BOOT) pp->p_nboots++; else pp->p_nboots += 2; 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; #endif } if (moved) { if (pp->p_ncshot > 0) if (--pp->p_ncshot == MAXNCSHOT) { cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL); outstr(pp, " ok", 3); } if (pp->p_undershot) { fixshots(pp->p_y, pp->p_x, pp->p_over); pp->p_undershot = false; } drawplayer(pp, false); pp->p_over = Maze[y][x]; pp->p_y = y; pp->p_x = x; drawplayer(pp, true); } }
/* * move_flyer: * Update the position of a player in flight */ static void move_flyer(PLAYER *pp) { int x, y; if (pp->p_undershot) { fixshots(pp->p_y, pp->p_x, pp->p_over); pp->p_undershot = FALSE; } /* Restore what the flier was flying over */ Maze[pp->p_y][pp->p_x] = pp->p_over; /* Fly: */ x = pp->p_x + pp->p_flyx; y = pp->p_y + pp->p_flyy; /* Bouncing off the edges of the maze: */ if (x < 1) { x = 1 - x; pp->p_flyx = -pp->p_flyx; } else if (x > WIDTH - 2) { x = (WIDTH - 2) - (x - (WIDTH - 2)); pp->p_flyx = -pp->p_flyx; } if (y < 1) { y = 1 - y; pp->p_flyy = -pp->p_flyy; } else if (y > HEIGHT - 2) { y = (HEIGHT - 2) - (y - (HEIGHT - 2)); pp->p_flyy = -pp->p_flyy; } /* Make sure we don't land on something we can't: */ again: switch (Maze[y][x]) { default: /* * Flier is over something other than space, a wall * or a door. Randomly move (drift) the flier a little bit * and then try again: */ switch (rand_num(4)) { case 0: PLUS_DELTA(x, WIDTH - 2); break; case 1: MINUS_DELTA(x, 1); break; case 2: PLUS_DELTA(y, HEIGHT - 2); break; case 3: MINUS_DELTA(y, 1); break; } goto again; /* Give a little boost when about to land on a wall or door: */ case WALL1: case WALL2: case WALL3: case WALL4: case WALL5: case DOOR: if (pp->p_flying == 0) pp->p_flying++; break; /* Spaces are okay: */ case SPACE: break; } /* Update flier's coordinates: */ pp->p_y = y; pp->p_x = x; /* Consume 'flying' time: */ if (pp->p_flying-- == 0) { /* Land: */ if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) { /* Land a player - they stustain a fall: */ checkdam(pp, NULL, NULL, rand_num(pp->p_damage / conf_fall_frac), FALL); pp->p_face = rand_dir(); showstat(pp); } else { /* Land boots: */ if (Maze[y][x] == BOOT) pp->p_face = BOOT_PAIR; Maze[y][x] = SPACE; } } /* Save under the flier: */ pp->p_over = Maze[y][x]; /* Draw in the flier: */ Maze[y][x] = pp->p_face; showexpl(y, x, pp->p_face); }
/* * 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); } }