/* * Hack -- quick debugging hook */ static void do_cmd_wiz_hack_ben(void) { int py = p_ptr->py; int px = p_ptr->px; int i, y, x; for (i = 0; i < MONSTER_FLOW_DEPTH; ++i) { /* Update map */ for (y = Term->offset_y; y < Term->offset_y + SCREEN_HGT; y++) { for (x = Term->offset_x; x < Term->offset_x + SCREEN_WID; x++) { byte a = TERM_RED; if (!in_bounds_fully(y, x)) continue; /* Display proper cost */ if (cave_cost[y][x] != i) continue; /* Reliability in yellow */ if (cave_when[y][x] == cave_when[py][px]) { a = TERM_YELLOW; } /* Display player/floors/walls */ if ((y == py) && (x == px)) { print_rel('@', a, y, x); } else if (cave_floor_bold(y, x)) { print_rel('*', a, y, x); } else { print_rel('#', a, y, x); } } } /* Prompt */ prt(format("Depth %d: ", i), 0, 0); /* Get key */ if (inkey() == ESCAPE) break; /* Redraw map */ prt_map(); } /* Done */ prt("", 0, 0); /* Redraw map */ prt_map(); }
/* * Determine if a given grid may be "tunneled" */ static bool do_cmd_tunnel_test(int y, int x) { /* Must have knowledge */ if (!(cave_info[y][x] & (CAVE_MARK))) { /* Message */ msg_print("You see nothing there."); /* Nope */ return (FALSE); } /* Must be a wall/door/etc */ if (cave_floor_bold(y, x)) { /* Message */ msg_print("You see nothing there to tunnel."); /* Nope */ return (FALSE); } /* Okay */ return (TRUE); }
static bool is_valid_pf(int y, int x) { /* Unvisited means allowed */ if (!(cave_info[y][x] & (CAVE_MARK))) return (TRUE); /* Require open space */ return (cave_floor_bold(y, x)); }
/* * Determine if a given grid may be "walked" */ static bool do_cmd_walk_test(int y, int x) { int m_idx = cave->m_idx[y][x]; /* Allow attack on visible monsters if unafraid */ if ((m_idx > 0) && (cave_monster(cave, m_idx)->ml) && !is_mimicking(m_idx)) { /* Handle player fear */ if(check_state(p_ptr, OF_AFRAID, p_ptr->state.flags)) { /* Extract monster name (or "it") */ char m_name[80]; const monster_type *m_ptr; m_ptr = cave_monster(cave, m_idx); monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* 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 (!(cave->info[y][x] & CAVE_MARK)) return TRUE; /* Require open space */ if (!cave_floor_bold(y, x)) { /* Rubble */ if (cave->feat[y][x] == FEAT_RUBBLE) msgt(MSG_HITWALL, "There is a pile of rubble in the way!"); /* Door */ else if (cave->feat[y][x] < FEAT_SECRET) return TRUE; /* Wall */ else msgt(MSG_HITWALL, "There is a wall in the way!"); /* Cancel repeat */ disturb(p_ptr, 0, 0); /* Nope */ return (FALSE); } /* Okay */ return (TRUE); }
static bool is_valid_pf(player_type *p_ptr, int y, int x) { int Ind = Get_Ind[p_ptr->conn]; int Depth = p_ptr->dun_depth; /* Unvisited means allowed */ if (!(p_ptr->cave_flag[y][x] & (CAVE_MARK))) return (TRUE); /* Require open space */ return (cave_floor_bold(Depth, y, x)); }
/* * Determine if a given grid may be "walked" */ static bool do_cmd_walk_test(int y, int x) { /* Allow attack on visible monsters if unafraid */ if ((cave_m_idx[y][x] > 0) && (mon_list[cave_m_idx[y][x]].ml)) { /* Handle player fear */ if(p_ptr->state.afraid) { /* Extract monster name (or "it") */ char m_name[80]; monster_type *m_ptr; m_ptr = &mon_list[cave_m_idx[y][x]]; monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Message */ message_format(MSG_AFRAID, 0, "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 (!(cave_info[y][x] & CAVE_MARK)) return TRUE; /* Require open space or PASS_WALL/KILL_WALL flag -Simon */ if (!cave_floor_bold(y, x) && !(player_has(PF_PASS_WALL) || player_has(PF_KILL_WALL))) { /* Rubble */ if (cave_feat[y][x] == FEAT_RUBBLE) message(MSG_HITWALL, 0, "There is a pile of rubble in the way!"); /* Door */ else if (cave_feat[y][x] < FEAT_SECRET) return TRUE; /* Wall */ else message(MSG_HITWALL, 0, "There is a wall in the way!"); /* Cancel repeat */ disturb(0, 0); /* Nope */ return (FALSE); } /* Okay */ return (TRUE); }
/* * 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 (cave_floor_bold(y, x) && (cave_feat[y][x] == FEAT_TREE)) return (FALSE); /* Sound */ sound(SOUND_DIG); /* Forget the wall */ cave_info[y][x] &= ~(CAVE_MARK); /* Remove the feature */ cave_set_feat(y, x, FEAT_FLOOR); /* Update the visuals */ p_ptr->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Result */ return (TRUE); }
/* Player and monster swap places */ bool player_monster_swap(monster_type *m_ptr) { char m_name[80]; cave_type *c_ptr; if (!m_ptr) return FALSE; if (has_flag(m_ptr, FLAG_NO_PUSHBACK)) return FALSE; c_ptr = &cave[m_ptr->fy][m_ptr->fx]; m_ptr->csleep = 0; /* Extract monster name (or "it") */ monster_desc(m_name, m_ptr, 0); /* Auto-Recall if possible and visible */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); /* Track a new monster */ if (m_ptr->ml) health_track(c_ptr->m_idx); /* displace? */ if (cave_floor_bold(p_ptr->py, p_ptr->px) || monst_can_pass_square(m_ptr, p_ptr->py, p_ptr->px, NULL)) { msg_format("You push past %s.", m_name); m_ptr->fy = p_ptr->py; m_ptr->fx = p_ptr->px; cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx; c_ptr->m_idx = 0; update_mon(cave[p_ptr->py][p_ptr->px].m_idx, TRUE); return TRUE; } else { msg_format("%^s is in your way!", m_name); energy_use = 0; return FALSE; } }
/** * Update the current "run" path * * Return TRUE if the running should be stopped */ static bool run_test(void) { int py = p_ptr->py; int px = p_ptr->px; int prev_dir; int new_dir; int check_dir = 0; int left_dir; int right_dir; int row, col; int i, max, inv; int option, option2; /* No options yet */ option = 0; option2 = 0; /* Where we came from */ prev_dir = p_ptr->run_old_dir; /* Range of newly adjacent grids */ max = (prev_dir & 0x01) + 1; /* Simplistic running for outdoors -NRM- */ if ((stage_map[p_ptr->stage][STAGE_TYPE] != CAVE) && (stage_map[p_ptr->stage][STAGE_TYPE] != TOWN)) { /* Look at every newly adjacent square. */ for (i = -max; i <= max; i++) { s16b this_o_idx, next_o_idx = 0; /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Visible monsters abort running */ if (cave_m_idx[row][col] > 0) { monster_type *m_ptr = &m_list[cave_m_idx[row][col]]; /* Visible monster */ if (m_ptr->ml) return (TRUE); } /* Visible objects abort running */ for (this_o_idx = cave_o_idx[row][col]; 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; /* Visible object */ if (o_ptr->marked && !squelch_hide_item(o_ptr)) return (TRUE); } } /* Assume main direction */ new_dir = p_ptr->run_old_dir; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Step if there's a path in the right direction */ if ((cave_feat[row][col] == FEAT_FLOOR) || (cave_feat[row][col] == FEAT_INVIS)) { p_ptr->run_cur_dir = new_dir; return (FALSE); } /* Check to the left */ left_dir = cycle[chome[prev_dir] - 1]; row = py + ddy[left_dir]; col = px + ddx[left_dir]; if ((cave_feat[row][col] == FEAT_FLOOR) || (cave_feat[row][col] == FEAT_INVIS)) option = left_dir; /* Check to the right */ right_dir = cycle[chome[prev_dir] + 1]; row = py + ddy[right_dir]; col = px + ddx[right_dir]; if ((cave_feat[row][col] == FEAT_FLOOR) || (cave_feat[row][col] == FEAT_INVIS)) option2 = right_dir; /* Stop if it's a fork */ if (option && option2) return (TRUE); /* Otherwise step in the secondary direction */ if (option) { p_ptr->run_cur_dir = left_dir; return (FALSE); } else if (option2) { p_ptr->run_cur_dir = right_dir; return (FALSE); } /* No paths, so try grass */ row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Step if there's grass in the right direction */ if ((cave_feat[row][col] == FEAT_GRASS) || (cave_feat[row][col] == FEAT_GRASS_INVIS)) { p_ptr->run_cur_dir = new_dir; return (FALSE); } /* Check to the left */ row = py + ddy[left_dir]; col = px + ddx[left_dir]; if ((cave_feat[row][col] == FEAT_GRASS) || (cave_feat[row][col] == FEAT_GRASS_INVIS)) option = left_dir; /* Check to the right */ right_dir = cycle[chome[prev_dir] + 1]; row = py + ddy[right_dir]; col = px + ddx[right_dir]; if ((cave_feat[row][col] == FEAT_GRASS) || (cave_feat[row][col] == FEAT_GRASS_INVIS)) option2 = right_dir; /* Stop if it's a fork */ if (option && option2) return (TRUE); /* Otherwise step in the secondary direction */ if (option) { p_ptr->run_cur_dir = left_dir; return (FALSE); } else if (option2) { p_ptr->run_cur_dir = right_dir; return (FALSE); } } /* Look at every newly adjacent square. */ for (i = -max; i <= max; i++) { s16b this_o_idx, next_o_idx = 0; /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Visible monsters abort running */ if (cave_m_idx[row][col] > 0) { monster_type *m_ptr = &m_list[cave_m_idx[row][col]]; /* Visible monster */ if (m_ptr->ml) return (TRUE); } /* Visible objects abort running */ for (this_o_idx = cave_o_idx[row][col]; 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; /* Visible object */ if (o_ptr->marked) return (TRUE); } /* Assume unknown */ inv = TRUE; /* Check memorized grids */ if (cave_info[row][col] & (CAVE_MARK)) { bool notice = TRUE; /* Examine the terrain */ switch (cave_feat[row][col]) { /* Floors */ case FEAT_FLOOR: /* Invis traps */ case FEAT_INVIS: case FEAT_GRASS_INVIS: /* Secret doors */ case FEAT_SECRET: /* Normal veins */ case FEAT_MAGMA: case FEAT_QUARTZ: /* Hidden treasure */ case FEAT_MAGMA_H: case FEAT_QUARTZ_H: /* Special passable terrain. */ case FEAT_LAVA: case FEAT_WATER: case FEAT_TREE: case FEAT_TREE2: case FEAT_GRASS: { /* Ignore */ notice = FALSE; /* Done */ break; } /* Walls */ case FEAT_WALL_EXTRA: case FEAT_WALL_INNER: case FEAT_WALL_OUTER: case FEAT_WALL_SOLID: case FEAT_PERM_EXTRA: case FEAT_PERM_INNER: case FEAT_PERM_OUTER: case FEAT_PERM_SOLID: { /* Ignore */ notice = FALSE; /* Done */ break; } /* Open doors */ case FEAT_OPEN: case FEAT_BROKEN: { /* Option -- ignore */ if (OPT(run_ignore_doors)) notice = FALSE; /* Done */ break; } /* Stairs */ case FEAT_LESS: case FEAT_MORE: { /* Option -- ignore */ if (OPT(run_ignore_stairs)) notice = FALSE; /* Done */ break; } } /* Interesting feature */ if (notice) return (TRUE); /* The grid is "visible" */ inv = FALSE; } /* Analyze unknown grids and floors */ if (inv || cave_floor_bold(row, col)) { /* Looking for open area */ if (p_ptr->run_open_area) { /* Nothing */ } /* The first new direction. */ else if (!option) { option = new_dir; } /* Three new directions. Stop running. */ else if (option2) { return (TRUE); } /* Two non-adjacent new directions. Stop running. */ else if (option != cycle[chome[prev_dir] + i - 1]) { return (TRUE); } /* Two new (adjacent) directions (case 1) */ else if (new_dir & 0x01) { check_dir = cycle[chome[prev_dir] + i - 2]; option2 = new_dir; } /* Two new (adjacent) directions (case 2) */ else { check_dir = cycle[chome[prev_dir] + i + 1]; option2 = option; option = new_dir; } } /* Obstacle, while looking for open area */ else { if (p_ptr->run_open_area) { if (i < 0) { /* Break to the right */ p_ptr->run_break_right = TRUE; } else if (i > 0) { /* Break to the left */ p_ptr->run_break_left = TRUE; } } } } /* Looking for open area */ if (p_ptr->run_open_area) { /* Hack -- look again */ for (i = -max; i < 0; i++) { new_dir = cycle[chome[prev_dir] + i]; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if (!(cave_info[row][col] & (CAVE_MARK)) || (cave_feat[row][col] < FEAT_SECRET) || (cave_feat[row][col] > FEAT_SHOP_HEAD)) { /* Looking to break right */ if (p_ptr->run_break_right) { return (TRUE); } } /* Obstacle */ else { /* Looking to break left */ if (p_ptr->run_break_left) { return (TRUE); } } } /* Hack -- look again */ for (i = max; i > 0; i--) { new_dir = cycle[chome[prev_dir] + i]; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if (!(cave_info[row][col] & (CAVE_MARK)) || (cave_feat[row][col] < FEAT_SECRET) || (cave_feat[row][col] > FEAT_SHOP_HEAD)) { /* Looking to break left */ if (p_ptr->run_break_left) { return (TRUE); } } /* Obstacle */ else { /* Looking to break right */ if (p_ptr->run_break_right) { return (TRUE); } } } } /* Not looking for open area */ else { /* No options */ if (!option) { return (TRUE); } /* One option */ else if (!option2) { /* Primary option */ p_ptr->run_cur_dir = option; /* No other options */ p_ptr->run_old_dir = option; } /* Two options, examining corners */ else if (OPT(run_use_corners) && !OPT(run_cut_corners)) { /* Primary option */ p_ptr->run_cur_dir = option; /* Hack -- allow curving */ p_ptr->run_old_dir = option2; } /* Two options, pick one */ else { /* Get next location */ row = py + ddy[option]; col = px + ddx[option]; /* Don't see that it is closed off. */ /* This could be a potential corner or an intersection. */ if (!see_wall(option, row, col) || !see_wall(check_dir, row, col)) { /* Can not see anything ahead and in the direction we */ /* are turning, assume that it is a potential corner. */ if (OPT(run_use_corners) && see_nothing(option, row, col) && see_nothing(option2, row, col)) { p_ptr->run_cur_dir = option; p_ptr->run_old_dir = option2; } /* STOP: we are next to an intersection or a room */ else { return (TRUE); } } /* This corner is seen to be enclosed; we cut the corner. */ else if (OPT(run_cut_corners)) { p_ptr->run_cur_dir = option2; p_ptr->run_old_dir = option2; } /* This corner is seen to be enclosed, and we */ /* deliberately go the long way. */ else { p_ptr->run_cur_dir = option; p_ptr->run_old_dir = option2; } } } /* About to hit a known wall, stop */ if (see_wall(p_ptr->run_cur_dir, py, px)) { return (TRUE); } /* Failure */ return (FALSE); }
/* * Update the current "run" path * * Return TRUE if the running should be stopped */ static bool run_test(void) { int fy = m_ptr->fy; int fx = m_ptr->fx; int prev_dir; int new_dir; int check_dir = 0; int row, col; int i, max; int option, option2; int feat; bool notice; /* No options yet */ option = 0; option2 = 0; /* Where we came from */ prev_dir = m_ptr->run_old_dir; /* Range of newly adjacent grids */ max = (prev_dir & 0x01) + 1; /* Look at every newly adjacent square. */ for (i = -max; i <= max; i++) { s16b this_o_idx, next_o_idx = 0; /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = fy + ddy[new_dir]; col = fx + ddx[new_dir]; /* Visible monsters abort running */ if (cave_m_idx[row][col] > 0) { return (TRUE); } /* Analyze unknown grids and floors */ if (cave_floor_bold(row, col)) { /* Looking for open area */ if (m_ptr->mflag & (MFLAG_RUN_OPEN_AREA)) { /* Nothing */ } /* The first new direction. */ else if (!option) { option = new_dir; } /* Three new directions. Stop running. */ else if (option2) { return (TRUE); } /* Two non-adjacent new directions. Stop running. */ else if (option != cycle[chome[prev_dir] + i - 1]) { return (TRUE); } /* Two new (adjacent) directions (case 1) */ else if (new_dir & 0x01) { check_dir = cycle[chome[prev_dir] + i - 2]; option2 = new_dir; } /* Two new (adjacent) directions (case 2) */ else { check_dir = cycle[chome[prev_dir] + i + 1]; option2 = option; option = new_dir; } } /* Obstacle, while looking for open area */ else { if (m_ptr->mflag & (MFLAG_RUN_OPEN_AREA)) { if (i < 0) { /* Break to the right */ m_ptr->mflag |= MFLAG_RUN_BREAK_RIGHT; } else if (i > 0) { /* Break to the left */ m_ptr->mflag |= MFLAG_RUN_BREAK_LEFT; } } } } /* Looking for open area */ if (m_ptr->mflag & (MFLAG_RUN_OPEN_AREA)run_open_area) { /* Hack -- look again */ for (i = -max; i < 0; i++) { new_dir = cycle[chome[prev_dir] + i]; row = fy + ddy[new_dir]; col = fx + ddx[new_dir]; /* Get feature */ feat = cave_feat[row][col]; /* Get mimiced feature */ feat = f_info[feat].mimic; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if (!(cave_info[row][col] & (CAVE_MARK)) || (!(f_info[feat].flags1 & (FF1_WALL))) ) { /* Looking to break right */ if ((m_ptr->mflag & (MFLAG_RUN_BREAK_RIGHT))) { return (TRUE); } } /* Obstacle */ else { /* Looking to break left */ if ((m_ptr->mflag & (MFLAG_RUN_BREAK_LEFT))) { return (TRUE); } } } /* Hack -- look again */ for (i = max; i > 0; i--) { new_dir = cycle[chome[prev_dir] + i]; row = fy + ddy[new_dir]; col = fx + ddx[new_dir]; /* Get feature */ feat = cave_feat[row][col]; /* Get mimiced feature */ feat = f_info[feat].mimic; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if (!(cave_info[row][col] & (CAVE_MARK)) || (!(f_info[feat].flags1 & (FF1_WALL)))) { /* Looking to break left */ if ((m_ptr->mflag & (MFLAG_RUN_BREAK_LEFT))) { return (TRUE); } } /* Obstacle */ else { /* Looking to break right */ if ((m_ptr->mflag & (MFLAG_RUN_BREAK_RIGHT))) { return (TRUE); } } } } /* Not looking for open area */ else { /* No options */ if (!option) { return (TRUE); } /* One option */ else if (!option2) { /* Primary option */ m_ptr->run_cur_dir = option; /* No other options */ m_ptr->run_old_dir = option; } /* Two options, examining corners */ else if (run_use_corners && !run_cut_corners) { /* Primary option */ m_ptr->run_cur_dir = option; /* Hack -- allow curving */ m_ptr->run_old_dir = option2; } /* Two options, pick one */ else { /* Get next location */ row = fy + ddy[option]; col = fx + ddx[option]; /* Don't see that it is closed off. */ /* This could be a potential corner or an intersection. */ if (!see_wall(option, row, col) || !see_wall(check_dir, row, col)) { /* Can not see anything ahead and in the direction we */ /* are turning, assume that it is a potential corner. */ if (run_use_corners && see_nothing(option, row, col) && see_nothing(option2, row, col)) { m_ptr->run_cur_dir = option; m_ptr->run_old_dir = option2; } /* STOP: we are next to an intersection or a room */ else { return (TRUE); } } /* This corner is seen to be enclosed; we cut the corner. */ else if (run_cut_corners) { m_ptr->run_cur_dir = option2; m_ptr->run_old_dir = option2; } /* This corner is seen to be enclosed, and we */ /* deliberately go the long way. */ else { m_ptr->run_cur_dir = option; m_ptr->run_old_dir = option2; } } } /* About to hit a known wall, stop */ if (see_wall(m_ptr->run_cur_dir, fy, fx)) { return (TRUE); } /* Failure */ return (FALSE); }
/* * Query the dungeon */ static void do_cmd_wiz_query(void) { int py = p_ptr->py; int px = p_ptr->px; int y, x; struct keypress cmd; u16b mask = 0x00; /* Get a "debug command" */ if (!get_com("Debug Command Query: ", &cmd)) return; /* Extract a flag */ switch (cmd.code) { case '0': mask = (1 << 0); break; case '1': mask = (1 << 1); break; case '2': mask = (1 << 2); break; case '3': mask = (1 << 3); break; case '4': mask = (1 << 4); break; case '5': mask = (1 << 5); break; case '6': mask = (1 << 6); break; case '7': mask = (1 << 7); break; case 'm': mask |= (CAVE_MARK); break; case 'g': mask |= (CAVE_GLOW); break; case 'r': mask |= (CAVE_ROOM); break; case 'i': mask |= (CAVE_ICKY); break; case 's': mask |= (CAVE_SEEN); break; case 'v': mask |= (CAVE_VIEW); break; case 't': mask |= (CAVE_TEMP); break; case 'w': mask |= (CAVE_WALL); break; } /* Scan map */ for (y = Term->offset_y; y < Term->offset_y + SCREEN_HGT; y++) { for (x = Term->offset_x; x < Term->offset_x + SCREEN_WID; x++) { byte a = TERM_RED; if (!in_bounds_fully(y, x)) continue; /* Given mask, show only those grids */ if (mask && !(cave->info[y][x] & mask)) continue; /* Given no mask, show unknown grids */ if (!mask && (cave->info[y][x] & (CAVE_MARK))) continue; /* Color */ if (cave_floor_bold(y, x)) a = TERM_YELLOW; /* Display player/floors/walls */ if ((y == py) && (x == px)) print_rel(L'@', a, y, x); else if (cave_floor_bold(y, x)) print_rel(L'*', a, y, x); else print_rel(L'#', a, y, x); } } /* Get keypress */ msg("Press any key."); message_flush(); /* Redraw map */ prt_map(); }
/** * Draw a visible path over the squares between (x1,y1) and (x2,y2). * * The path consists of "*", which are white except where there is a * monster, object or feature in the grid. * * This routine has (at least) three weaknesses: * - remembered objects/walls which are no longer present are not shown, * - squares which (e.g.) the player has walked through in the dark are * treated as unknown space. * - walls which appear strange due to hallucination aren't treated correctly. * * The first two result from information being lost from the dungeon arrays, * which requires changes elsewhere */ static int draw_path(u16b path_n, u16b *path_g, char *c, byte *a, int y1, int x1) { int i; bool on_screen; /* No path, so do nothing. */ if (path_n < 1) return 0; /* The starting square is never drawn, but notice if it is being * displayed. In theory, it could be the last such square. */ on_screen = panel_contains(y1, x1); /* Draw the path. */ for (i = 0; i < path_n; i++) { byte colour; /* Find the co-ordinates on the level. */ int y = GRID_Y(path_g[i]); int x = GRID_X(path_g[i]); /* * As path[] is a straight line and the screen is oblong, * there is only section of path[] on-screen. * If the square being drawn is visible, this is part of it. * If none of it has been drawn, continue until some of it * is found or the last square is reached. * If some of it has been drawn, finish now as there are no * more visible squares to draw. */ if (panel_contains(y,x)) on_screen = TRUE; else if (on_screen) break; else continue; /* Find the position on-screen */ move_cursor_relative(y,x); /* This square is being overwritten, so save the original. */ Term_what(Term->scr->cx, Term->scr->cy, a+i, c+i); /* Choose a colour. */ if (cave->m_idx[y][x] && cave_monster(cave, cave->m_idx[y][x])->ml) { /* Visible monsters are red. */ monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; /*mimics act as objects*/ if (rf_has(r_ptr->flags, RF_UNAWARE)) colour = TERM_YELLOW; else colour = TERM_L_RED; } else if (cave->o_idx[y][x] && object_byid(cave->o_idx[y][x])->marked) /* Known objects are yellow. */ colour = TERM_YELLOW; else if (!cave_floor_bold(y,x) && ((cave->info[y][x] & (CAVE_MARK)) || player_can_see_bold(y,x))) /* Known walls are blue. */ colour = TERM_BLUE; else if (!(cave->info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) /* Unknown squares are grey. */ colour = TERM_L_DARK; else /* Unoccupied squares are white. */ colour = TERM_WHITE; /* Draw the path segment */ (void)Term_addch(colour, '*'); } return i; }
/** * Debug scent trails and noise bursts. */ static void do_cmd_wiz_hack_ben(void) { #ifdef MONSTER_FLOW char cmd; int py = p_ptr->py; int px = p_ptr->px; int i, y, x, y2, x2; /* Get a "debug command" */ if (!get_com("Press 'S' for scent, 'N' for noise info: ", &cmd)) return; /* Analyze the command */ switch (cmd) { case 'S': case 's': { /* Update map */ for (y = Term->offset_y; y <= Term->offset_y + SCREEN_HGT; y++) { for (x = Term->offset_x; x <= Term->offset_x + SCREEN_WID; x++) { byte a; int age = get_scent(y, x); /* Must have scent */ if (age == -1) continue; /* Pretty colors by age */ if (age > SMELL_STRENGTH) a = TERM_L_DARK; else if (age < 10) a = TERM_BLUE; else if (age < 20) a = TERM_L_BLUE; else if (age < 30) a = TERM_GREEN; else if (age < 40) a = TERM_L_GREEN; else if (age < 50) a = TERM_YELLOW; else if (age < 60) a = TERM_ORANGE; else if (age < 70) a = TERM_L_RED; else a = TERM_RED; /* Display player/floors/walls */ if ((y == py) && (x == px)) { print_rel('@', a, y, x); } else { print_rel('0' + (age % 10), a, y, x); } } } /* Prompt */ prt("Scent ages", 0, 0); /* Wait for a keypress */ (void) inkey(); /* Redraw map */ prt_map(); break; } case 'N': case 'n': { /* Get a "debug command" */ if (!get_com ("Press 'D' for direction of flow, 'C' for actual cost values: ", &cmd)) return; if ((cmd == 'D') || (cmd == 'd')) { /* Update map */ for (y = Term->offset_y; y <= Term->offset_y + SCREEN_HGT; y++) { for (x = Term->offset_x; x <= Term->offset_x + SCREEN_WID; x++) { int lowest_cost = cave_cost[y][x]; int dir = -1; int cost; if (lowest_cost == 0) continue; for (i = 0; i < 8; i++) { /* Get the location */ y2 = y + ddy_ddd[i]; x2 = x + ddx_ddd[i]; cost = cave_cost[y2][x2]; if (!cost) continue; /* If this grid's scent is younger, save it */ if (lowest_cost > cost) lowest_cost = cost; /* If it isn't, look elsewhere */ else continue; /* Save this direction */ dir = i; } /* If we didn't find any younger scent, print a '5' */ if (dir == -1) print_rel('5', TERM_YELLOW, y, x); /* Otherwise, convert to true direction and print */ else { i = ddd[dir]; print_rel('0' + i, TERM_L_BLUE, y, x); } } } /* Prompt */ prt("Directions given to advancing monsters using noise info", 0, 0); /* Wait for a keypress */ (void) inkey(); /* Redraw map */ prt_map(); } /* Actual cost values */ else { int j; for (i = cost_at_center - 2; i <= 100 + NOISE_STRENGTH; ++i) { /* First show grids with no scent */ if (i == cost_at_center - 2) j = 0; /* Then show specially marked grids (bug-checking) */ else if (i == cost_at_center - 1) j = 255; /* Then show standard grids */ else j = i; /* Update map */ for (y = Term->offset_y; y <= Term->offset_y + SCREEN_HGT; y++) { for (x = Term->offset_x; x <= Term->offset_x + SCREEN_WID; x++) { byte a = TERM_YELLOW; /* Display proper cost */ if (cave_cost[y][x] != j) continue; /* Display player/floors/walls */ if ((y == py) && (x == px)) { print_rel('@', a, y, x); } else if (cave_floor_bold(y, x)) { print_rel('*', a, y, x); } else { print_rel('#', a, y, x); } } } /* Prompt */ if (j == 0) { prt("Grids with no scent", 0, 0); } else if (j == 255) { prt("Specially marked grids", 0, 0); } else { prt(format("Depth %d: ", j), 0, 0); } /* Get key */ if (inkey() == ESCAPE) break; /* Redraw map */ prt_map(); } } break; } default: { break; } } /* Done */ prt("", 0, 0); /* Redraw map */ prt_map(); #else /* MONSTER_FLOW */ /* Oops */ msg_print("Monster flow is not included in this copy of the game."); #endif /* MONSTER_FLOW */ }
/* * 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 = p_ptr->py; int px = p_ptr->px; int y = py + ddy[dir]; int x = px + ddx[dir]; int m_idx = cave->m_idx[y][x]; /* Attack monsters */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(m_idx)) { become_aware(m_idx); /* Mimic wakes up */ mon_clear_timed(m_idx, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE); } else { py_attack(y, x); } } /* Optionally alter traps/doors on movement */ else if (disarm && (cave->info[y][x] & CAVE_MARK) && (cave_isknowntrap(cave, y, x) || cave_iscloseddoor(cave, y, x))) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } /* Cannot walk through walls */ else if (!cave_floor_bold(y, x)) { /* Disturb the player */ disturb(p_ptr, 0, 0); /* Notice unknown obstacles */ if (!(cave->info[y][x] & CAVE_MARK)) { /* Rubble */ if (cave->feat[y][x] == FEAT_RUBBLE) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); cave->info[y][x] |= (CAVE_MARK); cave_light_spot(cave, y, x); } /* Closed door */ else if (cave->feat[y][x] < FEAT_SECRET) { msgt(MSG_HITWALL, "You feel a door blocking your way."); cave->info[y][x] |= (CAVE_MARK); cave_light_spot(cave, y, x); } /* Wall (or secret door) */ else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); cave->info[y][x] |= (CAVE_MARK); cave_light_spot(cave, y, x); } } /* Mention known obstacles */ else { if (cave->feat[y][x] == FEAT_RUBBLE) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (cave->feat[y][x] < FEAT_SECRET) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } /* Normal movement */ else { /* See if trap detection status will change */ bool old_dtrap = ((cave->info2[py][px] & (CAVE2_DTRAP)) != 0); bool new_dtrap = ((cave->info2[y][x] & (CAVE2_DTRAP)) != 0); /* Note the change in the detect status */ if (old_dtrap != new_dtrap) p_ptr->redraw |= (PR_DTRAP); /* Disturb player if the player is about to leave the area */ if (OPT(disturb_detect) && p_ptr->running && !p_ptr->running_firststep && old_dtrap && !new_dtrap) { disturb(p_ptr, 0, 0); return; } /* Move player */ monster_swap(py, px, y, x); /* New location */ y = py = p_ptr->py; x = px = p_ptr->px; /* Searching */ if (p_ptr->searching || (p_ptr->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) || one_in_(50 - p_ptr->state.skills[SKILL_SEARCH_FREQUENCY])) search(FALSE); /* Handle "store doors" */ if ((cave->feat[p_ptr->py][p_ptr->px] >= FEAT_SHOP_HEAD) && (cave->feat[p_ptr->py][p_ptr->px] <= FEAT_SHOP_TAIL)) { /* Disturb */ disturb(p_ptr, 0, 0); cmd_insert(CMD_ENTER_STORE); } /* All other grids (including traps) */ else { /* Handle objects (later) */ p_ptr->notice |= (PN_PICKUP); } /* Discover invisible traps */ if (cave->feat[y][x] == FEAT_INVIS) { /* Disturb */ disturb(p_ptr, 0, 0); /* Message */ msg("You found a trap!"); /* Pick a trap */ pick_trap(y, x); /* Hit the trap */ hit_trap(y, x); } /* Set off an visible trap */ else if (cave_isknowntrap(cave, y, x)) { /* Disturb */ disturb(p_ptr, 0, 0); /* Hit the trap */ hit_trap(y, x); } } p_ptr->running_firststep = FALSE; }
/* * Take one step along the current "run" path * * Called with a real direction to begin a new run, and with zero * to continue a run in progress. */ void run_step(int dir) { int x, y; /* Start run */ if (dir) { /* Initialize */ run_init(dir); /* Hack -- Set the run counter */ p_ptr->running = (p_ptr->command_arg ? p_ptr->command_arg : 1000); /* Calculate torch radius */ p_ptr->update |= (PU_TORCH); } /* Continue run */ else { if (!p_ptr->running_withpathfind) { /* Update run */ if (run_test()) { /* Disturb */ disturb(0, 0); /* Done */ return; } } else { /* Abort if we have finished */ if (pf_result_index < 0) { disturb(0, 0); p_ptr->running_withpathfind = FALSE; return; } /* Abort if we would hit a wall */ else if (pf_result_index == 0) { /* Get next step */ y = p_ptr->py + ddy[pf_result[pf_result_index] - '0']; x = p_ptr->px + ddx[pf_result[pf_result_index] - '0']; /* Known wall */ if ((cave_info[y][x] & (CAVE_MARK)) && !cave_floor_bold(y, x)) { disturb(0,0); p_ptr->running_withpathfind = FALSE; return; } } /* * Hack -- walking stick lookahead. * * If the player has computed a path that is going to end up in a wall, * we notice this and convert to a normal run. This allows us to click * on unknown areas to explore the map. * * We have to look ahead two, otherwise we don't know which is the last * direction moved and don't initialise the run properly. */ else if (pf_result_index > 0) { /* Get next step */ y = p_ptr->py + ddy[pf_result[pf_result_index] - '0']; x = p_ptr->px + ddx[pf_result[pf_result_index] - '0']; /* Known wall */ if ((cave_info[y][x] & (CAVE_MARK)) && !cave_floor_bold(y, x)) { disturb(0,0); p_ptr->running_withpathfind = FALSE; return; } /* Get step after */ y = y + ddy[pf_result[pf_result_index-1] - '0']; x = x + ddx[pf_result[pf_result_index-1] - '0']; /* Known wall */ if ((cave_info[y][x] & (CAVE_MARK)) && !cave_floor_bold(y, x)) { p_ptr->running_withpathfind = FALSE; run_init(pf_result[pf_result_index] - '0'); } } p_ptr->run_cur_dir = pf_result[pf_result_index--] - '0'; } } /* Decrease counter */ p_ptr->running--; /* Take time */ p_ptr->energy_use = 100; /* Move the player */ move_player(p_ptr->run_cur_dir); }
/* * Fire an object from the pack or floor. * * You may only fire items that "match" your missile launcher. * * 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. */ void do_cmd_fire(cmd_code code, cmd_arg args[]) { int dir, item; int i, j, y, x; s16b ty, tx; int tdam, tdis, thits; int bonus, chance; object_type *o_ptr; object_type *j_ptr; object_type *i_ptr; object_type object_type_body; bool hit_body = FALSE; byte missile_attr; char missile_char; char o_name[80]; u32b msg_type = 0; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor * op_ptr->delay_factor; /* Get the "bow" */ j_ptr = &p_ptr->inventory[INVEN_BOW]; /* Require a usable launcher */ if (!j_ptr->tval || !p_ptr->state.ammo_tval) { msg_print("You have nothing to fire with."); return; } /* Get item to fire and direction to fire in. */ item = args[0].item; dir = args[1].direction; /* Check the item being fired is usable by the player. */ if (!item_is_available(item, NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR))) { msg_format("That item is not within your reach."); return; } /* Get the object for the ammo */ o_ptr = object_from_item_idx(item); /* Check the ammo can be used with the launcher */ if (o_ptr->tval != p_ptr->state.ammo_tval) { msg_format("That ammo cannot be fired by your current weapon."); return; } /* Base range XXX XXX */ tdis = 6 + 2 * p_ptr->state.ammo_mult; /* Start at the player */ x = p_ptr->px; y = p_ptr->py; /* Predict the "target" location */ ty = y + 99 * ddy[dir]; tx = x + 99 * ddx[dir]; /* Check for target validity */ if ((dir == 5) && target_okay()) { target_get(&tx, &ty); if (distance(y, x, ty, tx) > tdis) { if (!get_check("Target out of range. Fire anyway? ")) return; } } /* Sound */ sound(MSG_SHOOT); object_notice_on_firing(o_ptr); /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR); /* Find the color and symbol for the object for throwing */ missile_attr = object_attr(o_ptr); missile_char = object_char(o_ptr); /* Use the proper number of shots */ thits = p_ptr->state.num_fire; /* Actually "fire" the object */ bonus = (p_ptr->state.to_h + o_ptr->to_h + j_ptr->to_h); chance = p_ptr->state.skills[SKILL_TO_HIT_BOW] + (bonus * BTH_PLUS_ADJ); /* Take a (partial) turn */ p_ptr->energy_use = (100 / thits); /* Calculate the path */ path_n = project_path(path_g, tdis, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(); /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { /* Visual effects */ print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); Term_xtra(TERM_XTRA_DELAY, msec); light_spot(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); } /* Delay anyway for consistency */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave_m_idx[y][x] > 0) { monster_type *m_ptr = &mon_list[cave_m_idx[y][x]]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x); int visible = m_ptr->ml; int multiplier = 1; const char *hit_verb = "hits"; const slay_t *best_s_ptr = NULL; /* Note the collision */ hit_body = TRUE; /* Did we hit it (penalize distance travelled) */ if (test_hit(chance2, r_ptr->ac, m_ptr->ml)) { bool fear = FALSE; /* Assume a default death */ cptr note_dies = " dies."; improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr); improve_attack_modifier(j_ptr, m_ptr, &best_s_ptr); if (best_s_ptr != NULL) hit_verb = best_s_ptr->range_verb; /* Some monsters get "destroyed" */ if (monster_is_unusual(r_ptr)) { /* Special note at death */ note_dies = " is destroyed."; } /* Calculate multiplier */ multiplier = p_ptr->state.ammo_mult; if (best_s_ptr != NULL) multiplier += best_s_ptr->mult; /* Apply damage: multiplier, slays, criticals, bonuses */ tdam = damroll(o_ptr->dd, o_ptr->ds); tdam += o_ptr->to_d + j_ptr->to_d; tdam *= multiplier; tdam = critical_shot(o_ptr->weight, o_ptr->to_h, tdam, &msg_type); object_notice_attack_plusses(o_ptr); object_notice_attack_plusses(&p_ptr->inventory[INVEN_BOW]); /* No negative damage; change verb if no damage done */ if (tdam <= 0) { tdam = 0; hit_verb = "fail to harm"; } /* Handle unseen monster */ if (!visible) { /* Invisible monster */ message_format(MSG_SHOOT_HIT, 0, "The %s finds a mark.", o_name); } /* Handle visible monster */ else { char m_name[80]; /* Get "the monster" or "it" */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Tell the player what happened */ if (msg_type == MSG_SHOOT_HIT) message_format(MSG_SHOOT_HIT, 0, "The %s %s %s.", o_name, hit_verb, m_name); else { if (msg_type == MSG_HIT_GOOD) { message_format(MSG_HIT_GOOD, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { message_format(MSG_HIT_GREAT, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { message_format(MSG_HIT_SUPERB, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } } /* 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(cave_m_idx[y][x]); } /* Complex message */ if (p_ptr->wizard) { msg_format("You do %d (out of %d) damage.", tdam, m_ptr->hp); } /* Hit the monster, check for death */ if (mon_take_hit(cave_m_idx[y][x], tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(cave_m_idx[y][x], tdam); /* Take note */ if (fear && m_ptr->ml) { char m_name[80]; /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Message */ message_format(MSG_FLEE, m_ptr->r_idx, "%^s flees in terror!", m_name); } } } /* Stop looking */ break; } } /* Get local object */ i_ptr = &object_type_body; /* Obtain a local object */ object_copy(i_ptr, o_ptr); /* Single object */ i_ptr->number = 1; 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); } /* Chance of breakage (during attacks) */ j = (hit_body ? breakage_chance(i_ptr) : 0); /* Drop (or break) near that location */ drop_near(i_ptr, j, y, x, TRUE); }
/* * 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(cmd_code code, cmd_arg args[]) { int dir, item; int i, j, y, x; s16b ty, tx; int chance, tdam, tdis; int weight; object_type *o_ptr; object_type *i_ptr; object_type object_type_body; bool hit_body = FALSE; byte missile_attr; char missile_char; char o_name[80]; u32b msg_type = 0; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor * op_ptr->delay_factor; /* Get item to throw and direction in which to throw it. */ item = args[0].item; dir = args[1].direction; /* Make sure the player isn't throwing wielded items */ if (item >= INVEN_WIELD && item < QUIVER_START) { msg_print("You have cannot throw wielded items."); return; } /* Check the item being thrown is usable by the player. */ if (!item_is_available(item, NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR))) { msg_format("That item is not within your reach."); return; } /* Get the object */ o_ptr = object_from_item_idx(item); object_notice_on_firing(o_ptr); /* Get local object */ i_ptr = &object_type_body; /* Obtain a local object */ object_copy(i_ptr, o_ptr); /* Distribute the charges of rods/wands/staves between the stacks */ distribute_charges(o_ptr, i_ptr, 1); /* Single object */ i_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, sizeof(o_name), i_ptr, ODESC_FULL); /* Find the color and symbol for the object for throwing */ missile_attr = object_attr(i_ptr); missile_char = object_char(i_ptr); /* Enforce a minimum "weight" of one pound */ weight = ((i_ptr->weight > 10) ? i_ptr->weight : 10); /* Hack -- Distance -- Reward strength, penalize weight */ tdis = (adj_str_blow[p_ptr->state.stat_ind[A_STR]] + 20) * 10 / weight; /* Max distance of 10 */ if (tdis > 10) tdis = 10; /* Hack -- Base damage from thrown object */ tdam = damroll(i_ptr->dd, i_ptr->ds); if (!tdam) tdam = 1; tdam += i_ptr->to_d; /* Chance of hitting */ chance = (p_ptr->state.skills[SKILL_TO_HIT_THROW] + (p_ptr->state.to_h * BTH_PLUS_ADJ)); /* Take a turn */ p_ptr->energy_use = 100; /* Start at the player */ y = p_ptr->py; x = p_ptr->px; /* Predict the "target" location */ ty = p_ptr->py + 99 * ddy[dir]; tx = p_ptr->px + 99 * ddx[dir]; /* Check for "target request" */ if ((dir == 5) && target_okay()) { target_get(&tx, &ty); } /* Calculate the path */ path_n = project_path(path_g, tdis, p_ptr->py, p_ptr->px, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(); /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { /* Visual effects */ print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); Term_xtra(TERM_XTRA_DELAY, msec); light_spot(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); } /* Delay anyway for consistency */ else { /* Pause anyway, for consistancy */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave_m_idx[y][x] > 0) { monster_type *m_ptr = &mon_list[cave_m_idx[y][x]]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x); int visible = m_ptr->ml; /* Note the collision */ hit_body = TRUE; /* Did we hit it (penalize range) */ if (test_hit(chance2, r_ptr->ac, m_ptr->ml)) { const char *hit_verb = "hits"; bool fear = FALSE; const slay_t *best_s_ptr = NULL; /* Assume a default death */ cptr note_dies = " dies."; /* Some monsters get "destroyed" */ if (monster_is_unusual(r_ptr)) { /* Special note at death */ note_dies = " is destroyed."; } /* Apply special damage - brought forward to fill in hit_verb XXX XXX XXX */ improve_attack_modifier(i_ptr, m_ptr, &best_s_ptr); if (best_s_ptr != NULL) { tdam *= best_s_ptr->mult; hit_verb = best_s_ptr->range_verb; } /* Apply special damage XXX XXX XXX */ tdam = critical_shot(i_ptr->weight, i_ptr->to_h, tdam, &msg_type); /* No negative damage; change verb if no damage done */ if (tdam <= 0) { tdam = 0; hit_verb = "fail to harm"; } /* 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, sizeof(m_name), m_ptr, 0); /* Tell the player what happened */ if (msg_type == MSG_SHOOT_HIT) message_format(MSG_SHOOT_HIT, 0, "The %s %s %s.", o_name, hit_verb, m_name); else { if (msg_type == MSG_HIT_GOOD) { message_format(MSG_HIT_GOOD, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { message_format(MSG_HIT_GREAT, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { message_format(MSG_HIT_SUPERB, 0, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } } /* 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(cave_m_idx[y][x]); } /* Learn the bonuses */ /* XXX Eddie This is messed up, better done for firing, */ /* should use that method [split last] instead */ /* check if inven_optimize removed what o_ptr referenced */ if (object_similar(i_ptr, o_ptr, OSTACK_PACK)) object_notice_attack_plusses(o_ptr); object_notice_attack_plusses(i_ptr); /* Complex message */ if (p_ptr->wizard) msg_format("You do %d (out of %d) damage.", tdam, m_ptr->hp); /* Hit the monster, check for death */ if (mon_take_hit(cave_m_idx[y][x], tdam, &fear, note_dies)) { /* Dead monster */ } /* No death */ else { /* Message */ message_pain(cave_m_idx[y][x], tdam); /* Take note */ if (fear && m_ptr->ml) { char m_name[80]; /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Message */ message_format(MSG_FLEE, m_ptr->r_idx, "%^s flees in terror!", m_name); } } } /* Stop looking */ break; } } /* Chance of breakage (during attacks) */ j = (hit_body ? breakage_chance(i_ptr) : 0); /* Drop (or break) near that location */ drop_near(i_ptr, j, y, x, TRUE); }
/** * This is a helper function used by do_cmd_throw and do_cmd_fire. * * It abstracts out the projectile path, display code, identify and clean up * logic, while using the 'attack' parameter to do work particular to each * kind of attack. */ static void ranged_helper(int item, int dir, int range, int shots, ranged_attack attack) { /* Get the ammo */ object_type *o_ptr = object_from_item_idx(item); int i, j; byte missile_attr = object_attr(o_ptr); char missile_char = object_char(o_ptr); object_type object_type_body; object_type *i_ptr = &object_type_body; char o_name[80]; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor; /* Start at the player */ int x = p_ptr->px; int y = p_ptr->py; /* Predict the "target" location */ s16b ty = y + 99 * ddy[dir]; s16b tx = x + 99 * ddx[dir]; bool hit_target = FALSE; /* Check for target validity */ if ((dir == 5) && target_okay()) { int taim; char msg[80]; target_get(&tx, &ty); taim = distance(y, x, ty, tx); if (taim > range) { sprintf (msg, "Target out of range by %d squares. Fire anyway? ", taim - range); if (!get_check(msg)) return; } } /* Sound */ sound(MSG_SHOOT); object_notice_on_firing(o_ptr); /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR); /* Actually "fire" the object -- Take a partial turn */ p_ptr->energy_use = (100 / shots); /* Calculate the path */ path_n = project_path(path_g, range, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(p_ptr); /* Start at the player */ x = p_ptr->px; y = p_ptr->py; /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(p_ptr); Term_xtra(TERM_XTRA_DELAY, msec); cave_light_spot(cave, y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(p_ptr); } else { /* Delay anyway for consistency */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave->m_idx[y][x] > 0) break; } /* Try the attack on the monster at (x, y) if any */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; int visible = m_ptr->ml; bool fear = FALSE; char m_name[80]; const char *note_dies = monster_is_unusual(r_ptr) ? " is destroyed." : " dies."; struct attack_result result = attack(o_ptr, y, x); int dmg = result.dmg; u32b msg_type = result.msg_type; const char *hit_verb = result.hit_verb; if (result.success) { hit_target = TRUE; /* Get "the monster" or "it" */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); object_notice_attack_plusses(o_ptr); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; hit_verb = "fail to harm"; } if (!visible) { /* Invisible monster */ msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name); } else { /* Visible monster */ if (msg_type == MSG_SHOOT_HIT) msgt(MSG_SHOOT_HIT, "The %s %s %s.", o_name, hit_verb, m_name); else if (msg_type == MSG_HIT_GOOD) { msgt(MSG_HIT_GOOD, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { msgt(MSG_HIT_GREAT, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { msgt(MSG_HIT_SUPERB, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } /* Track this monster */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); if (m_ptr->ml) health_track(p_ptr, cave->m_idx[y][x]); } /* Complex message */ if (p_ptr->wizard) msg("You do %d (out of %d) damage.", dmg, m_ptr->hp); /* Hit the monster, check for death */ if (!mon_take_hit(cave->m_idx[y][x], dmg, &fear, note_dies)) { message_pain(cave->m_idx[y][x], dmg); if (fear && m_ptr->ml) add_monster_message(m_name, cave->m_idx[y][x], MON_MSG_FLEE_IN_TERROR, TRUE); } } } /* Obtain a local object */ object_copy(i_ptr, o_ptr); object_split(i_ptr, o_ptr, 1); /* See if the ammunition broke or not */ j = breakage_chance(i_ptr, hit_target); /* Drop (or break) near that location */ drop_near(cave, i_ptr, j, y, x, TRUE); if (item >= 0) { /* The ammo is from the inventory */ inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } else { /* The ammo is from the floor */ floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } }
/* * Update the current "run" path * * Return TRUE if the running should be stopped */ static bool run_test(void) { int py = p_ptr->py; int px = p_ptr->px; int prev_dir; int new_dir; int check_dir = 0; int row, col; int i, max, inv; int option, option2; /* No options yet */ option = 0; option2 = 0; /* Where we came from */ prev_dir = p_ptr->run_old_dir; /* Range of newly adjacent grids */ max = (prev_dir & 0x01) + 1; /* Look at every newly adjacent square. */ for (i = -max; i <= max; i++) { object_type *o_ptr; /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Visible monsters abort running */ if (cave_m_idx[row][col] > 0) { monster_type *m_ptr = &mon_list[cave_m_idx[row][col]]; /* Visible monster */ if (m_ptr->ml) return (TRUE); } /* Visible objects abort running */ for (o_ptr = get_first_object(row, col); o_ptr; o_ptr = get_next_object(o_ptr)) { /* Visible object */ if (o_ptr->marked && !squelch_hide_item(o_ptr)) return (TRUE); } /* Assume unknown */ inv = TRUE; /* Check memorized grids */ if (cave_info[row][col] & (CAVE_MARK)) { bool notice = TRUE; /* Examine the terrain */ switch (cave_feat[row][col]) { /* Floors */ case FEAT_FLOOR: /* Invis traps */ case FEAT_INVIS: /* Secret doors */ case FEAT_SECRET: /* Normal veins */ case FEAT_MAGMA: case FEAT_QUARTZ: /* Hidden treasure */ case FEAT_MAGMA_H: case FEAT_QUARTZ_H: /* Walls */ case FEAT_WALL_EXTRA: case FEAT_WALL_INNER: case FEAT_WALL_OUTER: case FEAT_WALL_SOLID: case FEAT_PERM_EXTRA: case FEAT_PERM_INNER: case FEAT_PERM_OUTER: case FEAT_PERM_SOLID: { /* Ignore */ notice = FALSE; /* Done */ break; } } /* Interesting feature */ if (notice) return (TRUE); /* The grid is "visible" */ inv = FALSE; } /* Analyze unknown grids and floors */ if (inv || cave_floor_bold(row, col)) { /* Looking for open area */ if (p_ptr->run_open_area) { /* Nothing */ } /* The first new direction. */ else if (!option) { option = new_dir; } /* Three new directions. Stop running. */ else if (option2) { return (TRUE); } /* Two non-adjacent new directions. Stop running. */ else if (option != cycle[chome[prev_dir] + i - 1]) { return (TRUE); } /* Two new (adjacent) directions (case 1) */ else if (new_dir & 0x01) { check_dir = cycle[chome[prev_dir] + i - 2]; option2 = new_dir; } /* Two new (adjacent) directions (case 2) */ else { check_dir = cycle[chome[prev_dir] + i + 1]; option2 = option; option = new_dir; } } /* Obstacle, while looking for open area */ else { if (p_ptr->run_open_area) { if (i < 0) { /* Break to the right */ p_ptr->run_break_right = TRUE; } else if (i > 0) { /* Break to the left */ p_ptr->run_break_left = TRUE; } } } } /* Look at every soon to be newly adjacent square. */ for (i = -max; i <= max; i++) { /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + ddy[prev_dir] + ddy[new_dir]; col = px + ddx[prev_dir] + ddx[new_dir]; /* HACK: Ugh. Sometimes we come up with illegal bounds. This will * treat the symptom but not the disease. */ if (row >= DUNGEON_HGT || col >= DUNGEON_WID) continue; if (row < 0 || col < 0) continue; /* Visible monsters abort running */ if (cave_m_idx[row][col] > 0) { monster_type *m_ptr = &mon_list[cave_m_idx[row][col]]; /* Visible monster */ if (m_ptr->ml) return (TRUE); } } /* Looking for open area */ if (p_ptr->run_open_area) { /* Hack -- look again */ for (i = -max; i < 0; i++) { new_dir = cycle[chome[prev_dir] + i]; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if (!(cave_info[row][col] & (CAVE_MARK)) || (cave_feat[row][col] < FEAT_SECRET)) { /* Looking to break right */ if (p_ptr->run_break_right) { return (TRUE); } } /* Obstacle */ else { /* Looking to break left */ if (p_ptr->run_break_left) { return (TRUE); } } } /* Hack -- look again */ for (i = max; i > 0; i--) { new_dir = cycle[chome[prev_dir] + i]; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if (!(cave_info[row][col] & (CAVE_MARK)) || (cave_feat[row][col] < FEAT_SECRET)) { /* Looking to break left */ if (p_ptr->run_break_left) { return (TRUE); } } /* Obstacle */ else { /* Looking to break right */ if (p_ptr->run_break_right) { return (TRUE); } } } } /* Not looking for open area */ else { /* No options */ if (!option) { return (TRUE); } /* One option */ else if (!option2) { /* Primary option */ p_ptr->run_cur_dir = option; /* No other options */ p_ptr->run_old_dir = option; } /* Two options, examining corners */ else { /* Primary option */ p_ptr->run_cur_dir = option; /* Hack -- allow curving */ p_ptr->run_old_dir = option2; } } /* About to hit a known wall, stop */ if (see_wall(p_ptr->run_cur_dir, py, px)) { return (TRUE); } /* Failure */ return (FALSE); }