/*Place the effect in a stack of effects.*/ static void place_effect_idx(int x_idx, int y, int x) { /* Get this effect */ effect_type *x_ptr = &x_list[x_idx]; /* * Handle next_x_idx. Traps (and glyphs) always have first priority. */ if (cave_any_trap_bold(y, x)) { x_ptr->next_x_idx = x_list[cave_x_idx[y][x]].next_x_idx; x_list[cave_x_idx[y][x]].next_x_idx = x_idx; } else { x_ptr->next_x_idx = cave_x_idx[y][x]; cave_x_idx[y][x] = x_idx; } /* Update some CAVE_* flags */ update_los_proj_move(y, x); /* Redraw grid if necessary */ if (character_dungeon && !(x_ptr->x_flags & (EF1_HIDDEN))) { light_spot(y, x); } }
/* * Create magical stairs after finishing a quest monster. */ static void build_quest_stairs(int y, int x) { int ny, nx; /* Stagger around */ while (!cave_clean_bold(y, x)) { /* Pick a location */ scatter(&ny, &nx, y, x, 5, 1); /* Stagger */ y = ny; x = nx; } /* Destroy any objects */ delete_object(y, x); /* Explain the staircase */ msg_print("A magical staircase appears..."); /* Create stairs down */ cave_set_feat(y, x, FEAT_MORE); light_spot(y, x); /* Update the visuals */ p_ptr->update |= (PU_UPDATE_VIEW | PU_MONSTERS); }
static void hp_colour_change(game_event_type type, game_event_data *data, void *user) { /* * hack: redraw player, since the player's color * now indicates approximate health. Note that * using this command when graphics mode is on * causes the character to be a black square. */ if ((OPT(hp_changes_color)) && (arg_graphics == GRAPHICS_NONE)) { light_spot(p_ptr->py, p_ptr->px); } }
/** * Mark an object's flavour as as one the player is aware of. * * \param o_ptr is the object whose flavour should be marked as aware */ void object_flavor_aware(object_type *o_ptr) { int i; if (o_ptr->kind->aware) return; o_ptr->kind->aware = TRUE; /* Fix squelch/autoinscribe */ p_ptr->notice |= PN_SQUELCH; apply_autoinscription(o_ptr); for (i = 1; i < o_max; i++) { const object_type *floor_o_ptr = &o_list[i]; /* Some objects change tile on awareness */ /* So update display for all floor objects of this kind */ if (!floor_o_ptr->held_m_idx && floor_o_ptr->k_idx == o_ptr->k_idx) light_spot(floor_o_ptr->iy, floor_o_ptr->ix); } }
/* * Attempt to open the given chest at the given location * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_open_chest(int y, int x, s16b o_idx) { int i, j; bool flag = TRUE; bool more = FALSE; object_type *o_ptr = &o_list[o_idx]; /* Attempt to unlock it */ if (o_ptr->pval > 0) { /* Assume locked, and thus not open */ flag = FALSE; /* Get the "disarm" factor */ i = p_ptr->state.skills[SKILL_DISARM]; /* Penalize some conditions */ if (p_ptr->timed[TMD_BLIND] || no_light()) i = i / 10; if (p_ptr->timed[TMD_CONFUSED] || p_ptr->timed[TMD_IMAGE]) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success -- May still have traps */ if (randint0(100) < j) { message(MSG_LOCKPICK, 0, "You have picked the lock."); gain_exp(1); flag = TRUE; } /* Failure -- Keep trying */ else { /* We may continue repeating */ more = TRUE; flush(); message(MSG_LOCKPICK_FAIL, 0, "You failed to pick the lock."); } } /* Allowed to open */ if (flag) { /* Apply chest traps, if any */ chest_trap(y, x, o_idx); /* Let the Chest drop items */ chest_death(y, x, o_idx); /* Squelch chest if autosquelch calls for it */ p_ptr->notice |= PN_SQUELCH; /* Redraw chest, to be on the safe side (it may have been squelched) */ light_spot(y, x); } /* Result */ return (more); }
/** * Move player in the given direction, with the given "pickup" flag. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/etc. */ void move_player(int dir) { int py = p_ptr->py; int px = p_ptr->px; byte str_escape, dex_escape; /* Permit the player to move? */ bool can_move = FALSE; /* Player is jumping off a cliff */ bool falling = FALSE; /* Player hits a trap (always unless flying) */ bool trapped = TRUE; int temp; int y, x; /* Find the result of moving */ y = py + ddy[dir]; x = px + ddx[dir]; /* Hack -- attack monsters */ if (cave_m_idx[y][x] > 0) { /* Attack */ if (py_attack(y, x, TRUE)) return; } /* It takes some dexterity, or failing that strength, to get out of pits */ if (cave_feat[p_ptr->py][p_ptr->px] == (FEAT_TRAP_HEAD + 0x01)) { str_escape = adj_dex_dis[p_ptr->state.stat_ind[A_STR]]; dex_escape = adj_dex_dis[p_ptr->state.stat_ind[A_DEX]]; /* First attempt to leap out of the pit, */ if ((dex_escape + 1) * 2 < randint1(16)) { /* then attempt to climb out of the pit. */ if (str_escape + 3 < randint1(16)) { /* Failure costs a turn. */ msg_print("You remain stuck in the pit."); return; } else msg_print("You clamber out of the pit."); } else msg_print("You leap out of the pit."); } /* Option to disarm a visible trap. -TNB- */ /* Hack - Rogues can walk over their own trap - BR */ if (OPT(easy_alter) && (cave_feat[y][x] >= FEAT_TRAP_HEAD) && (cave_feat[y][x] <= FEAT_TRAP_TAIL)) { bool more = FALSE; /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); more = do_cmd_disarm_aux(y, x); /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); return; } /* Some terrain is impassable for the player, such as stone walls. */ else if (!cave_passable_bold(y, x)) { /* Disturb the player */ disturb(0, 0); /* Notice unknown obstacles */ if (!(cave_info[y][x] & (CAVE_MARK))) { /* Closed door */ if (cave_feat[y][x] < FEAT_SECRET) { message(MSG_HITWALL, 0, "You feel a door blocking your way."); cave_info[y][x] |= (CAVE_MARK); light_spot(y, x); } /* Wall (or secret door) */ else { message(MSG_HITWALL, 0, "You feel a wall blocking your way."); cave_info[y][x] |= (CAVE_MARK); light_spot(y, x); } } /* Mention known obstacles */ else { /* Closed door */ if (cave_feat[y][x] < FEAT_SECRET) { /* Option to automatically open doors. -TNB- */ if (OPT(easy_alter)) { bool more = FALSE; /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); /* Open the door */ more = do_cmd_open_aux(y, x); /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); return; } /* Otherwise, a message. */ message(MSG_HITWALL, 0, "There is a door blocking your way."); } /* Wall (or secret door) */ else { message(MSG_HITWALL, 0, "There is a wall blocking your way."); } } /* Sound */ sound(MSG_HITWALL); } /* Normal movement */ else { /*** Handle traversable terrain. ***/ switch (cave_feat[y][x]) { case FEAT_RUBBLE: { /* Dwarves move easily through rubble */ if (player_has(PF_DWARVEN)) can_move = TRUE; /* Bats, dragons can fly */ else if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) can_move = TRUE; else if (player_is_crossing == dir) { can_move = TRUE; player_is_crossing = 0; } else { player_is_crossing = dir; cmd_insert(CMD_WALK); } break; } case FEAT_TREE: case FEAT_TREE2: { /* Druids, rangers, elves and ents (SJGU) slip easily under * trees */ if (((player_has(PF_WOODSMAN)) || (player_has(PF_ELVEN))) || (player_has(PF_WOODEN))) can_move = TRUE; /* Bats, dragons can fly */ else if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) can_move = TRUE; /* Allow movement only if partway through already. */ else if (player_is_crossing == dir) { can_move = TRUE; player_is_crossing = 0; } else { player_is_crossing = dir; cmd_insert(CMD_WALK); } break; } case FEAT_WATER: /* Water now slows rather than stopping -NRM- */ { /* Stop any run. */ disturb(0, 0); can_move = TRUE; /* Speed will need updating */ p_ptr->update |= PU_BONUS; break; } case FEAT_LAVA: { /* Assume player will continue. */ temp = TRUE; /* Smart enough to stop running. */ if (p_ptr->running) { if (!get_check("Lava blocks your path. Step into it? ")) { temp = FALSE; p_ptr->running = 0; } } /* Smart enough to sense trouble. */ else if ((!p_resist_pos(P_RES_FIRE)) || (!p_resist_strong(P_RES_FIRE) && (p_ptr->chp <= 100)) || (!p_immune(P_RES_FIRE) && (p_ptr->chp <= 30))) { if (!get_check ("The heat of the lava scalds you! Really enter? ")) { temp = FALSE; } } /* Enter if OK or confirmed. */ if (temp) { /* Can always cross. */ can_move = TRUE; /* Feather fall makes one lightfooted. */ if (p_ptr->state.ffall) { notice_obj(OF_FEATHER, 0); temp = 49 + randint1(51); } else temp = 124 + randint1(126); /* Will take serious fire damage. */ fire_dam(temp, "burnt to a cinder in molten lava"); } break; } case FEAT_VOID: { /* Bats, dragons can fly */ if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) can_move = TRUE; else { /* Assume player will continue. */ temp = TRUE; /* Smart enough to stop running. */ if (p_ptr->running) { if (!get_check ("You have come to a cliff. Step off it? ")) { temp = FALSE; p_ptr->running = 0; } } /* Smart enough to sense trouble. */ else if (!p_ptr->timed[TMD_BLIND]) { if (!get_check("It's a cliff! Really step off it? ")) { temp = FALSE; } } /* Step off if confirmed. */ if (temp) { /* Can always jump. */ can_move = TRUE; /* Will take serious damage. */ falling = TRUE; } } break; } default: { /* All other terrain can be traversed normally. */ can_move = TRUE; } } /* If the player can move, handle various things. */ if (can_move) { /* Move player */ monster_swap(py, px, y, x); /* Update speed if stepping out of water */ if (cave_feat[py][px] == FEAT_WATER) p_ptr->update |= PU_BONUS; /* Update stealth for Unlight */ if (player_has(PF_UNLIGHT)) p_ptr->update |= PU_BONUS; /* Superstealth for ents in trees SJGU */ if ((player_has(PF_WOODEN)) && (tf_has (f_info[cave_feat[p_ptr->py][p_ptr->px]].flags, TF_TREE))) { if (!(tf_has(f_info[cave_feat[py][px]].flags, TF_TREE)) || !(p_ptr->timed[TMD_SSTEALTH])) { (void) inc_timed(TMD_SSTEALTH, 1, FALSE); p_ptr->update |= (PU_BONUS); } } else if ((player_has(PF_WOODEN)) && (tf_has(f_info[cave_feat[py][px]].flags, TF_TREE))) { if (p_ptr->timed[TMD_SSTEALTH]) { (void) dec_timed(TMD_SSTEALTH, 1, FALSE); p_ptr->update |= (PU_BONUS); } } /* New location */ y = py = p_ptr->py; x = px = p_ptr->px; /* No longer traversing. */ player_is_crossing = 0; /* Fall off a cliff */ if (falling) fall_off_cliff(); /* Spontaneous Searching */ if (p_ptr->state.skills[SKILL_SEARCH_FREQUENCY] > 49) { (void) search(FALSE); } else if (0 == randint0(50 - p_ptr->state.skills[SKILL_SEARCH_FREQUENCY])) { (void) search(FALSE); } /* Continuous Searching */ if (p_ptr->searching) { (void) search(FALSE); } /* Handle "store doors" */ if ((cave_feat[y][x] >= FEAT_SHOP_HEAD) && (cave_feat[y][x] <= FEAT_SHOP_TAIL)) { /* Disturb */ disturb(0, 0); cmd_insert(CMD_ENTER_STORE); } /* All other grids (including traps) */ else { /* Handle objects (later) */ p_ptr->notice |= (PN_PICKUP); } /* Flying players have a chance to miss traps */ if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) { if (((cave_feat[y][x] == FEAT_INVIS) || (cave_feat[y][x] == FEAT_GRASS_INVIS)) && (randint0(3) != 0)) trapped = FALSE; else if ((cave_feat[y][x] >= FEAT_TRAP_HEAD) && (cave_feat[y][x] <= FEAT_TRAP_TAIL) && (randint0(10) != 0)) trapped = FALSE; } /* Discover invisible traps */ else if (((cave_feat[y][x] == FEAT_INVIS) || (cave_feat[y][x] == FEAT_GRASS_INVIS) || (cave_feat[y][x] == FEAT_TREE_INVIS) || (cave_feat[y][x] == FEAT_TREE2_INVIS)) && trapped) { /* Disturb */ disturb(0, 0); /* Message */ msg_print("You stumble upon a trap!"); /* Pick a trap */ pick_trap(y, x); /* Hit the floor trap. */ hit_trap(y, x); } /* Set off a visible trap */ else if ((cave_feat[y][x] >= FEAT_TRAP_HEAD) && (cave_feat[y][x] <= FEAT_TRAP_TAIL) && trapped) { /* Disturb */ disturb(0, 0); /* Hit the floor trap. */ hit_trap(y, x); } /* Walk on a monster trap */ else if ((cave_feat[y][x] >= FEAT_MTRAP_HEAD) && (cave_feat[y][x] <= FEAT_MTRAP_TAIL)) { msg_print("You inspect your cunning trap."); } } } }
/** * Rogues may set traps. Only one such trap may exist at any one time, * but an old trap can be disarmed to free up equipment for a new trap. * -LM- */ extern bool py_set_trap(int y, int x) { int max_traps; s16b this_o_idx, next_o_idx = 0; object_type *o_ptr; bool destroy_message = FALSE; max_traps = 1 + ((p_ptr->lev >= 25) ? 1 : 0) + (player_has(PF_EXTRA_TRAP) ? 1 : 0); if (p_ptr->timed[TMD_BLIND] || no_light()) { msg_print("You can not see to set a trap."); return FALSE; } if (p_ptr->timed[TMD_CONFUSED] || p_ptr->timed[TMD_IMAGE]) { msg_print("You are too confused."); return FALSE; } /* Paranoia -- Forbid more than max_traps being set. */ if (num_trap_on_level >= max_traps) { msg_print ("You must disarm your existing trap to free up your equipment."); return FALSE; } /* No setting traps while shapeshifted */ if (SCHANGE) { msg_print("You can not set traps while shapechanged."); msg_print("Use the ']' command to return to your normal form."); return FALSE; } /* Scan all objects in the grid */ for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx) { /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Artifact */ if (o_ptr->name1) { msg_print("There is an indestructible object here."); return FALSE; } /* Visible object to be destroyed */ if (!squelch_hide_item(o_ptr)) destroy_message = TRUE; } /* Verify */ if (cave_o_idx[y][x]) { if (destroy_message) if (!get_check("Destroy all items and set a trap?")) return FALSE; for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx) { /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Delete the object */ delete_object_idx(this_o_idx); } /* Redraw */ light_spot(y, x); } /* Set the trap, and draw it. */ cave_set_feat(y, x, FEAT_MTRAP_BASE); /* Notify the player. */ msg_print("You set a monster trap."); /* Increment the number of monster traps. */ num_trap_on_level++; /* A trap has been set */ 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); }
/* * 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); }
/** * Move player in the given direction, with the given "pickup" flag. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/etc. */ void move_player(int dir, bool no_options) { unsigned int py = p_ptr->py; unsigned int px = p_ptr->px; byte str_escape, dex_escape; /* Permit the player to move? */ bool can_move = FALSE; /* Player is jumping off a cliff */ bool falling = FALSE; /* Player hits a trap (always unless flying) */ bool trapped = TRUE; /* Sliding on the ice */ bool icy_slide = FALSE; int temp; unsigned int y, x; feature_type *f_ptr; /* Find the result of moving */ y = py + ddy[dir]; x = px + ddx[dir]; f_ptr = &f_info[cave_feat[y][x]]; /* Hack -- attack monsters */ if (cave_m_idx[y][x] > 0) { /* Attack */ if (py_attack(y, x, TRUE)) return; } /* It takes some dexterity, or failing that strength, to get out of pits */ if (cave_feat[py][px] == FEAT_PIT) { str_escape = adj_dex_dis[p_ptr->state.stat_ind[A_STR]]; dex_escape = adj_dex_dis[p_ptr->state.stat_ind[A_DEX]]; /* First attempt to leap out of the pit, */ if ((dex_escape + 1) * 2 < randint1(16)) { /* then attempt to climb out of the pit. */ if (str_escape + 3 < randint1(16)) { /* Failure costs a turn. */ msg("You remain stuck in the pit."); /* Failure clears movement */ p_ptr->previous_action[0] = ACTION_NOTHING; return; } else msg("You clamber out of the pit."); } else msg("You leap out of the pit."); } /* Rooted players cannot move */ if (p_ptr->timed[TMD_ROOT]) { can_move = FALSE; msg("You are rooted to the ground and can't move."); /* Prevent repeated attempts */ disturb(0, 0); return; } /* Option to disarm a visible trap. -TNB- */ /* Hack - Rogues can walk over their own trap - BR */ else if (cave_visible_trap(y, x) && cave_player_trap(y, x) && OPT(easy_alter)) { bool more = FALSE; /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); more = do_cmd_disarm_aux(y, x); /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); return; } /* Some terrain is impassable for the player, such as stone walls. */ else if (!tf_has(f_ptr->flags, TF_PASSABLE)) { /* Disturb the player */ disturb(0, 0); /* Notice unknown obstacles */ if (!sqinfo_has(cave_info[y][x], SQUARE_MARK)) { /* Closed door */ if (tf_has(f_ptr->flags, TF_DOOR_CLOSED)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); sqinfo_on(cave_info[y][x], SQUARE_MARK); light_spot(y, x); } /* Wall (or secret door) */ else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); sqinfo_on(cave_info[y][x], SQUARE_MARK); light_spot(y, x); } } /* Mention known obstacles */ else { /* Closed door */ if (tf_has(f_ptr->flags, TF_DOOR_CLOSED)) { /* Option to automatically open doors. -TNB- */ if (OPT(easy_alter)) { bool more = FALSE; /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); /* Open the door */ more = do_cmd_open_aux(y, x); /* Cancel repeat unless we may continue */ if (!more) disturb(0, 0); /* Clear action list */ p_ptr->previous_action[0] = ACTION_NOTHING; return; } /* Otherwise, a message. */ msgt(MSG_HITWALL, "There is a door blocking your way."); } /* Wall (or secret door) */ else { msgt(MSG_HITWALL, "There is a wall blocking your way."); } } /* Sound */ sound(MSG_HITWALL); } /* Normal movement */ else { /* Assume terrain can be traversed normally. */ can_move = TRUE; /* Terrain blocked by a friendly monster */ if (cave_m_idx[y][x] > 0) { monster_type *n_ptr = & m_list[cave_m_idx[y][x]]; /* Push monster if it doesn't have a target and hasn't been pushed. * This allows the player to move into a corridor with a monster in * front of him, and have the monster move ahead, if it is faster. If its * not faster, the player will push over it on the second move, as the push * flag below will have been set. */ if(((n_ptr->mflag & MFLAG_PUSH) == 0) && !(n_ptr->ty) && !(n_ptr->tx) && push_aside_player(p_ptr->py, p_ptr->px, n_ptr)) { int dy = n_ptr->fy - y; int dx = n_ptr->fx - x; unsigned int count = 0; n_ptr->ty = n_ptr->fy; n_ptr->tx = n_ptr->fx; /* Hack -- get new target as far as the monster can move in the direction * pushed. We do this with a walking stick approach to prevent us getting * invalid target locations like (0,0) */ while (in_bounds_fully(n_ptr->ty + dy, n_ptr->tx + dx) && cave_exist_mon(&r_info[n_ptr->r_idx], n_ptr->ty + dy, n_ptr->tx + dx, TRUE) && (count++ < (MAX_SIGHT / 2))) { n_ptr->ty = n_ptr->ty + dy; n_ptr->tx = n_ptr->tx + dx; } /* Clear target if none available */ if ((n_ptr->ty == n_ptr->fy) && (n_ptr->tx == n_ptr->fx)) { n_ptr->ty = 0; n_ptr->tx = 0; } } /* The other monster cannot switch places */ else if (!cave_exist_mon(&r_info[n_ptr->r_idx], p_ptr->py, p_ptr->px, TRUE)) { /* Try to push it aside. Allow aborting of move if an ally */ if ((!push_aside_player(p_ptr->py, p_ptr->px, n_ptr)) && (get_reaction(F_PLAYER, n_ptr->faction) <= REACT_FRIEND)) { /* No warning if sliding */ if (no_options) {} /* Don't provide more warning */ else if (!get_check("Are you sure?")) { temp = FALSE; p_ptr->running = 0; can_move = FALSE; } } } /* Hack -- we clear the target if we move over a monster */ else { n_ptr->ty = 0; n_ptr->tx = 0; } /* Mark monsters as pushed */ n_ptr->mflag |= (MFLAG_PUSH); } /*** Handle traversable terrain. ***/ if (tf_has(f_ptr->flags, TF_ROCK)) { /* Dwarves move easily through rubble */ if (player_has(PF_DWARVEN)) can_move = TRUE; /* Bats, dragons can fly */ else if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) can_move = TRUE; /* Require more energy */ else { can_move = TRUE; p_ptr->energy_use += 100; } } if (tf_has(f_ptr->flags, TF_TREE)) { /* Druids, rangers, Plant Cutie Marks slip easily under * trees */ if ((player_has(PF_WOODSMAN)) || (player_has(PF_PLANT_FRIEND))) can_move = TRUE; /* Bats, dragons can fly */ else if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) can_move = TRUE; /* Require more energy */ else { can_move = TRUE; p_ptr->energy_use += 100; } } /* Water now slows rather than stopping -NRM- */ if (tf_has(f_ptr->flags, TF_WATERY)) { /* Stop any run. */ disturb(0, 0); can_move = TRUE; /* Speed will need updating */ p_ptr->update |= PU_BONUS; } /* Walking on to ice can cause you to slide an additional square. */ if (tf_has(f_ptr->flags, TF_ICY)) { /* Stop any run */ disturb(0, 0); can_move = TRUE; /* Slide is less likely with Cold Resist. Never slide with Levitation */ if (!p_ptr->state.ffall && ((!p_immune(P_RES_COLD)) && (randint1((p_resist_pos(P_RES_COLD) || p_resist_strong(P_RES_COLD)) ? 2 : 4) != 1))) icy_slide = TRUE; /* Speed will need updating */ p_ptr->update |= PU_BONUS; } if (tf_has(f_ptr->flags, TF_FIERY)) { /* Assume player will continue. */ temp = TRUE; /* Smart enough to stop running. */ if (p_ptr->running) { /* Ice keeps sliding */ if (no_options) {} else if (!get_check("Lava blocks your path. Step into it? ")) { temp = FALSE; p_ptr->running = 0; } } /* Smart enough to sense trouble. */ else if ((!p_resist_pos(P_RES_FIRE)) || (!p_resist_strong(P_RES_FIRE) && (p_ptr->chp <= 100)) || (!p_immune(P_RES_FIRE) && (p_ptr->chp <= 30))) { /* Sliding continues regardless */ if (no_options) {} else if (!get_check ("The heat of the lava scalds you! Really enter? ")) { temp = FALSE; } } /* Enter if OK or confirmed. */ if (temp) { /* Can always cross. */ can_move = TRUE; /* Feather fall makes one lightfooted. */ if (p_ptr->state.ffall) { notice_obj(OF_FEATHER, 0); temp = 49 + randint1(51); } else temp = 124 + randint1(126); /* Will take serious fire damage. */ fire_dam(temp, "burnt to a cinder in molten lava", SOURCE_ENVIRONMENTAL); } else /* Player refuse to go. */ can_move = FALSE; } if (tf_has(f_ptr->flags, TF_BURNING)) { /* Assume player will continue */ temp = TRUE; /* Smart enough to stop running */ if (p_ptr->running) { if (no_options) {} else if (!get_check("Your path is block by a burning tree. Step into it? ")) { temp = FALSE; p_ptr->running = 9; } } /* Smart enough to sense trouble */ else if ((!p_resist_pos(P_RES_FIRE)) || (!p_resist_strong(P_RES_FIRE) && (p_ptr->chp <= 100)) || (!p_immune(P_RES_FIRE) && (p_ptr->chp <= 30))) { if (no_options) {} else if (!get_check ("The heat of the fire burns you! Really enter? ")) { temp = FALSE; } } /* Enter if OK or confirmed. */ if (temp) { /* Can always cross. */ can_move = TRUE; /* Take light damage from the fire */ temp = 49 + randint1(51); /* Will take serious fire damage. */ fire_dam(temp, "burnt to death in a fire.", SOURCE_ENVIRONMENTAL); } else /* Player refuse to go. */ can_move = FALSE; } if (tf_has(f_ptr->flags, TF_FALL)) { /* Bats, dragons can fly */ if (!(p_ptr->schange == SHAPE_BAT) && !(p_ptr->schange == SHAPE_WYRM)) { /* Assume player will continue. */ temp = TRUE; /* Smart enough to stop running. */ if (p_ptr->running) { if (no_options) {} else if (!get_check ("You have come to a cliff. Step off it? ")) { can_move = FALSE; temp = FALSE; p_ptr->running = 0; } } /* Smart enough to sense trouble. */ else if (!p_ptr->timed[TMD_BLIND]) { if (no_options) {} else if (!get_check("It's a cliff! Really step off it? ")) { can_move = FALSE; temp = FALSE; } } /* Step off if confirmed. */ if (temp) { /* Will take serious damage. */ falling = TRUE; } } } } /* If the player can move, handle various things. */ if (can_move) { /* Move player */ monster_swap(py, px, y, x); /* Update speed if stepping out of water */ if (tf_has(f_info[cave_feat[py][px]].flags, TF_WATERY)) p_ptr->update |= PU_BONUS; /* Update stealth for Unlight */ if (player_has(PF_UNLIGHT)) p_ptr->update |= PU_BONUS; /* Update speed for Plant cutie mark woodspersons */ if (player_has(PF_WOODSMAN) && player_has(PF_PLANT_FRIEND)) p_ptr->update |= PU_BONUS; /* New location */ y = py = p_ptr->py; x = px = p_ptr->px; f_ptr = &f_info[cave_feat[y][x]]; /* Fall off a cliff */ if (falling) fall_off_cliff(); /* Sliding on ice prevents searching */ if (!icy_slide) { /* Spontaneous Searching */ if (p_ptr->state.skills[SKILL_SEARCH_FREQUENCY] > 49) { (void) search(FALSE); } else if (0 == randint0(50 - p_ptr->state.skills[SKILL_SEARCH_FREQUENCY])) { (void) search(FALSE); } /* Continuous Searching */ if (p_ptr->searching) { (void) search(FALSE); } } /* Handle "store doors" */ if (tf_has(f_ptr->flags, TF_SHOP)) { /* Disturb */ disturb(0, 0); cmd_insert(CMD_ENTER_STORE); } /* All other grids (including traps) */ else { /* Handle objects (later) */ p_ptr->notice |= (PN_PICKUP); } /* Flying players have a chance to miss traps */ if ((p_ptr->schange == SHAPE_BAT) || (p_ptr->schange == SHAPE_WYRM)) { if (cave_invisible_trap(y, x) && cave_player_trap(y, x) && (randint0(3) != 0)) trapped = FALSE; else if (cave_visible_trap(y, x) && cave_player_trap(y, x) && (randint0(10) != 0)) trapped = FALSE; } /* Discover invisible traps */ if (cave_invisible_trap(y, x) && trapped) { /* Disturb */ disturb(0, 0); /* Hit the trap. */ hit_trap(y, x); } /* Set off a visible trap */ else if (cave_visible_trap(y, x) && cave_player_trap(y, x) && trapped) { /* Disturb */ disturb(0, 0); /* Hit the trap. */ hit_trap(y, x); } /* Walk on a monster trap */ else if (cave_monster_trap(y, x)) { msg("You inspect your cunning trap."); } /* Slide an additional square on the ice */ if (icy_slide) move_player(dir, TRUE); } }