/** * Hack -- Teleport to the target. Oangband asks for a target after * the command. */ static void do_cmd_wiz_bamf(void) { feature_type *f_ptr; /* target starts at player. */ s16b ny = 0; s16b nx = 0; /* Use the targeting function. */ if (!target_set_interactive(TARGET_LOOK, -1, -1)) return; /* grab the target coords. */ target_get(&nx, &ny); /* Test for passable terrain. */ f_ptr = &f_info[cave_feat[ny][nx]]; if (!tf_has(f_ptr->flags, TF_PASSABLE)) { msg("The square you are aiming for is impassable."); } /* The simple act of controlled teleport. */ else teleport_player_to(ny, nx, TRUE); }
/* * Hack -- Teleport to the target */ static void do_cmd_wiz_bamf(void) { s16b x, y; /* Must have a target */ if (!target_okay()) return; /* Teleport to the target */ target_get(&x, &y); teleport_player_to(y, x); }
void target_wait_all(void) { int i; struct target *t; target_array_lock(); for (i = 0; i < MAX_TARGET_COUNT; ++i) { if (target_use[i] == 0) continue; t = target_get(i); LOGI("join recv thread [%d] is started\n", i); target_wait(t); LOGI("join recv thread %d. done\n", i); } target_array_unlock(); }
/* * for all targets */ int target_send_msg_to_all(struct msg_target_t *msg) { int i, ret = 0; struct target *t; target_array_lock(); for (i = 0; i < MAX_TARGET_COUNT; ++i) { if (target_use[i] == 0) continue; t = target_get(i); if (target_send_msg(t, msg)) ret = 1; } target_array_unlock(); return ret; }
/** * 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); } }
uint64_t target_get_total_alloc(pid_t pid) { int i; uint64_t ret = 0; struct target *t; target_array_lock(); for (i = 0; i < MAX_TARGET_COUNT; i++) { if (target_use[i] == 0) continue; t = target_get(i); if (target_get_pid(t) == pid) { ret = t->allocmem; goto unlock; } } unlock: target_array_unlock(); return ret; }
/** * Hack -- Teleport to the target. Oangband asks for a target after * the command. */ static void do_cmd_wiz_bamf(void) { /* target starts at player. */ s16b ny = 0; s16b nx = 0; /* Use the targeting function. */ if (!target_set_interactive(TARGET_LOOK, -1, -1)) return; /* grab the target coords. */ target_get(&nx, &ny); /* Test for passable terrain. */ if (!cave_passable_bold(ny, nx)) { msg_print("The square you are aiming for is impassable."); } /* The simple act of controlled teleport. */ else teleport_player_to(ny, nx, 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); } }
/** * 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(struct object *obj, int dir, int range, int shots, ranged_attack attack) { int i, j; char o_name[80]; int path_n; struct loc path_g[256]; /* Start at the player */ int x = player->px; int y = player->py; /* Predict the "target" location */ int ty = y + 99 * ddy[dir]; int tx = x + 99 * ddx[dir]; bool hit_target = FALSE; bool none_left = FALSE; struct object *missile; /* Check for target validity */ if ((dir == 5) && target_okay()) { int taim; target_get(&tx, &ty); taim = distance(y, x, ty, tx); if (taim > range) { char msg[80]; strnfmt(msg, sizeof(msg), "Target out of range by %d squares. Fire anyway? ", taim - range); if (!get_check(msg)) return; } } /* Sound */ sound(MSG_SHOOT); /* Describe the object */ object_desc(o_name, sizeof(o_name), obj, ODESC_FULL | ODESC_SINGULAR); /* Actually "fire" the object -- Take a partial turn */ player->upkeep->energy_use = (z_info->move_energy / shots); /* Calculate the path */ path_n = project_path(path_g, range, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(player); /* Start at the player */ x = player->px; y = player->py; /* Project along the path */ for (i = 0; i < path_n; ++i) { struct monster *mon = NULL; int ny = path_g[i].y; int nx = path_g[i].x; bool see = square_isseen(cave, ny, nx); /* Stop before hitting walls */ if (!(square_ispassable(cave, ny, nx)) && !(square_isprojectable(cave, ny, nx))) break; /* Advance */ x = nx; y = ny; /* Tell the UI to display the missile */ event_signal_missile(EVENT_MISSILE, obj, see, y, x); /* Try the attack on the monster at (x, y) if any */ mon = square_monster(cave, y, x); if (mon) { int visible = mflag_has(mon->mflag, MFLAG_VISIBLE); bool fear = FALSE; const char *note_dies = monster_is_unusual(mon->race) ? " is destroyed." : " dies."; struct attack_result result = attack(obj, y, x); int dmg = result.dmg; u32b msg_type = result.msg_type; char hit_verb[20]; my_strcpy(hit_verb, result.hit_verb, sizeof(hit_verb)); mem_free(result.hit_verb); if (result.success) { hit_target = TRUE; object_notice_attack_plusses(obj); /* Learn by use for other equipped items */ equip_notice_to_hit_on_attack(player); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; msg_type = MSG_MISS; my_strcpy(hit_verb, "fails to harm", sizeof(hit_verb)); } if (!visible) { /* Invisible monster */ msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name); } else { for (j = 0; j < (int)N_ELEMENTS(ranged_hit_types); j++) { char m_name[80]; const char *dmg_text = ""; if (msg_type != ranged_hit_types[j].msg) continue; if (OPT(show_damage)) dmg_text = format(" (%d)", dmg); monster_desc(m_name, sizeof(m_name), mon, MDESC_OBJE); if (ranged_hit_types[j].text) msgt(msg_type, "Your %s %s %s%s. %s", o_name, hit_verb, m_name, dmg_text, ranged_hit_types[j].text); else msgt(msg_type, "Your %s %s %s%s.", o_name, hit_verb, m_name, dmg_text); } /* Track this monster */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { monster_race_track(player->upkeep, mon->race); health_track(player->upkeep, mon); } } /* Hit the monster, check for death */ if (!mon_take_hit(mon, dmg, &fear, note_dies)) { message_pain(mon, dmg); if (fear && mflag_has(mon->mflag, MFLAG_VISIBLE)) { char m_name[80]; monster_desc(m_name, sizeof(m_name), mon, MDESC_DEFAULT); add_monster_message(m_name, mon, MON_MSG_FLEE_IN_TERROR, TRUE); } } } /* Stop the missile */ break; } /* Stop if non-projectable but passable */ if (!(square_isprojectable(cave, ny, nx))) break; } /* Get the missile */ if (object_is_carried(player, obj)) missile = gear_object_for_use(obj, 1, TRUE, &none_left); else missile = floor_object_for_use(obj, 1, TRUE, &none_left); /* Drop (or break) near that location */ drop_near(cave, missile, breakage_chance(missile, hit_target), y, x, TRUE); }
/* * 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); }
/** * Let the user select an item, save its "index" * * Return TRUE only if an acceptable item was chosen by the user. * * The selected item must satisfy the "item_tester_hook()" function, * if that hook is set, and the "item_tester_tval", if that value is set. * * All "item_tester" restrictions are cleared before this function returns. * * The user is allowed to choose acceptable items from the equipment, * inventory, or floor, respectively, if the proper flag was given, * and there are any acceptable items in that location. * * The equipment or inventory are displayed (even if no acceptable * items are in that location) if the proper flag was given. * * If there are no acceptable items available anywhere, and "str" is * not NULL, then it will be used as the text of a warning message * before the function returns. * * Note that the user must press "-" to specify the item on the floor, * and there is no way to "examine" the item on the floor, while the * use of "capital" letters will "examine" an inventory/equipment item, * and prompt for its use. * * If a legal item is selected from the inventory, we save it in "cp" * directly (0 to 35), and return TRUE. * * If a legal item is selected from the floor, we save it in "cp" as * a negative (-1 to -511), and return TRUE. * * If no item is available, we do nothing to "cp", and we display a * warning message, using "str" if available, and return FALSE. * * If no item is selected, we do nothing to "cp", and return FALSE. * * Global "p_ptr->command_wrk" is used to choose between equip/inven/floor * listings. It is equal to USE_INVEN or USE_EQUIP or USE_FLOOR, except * when this function is first called, when it is equal to zero, which will * cause it to be set to USE_INVEN. * * We always erase the prompt when we are done, leaving a blank line, * or a warning message, if appropriate, if no items are available. * * Note that only "acceptable" floor objects get indexes, so between two * commands, the indexes of floor objects may change. XXX XXX XXX */ bool get_item(int *cp, const char *pmt, const char *str, cmd_code cmd, int mode) { s16b py = p_ptr->py; s16b px = p_ptr->px; unsigned char cmdkey = cmd_lookup_key(cmd, KEYMAP_MODE_ORIG); bool done, item; int j, k; bool oops = FALSE; bool toggle = FALSE; bool use_inven = ((mode & USE_INVEN) ? TRUE : FALSE); bool use_equip = ((mode & USE_EQUIP) ? TRUE : FALSE); bool use_floor = ((mode & (USE_FLOOR | USE_TARGET)) ? TRUE : FALSE); bool use_quiver = ((mode & QUIVER_TAGS) ? TRUE : FALSE); bool can_squelch = ((mode & CAN_SQUELCH) ? TRUE : FALSE); bool is_harmless = ((mode & IS_HARMLESS) ? TRUE : FALSE); bool quiver_tags = ((mode & QUIVER_TAGS) ? TRUE : FALSE); bool allow_inven = FALSE; bool allow_equip = FALSE; bool allow_floor = FALSE; int floor_num; ui_event which; prompt = pmt; olist_mode = 0; item_mode = mode; item_cmd = cmd; /* Not done */ done = FALSE; /* No item selected */ item = FALSE; *cp = 0; /* Object list display modes */ if (mode & SHOW_FAIL) olist_mode |= (OLIST_FAIL); else olist_mode |= (OLIST_WEIGHT); if (mode & SHOW_PRICES) olist_mode |= (OLIST_PRICE); show_list = OPT(show_lists) ? TRUE : FALSE; /* Set target for telekinesis */ if (mode & (USE_TARGET)) { target_get(&px, &py); if (!(px && py)) return FALSE; } /* Full inventory */ i1 = 0; i2 = INVEN_PACK - 1; /* Forbid inventory */ if (!use_inven) i2 = -1; /* Restrict inventory indexes */ while ((i1 <= i2) && (!get_item_okay(i1))) i1++; while ((i1 <= i2) && (!get_item_okay(i2))) i2--; /* Accept inventory */ if (i1 <= i2) allow_inven = TRUE; /* Full equipment */ e1 = INVEN_WIELD; e2 = ALL_INVEN_TOTAL - 1; /* Forbid equipment */ if (!use_equip) e2 = -1; /* Restrict equipment indexes */ while ((e1 <= e2) && (!get_item_okay(e1))) e1++; while ((e1 <= e2) && (!get_item_okay(e2))) e2--; /* Accept equipment */ if (e1 <= e2) allow_equip = TRUE; /* Reject quiver */ if (e2 < QUIVER_START) use_quiver = FALSE; /* Scan all non-gold objects in the grid */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), py, px, 0x03); /* Full floor */ f1 = 0; f2 = floor_num - 1; /* Forbid floor */ if (!use_floor) f2 = -1; /* Restrict floor indexes */ while ((f1 <= f2) && (!get_item_okay(0 - floor_list[f1]))) f1++; while ((f1 <= f2) && (!get_item_okay(0 - floor_list[f2]))) f2--; /* Accept floor */ if (f1 <= f2) allow_floor = TRUE; /* Require at least one legal choice */ if (!allow_inven && !allow_equip && !allow_floor) { /* Oops */ oops = TRUE; done = TRUE; } /* Analyze choices, prepare for initial menu */ else { /* Hack -- Start on equipment if requested */ if ((p_ptr->command_wrk == USE_EQUIP) && use_equip){ p_ptr->command_wrk = USE_EQUIP; build_obj_list(INVEN_WIELD, e2, NULL, olist_mode); } /* If we are using the quiver then start on equipment */ else if (use_quiver){ p_ptr->command_wrk = USE_EQUIP; build_obj_list(INVEN_WIELD, e2, NULL, olist_mode); } /* Use inventory if allowed */ else if (use_inven){ p_ptr->command_wrk = USE_INVEN; build_obj_list(0, i2, NULL, olist_mode); } /* Use equipment if allowed */ else if (use_equip){ p_ptr->command_wrk = USE_EQUIP; build_obj_list(INVEN_WIELD, e2, NULL, olist_mode); } /* Use floor if allowed */ else if (use_floor){ p_ptr->command_wrk = USE_FLOOR; build_obj_list(0, f2, floor_list, olist_mode); } /* Hack -- Use (empty) inventory */ else { p_ptr->command_wrk = USE_INVEN; build_obj_list(0, i2, NULL, olist_mode); } } /* Clear all current messages */ msg_flag = FALSE; /* Start out in "display" mode */ if (show_list) { /* Save screen */ screen_save(); } /* Repeat until done */ while (!done) { static bool refresh; int ni = 0; int ne = 0; /* Scan windows */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Unused */ if (!angband_term[j]) continue; /* Count windows displaying inven */ if (op_ptr->window_flag[j] & (PW_INVEN)) ni++; /* Count windows displaying equip */ if (op_ptr->window_flag[j] & (PW_EQUIP)) ne++; } /* Toggle if needed */ if (((p_ptr->command_wrk == USE_EQUIP) && ni && !ne) || ((p_ptr->command_wrk == USE_INVEN) && !ni && ne)) { /* Toggle */ toggle_inven_equip(); /* Track toggles */ toggle = !toggle; } /* Redraw */ p_ptr->redraw |= (PR_INVEN | PR_EQUIP); /* Redraw windows */ redraw_stuff(p_ptr); /* Change the list if needed */ if (refresh) { /* Rebuild object list */ if (p_ptr->command_wrk == USE_INVEN) build_obj_list(0, i2, NULL, olist_mode); else if (p_ptr->command_wrk == USE_EQUIP) build_obj_list(INVEN_WIELD, e2, NULL, olist_mode); else build_obj_list(0, f2, floor_list, mode); refresh = FALSE; } /* Show the prompt */ item_prompt(mode); redraw_stuff(p_ptr); /* Menu if requested */ if (show_list) { which = item_menu(cmd, mode); if (which.type == EVT_ESCAPE) which.key.code = ESCAPE; } /* Get a key */ else which = inkey_ex(); /* Parse it */ switch (which.key.code) { case ESCAPE: { done = TRUE; break; } case '*': case '?': case ' ': { if (!OPT(show_lists)) { /* Save screen */ screen_save(); /* Flip flag */ show_list = TRUE; } refresh = TRUE; break; } case '/': { /* Toggle to inventory */ if (use_inven && (p_ptr->command_wrk != USE_INVEN)) { p_ptr->command_wrk = USE_INVEN; refresh = TRUE; } /* Toggle to equipment */ else if (use_equip && (p_ptr->command_wrk != USE_EQUIP)) { p_ptr->command_wrk = USE_EQUIP; refresh = TRUE; } /* No toggle allowed */ else { bell("Cannot switch item selector!"); break; } /* Need to redraw */ break; } case '-': { /* Paranoia */ if (!allow_floor) { bell("Cannot select floor!"); break; } /* There is only one item */ if (OPT(pickup_single) && (floor_num == 1)) { /* Fall through */ } else { p_ptr->command_wrk = (USE_FLOOR); refresh = TRUE; break; } } case '.': { /* * If we are allow to use the floor, select * the top item. -BR- */ if (allow_floor) { int k; /* Special index */ k = 0 - floor_list[0]; /* Allow player to "refuse" certain actions */ if (!get_item_allow(k, cmdkey, is_harmless)) { done = TRUE; break; } /* Accept that choice */ (*cp) = k; item = TRUE; done = TRUE; } break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* Look up the tag */ if (!get_tag(&k, which.key.code, cmd, quiver_tags)) { bell("Illegal object choice (tag)!"); break; } /* Hack -- Validate the item */ if ((k < INVEN_WIELD) ? !allow_inven : !allow_equip) { bell("Illegal object choice (tag)!"); break; } /* Validate the item */ if (!get_item_okay(k)) { bell("Illegal object choice (tag)!"); break; } /* Allow player to "refuse" certain actions */ if (!get_item_allow(k, cmdkey, is_harmless)) { done = TRUE; break; } /* Accept that choice */ (*cp) = k; item = TRUE; done = TRUE; break; } case KC_ENTER: { /* Choose "default" inventory item */ if (p_ptr->command_wrk == USE_INVEN) { if (i1 != i2) { bell("Illegal object choice (default)!"); break; } k = i1; } /* Choose the "default" slot (0) of the quiver */ else if (quiver_tags) k = e1; /* Choose "default" equipment item */ else if (p_ptr->command_wrk == USE_EQUIP) { if (e1 != e2) { bell("Illegal object choice (default)!"); break; } k = e1; } /* Choose "default" floor item */ else { if (f1 != f2) { bell("Illegal object choice (default)!"); break; } k = 0 - floor_list[f1]; } /* Validate the item */ if (!get_item_okay(k)) { bell("Illegal object choice (default)!"); break; } /* Allow player to "refuse" certain actions */ if (!get_item_allow(k, cmdkey, is_harmless)) { done = TRUE; break; } /* Accept that choice */ (*cp) = k; item = TRUE; done = TRUE; break; } case '!': { /* Try squelched items */ if (can_squelch) { (*cp) = ALL_SQUELCHED; item = TRUE; done = TRUE; break; } /* Just fall through */ } default: { bool verify; /* Note verify */ verify = (isupper((unsigned char) which.key.code) ? TRUE : FALSE); /* Lowercase */ which.key.code = tolower((unsigned char) which.key.code); /* Convert letter to inventory index */ if (p_ptr->command_wrk == USE_INVEN) { k = label_to_inven(which.key.code); if (k < 0) { bell("Illegal object choice (inven)!"); break; } } /* Convert letter to equipment index */ else if (p_ptr->command_wrk == USE_EQUIP) { k = label_to_equip(which.key.code); if (k < 0) { bell("Illegal object choice (equip)!"); break; } } /* Convert letter to floor index */ else { k = (islower((unsigned char) which.key.code) ? A2I((unsigned char)which.key.code) : -1); if (k < 0 || k >= floor_num) { bell("Illegal object choice (floor)!"); break; } /* Special index */ k = 0 - floor_list[k]; } /* Validate the item */ if (!get_item_okay(k)) { bell("Illegal object choice (normal)!"); break; } /* Verify the item */ if (verify && !verify_item("Try", k)) { done = TRUE; break; } /* Allow player to "refuse" certain actions */ if (!get_item_allow(k, cmdkey, is_harmless)) { done = TRUE; break; } /* Accept that choice */ (*cp) = k; item = TRUE; done = TRUE; break; } } } /* Fix the screen if necessary */ if (show_list) { /* Load screen */ screen_load(); /* Hack -- Cancel "display" */ show_list = FALSE; } /* Kill buttons */ button_kill('*'); button_kill('/'); button_kill('-'); button_kill('!'); /* Forget the item_tester_tval restriction */ item_tester_tval = 0; /* Forget the item_tester_hook restriction */ item_tester_hook = NULL; /* Toggle again if needed */ if (toggle) toggle_inven_equip(); /* Update */ p_ptr->redraw |= (PR_INVEN | PR_EQUIP); redraw_stuff(p_ptr); /* Clear the prompt line */ prt("", 0, 0); /* Warning if needed */ if (oops && str) msg(str); /* Result */ return (item); }