static void _necro_do_summon(int what, int num, bool fail) { int x = px; int y = py; if (fail) /* Failing spells should not be insta-death ... */ num = MAX(1, num/4); else num = spell_power(num); if (!fail && use_old_target && target_okay() && los(py, px, target_row, target_col) && !one_in_(3)) { y = target_row; x = target_col; } if (trump_summoning(num, !fail, y, x, 0, what, PM_ALLOW_UNIQUE)) { if (fail) { if (num == 1) msg_print("The summoned monster gets angry!"); else msg_print("The summoned monsters get angry!"); } } }
/* * Handle ball spells. * * Balls act like bolt spells, except that they do not pass their target, * and explode when they hit a monster, a wall, their target, or the edge * of sight. Within the explosion radius, they affect items on the floor. * * Balls may jump to the target, and have any source diameter (which affects * how quickly their damage falls off with distance from the center of the * explosion). */ bool project_ball(int who, int rad, int y0, int x0, int y1, int x1, int dam, int typ, u32b flg, int source_diameter) { /* Add the ball bitflags */ flg |= PROJECT_BOOM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_WALL | PROJECT_EFCT; /* Add the STOP flag if appropriate */ if ((who == SOURCE_PLAYER) && (!target_okay() || y1 != p_ptr->target_row || x1 != p_ptr->target_col)) { flg |= (PROJECT_STOP); } /* Hurt the character unless he controls the spell */ if (who != SOURCE_PLAYER) flg |= (PROJECT_PLAY); /*Hack - poison cloud poison spells have a lingering cloud */ else if (typ == GF_POIS) { if (game_mode != GAME_NPPMORIA) flg |= (PROJECT_CLOUD); } /* Limit radius to nine (up to 256 grids affected) */ if (rad > 9) rad = 9; /* Cast a ball */ return (project(who, rad, y0, x0, y1, x1, dam, typ, flg, 0, source_diameter)); }
/* * Hack -- Teleport to the target */ static void do_cmd_wiz_bamf(void) { /* Must have a target */ if (target_okay()) { /* Teleport to the target */ teleport_player_to(p_ptr->target_row, p_ptr->target_col); } }
/** * True if the player's current target is in LOS. */ bool target_sighted(void) { return target_okay() && panel_contains(target.grid.y, target.grid.x) && /* either the target is a grid and is visible, or it is a monster * that is visible */ ((!target.midx && square_isseen(cave, target.grid)) || (target.midx && monster_is_visible(cave_monster(cave, target.midx)))); }
/** * True if the player's current target is in LOS. */ bool target_sighted(void) { return target_okay() && panel_contains(target_y, target_x) && /* either the target is a grid and is visible, or it is a monster * that is visible */ ((!target_who && square_isseen(cave, target_y, target_x)) || (target_who && mflag_has(target_who->mflag, MFLAG_VISIBLE))); }
/* * 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 WizardModeDialog::wiz_teleport_to_target(void) { /* Must have a target */ if (target_okay()) { /* Teleport to the target */ teleport_player_to(p_ptr->target_row, p_ptr->target_col); this->accept(); } else this->reject(); }
/* * Handle target grids for projections under the control of * the character. - Chris Wilde, Morgul */ static void adjust_target(int dir, int y0, int x0, int *y1, int *x1) { // First target closest, if there is anything there if (dir == DIR_CLOSEST && target_okay()) { *y1 = p_ptr->target_row; *x1 = p_ptr->target_col; } /* If no direction is given, and a target is, use the target. */ else if ((dir == DIR_TARGET) && target_okay()) { *y1 = p_ptr->target_row; *x1 = p_ptr->target_col; } /* Otherwise, use the given direction */ else { *y1 = y0 + MAX_RANGE * ddy[dir]; *x1 = x0 + MAX_RANGE * ddx[dir]; } }
bool do_blow(int type) { int x = 0, y = 0; int dir; int m_idx = 0; /* For ergonomics sake, use currently targeted monster. This allows a macro of \e*tmaa or similar to pick an adjacent foe, while \emaa*t won't work, since get_rep_dir2() won't allow a target. */ if (use_old_target && target_okay()) { y = target_row; x = target_col; m_idx = cave[y][x].m_idx; if (m_idx) { if (m_list[m_idx].cdis > 1) m_idx = 0; else dir = 5; } } if (!m_idx) { if (!get_rep_dir2(&dir)) return FALSE; if (dir == 5) return FALSE; y = py + ddy[dir]; x = px + ddx[dir]; m_idx = cave[y][x].m_idx; if (!m_idx) { msg_print("There is no monster there."); return FALSE; } } if (m_idx) py_attack(y, x, type); return TRUE; }
/* * Player casts a orb spell that creates an effect, but does not affect anything else. */ bool fire_effect_orb(int typ, int dir, int dam, int rad) { int y1, x1; u32b flg = 0L; /* Add the ball bitflags */ flg |= (PROJECT_BOOM | PROJECT_WALL | PROJECT_EFCT | PROJECT_CLOUD); /* Get target */ adjust_target(dir, p_ptr->py, p_ptr->px, &y1, &x1); /* Add the STOP flag if appropriate */ if (!target_okay() || y1 != p_ptr->target_row || x1 != p_ptr->target_col) { flg |= (PROJECT_STOP); } /* Limit radius to nine (up to 256 grids affected) */ if (rad > 9) rad = 9; /* Cast a ball */ return (project(SOURCE_PLAYER, rad, p_ptr->py, p_ptr->px, y1, x1, dam, typ, flg, 0L, 10 + rad * 10)); }
/** * 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); } }
/* * Request a game command from the uI and carry out whatever actions * go along with it. */ void process_command(cmd_context ctx, bool no_request) { int idx; game_command cmd; /* If we've got a command to process, do it. */ if (cmd_get(ctx, &cmd, !no_request) == 0) { idx = cmd_idx(cmd.command); if (idx == -1) return; /* Do some sanity checking on those arguments that might have been declared as "unknown", such as directions and targets. */ switch (cmd.command) { case CMD_WALK: case CMD_RUN: case CMD_JUMP: case CMD_OPEN: case CMD_CLOSE: case CMD_TUNNEL: case CMD_DISARM: case CMD_BASH: case CMD_ALTER: case CMD_JAM: case CMD_MAKE_TRAP: { /* Direction hasn't been specified, so we ask for one. */ if (cmd.args[0].direction == DIR_UNKNOWN) { if (!get_rep_dir(&cmd.args[0].direction)) return; } break; } /* * These take an item number and a "target" as arguments, * though a target isn't always actually needed, so we'll * only prompt for it via callback if the item being used needs it. */ case CMD_USE_WAND: case CMD_USE_ROD: case CMD_QUAFF: case CMD_ACTIVATE: case CMD_READ_SCROLL: case CMD_FIRE: case CMD_THROW: case CMD_STEAL: { bool get_target = FALSE; if (cmd.command == CMD_FIRE || cmd.command == CMD_THROW || obj_needs_aim(object_from_item_idx(cmd.args[0].choice))) { if (cmd.args[1].direction == DIR_UNKNOWN) get_target = TRUE; if (cmd.args[1].direction == DIR_TARGET && !target_okay()) get_target = TRUE; } if (get_target && !get_aim_dir(&cmd.args[1].direction, FALSE)) return; break; } /* This takes a choice and a direction. */ case CMD_CAST: { bool get_target = FALSE; if (spell_needs_aim(cp_ptr->spell_book, cmd.args[0].choice)) { if (cmd.args[1].direction == DIR_UNKNOWN) get_target = TRUE; if (cmd.args[1].direction == DIR_TARGET && !target_okay()) get_target = TRUE; } if (get_target && !get_aim_dir(&cmd.args[1].direction, FALSE)) return; break; } default: { /* I can see the point of the compiler warning, but still... */ break; } } /* Command repetition */ if (game_cmds[idx].repeat_allowed) { /* Auto-repeat */ if (game_cmds[idx].auto_repeat_n > 0 && p_ptr->command_arg == 0 && p_ptr->command_rep == 0) p_ptr->command_arg = game_cmds[idx].auto_repeat_n; allow_repeated_command(); } repeat_prev_allowed = TRUE; if (game_cmds[idx].fn) game_cmds[idx].fn(cmd.command, cmd.args); } }
/* * Get an "aiming direction" (1,2,3,4,6,7,8,9 or 5) from the user. * * Return TRUE if a direction was chosen, otherwise return FALSE. * * The direction "5" is special, and means "use current target". Also DIR_TARGET * * This function tracks and uses the "global direction", and uses * that as the "desired direction", if it is set. * * Note that "Force Target", if set, will pre-empt user interaction, * if there is a usable target already set. * * Currently this function applies confusion directly. */ bool get_aim_dir(int *dp, bool target_trap) { /* Global direction */ int dir = 0; int old_dir; bool done = FALSE; int mode = TARGET_QUIET; if (target_trap) mode |= TARGET_TRAP; else mode |= TARGET_KILL; if (*dp == DIR_CLOSEST) { if (target_set_closest(mode)) { return(TRUE); } } /* Initialize */ (*dp) = 0; /* Hack -- auto-target if requested */ if (use_old_target && target_okay() && !dir) dir = DIR_TARGET; else ui_update_message_label(color_string("Please select a target.", TERM_L_RED)); ui_targeting_show(MODE_TARGETING_AIMING); /* Ask until satisfied */ while (!dir && !done) { ui_show_cursor(p_ptr->py, p_ptr->px); /* Get a command (or Cancel) */ UserInput input = ui_get_input(); // Paranoia if (input.mode == INPUT_MODE_NONE) break; if ((input.key == Qt::Key_Escape) || (input.key == Qt::Key_X)) { break; } // Do nothing if (input.mode == INPUT_MODE_MOUSE_WHEEL) { continue; } // Skip interactive mode and directly choose target. if (input.mode == INPUT_MODE_MOUSE_DOUBLE_CLICK) { if (set_selected_target(mode, input.y, input.x)) dir = DIR_TARGET; else if (set_selected_target(TARGET_GRID, input.y, input.x)) dir = DIR_TARGET; continue; } if (input.mode == INPUT_MODE_MOUSE_SINGLE_CLICK) { /* Calculate approximate angle */ if (target_set_interactive(mode, input.x, input.y)) dir = DIR_TARGET; else done = TRUE; continue; } /* Analyze */ switch (input.key) { case Qt::Key_M: case Qt::Key_Asterisk: { /* Set new target, use target if legal */ int mode = TARGET_KILL; if (target_trap) mode |= TARGET_TRAP; if (target_set_interactive(mode, -1, -1)) dir = DIR_TARGET; else done = TRUE; continue; } case Qt::Key_C: case Qt::Key_Comma: { /* Set to closest target */ if (target_set_closest(TARGET_KILL)) { dir = DIR_CLOSEST; continue; } break; } case Qt::Key_Question: { do_cmd_list_targeting_commands(); continue; } case Qt::Key_H: case Qt::Key_5: case Qt::Key_Period: case Qt::Key_Clear: { /* Use current target, if set and legal */ if (target_okay()) dir = DIR_TARGET; break; } default: { /* Possible direction */ dir = target_dir(input); break; } } /* Error */ if (!dir) color_message("Illegal aim direction!", TERM_ORANGE); } ui_targeting_hide(); ui_show_cursor(-1, -1); ui_clear_message_label(); /* No direction */ if (!dir) return (FALSE); /* Save the direction */ old_dir = dir; /* Check for confusion */ if (p_ptr->timed[TMD_CONFUSED]) { /* Random direction */ dir = ddd[randint0(8)]; } /* Notice confusion */ if (old_dir != dir) { /* Warn the user */ message(QString("You are confused.")); } /* Save direction */ (*dp) = dir; /* A "valid" direction was entered */ return (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); }
/* * Get an "aiming direction" (1,2,3,4,6,7,8,9 or 5) from the user. * * Return TRUE if a direction was chosen, otherwise return FALSE. * * The direction "5" is special, and means "use current target". * * This function tracks and uses the "global direction", and uses * that as the "desired direction", if it is set. * * Note that "Force Target", if set, will pre-empt user interaction, * if there is a usable target already set. * * Currently this function applies confusion directly. */ bool get_aim_dir(int *dp) { /* Global direction */ int dir = 0; ui_event_data ke; cptr p; /* Initialize */ (*dp) = 0; /* Hack -- auto-target if requested */ if (OPT(use_old_target) && target_okay() && !dir) dir = 5; /* Ask until satisfied */ while (!dir) { /* Choose a prompt */ if (!target_okay()) p = "Direction ('*' or <click> to target, \"'\" for closest, Escape to cancel)? "; else p = "Direction ('5' for target, '*' or <click> to re-target, Escape to cancel)? "; /* Get a command (or Cancel) */ if (!get_com_ex(p, &ke)) break; if (ke.type == EVT_MOUSE) { if (target_set_interactive (TARGET_KILL, KEY_GRID_X(ke), KEY_GRID_Y(ke))) dir = 5; } else if (ke.type == EVT_KBRD) { if (ke.key == '*') { /* Set new target, use target if legal */ if (target_set_interactive(TARGET_KILL, -1, -1)) dir = 5; } else if (ke.key == '\'') { /* Set to closest target */ if (target_set_closest(TARGET_KILL)) dir = 5; } else if (ke.key == 't' || ke.key == '5' || ke.key == '0' || ke.key == '.') { if (target_okay()) dir = 5; } else { /* Possible direction */ int keypresses_handled = 0; while (ke.key != 0) { int this_dir; /* XXX Ideally show and move the cursor here to indicate * the currently "Pending" direction. XXX */ this_dir = target_dir(ke.key); if (this_dir) dir = dir_transitions[dir][this_dir]; else break; if (lazymove_delay == 0 || ++keypresses_handled > 1) break; /* See if there's a second keypress within the defined * period of time. */ inkey_scan = lazymove_delay; ke = inkey_ex(); } } } /* Error */ if (!dir) bell("Illegal aim direction!"); } /* No direction */ if (!dir) return (FALSE); /* Save direction */ (*dp) = dir; /* Check for confusion */ if (p_ptr->timed[TMD_CONFUSED]) { /* Random direction */ dir = ddd[randint0(8)]; } /* Notice confusion */ if ((*dp) != dir) { /* Warn the user */ msg_print("You are confused."); } /* Save direction */ (*dp) = dir; /* A "valid" direction was entered */ return (TRUE); }
/* A special fetch(), that places item in player's inventory */ static bool _whip_fetch(int dir, int rng) { int ty, tx; cave_type *c_ptr; object_type *o_ptr; char o_name[MAX_NLEN]; /* Use a target */ if (dir == 5 && target_okay()) { tx = target_col; ty = target_row; if (distance(py, px, ty, tx) > rng) { msg_print("You can't fetch something that far away!"); return FALSE; } c_ptr = &cave[ty][tx]; /* We need an item to fetch */ if (!c_ptr->o_idx) { msg_print("There is no object at this place."); return TRUE; /* didn't work, but charge the player energy anyway */ } /* Fetching from a vault is OK */ /* Line of sight is required */ if (!player_has_los_bold(ty, tx)) { msg_print("You have no direct line of sight to that location."); return FALSE; } else if (!projectable(py, px, ty, tx)) { msg_print("You have no direct line of sight to that location."); return FALSE; } } else { /* Use a direction */ ty = py; /* Where to drop the item */ tx = px; do { ty += ddy[dir]; tx += ddx[dir]; c_ptr = &cave[ty][tx]; if ((distance(py, px, ty, tx) > MAX_RANGE) || !in_bounds(ty, tx) || !cave_have_flag_bold(ty, tx, FF_PROJECT)) { return TRUE; /* didn't work, but charge the player energy anyway */ } } while (!c_ptr->o_idx); } o_ptr = &o_list[c_ptr->o_idx]; if (o_ptr->weight > p_ptr->lev * 15) { msg_print("The object is too heavy."); return TRUE; /* didn't work, but charge the player energy anyway */ } object_desc(o_name, o_ptr, OD_NAME_ONLY); /* Get the object */ if (!inven_carry_okay(o_ptr)) { cmsg_format(TERM_VIOLET, "You fail to fetch %^s since your pack is full.", o_name); /* Leave the object where it is */ } else { msg_format("You skillfully crack your whip and fetch %^s.", o_name); py_pickup_aux(c_ptr->o_idx); } return TRUE; }
/** * Get an "aiming direction" (1,2,3,4,6,7,8,9 or 5) from the user. * * Return true if a direction was chosen, otherwise return false. * * The direction "5" is special, and means "use current target". * * This function tracks and uses the "global direction", and uses * that as the "desired direction", if it is set. * * Note that "Force Target", if set, will pre-empt user interaction, * if there is a usable target already set. */ bool textui_get_aim_dir(int *dp) { /* Global direction */ int dir = 0; ui_event ke; const char *p; /* Initialize */ (*dp) = 0; /* Hack -- auto-target if requested */ if (OPT(use_old_target) && target_okay() && !dir) dir = 5; /* Ask until satisfied */ while (!dir) { /* Choose a prompt */ if (!target_okay()) p = "Direction ('*' or <click> to target, \"'\" for closest, Escape to cancel)? "; else p = "Direction ('5' for target, '*' or <click> to re-target, Escape to cancel)? "; /* Get a command (or Cancel) */ if (!get_com_ex(p, &ke)) break; if (ke.type == EVT_MOUSE) { if (ke.mouse.button == 1) { if (target_set_interactive(TARGET_KILL, KEY_GRID_X(ke), KEY_GRID_Y(ke))) dir = 5; } else if (ke.mouse.button == 2) { break; } } else if (ke.type == EVT_KBRD) { if (ke.key.code == '*') { /* Set new target, use target if legal */ if (target_set_interactive(TARGET_KILL, -1, -1)) dir = 5; } else if (ke.key.code == '\'') { /* Set to closest target */ if (target_set_closest(TARGET_KILL)) dir = 5; } else if (ke.key.code == 't' || ke.key.code == '5' || ke.key.code == '0' || ke.key.code == '.') { if (target_okay()) dir = 5; } else { /* Possible direction */ int keypresses_handled = 0; while (ke.key.code != 0) { int this_dir; /* XXX Ideally show and move the cursor here to indicate * the currently "Pending" direction. XXX */ this_dir = target_dir(ke.key); if (this_dir) dir = dir_transitions[dir][this_dir]; else break; if (op_ptr->lazymove_delay == 0 || ++keypresses_handled > 1) break; /* See if there's a second keypress within the defined * period of time. */ inkey_scan = op_ptr->lazymove_delay; ke = inkey_ex(); } } } /* Error */ if (!dir) bell("Illegal aim direction!"); } /* No direction */ if (!dir) return (false); /* Save direction */ (*dp) = dir; /* A "valid" direction was entered */ return (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); }
static bool _necro_do_touch(int type, int dice, int sides, int base) { int x, y; int dir = 0; int m_idx = 0; if (!_necro_check_touch()) return FALSE; /* For ergonomics sake, use currently targeted monster. This allows a macro of \e*tmaa or similar to pick an adjacent foe, while \emaa*t won't work, since get_rep_dir2() won't allow a target. */ if (use_old_target && target_okay()) { y = target_row; x = target_col; m_idx = cave[y][x].m_idx; if (m_idx) { if (m_list[m_idx].cdis > 1) m_idx = 0; else dir = 5; /* Hack so that fire_ball() works correctly */ } } if (!m_idx) { if (!get_rep_dir2(&dir)) return FALSE; if (dir == 5) return FALSE; y = py + ddy[dir]; x = px + ddx[dir]; m_idx = cave[y][x].m_idx; if (!m_idx) { msg_print("There is no monster there."); return FALSE; } } if (m_idx) { int dam; monster_type *m_ptr = &m_list[m_idx]; if (!is_hostile(m_ptr) && !(p_ptr->stun || p_ptr->confused || p_ptr->image || IS_SHERO() || !m_ptr->ml)) { if (!get_check("Really hit it? ")) return FALSE; } dam = _necro_damroll(dice, sides, base); on_p_hit_m(m_idx); touch_zap_player(m_idx); if (fire_ball(type, dir, dam, 0)) { if (type == GF_OLD_DRAIN) hp_player(dam); } } return 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(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); }
static void _bite_spell(int cmd, variant *res) { switch (cmd) { case SPELL_NAME: var_set_string(res, "Vampiric Bite"); break; case SPELL_DESC: var_set_string(res, "As a vampire, you must feed on fresh blood in order to sustain your unlife!"); break; case SPELL_INFO: var_set_string(res, info_damage(0, 0, _bite_amt())); break; case SPELL_CAST: var_set_bool(res, FALSE); if (d_info[dungeon_type].flags1 & DF1_NO_MELEE) { msg_print("Something prevents you from attacking."); return; } else { int x = 0, y = 0, amt, m_idx = 0; int dir = 0; if (use_old_target && target_okay()) { y = target_row; x = target_col; m_idx = cave[y][x].m_idx; if (m_idx) { if (m_list[m_idx].cdis > 1) m_idx = 0; else dir = 5; } } if (!m_idx) { if (!get_rep_dir2(&dir)) return; if (dir == 5) return; y = py + ddy[dir]; x = px + ddx[dir]; m_idx = cave[y][x].m_idx; if (!m_idx) { msg_print("There is no monster there."); return; } } var_set_bool(res, TRUE); msg_print("You grin and bare your fangs..."); amt = _bite_amt(); vampiric_drain_hack = TRUE; if (project(0, 0, y, x, amt, GF_OLD_DRAIN, PROJECT_STOP | PROJECT_KILL | PROJECT_THRU, -1)) { vampire_feed(amt); } else msg_print("Yechh. That tastes foul."); vampiric_drain_hack = FALSE; } break; case SPELL_COST_EXTRA: var_set_int(res, MIN(_bite_amt() / 10, 29)); break; default: default_spell(cmd, res); break; } }