/** * Determine if a given grid may be "walked" */ static bool do_cmd_walk_test(int y, int x) { int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); /* Allow attack on visible monsters if unafraid */ if (m_idx > 0 && mflag_has(mon->mflag, MFLAG_VISIBLE) && !is_mimicking(mon)) { /* Handle player fear */ if (player_of_has(player, OF_AFRAID)) { /* Extract monster name (or "it") */ char m_name[80]; monster_desc(m_name, sizeof(m_name), mon, MDESC_DEFAULT); /* Message */ msgt(MSG_AFRAID, "You are too afraid to attack %s!", m_name); /* Nope */ return (false); } return (true); } /* If we don't know the grid, allow attempts to walk into it */ if (!square_isknown(cave, y, x)) return true; /* Require open space */ if (!square_ispassable(cave, y, x)) { if (square_isrubble(cave, y, x)) /* Rubble */ msgt(MSG_HITWALL, "There is a pile of rubble in the way!"); else if (square_iscloseddoor(cave, y, x)) /* Door */ return true; else if (square_isbright(cave, y, x)) /* Lava */ msgt(MSG_HITWALL, "The heat of the lava turns you away!"); else /* Wall */ msgt(MSG_HITWALL, "There is a wall in the way!"); /* Cancel repeat */ disturb(player, 0); /* Nope */ return (false); } /* Okay */ return (true); }
/** * Determine if a given grid may be "opened" */ static bool do_cmd_open_test(int y, int x) { /* Must have knowledge */ if (!square_isknown(cave, y, x)) { msg("You see nothing there."); return false; } /* Must be a closed door */ if (!square_iscloseddoor(cave, y, x)) { msgt(MSG_NOTHING_TO_OPEN, "You see nothing there to open."); return false; } return (true); }
/** * Determine if a given grid may be "disarmed" */ static bool do_cmd_disarm_test(int y, int x) { /* Must have knowledge */ if (!square_isknown(cave, y, x)) { msg("You see nothing there."); return false; } /* Look for a closed, unlocked door to lock */ if (square_iscloseddoor(cave, y, x) && !square_islockeddoor(cave, y, x)) return true; /* Look for a trap */ if (!square_isknowntrap(cave, y, x)) { msg("You see nothing there to disarm."); return false; } /* Okay */ return true; }
/** * Lock a closed door to a given power */ void square_set_door_lock(struct chunk *c, int y, int x, int power) { struct trap_kind *lock = lookup_trap("door lock"); struct trap *trap; /* Verify it's a closed door */ if (!square_iscloseddoor(c, y, x)) return; /* If there's no lock there, add one */ if (!square_trap_specific(c, y, x, lock->tidx)) place_trap(c, y, x, lock->tidx, 0); /* Set the power (of all locks - there should be only one) */ trap = c->squares[y][x].trap; while (trap) { if (trap->kind == lock) trap->xtra = power; trap = trap->next; } }
/** * Manipulate an adjacent grid in some way * * Attack monsters, tunnel through walls, disarm traps, open doors. * * This command must always take energy, to prevent free detection * of invisible monsters. * * The "semantics" of this command must be chosen before the player * is confused, and it must be verified against the new grid. */ void do_cmd_alter_aux(int dir) { int y, x; bool more = false; /* Get location */ y = player->py + ddy[dir]; x = player->px + ddx[dir]; /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* Apply confusion */ if (player_confuse_dir(player, &dir, false)) { /* Get location */ y = player->py + ddy[dir]; x = player->px + ddx[dir]; } /* Action depends on what's there */ if (cave->squares[y][x].mon > 0) /* Attack monsters */ py_attack(y, x); else if (square_isdiggable(cave, y, x)) /* Tunnel through walls and rubble */ more = do_cmd_tunnel_aux(y, x); else if (square_iscloseddoor(cave, y, x)) /* Open closed doors */ more = do_cmd_open_aux(y, x); else if (square_isknowntrap(cave, y, x)) /* Disarm traps */ more = do_cmd_disarm_aux(y, x); else /* Oops */ msg("You spin around."); /* Cancel repetition unless we can continue */ if (!more) disturb(player, 0); }
/** * Determine if a given grid may be "tunneled" */ static bool do_cmd_tunnel_test(int y, int x) { /* Must have knowledge */ if (!square_isknown(cave, y, x)) { msg("You see nothing there."); return (false); } /* Titanium */ if (square_isperm(cave, y, x)) { msg("This seems to be permanent rock."); return (false); } /* Must be a wall/door/etc */ if (!(square_isdiggable(cave, y, x) || square_iscloseddoor(cave, y, x))) { msg("You see nothing there to tunnel."); return (false); } /* Okay */ return (true); }
/** * Return the power of the lock on a door */ int square_door_power(struct chunk *c, int y, int x) { struct trap_kind *lock = lookup_trap("door lock"); struct trap *trap; /* Verify it's a closed door */ if (!square_iscloseddoor(c, y, x)) return 0; /* Is there a lock there? */ if (!square_trap_specific(c, y, x, lock->tidx)) return 0; /* Get the power and return it */ trap = c->squares[y][x].trap; while (trap) { if (trap->kind == lock) return trap->xtra; trap = trap->next; } return 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). * * Attempting to do so will produce floor grids which are not part * of the room, and whose "illumination" status do not change with * the rest of the room. */ static bool twall(int y, int x) { /* Paranoia -- Require a wall or door or some such */ if (!(square_isdiggable(cave, y, x) || square_iscloseddoor(cave, y, x))) return (false); /* Sound */ sound(MSG_DIG); /* Forget the wall */ square_forget(cave, y, x); /* Remove the feature */ square_tunnel_wall(cave, y, x); /* Update the visuals */ player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Fully update the flow */ player->upkeep->update |= (PU_FORGET_FLOW | PU_UPDATE_FLOW); /* Result */ return (true); }
/** * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int y = player->py + ddy[dir]; int x = player->px + ddx[dir]; int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); bool alterable = (square_isknowntrap(cave, y, x) || square_iscloseddoor(cave, y, x)); /* Attack monsters, alter traps/doors on movement, hit obstacles or move */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(mon)) { become_aware(mon); /* Mimic wakes up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); } else { py_attack(y, x); } } else if (disarm && square_isknown(cave, y, x) && alterable) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } else if (player->upkeep->running && square_isknowntrap(cave, y, x)) { /* Stop running before known traps */ disturb(player, 0); } else if (!square_ispassable(cave, y, x)) { disturb(player, 0); /* Notice unknown obstacles, mention known obstacles */ if (!square_isknown(cave, y, x)) { if (square_isrubble(cave, y, x)) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else if (square_iscloseddoor(cave, y, x)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } } else { if (square_isrubble(cave, y, x)) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (square_iscloseddoor(cave, y, x)) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } else { /* Move player */ monster_swap(player->py, player->px, y, x); /* Handle store doors, or notice objects */ if (square_isshop(cave, y, x)) { disturb(player, 0); event_signal(EVENT_ENTER_STORE); event_remove_handler_type(EVENT_ENTER_STORE); event_signal(EVENT_USE_STORE); event_remove_handler_type(EVENT_USE_STORE); event_signal(EVENT_LEAVE_STORE); event_remove_handler_type(EVENT_LEAVE_STORE); } else { square_know_pile(cave, y, x); cmdq_push(CMD_AUTOPICKUP); } /* Discover invisible traps, set off visible ones */ if (square_issecrettrap(cave, y, x)) { disturb(player, 0); hit_trap(y, x); } else if (square_isknowntrap(cave, y, x)) { disturb(player, 0); hit_trap(y, x); } /* Update view and search */ update_view(cave, player); search(); } player->upkeep->running_firststep = false; }
/** * Disarms a trap, or a chest * * Traps must be visible, chests must be known trapped */ void do_cmd_disarm(struct command *cmd) { int y, x, dir; int err; struct object *obj; bool more = false; /* Get arguments */ err = cmd_get_arg_direction(cmd, "direction", &dir); if (err || dir == DIR_UNKNOWN) { int y2, x2; int n_traps, n_chests; n_traps = count_feats(&y2, &x2, square_isknowntrap, true); n_chests = count_chests(&y2, &x2, CHEST_TRAPPED); if (n_traps + n_chests == 1) { dir = coords_to_dir(y2, x2); cmd_set_arg_direction(cmd, "direction", dir); } else if (cmd_get_direction(cmd, "direction", &dir, n_chests > 0)) { /* If there are chests to disarm, 5 is allowed as a direction */ return; } } /* Get location */ y = player->py + ddy[dir]; x = player->px + ddx[dir]; /* Check for chests */ obj = chest_check(y, x, CHEST_TRAPPED); /* Verify legality */ if (!obj && !do_cmd_disarm_test(y, x)) { /* Cancel repeat */ disturb(player, 0); return; } /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* Apply confusion */ if (player_confuse_dir(player, &dir, false)) { /* Get location */ y = player->py + ddy[dir]; x = player->px + ddx[dir]; /* Check for chests */ obj = chest_check(y, x, CHEST_TRAPPED); } /* Monster */ if (cave->squares[y][x].mon > 0) { msg("There is a monster in the way!"); py_attack(y, x); } else if (obj) /* Chest */ more = do_cmd_disarm_chest(y, x, obj); else if (square_iscloseddoor(cave, y, x) && !square_islockeddoor(cave, y, x)) /* Door to lock */ more = do_cmd_lock_door(y, x); else /* Disarm trap */ more = do_cmd_disarm_aux(y, x); /* Cancel repeat unless told not to */ if (!more) disturb(player, 0); }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns true if the monster is able to move through the grid. */ static bool process_monster_can_move(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny, bool *did_something) { struct monster_lore *lore = get_lore(mon->race); /* Only fiery creatures can handle lava */ if (square_isfiery(c, ny, nx) && !rf_has(mon->race->flags, RF_IM_FIRE)) return false; /* Floor is open? */ if (square_ispassable(c, ny, nx)) return true; /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) return false; /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { rf_on(lore->flags, RF_PASS_WALL); rf_on(lore->flags, RF_KILL_WALL); } /* Monster may be able to deal with walls and doors */ if (rf_has(mon->race->flags, RF_PASS_WALL)) { return true; } else if (rf_has(mon->race->flags, RF_KILL_WALL)) { /* Remove the wall */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Fully update the flow since terrain changed */ player->upkeep->update |= (PU_FORGET_FLOW | PU_UPDATE_FLOW); return true; } else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool may_bash = rf_has(mon->race->flags, RF_BASH_DOOR) && one_in_(2); /* Take a turn */ *did_something = true; /* Learn about door abilities */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { rf_on(lore->flags, RF_OPEN_DOOR); rf_on(lore->flags, RF_BASH_DOOR); } /* Creature can open or bash doors */ if (!rf_has(mon->race->flags, RF_OPEN_DOOR) && !rf_has(mon->race->flags, RF_BASH_DOOR)) return false; /* Stuck door -- try to unlock it */ if (square_islockeddoor(c, ny, nx)) { int k = square_door_power(c, ny, nx); if (randint0(mon->hp / 10) > k) { if (may_bash) msg("%s slams against the door.", m_name); else msg("%s fiddles with the lock.", m_name); /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Handle viewable doors */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Closed or secret door -- open or bash if allowed */ if (may_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return true; } else if (rf_has(mon->race->flags, RF_OPEN_DOOR)) { square_open_door(c, ny, nx); } } } return false; }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns TRUE if the monster is able to move through the grid. */ static bool process_monster_can_move(struct chunk *c, struct monster *m_ptr, const char *m_name, int nx, int ny, bool *did_something) { monster_lore *l_ptr = get_lore(m_ptr->race); /* Floor is open? */ if (square_ispassable(c, ny, nx)) return TRUE; /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) return FALSE; /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_PASS_WALL); rf_on(l_ptr->flags, RF_KILL_WALL); } /* Monster moves through walls (and doors) */ if (rf_has(m_ptr->race->flags, RF_PASS_WALL)) return TRUE; /* Monster destroys walls (and doors) */ else if (rf_has(m_ptr->race->flags, RF_KILL_WALL)) { /* Forget the wall */ sqinfo_off(c->squares[ny][nx].info, SQUARE_MARK); /* Notice */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Update the flow, since walls affect flow */ player->upkeep->update |= PU_UPDATE_FLOW; return TRUE; } /* Handle doors and secret doors */ else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool may_bash = rf_has(m_ptr->race->flags, RF_BASH_DOOR) && one_in_(2); /* Take a turn */ *did_something = TRUE; /* Learn about door abilities */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_OPEN_DOOR); rf_on(l_ptr->flags, RF_BASH_DOOR); } /* Creature can open or bash doors */ if (!rf_has(m_ptr->race->flags, RF_OPEN_DOOR) && !rf_has(m_ptr->race->flags, RF_BASH_DOOR)) return FALSE; /* Stuck door -- try to unlock it */ if (square_islockeddoor(c, ny, nx)) { int k = square_door_power(c, ny, nx); if (randint0(m_ptr->hp / 10) > k) { if (may_bash) msg("%s slams against the door.", m_name); else msg("%s fiddles with the lock.", m_name); /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Handle viewable doors */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Closed or secret door -- open or bash if allowed */ if (may_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return TRUE; } else if (rf_has(m_ptr->race->flags, RF_OPEN_DOOR)) { square_open_door(c, ny, nx); } } } return FALSE; }
/** * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int py = player->py; int px = player->px; int y = py + ddy[dir]; int x = px + ddx[dir]; int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); bool alterable = (square_isknowntrap(cave, y, x) || square_iscloseddoor(cave, y, x)); /* Attack monsters, alter traps/doors on movement, hit obstacles or move */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(mon)) { become_aware(mon); /* Mimic wakes up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); } else { py_attack(y, x); } } else if (disarm && square_isknown(cave, y, x) && alterable) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } else if (player->upkeep->running && square_isknowntrap(cave, y, x)) { /* Stop running before known traps */ disturb(player, 0); } else if (!square_ispassable(cave, y, x)) { /* Disturb the player */ disturb(player, 0); /* Notice unknown obstacles, mention known obstacles */ if (!square_isknown(cave, y, x)) { if (square_isrubble(cave, y, x)) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else if (square_iscloseddoor(cave, y, x)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } } else { if (square_isrubble(cave, y, x)) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (square_iscloseddoor(cave, y, x)) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } else { /* See if trap detection status will change */ bool old_dtrap = square_isdtrap(cave, py, px); bool new_dtrap = square_isdtrap(cave, y, x); /* Note the change in the detect status */ if (old_dtrap != new_dtrap) player->upkeep->redraw |= (PR_DTRAP); /* Disturb player if the player is about to leave the area */ if (player->upkeep->running && !player->upkeep->running_firststep && old_dtrap && !new_dtrap) { disturb(player, 0); return; } /* Move player */ monster_swap(py, px, y, x); /* New location */ y = py = player->py; x = px = player->px; /* Searching */ if (player->searching || (player->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) || one_in_(50 - player->state.skills[SKILL_SEARCH_FREQUENCY])) search(false); /* Handle store doors, or notice objects */ if (square_isshop(cave, player->py, player->px)) { /* Disturb */ disturb(player, 0); event_signal(EVENT_ENTER_STORE); event_remove_handler_type(EVENT_ENTER_STORE); event_signal(EVENT_USE_STORE); event_remove_handler_type(EVENT_USE_STORE); event_signal(EVENT_LEAVE_STORE); event_remove_handler_type(EVENT_LEAVE_STORE); } else { /* Know objects, queue autopickup */ floor_pile_know(cave, player->py, player->px); cmdq_push(CMD_AUTOPICKUP); } /* Discover invisible traps, set off visible ones */ if (square_issecrettrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap. */ hit_trap(y, x); } else if (square_isknowntrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap */ hit_trap(y, x); } } player->upkeep->running_firststep = false; }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns true if the monster is able to move through the grid. */ static bool monster_turn_can_move(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny, bool *did_something) { struct monster_lore *lore = get_lore(mon->race); /* Dangerous terrain in the way */ if (monster_hates_grid(c, mon, ny, nx)) { return false; } /* Floor is open? */ if (square_ispassable(c, ny, nx)) { return true; } /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) { return false; } /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (monster_is_visible(mon)) { rf_on(lore->flags, RF_PASS_WALL); rf_on(lore->flags, RF_KILL_WALL); } /* Monster may be able to deal with walls and doors */ if (rf_has(mon->race->flags, RF_PASS_WALL)) { return true; } else if (rf_has(mon->race->flags, RF_KILL_WALL)) { /* Remove the wall */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); return true; } else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool can_open = rf_has(mon->race->flags, RF_OPEN_DOOR); bool can_bash = rf_has(mon->race->flags, RF_BASH_DOOR); bool will_bash = false; /* Take a turn */ *did_something = true; /* Learn about door abilities */ if (monster_is_visible(mon)) { rf_on(lore->flags, RF_OPEN_DOOR); rf_on(lore->flags, RF_BASH_DOOR); } /* If creature can open or bash doors, make a choice */ if (can_open) { /* Sometimes bash anyway (impatient) */ if (can_bash) { will_bash = one_in_(2) ? true : false; } } else if (can_bash) { /* Only choice */ will_bash = true; } else { /* Door is an insurmountable obstacle */ return false; } /* Now outcome depends on type of door */ if (square_islockeddoor(c, ny, nx)) { /* Locked door -- test monster strength against door strength */ int k = square_door_power(c, ny, nx); if (randint0(mon->hp / 10) > k) { if (will_bash) { msg("%s slams against the door.", m_name); } else { msg("%s fiddles with the lock.", m_name); } /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Closed or secret door -- always open or bash */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); if (will_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return true; } else { square_open_door(c, ny, nx); } } } return false; }