bool target_set_closest(int mode) { int y, x, m_idx; monster_type *m_ptr; char m_name[80]; bool visibility; struct point_set *targets; /* Cancel old target */ target_set_monster(0); /* Get ready to do targetting */ targets = target_set_interactive_prepare(mode); /* If nothing was prepared, then return */ if (point_set_size(targets) < 1) { msg("No Available Target."); point_set_dispose(targets); return FALSE; } /* Find the first monster in the queue */ y = targets->pts[0].y; x = targets->pts[0].x; m_idx = cave->m_idx[y][x]; /* Target the monster, if possible */ if ((m_idx <= 0) || !target_able(m_idx)) { msg("No Available Target."); point_set_dispose(targets); return FALSE; } /* Target the monster */ m_ptr = cave_monster(cave, m_idx); monster_desc(m_name, sizeof(m_name), m_ptr, 0x00); if (!(mode & TARGET_QUIET)) msg("%^s is targeted.", m_name); Term_fresh(); /* Set up target information */ monster_race_track(m_ptr->r_idx); health_track(p_ptr, cave->m_idx[y][x]); target_set_monster(m_idx); /* Visual cue */ Term_get_cursor(&visibility); (void)Term_set_cursor(TRUE); move_cursor_relative(y, x); Term_redraw_section(x, y, x, y); /* TODO: what's an appropriate amount of time to spend highlighting */ Term_xtra(TERM_XTRA_DELAY, 150); (void)Term_set_cursor(visibility); point_set_dispose(targets); return TRUE; }
/** * Redraw the screen * * This command performs various low level updates, clears all the "extra" * windows, does a total redraw of the main window, and requests all of the * interesting updates and redraws that I can think of. * * This command is also used to "instantiate" the results of the user * selecting various things, such as graphics mode, so it must call * the "TERM_XTRA_REACT" hook before redrawing the windows. * */ void do_cmd_redraw(void) { int j; term *old = Term; /* Low level flush */ Term_flush(); /* Reset "inkey()" */ event_signal(EVENT_INPUT_FLUSH); if (character_dungeon) verify_panel(); /* Hack -- React to changes */ Term_xtra(TERM_XTRA_REACT, 0); if (character_dungeon) { /* Combine the pack (later) */ player->upkeep->notice |= (PN_COMBINE); /* Update torch, gear */ player->upkeep->update |= (PU_TORCH | PU_INVEN); /* Update stuff */ player->upkeep->update |= (PU_BONUS | PU_HP | PU_SPELLS); /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); /* Redraw everything */ player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_INVEN | PR_EQUIP | PR_MESSAGE | PR_MONSTER | PR_OBJECT | PR_MONLIST | PR_ITEMLIST); } /* Clear screen */ Term_clear(); if (character_dungeon) { /* Hack -- update */ handle_stuff(player); /* Place the cursor on the player */ if (0 != character_dungeon) move_cursor_relative(player->px, player->py); } /* Redraw every window */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { if (!angband_term[j]) continue; Term_activate(angband_term[j]); Term_redraw(); Term_fresh(); Term_activate(old); } }
/** * Load the attr/char at each point along "path" which is on screen from * "a" and "c". This was saved in draw_path(). */ static void load_path(u16b path_n, u16b *path_g, wchar_t *c, int *a) { int i; for (i = 0; i < path_n; i++) { int y = GRID_Y(path_g[i]); int x = GRID_X(path_g[i]); if (!panel_contains(y, x)) continue; move_cursor_relative(y, x); Term_addch(a[i], c[i]); } Term_fresh(); }
/* * Load the attr/char at each point along "path" which is on screen from * "a" and "c". This was saved in draw_path(). */ static void load_path(u16b path_n, u16b *path_g, char *c, byte *a) { int i; for (i = 0; i < path_n; i++) { if (!panel_contains(GRID_Y(path_g[i]), GRID_X(path_g[i]))) continue; move_cursor_relative(GRID_Y(path_g[i]), GRID_X(path_g[i])); (void)Term_addch(a[i], c[i]); } (void)Term_fresh(); }
/** * Display the object name of the selected object and allow for full object * recall. Returns an event that occurred display. * * This will only work for a single object on the ground and not a pile. This * loop is similar to the monster recall loop in target_set_interactive_aux(). * The out_val array size needs to match the size that is passed in (since * this code was extracted from there). * * \param o_ptr is the object to describe. * \param y is the cave row of the object. * \param x is the cave column of the object. * \param out_val is the string that holds the name of the object and is * returned to the caller. * \param s1 is part of the output string. * \param s2 is part of the output string. * \param s3 is part of the output string. * \param coords is part of the output string */ static ui_event target_recall_loop_object(object_type *o_ptr, int y, int x, char out_val[TARGET_OUT_VAL_SIZE], const char *s1, const char *s2, const char *s3, char *coords) { bool recall = FALSE; ui_event press; while (1) { if (recall) { display_object_recall_interactive(o_ptr); press = inkey_m(); } else { char o_name[80]; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (player->wizard) { strnfmt(out_val, TARGET_OUT_VAL_SIZE, "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, o_name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } else { strnfmt(out_val, TARGET_OUT_VAL_SIZE, "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); } if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) recall = !recall; else if ((press.type == EVT_KBRD) && (press.key.code == 'r')) recall = !recall; else break; } return press; }
/** * Target closest monster. * * XXX: Move to using CMD_TARGET_CLOSEST at some point instead of invoking * target_set_closest() directly. */ void textui_target_closest(void) { if (target_set_closest(TARGET_KILL)) { bool visibility; int x, y; target_get(&x, &y); /* Visual cue */ Term_fresh(); Term_get_cursor(&visibility); (void)Term_set_cursor(TRUE); move_cursor_relative(y, x); Term_redraw_section(x, y, x, y); /* TODO: what's an appropriate amount of time to spend highlighting */ Term_xtra(TERM_XTRA_DELAY, 150); (void)Term_set_cursor(visibility); } }
void pre_turn_refresh(void) { term *old = Term; int j; if (character_dungeon) { /* Redraw map */ player->upkeep->redraw |= (PR_MAP | PR_STATE); player->upkeep->redraw |= (PR_MONLIST | PR_ITEMLIST); handle_stuff(player); move_cursor_relative(player->px, player->py); for (j = 0; j < ANGBAND_TERM_MAX; j++) { if (!angband_term[j]) continue; Term_activate(angband_term[j]); Term_fresh(); } } Term_activate(old); }
/** * 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, wchar_t *c, int *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_at(cave, y, x)->ml) { /* Visible monsters are red. */ monster_type *m_ptr = cave_monster_at(cave, y, x); /* Mimics act as objects */ if (rf_has(m_ptr->race->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_ispassable(cave, 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, L'*'); } return i; }
/* * 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); }
/* * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * This function correctly handles multiple objects per grid, and objects * and terrain features in the same grid, though the latter never happens. * * This function must handle blindness/hallucination. */ static ui_event_data target_set_interactive_aux(int y, int x, int mode, cptr info, bool list_floor_objects) { s16b this_o_idx, next_o_idx = 0; s16b this_x_idx, next_x_idx = 0; cptr s1, s2, s3; bool floored; u16b feat; ui_event_data query; char out_val[256]; char coords[20]; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { int i; char feat_name[80]; /* Terrain suffix for monsters and objects */ char terrain_suffix[200]; /* Temporary array of visible effects */ s16b x_seen[50]; u16b size_x_seen = 0; /* Paranoia */ query.key = ' '; /* Default */ s1 = "You see "; s2 = ""; s3 = "on "; /* The player */ if (cave_m_idx[y][x] < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Feature (apply "mimic") */ feat = f_info[cave_feat[y][x]].f_mimic; /* Require knowledge about grid, or ability to see grid */ if (!(cave_info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) { /* Forget feature */ feat = FEAT_NONE; } else { /* Hack -- track the current feature */ feature_kind_track(feat); /* Window stuff */ p_ptr->redraw |= (PR_FEATURE); } /* Pick a prefix */ if (*s2 && (!feat_ff1_match(feat, FF1_MOVE) || !feat_ff1_match(feat, FF1_LOS) || feat_ff1_match(feat, FF1_SHOP | FF1_DOOR) || feat_ff2_match(feat, FF2_SHALLOW | FF2_DEEP) || feat_ff3_match(feat, FF3_NEED_TREE))) { s3 = "in "; } /* Get a default name */ if (feat <= FEAT_NONE) { my_strcpy(feat_name, "an unknown grid", sizeof(feat_name)); } /* Get the real name */ else { feature_desc(feat_name, sizeof (feat_name), feat, TRUE, FALSE); } /* List all effects in the grid */ for (this_x_idx = cave_x_idx[y][x]; this_x_idx; this_x_idx = next_x_idx) { effect_type *x_ptr; /* Get the effect */ x_ptr = &x_list[this_x_idx]; /* Get the next effect */ next_x_idx = x_ptr->next_x_idx; /* Describe it, if not hidden */ if (!(x_ptr->x_flags & (EF1_HIDDEN)) && x_ptr->x_f_idx) { /* Check for available space */ if (size_x_seen < N_ELEMENTS(x_seen)) { x_seen[size_x_seen++] = x_ptr->x_f_idx; } } } /* Prepare the terrain suffix for monsters and objects */ my_strcpy(terrain_suffix, format(" %s%s", s3, feat_name), sizeof(terrain_suffix)); /* Concat the collected effect names */ for (i = 0; i < size_x_seen; i++) { char x_name[80]; /* Obtain an object description */ feature_desc(x_name, sizeof(x_name), x_seen[i], TRUE, TRUE); /* First effect */ if (i == 0) { if ((feat == FEAT_NONE) || !feat_ff1_match(feat, FF1_MOVE) || cave_any_trap_bold(y, x)) { /* Basic info */ my_strcat(terrain_suffix, format(" with %s", x_name), sizeof(terrain_suffix)); } else { /* Basic info */ my_strcat(terrain_suffix, format(" beneath %s", x_name), sizeof(terrain_suffix)); } } /* Basic info */ else if (i < (size_x_seen - 1)) { my_strcat(terrain_suffix, format(", %s", x_name), sizeof(terrain_suffix)); } /* Basic info */ else { my_strcat(terrain_suffix, format(" and %s", x_name), sizeof(terrain_suffix)); } } /* Ignore the terrain suffix if certain things happen */ if ((size_x_seen == 0) && (feat <= FEAT_FLOOR)) { terrain_suffix[0] = '\0'; } /* Hack -- hallucination */ if (p_ptr->timed[TMD_IMAGE]) { cptr name = "something strange"; /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s, [%s] %s (%d:%d).", s1, s2, name, info, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s [%s], %s.", s1, s2, name, info, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Stop on everything but "return" */ if ((query.key != '\n') && (query.key != '\r')) break; /* Repeat forever */ continue; } /* Actual monsters */ 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]; /* Visible */ if (m_ptr->ml) { bool recall = FALSE; char m_name[80]; if (m_ptr->mimic_k_idx) { /*get the description*/ mimic_desc_object(m_name, sizeof(m_name), m_ptr->mimic_k_idx); } else { /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0x08); /* Hack -- track this monster race */ monster_race_track(m_ptr->r_idx); /* Hack -- health bar for this monster */ health_track(cave_m_idx[y][x]); /*Track the feature*/ feature_kind_track(cave_feat[y][x]); /* Window stuff */ p_ptr->redraw |= (PR_FEATURE); /* Hack -- handle stuff */ handle_stuff(); } /* Interact */ while (1) { if (recall) button_add("[CLEAR_RECALL]", 'r'); else button_add("[RECALL]", 'r'); if (cave_o_idx[y][x] > 0)button_add("[VIEW_FLOOR]", 'f'); event_signal(EVENT_MOUSEBUTTONS); /* Recall, but not mimics */ if ((recall) && (!(m_ptr->mimic_k_idx))) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->r_idx); /* Hack -- Complete the prompt (again) */ Term_addstr(-1, TERM_WHITE, format(" [r,%s]", info)); /* Command */ query = inkey_ex(); /* Load screen */ screen_load(); } /* Normal */ else { /* Basic info */ strnfmt(out_val, sizeof(out_val), "%s%s%s", s1, s2, m_name); /* Describe the monster, unless a mimic */ if (!(m_ptr->mimic_k_idx)) { char buf[80]; look_mon_desc(buf, sizeof(buf), cave_m_idx[y][x]); /* Monster state, terrain suffix, options */ my_strcat(out_val, format(" (%s)%s [r,%s]", buf, terrain_suffix, info), sizeof(out_val)); } /* Mimics */ else { /* Terrain suffix, options */ my_strcat(out_val, format("%s [I,%s]", terrain_suffix, info), sizeof(out_val)); } /* Wizards want coordinates */ if (p_ptr->wizard) { my_strcat(out_val, format(" (%d:%d)", y, x), sizeof(out_val)); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ query = inkey_ex(); } button_kill('r'); button_kill('f'); event_signal(EVENT_MOUSEBUTTONS); /* Handle fake object recall */ if (m_ptr->mimic_k_idx) { object_type body; object_type *o_ptr = &body; /* Validate input first */ if (query.key != 'I') break; /* Paranoia */ object_wipe(o_ptr); /* Prepare */ object_prep(o_ptr, m_ptr->mimic_k_idx); /* Fake history */ object_history(o_ptr, ORIGIN_FLOOR, 0); /* Clear prompt. Place cursor */ prt("", 0, 0); /* Show the fake info - EXPERIMENTAL */ object_info_screen(o_ptr); } /* Regular monsters */ else { /* Normal commands */ if (query.key != 'r') break; /* Toggle recall */ recall = !recall; } } /* Stop on everything but "return"/"space", or floor */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ') && (query.key != 'f')) break; /* continue with 'f' only if there are floor items....*/ if ((query.key == 'f') && (!cave_o_idx[y][x])) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Hack -- take account of gender */ if (r_ptr->flags1 & (RF1_FEMALE)) s1 = "She is "; else if (r_ptr->flags1 & (RF1_MALE)) s1 = "He is "; /* Use a preposition */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Get the object */ o_ptr = &o_list[this_o_idx]; /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /*Don't let the player see certain objects (used for vault treasure)*/ if ((o_ptr->ident & (IDENT_HIDE_CARRY)) && (!p_ptr->wizard) && (!cheat_peek)) continue; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ strnfmt(out_val, sizeof(out_val), "%s%s%s [%s]", s1, s2, o_name, info); /* Wizards want coordinates */ if (p_ptr->wizard) { my_strcat(out_val, format(" (%d:%d)", y, x), sizeof(out_val)); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Assume not floored */ floored = FALSE; /* Scan all objects in the grid */ if (TRUE) { int floor_list[MAX_FLOOR_STACK]; int floor_num; track_object(-floor_list[0]); handle_stuff(); /* Scan for floor objects */ floor_num = scan_floor(floor_list, MAX_FLOOR_STACK, y, x, 0x02); /* Actual pile */ if (floor_num > 1) { /* Floored */ floored = TRUE; /* Describe */ while (1) { /* Basic info */ strnfmt(out_val, sizeof(out_val), "%s%sa pile of %d objects%s [r,%s]", s1, s2, floor_num, terrain_suffix, info); /* Wizards want coordinates */ if (p_ptr->wizard) { my_strcat(out_val, format(" (%d:%d)", y, x), sizeof(out_val)); } prt(out_val, 0, 0); if (list_floor_objects) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD)); } move_cursor_relative(y, x); query = inkey_ex(); if (list_floor_objects) { screen_load(); } /* Display objects */ if (query.key == 'r') { int pos; pos = query.key - 'a'; if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(); } } /* Done */ break; } /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Preposition */ s2 = "on "; } } /* Scan all objects in the grid */ for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = &o_list[this_o_idx]; /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Skip objects if floored */ if (floored) continue; /* Describe it */ if (o_ptr->marked) { char o_name[80]; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Basic info */ strnfmt(out_val, sizeof(out_val), "%s%s%s%s [I,%s]", s1, s2, o_name, terrain_suffix, info); /* Wizards want coordinates */ if (p_ptr->wizard) { my_strcat(out_val, format(" (%d:%d)", y, x), sizeof(out_val)); } /* Show object. Handle object recall */ while (TRUE) { /* Print the prompt */ prt(out_val, 0, 0); /* Move cursor to that location */ move_cursor_relative(y, x); /* Read input key */ query = inkey_ex(); /* No object recall */ if (query.key != 'I') break; /* Object recall. Clear the first line */ prt("", 0, 0); /* Do it */ object_info_screen(o_ptr); } /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; /* Display terrain */ if (TRUE) { u16b temp_feat; bool enable_recall; bool show_recall = FALSE; char temp_name[80]; /* * Display terrain and effects */ for (i = 0; i <= size_x_seen; i++) { /* Hack - This is the mark for the feature stored in cave_feat */ if (i == size_x_seen) { temp_feat = feat; /* Just copy the feature name */ my_strcpy(temp_name, feat_name, sizeof(temp_name)); } /* Any other value is an effect stored x_list */ else { temp_feat = x_seen[i]; /* Get the effect's name */ feature_desc(temp_name, sizeof(temp_name), temp_feat, TRUE, TRUE); } /* Don't display feature recall if the grid is unknown */ enable_recall = (temp_feat != FEAT_NONE); /* Handle recall */ while (TRUE) { /* Handle recall mode */ if (show_recall && enable_recall) { /* Save screen */ screen_save(); /* Recall feature on screen */ screen_feature_roff(temp_feat); } /* Display a message */ strnfmt(out_val, sizeof(out_val), "%s%s%s [%s%s]%s", s1, s2, temp_name, (enable_recall ? "r,": ""), info, (i < size_x_seen) ? " (more)": ""); /* Wizards want coordinates */ if (p_ptr->wizard) { my_strcat(out_val, format(" (%d:%d)", y, x), sizeof(out_val)); } /*Track this feature*/ feature_kind_track(temp_feat); /* Hack -- handle stuff */ handle_stuff(); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Load screen if necessary */ if (show_recall && enable_recall) { screen_load(); } /* Stop on everything but the recall command, if enabled */ if (!enable_recall || (query.key != 'r')) break; /* Toggle recall */ show_recall = !show_recall; } /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; } } /* Hack -- handle stuff */ handle_stuff(); /* Stop on everything but "return" */ if ((query.key != '\n') && (query.key != '\r')) break; } /* Keep going */ return (query); }
/* * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * This function correctly handles multiple objects per grid, and objects * and terrain features in the same grid, though the latter never happens. * * This function must handle blindness/hallucination. */ static ui_event_data target_set_interactive_aux(int y, int x, int mode) { s16b this_o_idx = 0, next_o_idx = 0; cptr s1, s2, s3; bool boring; bool floored; int feat; int floor_list[MAX_FLOOR_STACK]; int floor_num; ui_event_data query; char out_val[256]; char coords[20]; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ query.key = ' '; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave_m_idx[y][x] < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hack -- hallucination */ if (p_ptr->timed[TMD_IMAGE]) { cptr name = "something strange"; /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Stop on everything but "return" */ if ((query.key != '\n') && (query.key != '\r')) break; /* Repeat forever */ continue; } /* Actual monsters */ 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]; /* Visible */ if (m_ptr->ml) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2); /* Hack -- track this monster race */ monster_race_track(m_ptr->r_idx); /* Hack -- health bar for this monster */ health_track(cave_m_idx[y][x]); /* Hack -- handle stuff */ handle_stuff(); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->r_idx); /* Command */ query = inkey_ex(); /* Load screen */ screen_load(); } /* Normal */ else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave_m_idx[y][x]); /* Describe, and prompt for recall */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d).", s1, s2, s3, m_name, buf, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s.", s1, s2, s3, m_name, buf, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ query = inkey_ex(); } /* Normal commands */ if (query.key != 'r') break; /* Toggle recall */ recall = !recall; } /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Hack -- take account of gender */ if (rf_has(r_ptr->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(r_ptr->flags, RF_MALE)) s1 = "He is "; /* Use a preposition */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Get the object */ o_ptr = &o_list[this_o_idx]; /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Assume not floored */ floored = FALSE; floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x02); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px))) { /* Not boring */ boring = FALSE; track_object(-floor_list[0]); handle_stuff(); /* If there is more than one item... */ if (floor_num > 1) while (1) { floored = TRUE; /* Describe the pile */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d).", s1, s2, s3, floor_num, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s.", s1, s2, s3, floor_num, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Display objects */ if (query.key == 'r') { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD)); /* Describe the pile */ prt(out_val, 0, 0); query = inkey_ex(); /* Load screen */ screen_load(); pos = query.key - 'a'; if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(); continue; } rdone = 1; } /* Now that the user's done with the display loop, let's */ /* the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = &o_list[floor_list[0]]; /* Not boring */ boring = FALSE; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; /* Sometimes stop at "space" key */ if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; /* Feature (apply "mimic") */ feat = f_info[cave_feat[y][x]].mimic; /* Require knowledge about grid, or ability to see grid */ if (!(cave_info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) { /* Forget feature */ feat = FEAT_NONE; } /* Terrain feature if needed */ if (boring || (feat > FEAT_INVIS)) { cptr name = f_info[feat].name; /* Hack -- handle unknown grids */ if (feat == FEAT_NONE) name = "unknown grid"; /* Pick a prefix */ if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if ((feat >= FEAT_SHOP_HEAD) && (feat <= FEAT_SHOP_TAIL)) { s3 = "the entrance to the "; } /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey_ex(); /* Stop on everything but "return"/"space" */ if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break; } /* Stop on everything but "return" */ if ((query.key != '\n') && (query.key != '\r')) break; } /* Keep going */ return (query); }
/* * 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 cur_tar_y, int cur_tar_x) { int i; bool on_screen; byte color_type; /* No path, so do nothing. */ if (path_n < 1) return (FALSE); /* 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++) { /* Find the co-ordinates on the level. */ int y = GRID_Y(path_g[i]); int x = GRID_X(path_g[i]); byte this_a; char this_c; /* * 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. */ /* Visible monsters are orange. */ if (cave_m_idx[y][x] && mon_list[cave_m_idx[y][x]].ml) { monster_type *m_ptr = &mon_list[cave_m_idx[y][x]]; /*mimics act as objects*/ if (m_ptr->mimic_k_idx) color_type = TERM_YELLOW; else color_type = TERM_ORANGE; } /* Known objects are yellow. */ else if (cave_o_idx[y][x] && o_list[cave_o_idx[y][x]].marked) { color_type = TERM_YELLOW; } /* Effects are green */ else if ((cave_x_idx[y][x] > 0) && (cave_info[y][x] & (CAVE_SEEN | CAVE_MARK))) { color_type = TERM_GREEN; } /* Known walls are blue. */ else if (!cave_project_bold(y,x) && ((cave_info[y][x] & (CAVE_MARK)) || player_can_see_bold(y,x))) { color_type = TERM_BLUE; } /* Unknown squares are grey. */ else if (!(cave_info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) { color_type = TERM_L_DARK; } /* Unoccupied squares are white. */ else { color_type = TERM_WHITE; } /* ALways use red for the current target square */ if ((cur_tar_y == y) && (cur_tar_x == x)) color_type = TERM_RED; /* Get the character */ if (!use_graphics) { this_a = color_type; this_c = '*'; } /* Graphics are being used */ else { this_a = color_to_attr[TILE_BALL_INFO][color_type]; this_c = color_to_char[TILE_BALL_INFO][color_type]; } /* Visual effects -- Display */ print_rel(this_c, this_a, y, x); } return i; }
/* * 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); }
/** * 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); } }
//static struct keypress target_set_interactive_aux(int y, int x, int mode) static ui_event target_set_interactive_aux(int y, int x, int mode) { s16b this_o_idx = 0, next_o_idx = 0; const char *s1, *s2, *s3; bool boring; int floor_list[MAX_FLOOR_STACK]; int floor_num; //struct keypress query; ui_event press; char out_val[256]; char coords[20]; const char *name; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ press.type = EVT_KBRD; press.key.code = ' '; press.key.mods = 0; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave->m_idx[y][x] < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hallucination messes things up */ if (p_ptr->timed[TMD_IMAGE]) { const char *name = "something strange"; /* Display a message */ if (p_ptr->wizard) strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); else strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); prt(out_val, 0, 0); move_cursor_relative(y, x); //input = inkey_m(); //if ( press.key = inkey(); /* Stop on everything but "return" */ if (press.key.code == KC_ENTER) continue; return press; } /* Actual monsters */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster_at(cave, y, x); const monster_lore *l_ptr = get_lore(m_ptr->race); /* Visible */ if (m_ptr->ml && !m_ptr->unaware) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2); /* Hack -- track this monster race */ monster_race_track(m_ptr->race); /* Hack -- health bar for this monster */ health_track(p_ptr, m_ptr); /* Hack -- handle stuff */ handle_stuff(p_ptr); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->race, l_ptr); /* Command */ press = inkey_m(); /* Load screen */ screen_load(); } /* Normal */ else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave->m_idx[y][x]); /* Describe, and prompt for recall */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d).", s1, s2, s3, m_name, buf, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s.", s1, s2, s3, m_name, buf, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ press = inkey_m(); } /* Normal commands */ if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) recall = !recall; else if ((press.type == EVT_KBRD) && (press.key.code == 'r')) recall = !recall; else break; } if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if (press.key.code != KC_ENTER && press.key.code != ' ') break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Take account of gender */ if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a verb */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } /* Disabled since monsters now carry their drops else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } */ prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Assume not floored */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x0A); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px))) { /* Not boring */ boring = FALSE; track_object(-floor_list[0]); handle_stuff(p_ptr); /* If there is more than one item... */ if (floor_num > 1) while (1) { /* Describe the pile */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d).", s1, s2, s3, floor_num, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s.", s1, s2, s3, floor_num, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); /* Display objects */ if (((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) || ((press.type == EVT_KBRD) && (press.key.code == 'r'))) { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD)); /* Describe the pile */ prt(out_val, 0, 0); press = inkey_m(); /* Load screen */ screen_load(); if (press.type == EVT_MOUSE) { pos = press.mouse.y-1; } else { pos = press.key.code - 'a'; } if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(p_ptr); continue; } rdone = 1; } /* Now that the user's done with the display loop, let's */ /* the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = object_byid(floor_list[0]); /* Not boring */ boring = FALSE; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; name = cave_apparent_name(cave, p_ptr, y, x); /* Terrain feature if needed */ if (boring || cave_isinteresting(cave, y, x)) { /* Hack -- handle unknown grids */ /* Pick a prefix */ if (*s2 && cave_isdoor(cave, y, x)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if (cave_isshop(cave, y, x)) { s3 = "the entrance to the "; } /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; } } /* Stop on everything but "return" */ if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button != 2) break; } else { if (press.key.code != KC_ENTER) break; } } /* Keep going */ return (press); }
/** * Pick up objects and treasure on the floor, now also used for telekinesis. * * Called with pickup: * 0 to grab gold and describe non-gold objects. * 1 to pick up objects either with or without displaying a menu. * 2 to pick up objects, forcing a menu for multiple objects. * 3 to pick up objects, forcing a menu for any number of objects. * * Scan the list of objects in that floor grid. Pick up gold automatically. * Pick up objects automatically until pile or backpack space is full if * auto-pickup option is on, carry_query_floor option is not, and menus are * not forced (which the "get" command does). Otherwise, store objects on * floor in an array, and tally both how many there are and can be picked up. * * If the player is not picking up objects, describe a single object or * indicate the presence of a floor stack. If player is picking up objects, * name a single object, or indicate a stack of objects, that cannot go in * the backpack. * * Pick up a single object without menus, unless menus for single items are * forced. Confirm pickup if that option is on. * * Pick up multiple objects (unless using autopickup, no confirm) using Tim * Baker's menu system. Recursively call this function (forcing menus for any * number of objects) until objects are gone, backpack is full, or player is * satisfied. * * Keep track of number of objects picked up (to calculate time spent). */ byte py_pickup(int pickup, int y, int x) { s16b this_o_idx, next_o_idx = 0; char o_name[120]; object_type *o_ptr; /* Objects picked up. Used to determine time cost of command. */ byte objs_picked_up = 0; size_t floor_num = 0; int floor_list[MAX_FLOOR_STACK + 1], floor_o_idx = 0; int can_pickup = 0; bool call_function_again = FALSE; bool blind = ((p_ptr->timed[TMD_BLIND]) || (no_light())); bool msg = TRUE; bool telekinesis = (!(y == p_ptr->py) || !(x == p_ptr->px)); /* Nothing to pick up -- return */ if (!cave_o_idx[y][x]) return (0); /* Always pickup gold, effortlessly */ if (!telekinesis) py_pickup_gold(); /* Scan the pile of objects */ for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx) { /* Access the object */ o_ptr = &o_list[this_o_idx]; /* Access the next object */ next_o_idx = o_ptr->next_o_idx; /* Ordinary pickup */ if (!telekinesis) { /* Ignore all hidden objects and non-objects */ if (squelch_hide_item(o_ptr) || !o_ptr->k_idx) continue; /* Hack -- disturb */ disturb(0, 0); /* Automatically pick up some items */ if (auto_pickup_okay(o_ptr)) { /* Pick up the object */ py_pickup_aux(this_o_idx, TRUE); objs_picked_up++; /* Check the next object */ continue; } } /* Tally objects and store them in an array. */ /* Remember this object index */ floor_list[floor_num] = this_o_idx; /* Count non-gold objects that remain on the floor. */ floor_num++; /* Tally objects that can be picked up.*/ if (inven_carry_okay(o_ptr)) can_pickup++; } /* There are no non-gold objects */ if (!floor_num) return (objs_picked_up); /* Get hold of the last floor index */ floor_o_idx = floor_list[floor_num - 1]; /* Mention the objects if player is not picking them up. */ if (pickup == 0 || !(can_pickup || telekinesis)) { const char *p = "see"; /* One object */ if (floor_num == 1) { if (!can_pickup) p = "have no room for"; else if (blind) p = "feel"; /* Get the object */ o_ptr = &o_list[floor_o_idx]; /* Describe the object. Less detail if blind. */ if (blind) object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_BASE); else object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Message */ message_flush(); msg_format("You %s %s.", p, o_name); } else { /* Optionally, display more information about floor items */ if (OPT(pickup_detail)) { ui_event_data e; if (!can_pickup) p = "have no room for the following objects"; else if (blind) p = "feel something on the floor"; /* Scan all marked objects in the grid */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x03); /* Save screen */ screen_save(); /* Display objects on the floor */ show_floor(floor_list, floor_num, (OLIST_WEIGHT)); /* Display prompt */ prt(format("You %s: ", p), 0, 0); /* Move cursor back to character, if needed */ if (OPT(highlight_player)) move_cursor_relative(p_ptr->py, p_ptr->px); /* Wait for it. Use key as next command. */ e = inkey_ex(); Term_event_push(&e); /* Restore screen */ screen_load(); } /* Show less detail */ else { message_flush(); if (!can_pickup) msg_print("You have no room for any of the items on the floor."); else msg_format("You %s a pile of %d items.", (blind ? "feel" : "see"), floor_num); } } /* Done */ return (objs_picked_up); } /* We can pick up objects. Menus are not requested (yet). */ if (pickup == 1) { /* Scan floor (again) */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x03); /* Use a menu interface for multiple objects, or get single objects */ if (floor_num > 1) pickup = 2; else this_o_idx = floor_o_idx; } /* Display a list if requested. */ if (pickup == 2) { cptr q, s; int item; /* Get an object or exit. */ q = "Get which item?"; s = "You see nothing there."; /* Telekinesis */ if (telekinesis) { item_tester_hook = inven_carry_okay; if (!get_item(&item, q, s, CMD_PICKUP, USE_TARGET)) return (objs_picked_up); this_o_idx = 0 - item; } else { /* Restrict the choices */ item_tester_hook = inven_carry_okay; if (!get_item(&item, q, s, CMD_PICKUP, USE_FLOOR)) return (objs_picked_up); this_o_idx = 0 - item; call_function_again = TRUE; } /* With a list, we do not need explicit pickup messages */ msg = FALSE; } /* Pick up object, if legal */ if (this_o_idx) { /* Regular pickup or telekinesis with pack not full */ if (can_pickup) { /* Pick up the object */ py_pickup_aux(this_o_idx, msg); } /* Telekinesis with pack full */ else { /* Access the object */ o_ptr = &o_list[this_o_idx]; /* Drop it */ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px, TRUE); /* Delete the old object */ delete_object_idx(this_o_idx); } } /* Indicate an object picked up. */ objs_picked_up = 1; /* If requested, call this function recursively. Count objects picked up. * Force the display of a menu in all cases. */ if (call_function_again) objs_picked_up += py_pickup(3, y, x); /* Indicate how many objects have been picked up. */ return (objs_picked_up); }
static void _greater_whirlwind_attack_spell(int cmd, variant *res) { switch (cmd) { case SPELL_NAME: var_set_string(res, "Greater Ambush"); break; case SPELL_DESC: var_set_string(res, "Perform a massive ambush on nearby monsters."); break; case SPELL_CAST: { int i, x, y; cave_type *c_ptr; monster_type *m_ptr; /* cba d218l e3@7k f456j ghi */ typedef struct _offset_t { int dx; int dy; } _offset; static _offset offsets[] = { { 0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, { 0, 1}, { 1, 1}, { 1, 0}, { 1, -1}, { 1, -2}, { 0, -2}, {-1, -2}, {-2, -1}, {-2, 0}, {-2, 1}, {-1, 2}, { 0, 2}, { 1, 2}, { 2, 1}, { 2, 0}, { 2, -1}, { 0, 0}, /* sentinel */ }; for (i = 0;; i++) { _offset offset = offsets[i]; if (offset.dx == 0 && offset.dy == 0) break; y = py + offset.dy; x = px + offset.dx; if (!in_bounds(y, x)) continue; if (!projectable(py, px, y, x)) continue; c_ptr = &cave[y][x]; if (!c_ptr->m_idx) continue; m_ptr = &m_list[c_ptr->m_idx]; if (m_ptr->ml || cave_have_flag_bold(y, x, FF_PROJECT)) { int msec = delay_factor * delay_factor * delay_factor; if (panel_contains(y, x) && player_can_see_bold(y, x)) { char c = 0x30; byte a = TERM_WHITE; print_rel(c, a, y, x); move_cursor_relative(y, x); Term_fresh(); Term_xtra(TERM_XTRA_DELAY, msec); lite_spot(y, x); Term_fresh(); } else Term_xtra(TERM_XTRA_DELAY, msec); py_attack(y, x, 0); } } var_set_bool(res, TRUE); break; } default: default_spell(cmd, res); break; } }
/** * 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, struct loc *path_g, wchar_t *c, int *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 = path_g[i].y; int x = path_g[i].x; struct monster *mon = square_monster(cave, y, x); struct object *obj = square_object(cave, y, x); /* * 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 (mon && mflag_has(mon->mflag, MFLAG_VISIBLE)) { /* Mimics act as objects */ if (rf_has(mon->race->flags, RF_UNAWARE)) colour = COLOUR_YELLOW; else /* Visible monsters are red. */ colour = COLOUR_L_RED; } else if (obj && obj->marked) /* Known objects are yellow. */ colour = COLOUR_YELLOW; else if ((!square_isprojectable(cave, y,x) && square_ismark(cave, y, x)) || square_isseen(cave, y, x)) /* Known walls are blue. */ colour = COLOUR_BLUE; else if (!square_ismark(cave, y, x) && !square_isseen(cave, y, x)) /* Unknown squares are grey. */ colour = COLOUR_L_DARK; else /* Unoccupied squares are white. */ colour = COLOUR_WHITE; /* Draw the path segment */ (void)Term_addch(colour, L'*'); } return i; }
/** * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * This function correctly handles multiple objects per grid, and objects * and terrain features in the same grid, though the latter never happens. * * This function must handle blindness/hallucination. */ static ui_event target_set_interactive_aux(int y, int x, int mode) { struct object *obj = NULL; const char *s1, *s2, *s3; bool boring; int floor_max = z_info->floor_size; struct object **floor_list = mem_zalloc(floor_max * sizeof(*floor_list)); int floor_num; ui_event press; char out_val[TARGET_OUT_VAL_SIZE]; char coords[20]; const char *name; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ press.type = EVT_KBRD; press.key.code = ' '; press.key.mods = 0; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave->squares[y][x].mon < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hallucination messes things up */ if (player->timed[TMD_IMAGE]) { const char *name = "something strange"; /* Display a message */ if (player->wizard) strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); else strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); prt(out_val, 0, 0); move_cursor_relative(y, x); press.key = inkey(); /* Stop on everything but "return" */ if (press.key.code == KC_ENTER) continue; mem_free(floor_list); return press; } /* Actual monsters */ if (cave->squares[y][x].mon > 0) { monster_type *m_ptr = square_monster(cave, y, x); const monster_lore *l_ptr = get_lore(m_ptr->race); /* Visible */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->mflag, MFLAG_UNAWARE)) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND_VIS); /* Hack -- track this monster race */ monster_race_track(player->upkeep, m_ptr->race); /* Hack -- health bar for this monster */ health_track(player->upkeep, m_ptr); /* Hack -- handle stuff */ handle_stuff(player); /* Interact */ while (1) { /* Recall or target */ if (recall) { lore_show_interactive(m_ptr->race, l_ptr); press = inkey_m(); } else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave->squares[y][x].mon); /* Describe, and prompt for recall */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, m_name, buf, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s.", s1, s2, s3, m_name, buf, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ press = inkey_m(); } /* Normal commands */ if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) recall = !recall; else if ((press.type == EVT_KBRD) && (press.key.code == 'r')) recall = !recall; else break; } if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if (press.key.code != KC_ENTER && press.key.code != ' ') break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Take account of gender */ if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a verb */ s2 = "carrying "; /* Scan all objects being carried */ for (obj = m_ptr->held_obj; obj; obj = obj->next) { char o_name[80]; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, o_name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (obj) break; /* Use a preposition */ s2 = "on "; } } /* A trap */ if (square_isvisibletrap(cave, y, x)) { struct trap *trap = cave->squares[y][x].trap; /* Not boring */ boring = FALSE; /* Interact */ while (1) { /* Change the intro */ if (cave->squares[y][x].mon < 0) { s1 = "You are "; s2 = "on "; } else { s1 = "You see "; s2 = ""; } /* Pick proper indefinite article */ s3 = (is_a_vowel(trap->kind->desc[0])) ? "an " : "a "; /* Describe, and prompt for recall */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, trap->kind->name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, trap->kind->desc, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ press = inkey_m(); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } } /* Double break */ if (square_isvisibletrap(cave, y, x)) break; /* Assume not floored */ floor_num = scan_floor(floor_list, floor_max, y, x, 0x0A, NULL); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(player->timed[TMD_BLIND]) || (y == player->py && x == player->px))) { /* Not boring */ boring = FALSE; track_object(player->upkeep, floor_list[0]); handle_stuff(player); /* If there is more than one item... */ if (floor_num > 1) while (1) { /* Describe the pile */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, floor_num, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } else { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s.", s1, s2, s3, floor_num, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); /* Display objects */ if (((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) || ((press.type == EVT_KBRD) && (press.key.code == 'r'))) { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD), NULL); /* Describe the pile */ prt(out_val, 0, 0); press = inkey_m(); /* Load screen */ screen_load(); if (press.type == EVT_MOUSE) { pos = press.mouse.y-1; } else { pos = press.key.code - 'a'; } if (0 <= pos && pos < floor_num) { track_object(player->upkeep, floor_list[pos]); handle_stuff(player); continue; } rdone = 1; } /* Now that the user's done with the display loop, * let's do the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { /* Get the single object in the list */ struct object *obj = floor_list[0]; /* Allow user to recall an object */ press = target_recall_loop_object(obj, y, x, out_val, s1, s2, s3, coords); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Plurals */ s1 = VERB_AGREEMENT(obj->number, "It is ", "They are "); /* Preposition */ s2 = "on "; } } /* Double break */ if (obj) break; name = square_apparent_name(cave, player, y, x); /* Terrain feature if needed */ if (boring || square_isinteresting(cave, y, x)) { /* Hack -- handle unknown grids */ /* Pick a prefix */ if (*s2 && square_isdoor(cave, y, x)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if (square_isshop(cave, y, x)) s3 = "the entrance to the "; /* Display a message */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; } } /* Stop on everything but "return" */ if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button != 2) break; } else { if (press.key.code != KC_ENTER) break; } } mem_free(floor_list); /* Keep going */ return (press); }