/* File: cmd2.c */ /* Purpose: Movement commands (part 2) */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Go up one level -RAK- */ void do_cmd_go_up(void) { cave_type *c_ptr; /* Player grid */ c_ptr = &cave[py][px]; /* Verify stairs */ if (c_ptr->feat != FEAT_LESS) { msg_print("I see no up staircase here."); return; } /* Hack -- take a turn */ energy_use = 100; /* Success */ msg_print("You enter a maze of up staircases."); /* Go up the stairs */ dun_level--; new_level_flag = TRUE; /* Create a way back */ create_down_stair = TRUE; } /* * Go down one level */ void do_cmd_go_down(void) { cave_type *c_ptr; /* Player grid */ c_ptr = &cave[py][px]; /* Verify stairs */ if (c_ptr->feat != FEAT_MORE) { msg_print("I see no down staircase here."); return; } /* Hack -- take a turn */ energy_use = 100; /* Success */ msg_print("You enter a maze of down staircases."); /* Go down */ dun_level++; new_level_flag = TRUE; /* Create a way back */ create_up_stair = TRUE; } /* * Simple command to "search" for one turn */ void do_cmd_search(void) { /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Take a turn */ energy_use = 100; /* Search */ search(); } /* * Hack -- toggle search mode */ void do_cmd_toggle_search(void) { /* Stop searching */ if (p_ptr->searching) { /* Clear the searching flag */ p_ptr->searching = FALSE; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the state */ p_ptr->redraw |= (PR_STATE); } /* Start searching */ else { /* Set the searching flag */ p_ptr->searching = TRUE; /* Update stuff */ p_ptr->update |= (PU_BONUS); /* Redraw stuff */ p_ptr->redraw |= (PR_STATE | PR_SPEED); } } /* * Determine if a grid contains a chest */ static s16b chest_check(int y, int x) { cave_type *c_ptr = &cave[y][x]; s16b this_o_idx, next_o_idx = 0; /* Scan all objects in the grid */ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Skip unknown chests XXX XXX */ /* if (!o_ptr->marked) continue; */ /* Check for chest */ if (o_ptr->tval == TV_CHEST) return (this_o_idx); } /* No chest */ return (0); } /* * Allocates objects upon opening a chest -BEN- * * Disperse treasures from the given chest, centered at (x,y). * * Small chests often contain "gold", while Large chests always contain * items. Wooden chests contain 2 items, Iron chests contain 4 items, * and Steel chests contain 6 items. The "value" of the items in a * chest is based on the "power" of the chest, which is in turn based * on the level on which the chest is generated. */ static void chest_death(int y, int x, s16b o_idx) { int number; bool small; object_type forge; object_type *q_ptr; object_type *o_ptr = &o_list[o_idx]; /* Small chests often hold "gold" */ small = (o_ptr->sval < SV_CHEST_MIN_LARGE); /* Determine how much to drop (see above) */ number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2; /* Zero pval means empty chest */ if (!o_ptr->pval) number = 0; /* Opening a chest */ opening_chest = TRUE; /* Determine the "value" of the items */ object_level = ABS(o_ptr->pval) + 10; /* Drop some objects (non-chests) */ for (; number > 0; --number) { /* Get local object */ q_ptr = &forge; /* Wipe the object */ object_wipe(q_ptr); /* Small chests often drop gold */ if (small && (rand_int(100) < 75)) { /* Make some gold */ if (!make_gold(q_ptr)) continue; } /* Otherwise drop an item */ else { /* Make an object */ if (!make_object(q_ptr, FALSE, FALSE)) continue; } /* Drop it in the dungeon */ drop_near(q_ptr, -1, y, x); } /* Reset the object level */ object_level = dun_level; /* No longer opening a chest */ opening_chest = FALSE; /* Empty */ o_ptr->pval = 0; /* Known */ object_known(o_ptr); } /* * Chests have traps too. * * Exploding chest destroys contents (and traps). * Note that the chest itself is never destroyed. */ static void chest_trap(int y, int x, s16b o_idx) { int i, trap; object_type *o_ptr = &o_list[o_idx]; /* Ignore disarmed chests */ if (o_ptr->pval <= 0) return; /* Obtain the traps */ trap = chest_traps[o_ptr->pval]; /* Lose strength */ if (trap & (CHEST_LOSE_STR)) { msg_print("A small needle has pricked you!"); take_hit(damroll(1, 4), "a poison needle"); (void)do_dec_stat(A_STR); } /* Lose constitution */ if (trap & (CHEST_LOSE_CON)) { msg_print("A small needle has pricked you!"); take_hit(damroll(1, 4), "a poison needle"); (void)do_dec_stat(A_CON); } /* Poison */ if (trap & (CHEST_POISON)) { msg_print("A puff of green gas surrounds you!"); if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) { (void)set_poisoned(p_ptr->poisoned + 10 + randint(20)); } } /* Paralyze */ if (trap & (CHEST_PARALYZE)) { msg_print("A puff of yellow gas surrounds you!"); if (!p_ptr->free_act) { (void)set_paralyzed(p_ptr->paralyzed + 10 + randint(20)); } } /* Summon monsters */ if (trap & (CHEST_SUMMON)) { int num = 2 + randint(3); msg_print("You are enveloped in a cloud of smoke!"); for (i = 0; i < num; i++) { (void)summon_specific(y, x, dun_level, 0); } } /* Explode */ if (trap & (CHEST_EXPLODE)) { msg_print("There is a sudden explosion!"); msg_print("Everything inside the chest is destroyed!"); o_ptr->pval = 0; take_hit(damroll(5, 8), "an exploding chest"); } } /* * Attempt to open the given chest at the given location * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_open_chest(int y, int x, s16b o_idx) { int i, j; bool flag = TRUE; bool more = FALSE; object_type *o_ptr = &o_list[o_idx]; /* Take a turn */ energy_use = 100; /* Attempt to unlock it */ if (o_ptr->pval > 0) { /* Assume locked, and thus not open */ flag = FALSE; /* Get the "disarm" factor */ i = p_ptr->skill_dis; /* Penalize some conditions */ if (p_ptr->blind || no_lite()) i = i / 10; if (p_ptr->confused || p_ptr->image) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success -- May still have traps */ if (rand_int(100) < j) { msg_print("You have picked the lock."); gain_exp(1); flag = TRUE; } /* Failure -- Keep trying */ else { /* We may continue repeating */ more = TRUE; if (flush_failure) flush(); msg_print("You failed to pick the lock."); } } /* Allowed to open */ if (flag) { /* Apply chest traps, if any */ chest_trap(y, x, o_idx); /* Let the Chest drop items */ chest_death(y, x, o_idx); } /* Result */ return (more); } /* * Perform the basic "open" command on doors * * Assume destination is a closed/locked/jammed door * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_open_aux(int y, int x, int dir) { int i, j; cave_type *c_ptr; bool more = FALSE; /* XXX XXX XXX */ dir = (0, dir); /* Take a turn */ energy_use = 100; /* Get requested grid */ c_ptr = &cave[y][x]; /* Jammed door */ if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) { /* Stuck */ msg_print("The door appears to be stuck."); } /* Locked door */ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) { /* Disarm factor */ i = p_ptr->skill_dis; /* Penalize some conditions */ if (p_ptr->blind || no_lite()) i = i / 10; if (p_ptr->confused || p_ptr->image) i = i / 10; /* Extract the lock power */ j = c_ptr->feat - FEAT_DOOR_HEAD; /* Extract the difficulty XXX XXX XXX */ j = i - (j * 4); /* Always have a small chance of success */ if (j < 2) j = 2; /* Success */ if (rand_int(100) < j) { /* Message */ msg_print("You have picked the lock."); /* Open the door */ cave_set_feat(y, x, FEAT_OPEN); /* Update some things */ p_ptr->update |= (PU_VIEW | PU_LITE | PU_MONSTERS); /* Sound */ sound(SOUND_OPENDOOR); /* Experience */ gain_exp(1); } /* Failure */ else { /* Failure */ if (flush_failure) flush(); /* Message */ msg_print("You failed to pick the lock."); /* We may keep trying */ more = TRUE; } } /* Closed door */ else { /* Open the door */ cave_set_feat(y, x, FEAT_OPEN); /* Update some things */ p_ptr->update |= (PU_VIEW | PU_LITE | PU_MONSTERS); /* Sound */ sound(SOUND_OPENDOOR); } /* Result */ return (more); } /* * Open a closed/locked/jammed door or a closed/locked chest. * * Unlocking a locked door/chest is worth one experience point. */ void do_cmd_open(void) { int y, x, dir; s16b o_idx; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Get requested location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get requested grid */ c_ptr = &cave[y][x]; /* Check for chest */ o_idx = chest_check(y, x); /* Nothing useful */ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)) && !o_idx) { /* Message */ msg_print("You see nothing there to open."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Take a turn */ energy_use = 100; /* Message */ msg_print("There is a monster in the way!"); /* Attack */ py_attack(y, x); } /* Handle chests */ else if (o_idx) { /* Open the chest */ more = do_cmd_open_chest(y, x, o_idx); } /* Handle doors */ else { /* Open the door */ more = do_cmd_open_aux(y, x, dir); } } /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); } /* * Perform the basic "close" command * * Assume destination is an open/broken door * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_close_aux(int y, int x, int dir) { cave_type *c_ptr; bool more = FALSE; /* XXX XXX XXX */ dir = (0, dir); /* Take a turn */ energy_use = 100; /* Get grid and contents */ c_ptr = &cave[y][x]; /* Broken door */ if (c_ptr->feat == FEAT_BROKEN) { /* Message */ msg_print("The door appears to be broken."); } /* Open door */ else { /* Close the door */ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); /* Update some things */ p_ptr->update |= (PU_VIEW | PU_LITE | PU_MONSTERS); /* Sound */ sound(SOUND_SHUTDOOR); } /* Result */ return (more); } /* * Close an open door. */ void do_cmd_close(void) { int y, x, dir; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Get requested location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get grid and contents */ c_ptr = &cave[y][x]; /* Require open/broken door */ if ((c_ptr->feat != FEAT_OPEN) && (c_ptr->feat != FEAT_BROKEN)) { /* Message */ msg_print("You see nothing there to close."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Take a turn */ energy_use = 100; /* Message */ msg_print("There is a monster in the way!"); /* Attack */ py_attack(y, x); } /* Close the door */ else { /* Close the door */ more = do_cmd_close_aux(y, x, dir); } } /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); } /* * Tunnel through wall. Assumes valid location. * * Note that it is impossible to "extend" rooms past their * outer walls (which are actually part of the room). * * This will, however, produce grids which are NOT illuminated * (or darkened) along with the rest of the room. */ static bool twall(int y, int x) { cave_type *c_ptr = &cave[y][x]; /* Paranoia -- Require a wall or door or some such */ if (cave_floor_bold(y, x)) return (FALSE); /* Forget the wall */ c_ptr->info &= ~(CAVE_MARK); /* Remove the feature */ cave_set_feat(y, x, FEAT_FLOOR); /* Update some things */ p_ptr->update |= (PU_VIEW | PU_LITE | PU_FLOW | PU_MONSTERS); /* Result */ return (TRUE); } /* * Perform the basic "tunnel" command * * Assumes that the destination is a wall, a vein, a secret * door, or rubble. * * Assumes that no monster is blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_tunnel_aux(int y, int x, int dir) { cave_type *c_ptr; bool more = FALSE; /* XXX XXX XXX */ dir = (0, dir); /* Take a turn */ energy_use = 100; /* Get grid */ c_ptr = &cave[y][x]; /* Sound */ sound(SOUND_DIG); /* Titanium */ if (c_ptr->feat >= FEAT_PERM_EXTRA) { msg_print("This seems to be permanent rock."); } /* Granite */ else if (c_ptr->feat >= FEAT_WALL_EXTRA) { /* Tunnel */ if ((p_ptr->skill_dig > 40 + rand_int(1600)) && twall(y, x)) { msg_print("You have finished the tunnel."); } /* Keep trying */ else { /* We may continue tunelling */ msg_print("You tunnel into the granite wall."); more = TRUE; } } /* Quartz / Magma */ else if (c_ptr->feat >= FEAT_MAGMA) { bool okay = FALSE; bool gold = FALSE; bool hard = FALSE; /* Found gold */ if (c_ptr->feat >= FEAT_MAGMA_H) gold = TRUE; /* Extract "quartz" flag XXX XXX XXX */ if ((c_ptr->feat - FEAT_MAGMA) & 0x01) hard = TRUE; /* Quartz */ if (hard) { okay = (p_ptr->skill_dig > 20 + rand_int(800)); } /* Magma */ else { okay = (p_ptr->skill_dig > 10 + rand_int(400)); } /* Success */ if (okay && twall(y, x)) { /* Found treasure */ if (gold) { /* Place some gold */ place_gold(y, x); /* Message */ msg_print("You have found something!"); } /* Found nothing */ else { /* Message */ msg_print("You have finished the tunnel."); } } /* Failure (quartz) */ else if (hard) { /* Message, continue digging */ msg_print("You tunnel into the quartz vein."); more = TRUE; } /* Failure (magma) */ else { /* Message, continue digging */ msg_print("You tunnel into the magma vein."); more = TRUE; } } /* Rubble */ else if (c_ptr->feat == FEAT_RUBBLE) { /* Remove the rubble */ if ((p_ptr->skill_dig > rand_int(200)) && twall(y, x)) { /* Message */ msg_print("You have removed the rubble."); /* Hack -- place an object */ if (rand_int(100) < 10) { /* Create a simple object */ place_object(y, x, FALSE, FALSE); /* Observe new object */ if (player_can_see_bold(y, x)) { msg_print("You have found something!"); } } } else { /* Message, keep digging */ msg_print("You dig in the rubble."); more = TRUE; } } /* Default to secret doors */ else /* if (c_ptr->feat == FEAT_SECRET) */ { /* Message, keep digging */ msg_print("You tunnel into the granite wall."); more = TRUE; /* Hack -- Search */ search(); } /* Notice new floor grids */ if (!cave_floor_bold(y, x)) { /* Update some things */ p_ptr->update |= (PU_VIEW | PU_LITE | PU_FLOW | PU_MONSTERS); } /* Result */ return (more); } /* * Tunnels through "walls" (including rubble and closed doors) * * Note that you must tunnel in order to hit invisible monsters * in walls, though moving into walls still takes a turn anyway. * * Digging is very difficult without a "digger" weapon, but can be * accomplished by strong players using heavy weapons. */ void do_cmd_tunnel(void) { int y, x, dir; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a direction to tunnel, or Abort */ if (get_rep_dir(&dir)) { /* Get location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get grid */ c_ptr = &cave[y][x]; /* Oops */ if (cave_floor_grid(c_ptr)) { /* Message */ msg_print("You cannot tunnel through air."); } /* No tunnelling through doors */ else if (c_ptr->feat < FEAT_SECRET) { /* Message */ msg_print("You cannot tunnel through doors."); } /* A monster is in the way */ else if (c_ptr->m_idx) { /* Take a turn */ energy_use = 100; /* Message */ msg_print("There is a monster in the way!"); /* Attack */ py_attack(y, x); } /* Try digging */ else { /* Tunnel through walls */ more = do_cmd_tunnel_aux(y, x, dir); } } /* Cancel repetition unless we can continue */ if (!more) disturb(0, 0); } /* * Perform the basic "disarm" command * * Assume destination is a visible trap * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_disarm_chest(int y, int x, s16b o_idx) { int i, j; bool more = FALSE; object_type *o_ptr = &o_list[o_idx]; /* Take a turn */ energy_use = 100; /* Get the "disarm" factor */ i = p_ptr->skill_dis; /* Penalize some conditions */ if (p_ptr->blind || no_lite()) i = i / 10; if (p_ptr->confused || p_ptr->image) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval; /* Always have a small chance of success */ if (j < 2) j = 2; /* Must find the trap first. */ if (!object_known_p(o_ptr)) { msg_print("I don't see any traps."); } /* Already disarmed/unlocked */ else if (o_ptr->pval <= 0) { msg_print("The chest is not trapped."); } /* No traps to find. */ else if (!chest_traps[o_ptr->pval]) { msg_print("The chest is not trapped."); } /* Success (get a lot of experience) */ else if (rand_int(100) < j) { msg_print("You have disarmed the chest."); gain_exp(o_ptr->pval); o_ptr->pval = (0 - o_ptr->pval); } /* Failure -- Keep trying */ else if ((i > 5) && (randint(i) > 5)) { /* We may keep trying */ more = TRUE; if (flush_failure) flush(); msg_print("You failed to disarm the chest."); } /* Failure -- Set off the trap */ else { msg_print("You set off a trap!"); chest_trap(y, x, o_idx); } /* Result */ return (more); } /* * Perform the basic "disarm" command * * Assume destination is a visible trap * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_disarm_aux(int y, int x, int dir) { int i, j, power; cave_type *c_ptr; cptr name; bool more = FALSE; /* Take a turn */ energy_use = 100; /* Get grid and contents */ c_ptr = &cave[y][x]; /* Access trap name */ name = (f_name + f_info[c_ptr->feat].name); /* Get the "disarm" factor */ i = p_ptr->skill_dis; /* Penalize some conditions */ if (p_ptr->blind || no_lite()) i = i / 10; if (p_ptr->confused || p_ptr->image) i = i / 10; /* XXX XXX XXX Variable power? */ /* Extract trap "power" */ power = 5; /* Extract the difficulty */ j = i - power; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success */ if (rand_int(100) < j) { /* Message */ msg_format("You have disarmed the %s.", name); /* Reward */ gain_exp(power); /* Forget the trap */ c_ptr->info &= ~(CAVE_MARK); /* Remove the trap */ cave_set_feat(y, x, FEAT_FLOOR); /* move the player onto the trap grid */ move_player(dir, FALSE); } /* Failure -- Keep trying */ else if ((i > 5) && (randint(i) > 5)) { /* Failure */ if (flush_failure) flush(); /* Message */ msg_format("You failed to disarm the %s.", name); /* We may keep trying */ more = TRUE; } /* Failure -- Set off the trap */ else { /* Message */ msg_format("You set off the %s!", name); /* Move the player onto the trap */ move_player(dir, FALSE); } /* Result */ return (more); } /* * Disarms a trap, or chest */ void do_cmd_disarm(void) { int y, x, dir; s16b o_idx; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a direction (or abort) */ if (get_rep_dir(&dir)) { /* Get location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get grid and contents */ c_ptr = &cave[y][x]; /* Check for chests */ o_idx = chest_check(y, x); /* Disarm a trap */ if (!((c_ptr->feat >= FEAT_TRAP_HEAD) && (c_ptr->feat <= FEAT_TRAP_TAIL)) && !o_idx) { /* Message */ msg_print("You see nothing there to disarm."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Message */ msg_print("There is a monster in the way!"); /* Attack */ py_attack(y, x); } /* Disarm chest */ else if (o_idx) { /* Disarm the chest */ more = do_cmd_disarm_chest(y, x, o_idx); } /* Disarm trap */ else { /* Disarm the trap */ more = do_cmd_disarm_aux(y, x, dir); } } /* Cancel repeat unless told not to */ if (!more) disturb(0, 0); } /* * Perform the basic "bash" command * * Assume destination is a closed/locked/jammed door * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_bash_aux(int y, int x, int dir) { int bash, temp; cave_type *c_ptr; bool more = FALSE; /* Take a turn */ energy_use = 100; /* Get grid */ c_ptr = &cave[y][x]; /* Message */ msg_print("You smash into the door!"); /* Hack -- Bash power based on strength */ /* (Ranges from 3 to 20 to 100 to 200) */ bash = adj_str_blow[p_ptr->stat_ind[A_STR]]; /* Extract door power */ temp = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07); /* Compare bash power to door power XXX XXX XXX */ temp = (bash - (temp * 10)); /* Hack -- always have a chance */ if (temp < 1) temp = 1; /* Hack -- attempt to bash down the door */ if (rand_int(100) < temp) { /* Message */ msg_print("The door crashes open!"); /* Break down the door */ if (rand_int(100) < 50) { cave_set_feat(y, x, FEAT_BROKEN); } /* Open the door */ else { cave_set_feat(y, x, FEAT_OPEN); } /* Sound */ sound(SOUND_OPENDOOR); /* Hack -- Fall through the door */ move_player(dir, FALSE); /* Update some things */ p_ptr->update |= (PU_VIEW | PU_LITE); p_ptr->update |= (PU_DISTANCE); } /* Saving throw against stun */ else if (rand_int(100) < adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev) { /* Message */ msg_print("The door holds firm."); /* Allow repeated bashing */ more = TRUE; } /* High dexterity yields coolness */ else { /* Message */ msg_print("You are off-balance."); /* Hack -- Lose balance ala paralysis */ (void)set_paralyzed(p_ptr->paralyzed + 2 + rand_int(2)); } /* Result */ return (more); } /* * Bash open a door, success based on character strength * * For a closed door, pval is positive if locked; negative if stuck. * * For an open door, pval is positive for a broken door. * * A closed door can be opened - harder if locked. Any door might be * bashed open (and thereby broken). Bashing a door is (potentially) * faster! You move into the door way. To open a stuck door, it must * be bashed. A closed door can be jammed (see do_cmd_spike()). * * Creatures can also open or bash doors, see elsewhere. */ void do_cmd_bash(void) { int y, x, dir; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Bash location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get grid */ c_ptr = &cave[y][x]; /* Nothing useful */ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))) { /* Message */ msg_print("You see nothing there to bash."); } /* Monster in the way */ else if (c_ptr->m_idx) { /* Take a turn */ energy_use = 100; /* Message */ msg_print("There is a monster in the way!"); /* Attack */ py_attack(y, x); } /* Bash a closed door */ else { /* Bash the door */ more = do_cmd_bash_aux(y, x, dir); } } /* Unless valid action taken, cancel bash */ if (!more) disturb(0, 0); } /* * Manipulate an adjacent grid in some way * * Attack monsters, tunnel through walls, disarm traps, open doors. * * Consider confusion XXX XXX XXX * * This command must always take a turn, to prevent free detection * of invisible monsters. */ void do_cmd_alter(void) { int y, x, dir; cave_type *c_ptr; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a direction */ if (get_rep_dir(&dir)) { /* Get location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get grid */ c_ptr = &cave[y][x]; /* Take a turn */ energy_use = 100; /* Attack monsters */ if (c_ptr->m_idx) { /* Attack */ py_attack(y, x); } /* Tunnel through walls */ else if (c_ptr->feat >= FEAT_SECRET) { /* Tunnel */ more = do_cmd_tunnel_aux(y, x, dir); } /* Bash jammed doors */ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) { /* Tunnel */ more = do_cmd_bash_aux(y, x, dir); } /* Open closed doors */ else if (c_ptr->feat >= FEAT_DOOR_HEAD) { /* Tunnel */ more = do_cmd_open_aux(y, x, dir); } /* Disarm traps */ else if (c_ptr->feat >= FEAT_TRAP_HEAD) { /* Tunnel */ more = do_cmd_disarm_aux(y, x, dir); } /* Oops */ else { /* Oops */ msg_print("You attack the empty air."); } } /* Cancel repetition unless we can continue */ if (!more) disturb(0, 0); } /* * Find the index of some "spikes", if possible. * * XXX XXX XXX Let user choose a pile of spikes, perhaps? */ static bool get_spike(int *ip) { int i; /* Check every item in the pack */ for (i = 0; i < INVEN_PACK; i++) { object_type *o_ptr = &inventory[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Check the "tval" code */ if (o_ptr->tval == TV_SPIKE) { /* Save the spike index */ (*ip) = i; /* Success */ return (TRUE); } } /* Oops */ return (FALSE); } /* * Jam a closed door with a spike * * This command may NOT be repeated */ void do_cmd_spike(void) { int y, x, dir, item; cave_type *c_ptr; /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Get location */ y = py + ddy[dir]; x = px + ddx[dir]; /* Get grid and contents */ c_ptr = &cave[y][x]; /* Require closed door */ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))) { /* Message */ msg_print("You see nothing there to spike."); } /* Get a spike */ else if (!get_spike(&item)) { /* Message */ msg_print("You have no spikes!"); } /* Is a monster in the way? */ else if (c_ptr->m_idx) { /* Take a turn */ energy_use = 100; /* Message */ msg_print("There is a monster in the way!"); /* Attack */ py_attack(y, x); } /* Go for it */ else { /* Take a turn */ energy_use = 100; /* Successful jamming */ msg_print("You jam the door with a spike."); /* Convert "locked" to "stuck" XXX XXX XXX */ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08; /* Add one spike to the door */ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++; /* Use up, and describe, a single spike, from the bottom */ inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } } } /* * Support code for the "Walk" and "Jump" commands */ void do_cmd_walk(int pickup) { int dir; bool more = FALSE; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Take a turn */ energy_use = 100; /* Actually move the character */ move_player(dir, pickup); /* Allow more walking */ more = TRUE; } /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); } /* * Start running. */ void do_cmd_run(void) { int dir; /* Hack -- no running when confused */ if (p_ptr->confused) { msg_print("You are too confused!"); return; } /* Get a "repeated" direction */ if (get_rep_dir(&dir)) { /* Hack -- Set the run counter */ running = (command_arg ? command_arg : 1000); /* First step */ run_step(dir); } } /* * Stay still. Search. Enter stores. * Pick up treasure if "pickup" is true. */ void do_cmd_stay(int pickup) { cave_type *c_ptr = &cave[py][px]; /* Allow repeated command */ if (command_arg) { /* Set repeat count */ command_rep = command_arg - 1; /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Cancel the arg */ command_arg = 0; } /* Take a turn */ energy_use = 100; /* Spontaneous Searching */ if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos))) { search(); } /* Continuous Searching */ if (p_ptr->searching) { search(); } /* Handle "objects" */ carry(pickup); /* Hack -- enter a store if we are on one */ if ((c_ptr->feat >= FEAT_SHOP_HEAD) && (c_ptr->feat <= FEAT_SHOP_TAIL)) { /* Disturb */ disturb(0, 0); /* Hack -- enter store */ command_new = '_'; } } /* * Resting allows a player to safely restore his hp -RAK- */ void do_cmd_rest(void) { /* Prompt for time if needed */ if (command_arg <= 0) { cptr p = "Rest (0-9999, '*' for HP/SP, '&' as needed): "; char out_val[80]; /* Default */ strcpy(out_val, "&"); /* Ask for duration */ if (!get_string(p, out_val, 4)) return; /* Rest until done */ if (out_val[0] == '&') { command_arg = (-2); } /* Rest a lot */ else if (out_val[0] == '*') { command_arg = (-1); } /* Rest some */ else { command_arg = atoi(out_val); if (command_arg <= 0) return; } } /* Paranoia */ if (command_arg > 9999) command_arg = 9999; /* Take a turn XXX XXX XXX (?) */ energy_use = 100; /* Save the rest code */ resting = command_arg; /* Cancel searching */ p_ptr->searching = FALSE; /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Handle stuff */ handle_stuff(); /* Refresh */ Term_fresh(); } /* * Determines the odds of an object breaking when thrown at a monster * * Note that artifacts never break, see the "drop_near()" function. */ static int breakage_chance(object_type *o_ptr) { /* Examine the item type */ switch (o_ptr->tval) { /* Always break */ case TV_FLASK: case TV_POTION: case TV_BOTTLE: case TV_FOOD: case TV_JUNK: { return (100); } /* Often break */ case TV_LITE: case TV_SCROLL: case TV_ARROW: case TV_SKELETON: { return (50); } /* Sometimes break */ case TV_WAND: case TV_SHOT: case TV_BOLT: case TV_SPIKE: { return (25); } } /* Rarely break */ return (10); } /* * Fire an object from the pack or floor. * * You may only fire items that "match" your missile launcher. * * You must use slings + pebbles/shots, bows + arrows, xbows + bolts. * * See "calc_bonuses()" for more calculations and such. * * Note that "firing" a missile is MUCH better than "throwing" it. * * Note: "unseen" monsters are very hard to hit. * * Objects are more likely to break if they "attempt" to hit a monster. * * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots. * * The "extra shot" code works by decreasing the amount of energy * required to make each shot, spreading the shots out over time. * * Note that when firing missiles, the launcher multiplier is applied * after all the bonuses are added in, making multipliers very useful. * * Note that Bows of "Extra Might" get extra range and an extra bonus * for the damage multiplier. * * Note that Bows of "Extra Shots" give an extra shot. */ void do_cmd_fire(void) { int dir, item; int j, y, x, ny, nx, ty, tx; int tdam, tdis, thits, tmul; int bonus, chance; int cur_dis, visible; object_type forge; object_type *q_ptr; object_type *o_ptr; object_type *j_ptr; bool hit_body = FALSE; byte missile_attr; char missile_char; char o_name[80]; int msec = delay_factor * delay_factor * delay_factor; /* Get the "bow" (if any) */ j_ptr = &inventory[INVEN_BOW]; /* Require a launcher */ if (!j_ptr->tval) { msg_print("You have nothing to fire with."); return; } /* Require proper missile */ item_tester_tval = p_ptr->tval_ammo; /* Get an item (from inven or floor) */ if (!get_item(&item, "Fire which item? ", FALSE, TRUE, TRUE)) { if (item == -2) msg_print("You have nothing to fire."); return; } /* Access the item (if in the pack) */ if (item >= 0) { o_ptr = &inventory[item]; } else { o_ptr = &o_list[0 - item]; } /* Get a direction (or cancel) */ if (!get_aim_dir(&dir)) return; /* Get local object */ q_ptr = &forge; /* Obtain a local object */ object_copy(q_ptr, o_ptr); /* Single object */ q_ptr->number = 1; /* Reduce and describe inventory */ if (item >= 0) { inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } /* Reduce and describe floor item */ else { floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } /* Sound */ sound(SOUND_SHOOT); /* Describe the object */ object_desc(o_name, q_ptr, FALSE, 3); /* Find the color and symbol for the object for throwing */ missile_attr = object_attr(q_ptr); missile_char = object_char(q_ptr); /* Use the proper number of shots */ thits = p_ptr->num_fire; /* Use a base distance */ tdis = 10; /* Base damage from thrown object plus launcher bonus */ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + j_ptr->to_d; /* Actually "fire" the object */ bonus = (p_ptr->to_h + q_ptr->to_h + j_ptr->to_h); chance = (p_ptr->skill_thb + (bonus * BTH_PLUS_ADJ)); /* Assume a base multiplier */ tmul = 1; /* Analyze the launcher */ switch (j_ptr->sval) { /* Sling and ammo */ case SV_SLING: { tmul = 2; break; } /* Short Bow and Arrow */ case SV_SHORT_BOW: { tmul = 2; break; } /* Long Bow and Arrow */ case SV_LONG_BOW: { tmul = 3; break; } /* Light Crossbow and Bolt */ case SV_LIGHT_XBOW: { tmul = 3; break; } /* Heavy Crossbow and Bolt */ case SV_HEAVY_XBOW: { tmul = 4; break; } } /* Get extra "power" from "extra might" */ if (p_ptr->xtra_might) tmul++; /* Boost the damage */ tdam *= tmul; /* Base range */ tdis = 10 + 5 * tmul; /* Take a (partial) turn */ energy_use = (100 / thits); /* Start at the player */ y = py; x = px; /* Predict the "target" location */ tx = px + 99 * ddx[dir]; ty = py + 99 * ddy[dir]; /* Check for "target request" */ if ((dir == 5) && target_okay()) { tx = target_col; ty = target_row; } /* Hack -- Handle stuff */ handle_stuff(); /* Travel until stopped */ for (cur_dis = 0; cur_dis <= tdis; ) { /* Hack -- Stop at the target */ if ((y == ty) && (x == tx)) break; /* Calculate the new location (see "project()") */ ny = y; nx = x; mmove2(&ny, &nx, py, px, ty, tx); /* Stopped by walls/doors */ if (!cave_floor_bold(ny, nx)) break; /* Advance the distance */ cur_dis++; /* Save the new location */ x = nx; y = ny; /* The player can see the (on screen) missile */ if (panel_contains(y, x) && player_can_see_bold(y, x)) { /* Draw, Hilite, Fresh, Pause, Erase */ print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); Term_xtra(TERM_XTRA_DELAY, msec); lite_spot(y, x); Term_fresh(); } /* The player cannot see the missile */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Monster here, Try to hit it */ if (cave[y][x].m_idx) { cave_type *c_ptr = &cave[y][x]; monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Check the visibility */ visible = m_ptr->ml; /* Note the collision */ hit_body = TRUE; /* Did we hit it (penalize range) */ if (test_hit_fire(chance - cur_dis, r_ptr->ac, m_ptr->ml)) { bool fear = FALSE; /* Assume a default death */ cptr note_dies = " dies."; /* Some monsters get "destroyed" */ if ((r_ptr->flags3 & (RF3_DEMON)) || (r_ptr->flags3 & (RF3_UNDEAD)) || (r_ptr->flags2 & (RF2_STUPID)) || (strchr("Evg", r_ptr->d_char))) { /* Special note at death */ note_dies = " is destroyed."; } /* Handle unseen monster */ if (!visible) { /* Invisible monster */ msg_format("The %s finds a mark.", o_name); } /* Handle visible monster */ else { char m_name[80]; /* Get "the monster" or "it" */ monster_desc(m_name, m_ptr, 0); /* Message */ msg_format("The %s hits %s.", o_name, m_name); /* Hack -- Track this monster race */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Hack -- Track this monster */ if (m_ptr->ml) health_track(c_ptr->m_idx); } /* Apply special damage XXX XXX XXX */ tdam = tot_dam_aux(q_ptr, tdam, m_ptr); tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam); /* No negative damage */ if (tdam < 0) tdam = 0; /* Complex message */ if (wizard) { msg_format("You do %d (out of %d) damage.", tdam, m_ptr->hp); } /* Hit the monster, check for death */ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(c_ptr->m_idx, tdam); /* Take note */ if (fear && m_ptr->ml) { char m_name[80]; /* Sound */ sound(SOUND_FLEE); /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0); /* Message */ msg_format("%^s flees in terror!", m_name); } } } /* Stop looking */ break; } } /* Chance of breakage (during attacks) */ j = (hit_body ? breakage_chance(q_ptr) : 0); /* Drop (or break) near that location */ drop_near(q_ptr, j, y, x); } /* * Throw an object from the pack or floor. * * Note: "unseen" monsters are very hard to hit. * * Should throwing a weapon do full damage? Should it allow the magic * to hit bonus of the weapon to have an effect? Should it ever cause * the item to be destroyed? Should it do any damage at all? */ void do_cmd_throw(void) { int dir, item; int j, y, x, ny, nx, ty, tx; int chance, tdam, tdis; int mul, div; int cur_dis, visible; object_type forge; object_type *q_ptr; object_type *o_ptr; bool hit_body = FALSE; byte missile_attr; char missile_char; char o_name[80]; int msec = delay_factor * delay_factor * delay_factor; /* Get an item (from inven or floor) */ if (!get_item(&item, "Throw which item? ", FALSE, TRUE, TRUE)) { if (item == -2) msg_print("You have nothing to throw."); return; } /* Access the item (if in the pack) */ if (item >= 0) { o_ptr = &inventory[item]; } else { o_ptr = &o_list[0 - item]; } /* Get a direction (or cancel) */ if (!get_aim_dir(&dir)) return; /* Get local object */ q_ptr = &forge; /* Obtain a local object */ object_copy(q_ptr, o_ptr); /* Single object */ q_ptr->number = 1; /* Reduce and describe inventory */ if (item >= 0) { inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } /* Reduce and describe floor item */ else { floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } /* Description */ object_desc(o_name, q_ptr, FALSE, 3); /* Find the color and symbol for the object for throwing */ missile_attr = object_attr(q_ptr); missile_char = object_char(q_ptr); /* Extract a "distance multiplier" */ mul = 10; /* Enforce a minimum "weight" of one pound */ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10); /* Hack -- Distance -- Reward strength, penalize weight */ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div; /* Max distance of 10 */ if (tdis > 10) tdis = 10; /* Hack -- Base damage from thrown object */ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d; /* Chance of hitting */ chance = (p_ptr->skill_tht + (p_ptr->to_h * BTH_PLUS_ADJ)); /* Take a turn */ energy_use = 100; /* Start at the player */ y = py; x = px; /* Predict the "target" location */ tx = px + 99 * ddx[dir]; ty = py + 99 * ddy[dir]; /* Check for "target request" */ if ((dir == 5) && target_okay()) { tx = target_col; ty = target_row; } /* Hack -- Handle stuff */ handle_stuff(); /* Travel until stopped */ for (cur_dis = 0; cur_dis <= tdis; ) { /* Hack -- Stop at the target */ if ((y == ty) && (x == tx)) break; /* Calculate the new location (see "project()") */ ny = y; nx = x; mmove2(&ny, &nx, py, px, ty, tx); /* Stopped by walls/doors */ if (!cave_floor_bold(ny, nx)) break; /* Advance the distance */ cur_dis++; /* Save the new location */ x = nx; y = ny; /* The player can see the (on screen) missile */ if (panel_contains(y, x) && player_can_see_bold(y, x)) { /* Draw, Hilite, Fresh, Pause, Erase */ print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); Term_xtra(TERM_XTRA_DELAY, msec); lite_spot(y, x); Term_fresh(); } /* The player cannot see the missile */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Monster here, Try to hit it */ if (cave[y][x].m_idx) { cave_type *c_ptr = &cave[y][x]; monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Check the visibility */ visible = m_ptr->ml; /* Note the collision */ hit_body = TRUE; /* Did we hit it (penalize range) */ if (test_hit_fire(chance - cur_dis, r_ptr->ac, m_ptr->ml)) { bool fear = FALSE; /* Assume a default death */ cptr note_dies = " dies."; /* Some monsters get "destroyed" */ if ((r_ptr->flags3 & (RF3_DEMON)) || (r_ptr->flags3 & (RF3_UNDEAD)) || (r_ptr->flags2 & (RF2_STUPID)) || (strchr("Evg", r_ptr->d_char))) { /* Special note at death */ note_dies = " is destroyed."; } /* Handle unseen monster */ if (!visible) { /* Invisible monster */ msg_format("The %s finds a mark.", o_name); } /* Handle visible monster */ else { char m_name[80]; /* Get "the monster" or "it" */ monster_desc(m_name, m_ptr, 0); /* Message */ msg_format("The %s hits %s.", o_name, m_name); /* Hack -- Track this monster race */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Hack -- Track this monster */ if (m_ptr->ml) health_track(c_ptr->m_idx); } /* Apply special damage XXX XXX XXX */ tdam = tot_dam_aux(q_ptr, tdam, m_ptr); tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam); /* No negative damage */ if (tdam < 0) tdam = 0; /* Complex message */ if (wizard) { msg_format("You do %d (out of %d) damage.", tdam, m_ptr->hp); } /* Hit the monster, check for death */ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(c_ptr->m_idx, tdam); /* Take note */ if (fear && m_ptr->ml) { char m_name[80]; /* Sound */ sound(SOUND_FLEE); /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0); /* Message */ msg_format("%^s flees in terror!", m_name); } } } /* Stop looking */ break; } } /* Chance of breakage (during attacks) */ j = (hit_body ? breakage_chance(q_ptr) : 0); /* Drop (or break) near that location */ drop_near(q_ptr, j, y, x); }