diff options
Diffstat (limited to 'src/cmd1.cc')
-rw-r--r-- | src/cmd1.cc | 5147 |
1 files changed, 5147 insertions, 0 deletions
diff --git a/src/cmd1.cc b/src/cmd1.cc new file mode 100644 index 00000000..ff493174 --- /dev/null +++ b/src/cmd1.cc @@ -0,0 +1,5147 @@ +/* File: cmd1.c */ + +/* Purpose: Movement commands (part 1) */ + +/* + * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke + * + * This software may be copied and distributed for educational, research, and + * not for profit purposes provided that this copyright and statement are + * included in all such copies. + */ + +#include "angband.h" + +#include "quark.h" +#include "hooks.h" + +#define MAX_VAMPIRIC_DRAIN 100 + + +/* + * Determine if the player "hits" a monster (normal combat). + * Note -- Always miss 5%, always hit 5%, otherwise random. + */ +bool_ test_hit_fire(int chance, int ac, int vis) +{ + int k; + + + /* Percentile dice */ + k = rand_int(100); + + /* Hack -- Instant miss or hit */ + if (k < 10) return (k < 5); + + /* Never hit */ + if (chance <= 0) return (FALSE); + + /* Invisible monsters are harder to hit */ + if (!vis) chance = (chance + 1) / 2; + + /* Power competes against armor */ + if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE); + + /* Assume hit */ + return (TRUE); +} + + + +/* + * Determine if the player "hits" a monster (normal combat). + * + * Note -- Always miss 5%, always hit 5%, otherwise random. + */ +bool_ test_hit_norm(int chance, int ac, int vis) +{ + int k; + + + /* Percentile dice */ + k = rand_int(100); + + /* Hack -- Instant miss or hit */ + if (k < 10) return (k < 5); + + /* Wimpy attack never hits */ + if (chance <= 0) return (FALSE); + + /* Penalize invisible targets */ + if (!vis) chance = (chance + 1) / 2; + + /* Power must defeat armor */ + if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE); + + /* Assume hit */ + return (TRUE); +} + + + +/* + * Critical hits (from objects thrown by player) + * Factor in item weight, total plusses, and player level. + */ +s16b critical_shot(int weight, int plus, int dam, int skill) +{ + int i, k; + + + /* Extract "shot" power */ + i = (weight + ((p_ptr->to_h + plus) * 4) + + get_skill_scale(skill, 100)); + i += 50 * p_ptr->xtra_crit; + i += luck( -100, 100); + + /* Critical hit */ + if (randint(5000) <= i) + { + k = weight + randint(500); + + if (k < 500) + { + msg_print("It was a good hit!"); + dam = 2 * dam + 5; + } + else if (k < 1000) + { + msg_print("It was a great hit!"); + dam = 2 * dam + 10; + } + else + { + msg_print("It was a superb hit!"); + dam = 3 * dam + 15; + } + } + + return (dam); +} + +/* + * Critical hits (by player) + * + * Factor in weapon weight, total plusses, player level. + */ +s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool_ *done_crit) +{ + int i, k, num = randint(5000); + + *done_crit = FALSE; + + /* Extract "blow" power */ + i = (weight + ((p_ptr->to_h + plus) * 5) + + get_skill_scale(p_ptr->melee_style, 150)); + i += 50 * p_ptr->xtra_crit; + if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS)) + { + i += get_skill_scale(SKILL_CRITS, 40 * 50); + } + i += luck( -100, 100); + + /* Force good strikes */ + if (p_ptr->tim_deadly) + { + set_tim_deadly(p_ptr->tim_deadly - 1); + msg_print("It was a *GREAT* hit!"); + dam = 3 * dam + 20; + *done_crit = TRUE; + } + + /* Chance */ + else if (num <= i) + { + k = weight + randint(650); + if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS)) + { + k += get_skill_scale(SKILL_CRITS, 400); + } + + if (k < 400) + { + msg_print("It was a good hit!"); + dam = 2 * dam + 5; + } + else if (k < 700) + { + msg_print("It was a great hit!"); + dam = 2 * dam + 10; + } + else if (k < 900) + { + msg_print("It was a superb hit!"); + dam = 3 * dam + 15; + } + else if (k < 1300) + { + msg_print("It was a *GREAT* hit!"); + dam = 3 * dam + 20; + } + else + { + msg_print("It was a *SUPERB* hit!"); + dam = ((7 * dam) / 2) + 25; + } + *done_crit = TRUE; + } + + return (dam); +} + + + +/* + * Extract the "total damage" from a given object hitting a given monster. + * + * Note that "flasks of oil" do NOT do fire damage, although they + * certainly could be made to do so. XXX XXX + * + * Note that most brands and slays are x3, except Slay Animal (x2), + * Slay Evil (x2), and Kill dragon (x5). + */ +s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr, + s32b *special) +{ + int mult = 1; + + monster_race *r_ptr = race_inf(m_ptr); + + u32b f1, f2, f3, f4, f5, esp; + + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Some "weapons" and "ammo" do extra damage */ + switch (o_ptr->tval) + { + case TV_SHOT: + case TV_ARROW: + case TV_BOLT: + case TV_BOOMERANG: + case TV_HAFTED: + case TV_POLEARM: + case TV_SWORD: + case TV_AXE: + case TV_DIGGING: + { + /* Slay Animal */ + if ((f1 & (TR1_SLAY_ANIMAL)) && (r_ptr->flags3 & (RF3_ANIMAL))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_ANIMAL); + } + + if (mult < 2) mult = 2; + } + + /* Slay Evil */ + if ((f1 & (TR1_SLAY_EVIL)) && (r_ptr->flags3 & (RF3_EVIL))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_EVIL); + } + + if (mult < 2) mult = 2; + } + + /* Slay Undead */ + if ((f1 & (TR1_SLAY_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_UNDEAD); + } + + if (mult < 3) mult = 3; + } + + /* Slay Demon */ + if ((f1 & (TR1_SLAY_DEMON)) && (r_ptr->flags3 & (RF3_DEMON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DEMON); + } + + if (mult < 3) mult = 3; + } + + /* Slay Orc */ + if ((f1 & (TR1_SLAY_ORC)) && (r_ptr->flags3 & (RF3_ORC))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_ORC); + } + + if (mult < 3) mult = 3; + } + + /* Slay Troll */ + if ((f1 & (TR1_SLAY_TROLL)) && (r_ptr->flags3 & (RF3_TROLL))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_TROLL); + } + + if (mult < 3) mult = 3; + } + + /* Slay Giant */ + if ((f1 & (TR1_SLAY_GIANT)) && (r_ptr->flags3 & (RF3_GIANT))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_GIANT); + } + + if (mult < 3) mult = 3; + } + + /* Slay Dragon */ + if ((f1 & (TR1_SLAY_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DRAGON); + } + + if (mult < 3) mult = 3; + } + + /* Execute Dragon */ + if ((f1 & (TR1_KILL_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DRAGON); + } + + if (mult < 5) mult = 5; + } + + /* Execute Undead */ + if ((f5 & (TR5_KILL_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_UNDEAD); + } + + if (mult < 5) mult = 5; + } + + /* Execute Demon */ + if ((f5 & (TR5_KILL_DEMON)) && (r_ptr->flags3 & (RF3_DEMON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DEMON); + } + + if (mult < 5) mult = 5; + } + + + /* Brand (Acid) */ + if (f1 & (TR1_BRAND_ACID)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_ACID)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_ACID); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_ACID)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_ACID); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Elec) */ + if (f1 & (TR1_BRAND_ELEC)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_ELEC)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_ELEC); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_ELEC)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Fire) */ + if (f1 & (TR1_BRAND_FIRE)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_FIRE)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_FIRE); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags3 & (RF3_SUSCEP_FIRE)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Cold) */ + if (f1 & (TR1_BRAND_COLD)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_COLD)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_COLD); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags3 & (RF3_SUSCEP_COLD)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_SUSCEP_COLD); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Poison) */ + if (f1 & (TR1_BRAND_POIS) || (p_ptr->tim_poison)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_POIS); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_POIS); + } + if (mult < 6) mult = 6; + if (magik(95)) *special |= SPEC_POIS; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + if (magik(50)) *special |= SPEC_POIS; + } + } + + /* Wounding */ + if (f5 & (TR5_WOUNDING)) + { + /* Notice immunity */ + if (r_ptr->flags8 & (RF8_NO_CUT)) + { + if (m_ptr->ml) + { + r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT); + } + } + + /* Otherwise, take the damage */ + else + { + if (magik(50)) *special |= SPEC_CUT; + } + } + break; + } + } + + + /* Return the total damage */ + return (tdam * mult); +} + + +/* + * Search for hidden things + */ +void search(void) +{ + int y, x, chance; + + s16b this_o_idx, next_o_idx = 0; + + cave_type *c_ptr; + + + /* Start with base search ability */ + chance = p_ptr->skill_srh; + + /* Penalize various conditions */ + if (p_ptr->blind || no_lite()) chance = chance / 10; + if (p_ptr->confused || p_ptr->image) chance = chance / 10; + + /* Search the nearby grids, which are always in bounds */ + for (y = (p_ptr->py - 1); y <= (p_ptr->py + 1); y++) + { + for (x = (p_ptr->px - 1); x <= (p_ptr->px + 1); x++) + { + /* Sometimes, notice things */ + if (rand_int(100) < chance) + { + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Invisible trap */ + if ((c_ptr->t_idx != 0) && !(c_ptr->info & CAVE_TRDT)) + { + /* Pick a trap */ + pick_trap(y, x); + + /* Message */ + msg_print("You have found a trap."); + + /* Disturb */ + disturb(0); + } + + /* Secret door */ + if (c_ptr->feat == FEAT_SECRET) + { + /* Message */ + msg_print("You have found a secret door."); + + /* Pick a door XXX XXX XXX */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); + cave[y][x].mimic = 0; + lite_spot(y, x); + + /* Disturb */ + disturb(0); + } + + /* Scan all objects in the grid */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; + this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Skip non-chests */ + if (o_ptr->tval != TV_CHEST) continue; + + /* Skip non-trapped chests */ + if (!o_ptr->pval) continue; + + /* Identify once */ + if (!object_known_p(o_ptr)) + { + /* Message */ + msg_print("You have discovered a trap on the chest!"); + + /* Know the trap */ + object_known(o_ptr); + + /* Notice it */ + disturb(0); + } + } + } + } + } +} + + + + +/* + * Player "wants" to pick up an object or gold. + * Note that we ONLY handle things that can be picked up. + * See "move_player()" for handling of other things. + */ +void carry(int pickup) +{ + if (!p_ptr->disembodied) + { + py_pickup_floor(pickup); + } +} + + +/* + * Handle player hitting a real trap + */ +static void hit_trap(void) +{ + bool_ ident = FALSE; + + cave_type *c_ptr; + + + /* Disturb the player */ + disturb(0); + + /* Get the cave grid */ + c_ptr = &cave[p_ptr->py][p_ptr->px]; + if (c_ptr->t_idx != 0) + { + ident = player_activate_trap_type(p_ptr->py, p_ptr->px, NULL, -1); + if (ident) + { + t_info[c_ptr->t_idx].ident = TRUE; + msg_format("You identified the trap as %s.", + t_name + t_info[c_ptr->t_idx].name); + } + } +} + + +void touch_zap_player(monster_type *m_ptr) +{ + int aura_damage = 0; + + monster_race *r_ptr = race_inf(m_ptr); + + + if (r_ptr->flags2 & (RF2_AURA_FIRE)) + { + if (!(p_ptr->immune_fire)) + { + char aura_dam[80]; + + aura_damage = + damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17)); + + /* Hack -- Get the "died from" name */ + monster_desc(aura_dam, m_ptr, 0x88); + + msg_print("You are suddenly very hot!"); + + if (p_ptr->oppose_fire) aura_damage = (aura_damage + 2) / 3; + if (p_ptr->resist_fire) aura_damage = (aura_damage + 2) / 3; + if (p_ptr->sensible_fire) aura_damage = (aura_damage + 2) * 2; + + take_hit(aura_damage, aura_dam); + r_ptr->r_flags2 |= RF2_AURA_FIRE; + handle_stuff(); + } + } + + + if (r_ptr->flags2 & (RF2_AURA_ELEC)) + { + if (!(p_ptr->immune_elec)) + { + char aura_dam[80]; + + aura_damage = + damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17)); + + /* Hack -- Get the "died from" name */ + monster_desc(aura_dam, m_ptr, 0x88); + + if (p_ptr->oppose_elec) aura_damage = (aura_damage + 2) / 3; + if (p_ptr->resist_elec) aura_damage = (aura_damage + 2) / 3; + + msg_print("You get zapped!"); + take_hit(aura_damage, aura_dam); + r_ptr->r_flags2 |= RF2_AURA_ELEC; + handle_stuff(); + } + } +} + + +/* + * Carried monster can attack too. + * Based on monst_attack_monst. + */ +static void carried_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath, + int x, int y) +{ + monster_type *t_ptr = &m_list[m_idx]; + + monster_race *r_ptr; + + monster_race *tr_ptr = race_inf(t_ptr); + + int ap_cnt; + + int ac, rlev, pt; + + char t_name[80]; + + cptr sym_name = symbiote_name(TRUE); + + char temp[80]; + + bool_ blinked = FALSE, touched = FALSE; + + byte y_saver = t_ptr->fy; + + byte x_saver = t_ptr->fx; + + object_type *o_ptr; + + + /* Get the carried monster */ + o_ptr = &p_ptr->inventory[INVEN_CARRY]; + if (!o_ptr->k_idx) return; + + r_ptr = &r_info[o_ptr->pval]; + + /* Not allowed to attack */ + if (r_ptr->flags1 & RF1_NEVER_BLOW) return; + + /* Total armor */ + ac = t_ptr->ac; + + /* Extract the effective monster level */ + rlev = ((o_ptr->elevel >= 1) ? o_ptr->elevel : 1); + + /* Get the monster name (or "it") */ + monster_desc(t_name, t_ptr, 0); + + /* Assume no blink */ + blinked = FALSE; + + if (!t_ptr->ml) + { + msg_print("You hear noise."); + } + + /* Scan through all four blows */ + for (ap_cnt = 0; ap_cnt < 4; ap_cnt++) + { + bool_ visible = FALSE; + bool_ obvious = FALSE; + + int power = 0; + int damage = 0; + + cptr act = NULL; + + /* Extract the attack infomation */ + int effect = r_ptr->blow[ap_cnt].effect; + int method = r_ptr->blow[ap_cnt].method; + int d_dice = r_ptr->blow[ap_cnt].d_dice; + int d_side = r_ptr->blow[ap_cnt].d_side; + + /* Stop attacking if the target dies! */ + if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) + break; + + /* Hack -- no more attacks */ + if (!method) break; + + if (blinked) /* Stop! */ + { + /* break; */ + } + + /* Extract visibility (before blink) */ + visible = TRUE; + + /* Extract the attack "power" */ + power = get_attack_power(effect); + + /* Monster hits */ + if (!effect || check_hit2(power, rlev, ac)) + { + /* Always disturbing */ + disturb(1); + + /* Describe the attack method */ + switch (method) + { + case RBM_HIT: + { + act = "hits %s."; + touched = TRUE; + break; + } + + case RBM_TOUCH: + { + act = "touches %s."; + touched = TRUE; + break; + } + + case RBM_PUNCH: + { + act = "punches %s."; + touched = TRUE; + break; + } + + case RBM_KICK: + { + act = "kicks %s."; + touched = TRUE; + break; + } + + case RBM_CLAW: + { + act = "claws %s."; + touched = TRUE; + break; + } + + case RBM_BITE: + { + act = "bites %s."; + touched = TRUE; + break; + } + + case RBM_STING: + { + act = "stings %s."; + touched = TRUE; + break; + } + + case RBM_XXX1: + { + act = "XXX1's %s."; + break; + } + + case RBM_BUTT: + { + act = "butts %s."; + touched = TRUE; + break; + } + + case RBM_CRUSH: + { + act = "crushes %s."; + touched = TRUE; + break; + } + + case RBM_ENGULF: + { + act = "engulfs %s."; + touched = TRUE; + break; + } + + case RBM_CHARGE: + { + act = "charges %s."; + touched = TRUE; + break; + } + + case RBM_CRAWL: + { + act = "crawls on %s."; + touched = TRUE; + break; + } + + case RBM_DROOL: + { + act = "drools on %s."; + touched = FALSE; + break; + } + + case RBM_SPIT: + { + act = "spits on %s."; + touched = FALSE; + break; + } + + case RBM_GAZE: + { + act = "gazes at %s."; + touched = FALSE; + break; + } + + case RBM_WAIL: + { + act = "wails at %s."; + touched = FALSE; + break; + } + + case RBM_SPORE: + { + act = "releases spores at %s."; + touched = FALSE; + break; + } + + case RBM_XXX4: + { + act = "projects XXX4's at %s."; + touched = FALSE; + break; + } + + case RBM_BEG: + { + act = "begs %s for money."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_INSULT: + { + act = "insults %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_MOAN: + { + act = "moans at %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_SHOW: + { + act = "sings to %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + } + + /* Message */ + if (act) + { + strfmt(temp, act, t_name); + if (t_ptr->ml) + msg_format("%s %s", sym_name, temp); + + } + + /* Hack -- assume all attacks are obvious */ + obvious = TRUE; + + /* Roll out the damage */ + damage = damroll(d_dice, d_side); + + pt = GF_MISSILE; + + /* Apply appropriate damage */ + switch (effect) + { + case 0: + { + damage = 0; + pt = 0; + break; + } + + case RBE_HURT: + case RBE_SANITY: + { + damage -= (damage * ((ac < 150) ? ac : 150) / 250); + break; + } + + case RBE_POISON: + case RBE_DISEASE: + { + pt = GF_POIS; + break; + } + + case RBE_UN_BONUS: + case RBE_UN_POWER: + case RBE_ABOMINATION: + { + pt = GF_DISENCHANT; + break; + } + + case RBE_EAT_FOOD: + case RBE_EAT_LITE: + { + pt = damage = 0; + break; + } + + case RBE_EAT_ITEM: + case RBE_EAT_GOLD: + { + pt = damage = 0; + if (randint(2) == 1) blinked = TRUE; + break; + } + + case RBE_ACID: + { + pt = GF_ACID; + break; + } + + case RBE_ELEC: + { + pt = GF_ELEC; + break; + } + + case RBE_FIRE: + { + pt = GF_FIRE; + break; + } + + case RBE_COLD: + { + pt = GF_COLD; + break; + } + + case RBE_BLIND: + { + break; + } + + case RBE_CONFUSE: + case RBE_HALLU: + { + pt = GF_CONFUSION; + break; + } + + case RBE_TERRIFY: + { + pt = GF_TURN_ALL; + break; + } + + case RBE_PARALYZE: + { + pt = GF_OLD_SLEEP; /* sort of close... */ + break; + } + + case RBE_LOSE_STR: + case RBE_LOSE_INT: + case RBE_LOSE_WIS: + case RBE_LOSE_DEX: + case RBE_LOSE_CON: + case RBE_LOSE_CHR: + case RBE_LOSE_ALL: + case RBE_PARASITE: + { + break; + } + + case RBE_SHATTER: + { + if (damage > 23) + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 8); + } + break; + } + + case RBE_EXP_10: + case RBE_EXP_20: + case RBE_EXP_40: + case RBE_EXP_80: + { + pt = GF_NETHER; + break; + } + + case RBE_TIME: + { + pt = GF_TIME; + break; + } + + default: + { + pt = 0; + break; + } + } + + if (pt) + { + /* Do damage if not exploding */ + project(0, 0, t_ptr->fy, t_ptr->fx, + (pt == GF_OLD_SLEEP ? r_ptr->level : damage), pt, + PROJECT_KILL | PROJECT_STOP); + + if (touched) + { + /* Aura fire */ + if ((tr_ptr->flags2 & RF2_AURA_FIRE) && + !(r_ptr->flags3 & RF3_IM_FIRE)) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You are suddenly very hot!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_FIRE; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_FIRE, PROJECT_KILL | PROJECT_STOP); + } + + /* Aura elec */ + if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && + !(r_ptr->flags3 & (RF3_IM_ELEC))) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You get zapped!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_ELEC; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_ELEC, PROJECT_KILL | PROJECT_STOP); + } + } + } + } + + /* Monster missed player */ + else + { + /* Analyze failed attacks */ + switch (method) + { + case RBM_HIT: + case RBM_TOUCH: + case RBM_PUNCH: + case RBM_KICK: + case RBM_CLAW: + case RBM_BITE: + case RBM_STING: + case RBM_XXX1: + case RBM_BUTT: + case RBM_CRUSH: + case RBM_ENGULF: + case RBM_CHARGE: + { + /* Disturb */ + disturb(1); + + /* Message */ + msg_format("%s misses %s.", sym_name, t_name); + break; + } + } + } + + + /* Analyze "visible" monsters only */ + if (visible) + { + /* Count "obvious" attacks (and ones that cause damage) */ + if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) + { + /* Count attacks of this type */ + if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) + { + r_ptr->r_blows[ap_cnt]++; + } + } + } + } + + /* Blink away */ + if (blinked) + { + msg_format("You and %s flee laughing!", symbiote_name(FALSE)); + + teleport_player(MAX_SIGHT * 2 + 5); + } +} + +/* + * Carried monster can attack too. + * Based on monst_attack_monst. + */ +static void incarnate_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath, + int x, int y) +{ + monster_type *t_ptr = &m_list[m_idx]; + + monster_race *r_ptr; + + monster_race *tr_ptr = race_inf(t_ptr); + + int ap_cnt; + + int ac, rlev, pt; + + char t_name[80]; + + char temp[80]; + + bool_ blinked = FALSE, touched = FALSE; + + byte y_saver = t_ptr->fy; + + byte x_saver = t_ptr->fx; + + + if (!p_ptr->body_monster) return; + + r_ptr = race_info_idx(p_ptr->body_monster, 0); + + /* Not allowed to attack */ + if (r_ptr->flags1 & RF1_NEVER_BLOW) return; + + /* Total armor */ + ac = t_ptr->ac; + + /* Extract the effective monster level */ + rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); + + /* Get the monster name (or "it") */ + monster_desc(t_name, t_ptr, 0); + + /* Assume no blink */ + blinked = FALSE; + + if (!t_ptr->ml) + { + msg_print("You hear noise."); + } + + /* Scan through all four blows */ + for (ap_cnt = 0; ap_cnt < (p_ptr->num_blow > 4) ? 4 : p_ptr->num_blow; + ap_cnt++) + { + bool_ visible = FALSE; + bool_ obvious = FALSE; + + int power = 0; + int damage = 0; + + cptr act = NULL; + + /* Extract the attack infomation */ + int effect = r_ptr->blow[ap_cnt].effect; + int method = r_ptr->blow[ap_cnt].method; + int d_dice = r_ptr->blow[ap_cnt].d_dice; + int d_side = r_ptr->blow[ap_cnt].d_side; + + /* Stop attacking if the target dies! */ + if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) + break; + + /* Hack -- no more attacks */ + if (!method) break; + + if (blinked) /* Stop! */ + { + /* break; */ + } + + /* Extract visibility (before blink) */ + visible = TRUE; + + /* Extract the attack "power" */ + power = get_attack_power(effect); + + /* Monster hits */ + if (!effect || check_hit2(power, rlev, ac)) + { + /* Always disturbing */ + disturb(1); + + /* Describe the attack method */ + switch (method) + { + case RBM_HIT: + { + act = "hit %s."; + touched = TRUE; + break; + } + + case RBM_TOUCH: + { + act = "touch %s."; + touched = TRUE; + break; + } + + case RBM_PUNCH: + { + act = "punch %s."; + touched = TRUE; + break; + } + + case RBM_KICK: + { + act = "kick %s."; + touched = TRUE; + break; + } + + case RBM_CLAW: + { + act = "claw %s."; + touched = TRUE; + break; + } + + case RBM_BITE: + { + act = "bite %s."; + touched = TRUE; + break; + } + + case RBM_STING: + { + act = "sting %s."; + touched = TRUE; + break; + } + + case RBM_XXX1: + { + act = "XXX1's %s."; + break; + } + + case RBM_BUTT: + { + act = "butt %s."; + touched = TRUE; + break; + } + + case RBM_CRUSH: + { + act = "crush %s."; + touched = TRUE; + break; + } + + case RBM_ENGULF: + { + act = "engulf %s."; + touched = TRUE; + break; + } + + case RBM_CHARGE: + { + act = "charge %s."; + touched = TRUE; + break; + } + + case RBM_CRAWL: + { + act = "crawl on %s."; + touched = TRUE; + break; + } + + case RBM_DROOL: + { + act = "drool on %s."; + touched = FALSE; + break; + } + + case RBM_SPIT: + { + act = "spit on %s."; + touched = FALSE; + break; + } + + case RBM_GAZE: + { + act = "gaze at %s."; + touched = FALSE; + break; + } + + case RBM_WAIL: + { + act = "wail at %s."; + touched = FALSE; + break; + } + + case RBM_SPORE: + { + act = "release spores at %s."; + touched = FALSE; + break; + } + + case RBM_XXX4: + { + act = "project XXX4's at %s."; + touched = FALSE; + break; + } + + case RBM_BEG: + { + act = "beg %s for money."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_INSULT: + { + act = "insult %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_MOAN: + { + act = "moan at %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_SHOW: + { + act = "sing to %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + } + + /* Message */ + if (act) + { + strfmt(temp, act, t_name); + if (t_ptr->ml) + msg_format("You %s", temp); + + } + + /* Hack -- assume all attacks are obvious */ + obvious = TRUE; + + /* Roll out the damage */ + damage = damroll(d_dice, d_side) + p_ptr->to_d; + + pt = GF_MISSILE; + + /* Apply appropriate damage */ + switch (effect) + { + case 0: + { + damage = 0; + pt = 0; + break; + } + + case RBE_HURT: + case RBE_SANITY: + { + damage -= (damage * ((ac < 150) ? ac : 150) / 250); + break; + } + + case RBE_POISON: + case RBE_DISEASE: + { + pt = GF_POIS; + break; + } + + case RBE_UN_BONUS: + case RBE_UN_POWER: + { + pt = GF_DISENCHANT; + break; + } + + case RBE_EAT_FOOD: + case RBE_EAT_LITE: + { + pt = damage = 0; + break; + } + + case RBE_EAT_ITEM: + case RBE_EAT_GOLD: + { + pt = damage = 0; + if (randint(2) == 1) blinked = TRUE; + break; + } + + case RBE_ACID: + { + pt = GF_ACID; + break; + } + + case RBE_ELEC: + { + pt = GF_ELEC; + break; + } + + case RBE_FIRE: + { + pt = GF_FIRE; + break; + } + + case RBE_COLD: + { + pt = GF_COLD; + break; + } + + case RBE_BLIND: + { + break; + } + + case RBE_HALLU: + case RBE_CONFUSE: + { + pt = GF_CONFUSION; + break; + } + + case RBE_TERRIFY: + { + pt = GF_TURN_ALL; + break; + } + + case RBE_PARALYZE: + { + pt = GF_OLD_SLEEP; /* sort of close... */ + break; + } + + case RBE_LOSE_STR: + case RBE_LOSE_INT: + case RBE_LOSE_WIS: + case RBE_LOSE_DEX: + case RBE_LOSE_CON: + case RBE_LOSE_CHR: + case RBE_LOSE_ALL: + case RBE_PARASITE: + { + break; + } + + case RBE_SHATTER: + { + if (damage > 23) + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 8); + } + break; + } + + case RBE_EXP_10: + case RBE_EXP_20: + case RBE_EXP_40: + case RBE_EXP_80: + { + pt = GF_NETHER; + break; + } + + case RBE_TIME: + { + pt = GF_TIME; + break; + } + + default: + { + pt = 0; + break; + } + } + + if (pt) + { + /* Do damage if not exploding */ + project(0, 0, t_ptr->fy, t_ptr->fx, + (pt == GF_OLD_SLEEP ? p_ptr->lev * 2 : damage), pt, + PROJECT_KILL | PROJECT_STOP); + + if (touched) + { + /* Aura fire */ + if ((tr_ptr->flags2 & RF2_AURA_FIRE) && + !(r_ptr->flags3 & RF3_IM_FIRE)) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You are suddenly very hot!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_FIRE; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_FIRE, PROJECT_KILL | PROJECT_STOP); + } + + /* Aura elec */ + if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && + !(r_ptr->flags3 & (RF3_IM_ELEC))) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You get zapped!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_ELEC; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_ELEC, PROJECT_KILL | PROJECT_STOP); + } + + } + } + } + + /* Monster missed player */ + else + { + /* Analyze failed attacks */ + switch (method) + { + case RBM_HIT: + case RBM_TOUCH: + case RBM_PUNCH: + case RBM_KICK: + case RBM_CLAW: + case RBM_BITE: + case RBM_STING: + case RBM_XXX1: + case RBM_BUTT: + case RBM_CRUSH: + case RBM_ENGULF: + case RBM_CHARGE: + { + /* Disturb */ + disturb(1); + + /* Message */ + msg_format("You miss %s.", t_name); + + break; + } + } + } + + + /* Analyze "visible" monsters only */ + if (visible) + { + /* Count "obvious" attacks (and ones that cause damage) */ + if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) + { + /* Count attacks of this type */ + if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) + { + r_ptr->r_blows[ap_cnt]++; + } + } + } + } + + /* Blink away */ + if (blinked) + { + msg_print("You flee laughing!"); + + teleport_player(MAX_SIGHT * 2 + 5); + } +} + + +/* + * Fetch an attack description from dam_*.txt files. + */ + +static void flavored_attack(int percent, char *output) +{ + int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; + bool_ insane = (rand_int(100) < insanity); + + if (percent < 5) + { + if (!insane) + strcpy(output, "You scratch %s."); + else + get_rnd_line("dam_none.txt", output); + + } + else if (percent < 30) + { + if (!insane) + strcpy(output, "You hit %s."); + else + get_rnd_line("dam_med.txt", output); + } + else if (percent < 60) + { + if (!insane) + strcpy(output, "You wound %s."); + else + get_rnd_line("dam_lots.txt", output); + } + else if (percent < 95) + { + if (!insane) + strcpy(output, "You cripple %s."); + else + get_rnd_line("dam_huge.txt", output); + + } + else + { + if (!insane) + strcpy(output, "You demolish %s."); + else + get_rnd_line("dam_xxx.txt", output); + } +} + + +/* + * Apply the special effects of an attack + */ +void attack_special(monster_type *m_ptr, s32b special, int dam) +{ + char m_name[80]; + + monster_race *r_ptr = race_inf(m_ptr); + + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Special - Cut monster */ + if (special & SPEC_CUT) + { + /* Cut the monster */ + if (r_ptr->flags8 & (RF8_NO_CUT)) + { + if (m_ptr->ml) + { + r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT); + } + } + else if (rand_int(100) >= r_ptr->level) + { + /* Already partially poisoned */ + if (m_ptr->bleeding) msg_format("%^s is bleeding more strongly.", + m_name); + /* Was not poisoned */ + else + msg_format("%^s is bleeding.", m_name); + + m_ptr->bleeding += dam * 2; + } + } + + /* Special - Poison monster */ + if (special & SPEC_POIS) + { + /* Poison the monster */ + if (r_ptr->flags3 & (RF3_IM_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_POIS); + } + } + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_POIS); + } + /* Already partially poisoned */ + if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name); + /* Was not poisoned */ + else + msg_format("%^s is poisoned.", m_name); + + m_ptr->poisoned += dam * 2; + } + else if (rand_int(100) >= r_ptr->level) + { + /* Already partially poisoned */ + if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name); + /* Was not poisoned */ + else + msg_format("%^s is poisoned.", m_name); + + m_ptr->poisoned += dam; + } + } +} + + +/* + * Bare handed attacks + */ +static void py_attack_hand(int *k, monster_type *m_ptr, s32b *special) +{ + s16b special_effect = 0, stun_effect = 0, times = 0; + martial_arts *ma_ptr, *old_ptr, *blow_table = ma_blows; + int resist_stun = 0, max = MAX_MA; + monster_race *r_ptr = race_inf(m_ptr); + char m_name[80]; + bool_ desc = FALSE; + bool_ done_crit; + int plev = p_ptr->lev; + + if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) && + (p_ptr->melee_style == SKILL_BEAR)) + { + blow_table = bear_blows; + max = MAX_BEAR; + plev = get_skill(SKILL_BEAR); + } + if (p_ptr->melee_style == SKILL_HAND) + { + blow_table = ma_blows; + max = MAX_MA; + plev = get_skill(SKILL_HAND); + } + ma_ptr = &blow_table[0]; + old_ptr = &blow_table[0]; + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + if (r_ptr->flags1 & RF1_UNIQUE) resist_stun += 88; + if (r_ptr->flags3 & RF3_NO_CONF) resist_stun += 44; + if (r_ptr->flags3 & RF3_NO_SLEEP) resist_stun += 44; + if ((r_ptr->flags3 & RF3_UNDEAD) || + (r_ptr->flags3 & RF3_NONLIVING)) resist_stun += 88; + + if (plev) + { + for (times = 0; times < (plev < 7 ? 1 : plev / 7); times++) + { + do + { + ma_ptr = &blow_table[(randint(max)) - 1]; + } + while ((ma_ptr->min_level > plev) || (randint(plev) < ma_ptr->chance)); + + /* keep the highest level attack available we found */ + if ((ma_ptr->min_level > old_ptr->min_level) && + !(p_ptr->stun || p_ptr->confused)) + { + old_ptr = ma_ptr; + + if (wizard && cheat_xtra) + { + msg_print("Attack re-selected."); + } + } + else + { + ma_ptr = old_ptr; + } + } + } + + *k = damroll(ma_ptr->dd, ma_ptr->ds); + + if (ma_ptr->effect & MA_KNEE) + { + if (r_ptr->flags1 & RF1_MALE) + { + if (!desc) msg_format("You hit %s in the groin with your knee!", + m_name); + sound(SOUND_PAIN); + special_effect = MA_KNEE; + } + else if (!desc) msg_format(ma_ptr->desc, m_name); + + desc = TRUE; + } + if (ma_ptr->effect & MA_FULL_SLOW) + { + special_effect = MA_SLOW; + if (!desc) msg_format(ma_ptr->desc, m_name); + + desc = TRUE; + } + if (ma_ptr->effect & MA_SLOW) + { + if (! + ((r_ptr->flags1 & RF1_NEVER_MOVE) || + strchr("UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char))) + { + if (!desc) msg_format("You kick %s in the ankle.", m_name); + special_effect = MA_SLOW; + } + else if (!desc) msg_format(ma_ptr->desc, m_name); + + desc = TRUE; + } + if (ma_ptr->effect & MA_STUN) + { + if (ma_ptr->power) + { + stun_effect = (ma_ptr->power / 2) + randint(ma_ptr->power / 2); + } + + if (!desc) msg_format(ma_ptr->desc, m_name); + desc = TRUE; + } + if (ma_ptr->effect & MA_WOUND) + { + if (magik(ma_ptr->power)) + { + *special |= SPEC_CUT; + } + if (!desc) msg_format(ma_ptr->desc, m_name); + desc = TRUE; + } + + *k = critical_norm(plev * (randint(10)), ma_ptr->min_level, *k, -1, &done_crit); + + if ((special_effect & MA_KNEE) && ((*k + p_ptr->to_d) < m_ptr->hp)) + { + msg_format("%^s moans in agony!", m_name); + stun_effect = 7 + randint(13); + resist_stun /= 3; + } + if (((special_effect & MA_FULL_SLOW) || (special_effect & MA_SLOW)) && + ((*k + p_ptr->to_d) < m_ptr->hp)) + { + if (!(r_ptr->flags1 & RF1_UNIQUE) && + (randint(plev) > m_ptr->level) && m_ptr->mspeed > 60) + { + msg_format("%^s starts limping slower.", m_name); + m_ptr->mspeed -= 10; + } + } + + if (stun_effect && ((*k + p_ptr->to_d) < m_ptr->hp)) + { + if (plev > randint(m_ptr->level + resist_stun + 10)) + { + if (m_ptr->stunned) + msg_format("%^s is still stunned.", m_name); + else + msg_format("%^s is stunned.", m_name); + + m_ptr->stunned += (stun_effect); + } + } +} + + +/* + * Apply nazgul effects + */ +void do_nazgul(int *k, int *num, int num_blow, int weap, monster_race *r_ptr, + object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + + bool_ mundane; + bool_ allow_shatter = TRUE; + + /* Extract mundane-ness of the current weapon */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* It should be Slay Evil, Slay Undead, or *Slay Undead* */ + mundane = !(f1 & TR1_SLAY_EVIL) && !(f1 & TR1_SLAY_UNDEAD) && + !(f5 & TR5_KILL_UNDEAD); + + /* Some blades can resist shattering */ + if (f5 & TR5_RES_MORGUL) + allow_shatter = FALSE; + + /* Mega Hack -- Hitting Nazgul is REALY dangerous (ideas from Akhronath) */ + if (r_ptr->flags7 & RF7_NAZGUL) + { + if ((!o_ptr->name2) && (!artifact_p(o_ptr)) && allow_shatter) + { + msg_print("Your weapon *DISINTEGRATES*!"); + *k = 0; + + inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); + + /* To stop attacking */ + *num = num_blow; + } + else if (o_ptr->name2) + { + if (mundane) + { + msg_print + ("The Ringwraith is IMPERVIOUS to the mundane weapon."); + *k = 0; + } + + /* 25% chance of getting destroyed */ + if (magik(25) && allow_shatter) + { + msg_print("Your weapon is destroyed!"); + + inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); + + /* To stop attacking */ + *num = num_blow; + } + } + else if (artifact_p(o_ptr)) + { + if (mundane) + { + msg_print + ("The Ringwraith is IMPERVIOUS to the mundane weapon."); + *k = 0; + } + + apply_disenchant(INVEN_WIELD + weap); + + /* 1/1000 chance of getting destroyed */ + if (!rand_int(1000) && allow_shatter) + { + msg_print("Your weapon is destroyed!"); + + inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); + + /* To stop attacking */ + *num = num_blow; + } + } + + /* If any damage is done, then 25% chance of getting the Black Breath */ + if (*k) + { + if (magik(25)) + { + msg_print("Your foe calls upon your soul!"); + msg_print + ("You feel the Black Breath slowly draining you of life..."); + p_ptr->black_breath = TRUE; + } + } + } +} + + +/* + * Player attacks a (poor, defenseless) creature -RAK- + * + * If no "weapon" is available, then "punch" the monster one time. + */ +void py_attack(int y, int x, int max_blow) +{ + int num = 0, k, bonus, chance; + + s32b special = 0; + + cave_type *c_ptr = &cave[y][x]; + + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + + monster_race *r_ptr = race_inf(m_ptr); + + object_type *o_ptr; + + char m_name[80]; + + bool_ fear = FALSE; + + bool_ mdeath = FALSE; + + bool_ backstab = FALSE; + + bool_ vorpal_cut = FALSE; + + int chaos_effect = 0; + + bool_ stab_fleeing = FALSE; + + bool_ do_quake = FALSE; + + bool_ done_crit = FALSE; + + bool_ drain_msg = TRUE; + + int drain_result = 0, drain_heal = 0; + + int drain_left = MAX_VAMPIRIC_DRAIN; + + /* A massive hack -- life-draining weapons */ + u32b f1, f2, f3, f4, f5, esp; + + int weap; + + /* Disturb the player */ + disturb(0); + + if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW) + { + msg_print("You cannot attack in this form!"); + return; + } + + if (get_skill(SKILL_BACKSTAB)) + { + if ((m_ptr->csleep) && (m_ptr->ml)) + { + /* Can't backstab creatures that we can't see, right? */ + backstab = TRUE; + } + else if ((m_ptr->monfear) && (m_ptr->ml)) + { + stab_fleeing = TRUE; + } + } + + /* Disturb the monster */ + m_ptr->csleep = 0; + + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Dont even bother */ + if (r_ptr->flags7 & RF7_IM_MELEE) + { + msg_format("%^s is immune to melee attacks."); + return; + } + + /* Auto-Recall if possible and visible */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); + + /* Track a new monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + + /* Stop if friendly */ + if ((is_friend(m_ptr) >= 0) && + !(p_ptr->stun || p_ptr->confused || p_ptr->image || + !(m_ptr->ml))) + { + if (!(p_ptr->inventory[INVEN_WIELD].art_name)) + { + msg_format("You stop to avoid hitting %s.", m_name); + return; + } + + if (! + (streq + (quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'"))) + { + msg_format("You stop to avoid hitting %s.", m_name); + return; + } + + msg_format("Your black blade greedily attacks %s!", m_name); + } + + /* Break goi/manashield */ + if (p_ptr->invuln) + { + set_invuln(0); + } + if (p_ptr->disrupt_shield) + { + set_disrupt_shield(0); + } + + /* Handle player fear */ + if (p_ptr->afraid) + { + /* Message */ + if (m_ptr->ml) + msg_format("You are too afraid to attack %s!", m_name); + else + msg_format("There is something scary in your way!"); + + /* Done */ + return; + } + + /* Monsters can use barehanded combat, but not weapon combat */ + if ((p_ptr->body_monster) && + (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]) && + !(p_ptr->melee_style == SKILL_HAND)) + { + incarnate_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x); + } + /* Otherwise use your weapon(s) */ + else + { + int weapons; + if (p_ptr->melee_style == SKILL_MASTERY) + weapons = r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]; + else /* SKILL_HAND */ + weapons = 1; + + /* Attack with ALL the weapons !!!!! -- ooh that's gonna hurt YOU */ + for (weap = 0; weap < weapons; ++weap) + { + /* Monster is already dead ? oh :( */ + if (mdeath) break; + + /* Reset the blows counter */ + num = 0; + + /* Access the weapon */ + o_ptr = &p_ptr->inventory[INVEN_WIELD + weap]; + + /* Calculate the "attack quality" */ + bonus = p_ptr->to_h + p_ptr->to_h_melee + o_ptr->to_h; + chance = p_ptr->skill_thn + (bonus * BTH_PLUS_ADJ); + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (!(f4 & TR4_NEVER_BLOW)) + { + int num_blow = p_ptr->num_blow; + + /* Restrict to max_blow(if max_blow >= 0) */ + if ((max_blow >= 0) && + (num_blow > max_blow)) num_blow = max_blow; + + /* Attack once for each legal blow */ + while (num++ < num_blow) + { + /* Test for hit */ + if (test_hit_norm(chance, m_ptr->ac, m_ptr->ml)) + { + /* Sound */ + sound(SOUND_HIT); + + /* Hack -- bare hands do one damage */ + k = 1; + + /* Select a chaotic effect (50% chance) */ + if ((f1 & TR1_CHAOTIC) && (rand_int(2) == 0)) + { + if (randint(5) < 3) + { + /* Vampiric (20%) */ + chaos_effect = 1; + } + else if (rand_int(250) == 0) + { + /* Quake (0.12%) */ + chaos_effect = 2; + } + else if (rand_int(10)) + { + /* Confusion (26.892%) */ + chaos_effect = 3; + } + else if (rand_int(2) == 0) + { + /* Teleport away (1.494%) */ + chaos_effect = 4; + } + else + { + /* Polymorph (1.494%) */ + chaos_effect = 5; + } + } + + /* Vampiric drain */ + if ((f1 & TR1_VAMPIRIC) || (chaos_effect == 1)) + { + if (! + ((r_ptr->flags3 & RF3_UNDEAD) || + (r_ptr->flags3 & RF3_NONLIVING))) + drain_result = m_ptr->hp; + else + drain_result = 0; + } + + if (f1 & TR1_VORPAL && (randint(6) == 1)) + vorpal_cut = TRUE; + else + vorpal_cut = FALSE; + + /* Should we attack with hands or not ? */ + if (p_ptr->melee_style != SKILL_MASTERY) + { + py_attack_hand(&k, m_ptr, &special); + } + /* Handle normal weapon */ + else if (o_ptr->k_idx) + { + k = damroll(o_ptr->dd, o_ptr->ds); + k = tot_dam_aux(o_ptr, k, m_ptr, &special); + + if (backstab) + { + k += (k * + get_skill_scale(SKILL_BACKSTAB, + 100)) / 100; + } + else if (stab_fleeing) + { + k += (k * get_skill_scale(SKILL_BACKSTAB, 70)) / + 100; + } + + if ((p_ptr->impact && ((k > 50) || randint(7) == 1)) + || (chaos_effect == 2)) + { + do_quake = TRUE; + } + + k = critical_norm(o_ptr->weight, o_ptr->to_h, k, o_ptr->tval, &done_crit); + + /* Stunning blow */ + if (magik(get_skill(SKILL_STUN)) && (o_ptr->tval == TV_HAFTED) && (o_ptr->weight > 50) && done_crit) + { + if (!(r_ptr->flags4 & (RF4_BR_SOUN)) && !(r_ptr->flags4 & (RF4_BR_WALL)) && k) + { + int tmp; + + /* Get stunned */ + if (m_ptr->stunned) + { + msg_format("%^s is more dazed.", m_name); + tmp = m_ptr->stunned + get_skill_scale(SKILL_STUN, 30) + 10; + } + else + { + msg_format("%^s is dazed.", m_name); + tmp = get_skill_scale(SKILL_STUN, 60) + 20; + } + + /* Apply stun */ + m_ptr->stunned = (tmp < 200) ? tmp : 200; + } + } + + if (vorpal_cut) + { + int step_k = k; + + msg_format("Your weapon cuts deep into %s!", + m_name); + do + { + k += step_k; + } + while (randint(4) == 1); + } + + PRAY_GOD(GOD_TULKAS) + { + if (magik(wisdom_scale(130) - m_ptr->level) && (p_ptr->grace > 1000)) + { + msg_print("You feel the hand of Tulkas helping your blow."); + k += (o_ptr->to_d + p_ptr->to_d_melee) * 2; + } + else k += o_ptr->to_d + p_ptr->to_d_melee; + } + else k += o_ptr->to_d; + + /* Project some more nasty stuff? */ + if (p_ptr->tim_project) + { + project(0, p_ptr->tim_project_rad, y, x, p_ptr->tim_project_dam, p_ptr->tim_project_gf, p_ptr->tim_project_flag | PROJECT_JUMP); + if (!c_ptr->m_idx) + { + mdeath = TRUE; + break; + } + } + + do_nazgul(&k, &num, num_blow, weap, r_ptr, o_ptr); + + } + + /* Melkor can cast curse for you*/ + PRAY_GOD(GOD_MELKOR) + { + int lv = get_level_s(MELKOR_CURSE, 100); + + if (lv >= 10) + { + int chance = (wisdom_scale(30) * lv) / ((m_ptr->level < 1) ? 1 : m_ptr->level); + + if (chance < 1) chance = 1; + if ((p_ptr->grace > 5000) && magik(chance)) + { + do_melkor_curse(c_ptr->m_idx); + } + } + } + + /* May it clone the monster ? */ + if ((f4 & TR4_CLONE) && magik(30)) + { + msg_format("Oh no! Your weapon clones %^s!", + m_name); + multiply_monster(c_ptr->m_idx, FALSE, TRUE); + } + + /* Apply the player damage bonuses */ + k += p_ptr->to_d + p_ptr->to_d_melee; + + /* No negative damage */ + if (k < 0) k = 0; + + /* Message */ + if (!(backstab || stab_fleeing)) + { + /* These monsters never have flavoured combat msgs */ + if (strchr("vwjmelX,.*", r_ptr->d_char)) + { + msg_format("You hit %s.", m_name); + } + + /* Print flavoured messages if requested */ + else + { + char buff[255]; + + flavored_attack((100 * k) / m_ptr->maxhp, buff); + msg_format(buff, m_name); + } + } + else if (backstab) + { + char buf[80]; + + monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego); + + backstab = FALSE; + + msg_format + ("You cruelly stab the helpless, sleeping %s!", + buf); + } + else + { + char buf[80]; + + monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego); + + msg_format("You backstab the fleeing %s!", buf); + } + + /* Complex message */ + if (wizard) + { + msg_format("You do %d (out of %d) damage.", k, + m_ptr->hp); + } + + if (special) attack_special(m_ptr, special, k); + + /* Damage, check for fear and death */ + if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL)) + { + /* Hack -- High-level warriors can spread their attacks out + * among weaker foes. + */ + if ((has_ability(AB_SPREAD_BLOWS)) && (num < num_blow) && + (energy_use)) + { + energy_use = energy_use * num / num_blow; + } + mdeath = TRUE; + break; + } + + switch (is_friend(m_ptr)) + { + case 1: + msg_format("%^s gets angry!", m_name); + change_side(m_ptr); + break; + case 0: + msg_format("%^s gets angry!", m_name); + m_ptr->status = MSTATUS_NEUTRAL_M; + break; + } + + touch_zap_player(m_ptr); + + /* Are we draining it? A little note: If the monster is + dead, the drain does not work... */ + + if (drain_result) + { + drain_result -= m_ptr->hp; /* Calculate the difference */ + + if (drain_result > 0) /* Did we really hurt it? */ + { + drain_heal = damroll(4, (drain_result / 6)); + + if (cheat_xtra) + { + msg_format("Draining left: %d", drain_left); + } + + if (drain_left) + { + if (drain_heal < drain_left) + { + drain_left -= drain_heal; + } + else + { + drain_heal = drain_left; + drain_left = 0; + } + + if (drain_msg) + { + msg_format + ("Your weapon drains life from %s!", + m_name); + drain_msg = FALSE; + } + + hp_player(drain_heal); + /* We get to keep some of it! */ + } + } + } + + /* Confusion attack */ + if ((p_ptr->confusing) || (chaos_effect == 3)) + { + /* Cancel glowing hands */ + if (p_ptr->confusing) + { + p_ptr->confusing = FALSE; + msg_print("Your hands stop glowing."); + } + + /* Confuse the monster */ + if (r_ptr->flags3 & (RF3_NO_CONF)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_NO_CONF); + } + + msg_format("%^s is unaffected.", m_name); + } + else if (rand_int(100) < m_ptr->level) + { + msg_format("%^s is unaffected.", m_name); + } + else + { + msg_format("%^s appears confused.", m_name); + m_ptr->confused += + 10 + rand_int(get_skill(SKILL_COMBAT)) / 5; + } + } + + else if (chaos_effect == 4) + { + msg_format("%^s disappears!", m_name); + teleport_away(c_ptr->m_idx, 50); + num = num_blow + 1; /* Can't hit it anymore! */ + } + + else if ((chaos_effect == 5) && cave_floor_bold(y, x) && + (randint(90) > m_ptr->level)) + { + if (!((r_ptr->flags1 & RF1_UNIQUE) || + (r_ptr->flags4 & RF4_BR_CHAO) || + (m_ptr->mflag & MFLAG_QUEST))) + { + /* Handle polymorph */ + if (do_poly_monster(y, x)) + { + /* Polymorph succeeded */ + msg_format("%^s changes!", m_name); + + /* Hack -- Get new monster */ + m_ptr = &m_list[c_ptr->m_idx]; + + /* Oops, we need a different name... */ + monster_desc(m_name, m_ptr, 0); + + /* Hack -- Get new race */ + r_ptr = race_inf(m_ptr); + + fear = FALSE; + } + else + { + msg_format("%^s resists.", m_name); + } + } + else + { + msg_format("%^s is unaffected.", m_name); + } + } + } + + /* Player misses */ + else + { + /* Sound */ + sound(SOUND_MISS); + + backstab = FALSE; /* Clumsy! */ + + /* Message */ + msg_format("You miss %s.", m_name); + } + } + } + else + { + msg_print("You can't attack with that weapon."); + } + } + } + + /* Carried monster can attack too */ + if ((!mdeath) && m_list[c_ptr->m_idx].hp) + carried_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x); + + /* Hack -- delay fear messages */ + if (fear && m_ptr->ml) + { + /* Sound */ + sound(SOUND_FLEE); + + /* Message */ + msg_format("%^s flees in terror!", m_name); + } + + /* Mega-Hack -- apply earthquake brand */ + if (do_quake) + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 10); + } +} + + + +static bool_ pattern_tile(int y, int x) +{ + return ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) && + (cave[y][x].feat >= FEAT_PATTERN_START)); +} + + +static bool_ pattern_seq(int c_y, int c_x, int n_y, int n_x) +{ + if (!(pattern_tile(c_y, c_x)) && !(pattern_tile(n_y, n_x))) + return TRUE; + + if (cave[n_y][n_x].feat == FEAT_PATTERN_START) + { + if ((!(pattern_tile(c_y, c_x))) && + !(p_ptr->confused || p_ptr->stun || p_ptr->image)) + { + if (get_check + ("If you start walking the Straight Road, you must walk the whole way. Ok? ")) + return TRUE; + else + return FALSE; + } + else + return TRUE; + } + else if ((cave[n_y][n_x].feat == FEAT_PATTERN_OLD) || + (cave[n_y][n_x].feat == FEAT_PATTERN_END) || + (cave[n_y][n_x].feat == FEAT_PATTERN_XTRA2)) + { + if (pattern_tile(c_y, c_x)) + { + return TRUE; + } + else + { + msg_print + ("You must start walking the Straight Road from the startpoint."); + return FALSE; + } + } + else if ((cave[n_y][n_x].feat == FEAT_PATTERN_XTRA1) || + (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA1)) + { + return TRUE; + } + else if (cave[c_y][c_x].feat == FEAT_PATTERN_START) + { + if (pattern_tile(n_y, n_x)) + return TRUE; + else + { + msg_print("You must walk the Straight Road in correct order."); + return FALSE; + } + } + else if ((cave[c_y][c_x].feat == FEAT_PATTERN_OLD) || + (cave[c_y][c_x].feat == FEAT_PATTERN_END) || + (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA2)) + { + if (!pattern_tile(n_y, n_x)) + { + msg_print("You may not step off from the Straight Road."); + return FALSE; + } + else + { + return TRUE; + } + } + else + { + if (!pattern_tile(c_y, c_x)) + { + msg_print + ("You must start walking the Straight Road from the startpoint."); + return FALSE; + } + else + { + byte ok_move = FEAT_PATTERN_START; + switch (cave[c_y][c_x].feat) + { + case FEAT_PATTERN_1: + ok_move = FEAT_PATTERN_2; + break; + case FEAT_PATTERN_2: + ok_move = FEAT_PATTERN_3; + break; + case FEAT_PATTERN_3: + ok_move = FEAT_PATTERN_4; + break; + case FEAT_PATTERN_4: + ok_move = FEAT_PATTERN_1; + break; + default: + if (wizard) + msg_format("Funny Straight Road walking, %d.", + cave[c_y][c_x]); + return TRUE; /* Goof-up */ + } + + if ((cave[n_y][n_x].feat == ok_move) || + (cave[n_y][n_x].feat == cave[c_y][c_x].feat)) + return TRUE; + else + { + if (!pattern_tile(n_y, n_x)) + msg_print("You may not step off from the Straight Road."); + else + msg_print + ("You must walk the Straight Road in correct order."); + + return FALSE; + } + } + } +} + + + +bool_ player_can_enter(byte feature) +{ + bool_ pass_wall; + + bool_ only_wall = FALSE; + + + /* Player can not walk through "walls" unless in Shadow Form */ + if (p_ptr->wraith_form || (PRACE_FLAG(PR1_SEMI_WRAITH))) + pass_wall = TRUE; + else + pass_wall = FALSE; + + /* Wall mimicry force the player to stay in walls */ + if (p_ptr->mimic_extra & CLASS_WALL) + { + only_wall = TRUE; + } + + /* Don't let the player kill himself with one keystroke */ + if (p_ptr->wild_mode) + { + if (feature == FEAT_DEEP_WATER) + { + int wt = weight_limit() / 2; + + if ((calc_total_weight() >= wt) && !(p_ptr->ffall)) + return (FALSE); + } + else if (feature == FEAT_SHAL_LAVA || + feature == FEAT_DEEP_LAVA) + { + if (!(p_ptr->resist_fire || + p_ptr->immune_fire || + p_ptr->oppose_fire || + p_ptr->ffall)) + return (FALSE); + } + } + + if (feature == FEAT_TREES) + { + if (p_ptr->fly || + pass_wall || + (has_ability(AB_TREE_WALK)) || + (p_ptr->mimic_form == resolve_mimic_name("Ent")) || + ((p_ptr->grace >= 9000) && (p_ptr->praying) && (p_ptr->pgod == GOD_YAVANNA))) + return (TRUE); + } + + if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB)) + return (TRUE); + if ((p_ptr->fly) && + ((f_info[feature].flags1 & FF1_CAN_FLY) || + (f_info[feature].flags1 & FF1_CAN_LEVITATE))) + return (TRUE); + else if (only_wall && (f_info[feature].flags1 & FF1_FLOOR)) + return (FALSE); + else if ((p_ptr->ffall) && + (f_info[feature].flags1 & FF1_CAN_LEVITATE)) + return (TRUE); + else if ((pass_wall || only_wall) && + (f_info[feature].flags1 & FF1_CAN_PASS)) + return (TRUE); + else if (f_info[feature].flags1 & FF1_NO_WALK) + return (FALSE); + else if ((f_info[feature].flags1 & FF1_WEB) && + ((!(r_info[p_ptr->body_monster].flags7 & RF7_SPIDER)) && (p_ptr->mimic_form != resolve_mimic_name("Spider")))) + return (FALSE); + + return (TRUE); +} + +/* + * Move player in the given direction, with the given "pickup" flag. + * + * This routine should (probably) always induce energy expenditure. + * + * Note that moving will *always* take a turn, and will *always* hit + * any monster which might be in the destination grid. Previously, + * moving into walls was "free" and did NOT hit invisible monsters. + */ +void move_player_aux(int dir, int do_pickup, int run, bool_ disarm) +{ + int y, x, tmp; + + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + monster_type *m_ptr; + + monster_race *r_ptr = &r_info[p_ptr->body_monster], *mr_ptr; + + char m_name[80]; + + bool_ stormbringer = FALSE; + + bool_ old_dtrap, new_dtrap; + + bool_ oktomove = TRUE; + + + /* Hack - random movement */ + if (p_ptr->disembodied) + tmp = dir; + else if ((r_ptr->flags1 & RF1_RAND_25) && (r_ptr->flags1 & RF1_RAND_50)) + { + if (randint(100) < 75) + tmp = randint(9); + else + tmp = dir; + } + else if (r_ptr->flags1 & RF1_RAND_50) + { + if (randint(100) < 50) + tmp = randint(9); + else + tmp = dir; + } + else if (r_ptr->flags1 & RF1_RAND_25) + { + if (randint(100) < 25) + tmp = randint(9); + else + tmp = dir; + } + else + { + tmp = dir; + } + + if ((c_ptr->feat == FEAT_ICE) && (!p_ptr->ffall && !p_ptr->fly)) + { + if (magik(70 - p_ptr->lev)) + { + tmp = randint(9); + msg_print("You slip on the icy floor."); + } + else + tmp = dir; + } + + /* Find the result of moving */ + y = p_ptr->py + ddy[tmp]; + x = p_ptr->px + ddx[tmp]; + + /* Examine the destination */ + c_ptr = &cave[y][x]; + + /* Change oldpx and oldpy to place the player well when going back to big mode */ + if (p_ptr->wild_mode) + { + if (ddy[tmp] > 0) p_ptr->oldpy = 1; + if (ddy[tmp] < 0) p_ptr->oldpy = MAX_HGT - 2; + if (ddy[tmp] == 0) p_ptr->oldpy = MAX_HGT / 2; + if (ddx[tmp] > 0) p_ptr->oldpx = 1; + if (ddx[tmp] < 0) p_ptr->oldpx = MAX_WID - 2; + if (ddx[tmp] == 0) p_ptr->oldpx = MAX_WID / 2; + } + + /* Exit the area */ + if (!dun_level && !p_ptr->wild_mode && !is_quest(dun_level) && + ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1))) + { + /* Can the player enter the grid? */ + if (player_can_enter(c_ptr->mimic)) + { + /* Hack: move to new area */ + if ((y == 0) && (x == 0)) + { + p_ptr->wilderness_y--; + p_ptr->wilderness_x--; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = cur_wid - 2; + ambush_flag = FALSE; + } + + else if ((y == 0) && (x == MAX_WID - 1)) + { + p_ptr->wilderness_y--; + p_ptr->wilderness_x++; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = 1; + ambush_flag = FALSE; + } + + else if ((y == MAX_HGT - 1) && (x == 0)) + { + p_ptr->wilderness_y++; + p_ptr->wilderness_x--; + p_ptr->oldpy = 1; + p_ptr->oldpx = cur_wid - 2; + ambush_flag = FALSE; + } + + else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1)) + { + p_ptr->wilderness_y++; + p_ptr->wilderness_x++; + p_ptr->oldpy = 1; + p_ptr->oldpx = 1; + ambush_flag = FALSE; + } + + else if (y == 0) + { + p_ptr->wilderness_y--; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = x; + ambush_flag = FALSE; + } + + else if (y == cur_hgt - 1) + { + p_ptr->wilderness_y++; + p_ptr->oldpy = 1; + p_ptr->oldpx = x; + ambush_flag = FALSE; + } + + else if (x == 0) + { + p_ptr->wilderness_x--; + p_ptr->oldpx = cur_wid - 2; + p_ptr->oldpy = y; + ambush_flag = FALSE; + } + + else if (x == cur_wid - 1) + { + p_ptr->wilderness_x++; + p_ptr->oldpx = 1; + p_ptr->oldpy = y; + ambush_flag = FALSE; + } + + p_ptr->leaving = TRUE; + + return; + } + } + + /* Some hooks */ + if (process_hooks(HOOK_MOVE, "(d,d)", y, x)) return; + + { + hook_move_in in = { y, x }; + if (process_hooks_new(HOOK_MOVE, &in, NULL)) { + return; /* Prevent movement */ + } + } + + if (p_ptr->dripping_tread > 0) + { + geomancy_random_floor(y, x, FALSE); + p_ptr->dripping_tread -= 1; + if (p_ptr->dripping_tread == 0) + { + msg_print("You stop dripping raw elemental energies."); + } + } + + + /* Get the monster */ + m_ptr = &m_list[c_ptr->m_idx]; + mr_ptr = race_inf(m_ptr); + + if (p_ptr->inventory[INVEN_WIELD].art_name) + { + if (streq(quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'")) + stormbringer = TRUE; + } + + /* Hack -- attack monsters */ + if (c_ptr->m_idx && (m_ptr->ml || player_can_enter(c_ptr->feat))) + { + + /* Attack -- only if we can see it OR it is not in a wall */ + if ((is_friend(m_ptr) > 0) && + !(p_ptr->confused || p_ptr->image || !(m_ptr->ml) || p_ptr->stun) && + (pattern_seq(p_ptr->py, p_ptr->px, y, x)) && + ((player_can_enter(cave[y][x].feat)))) + { + m_ptr->csleep = 0; + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Auto-Recall if possible and visible */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); + + /* Track a new monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + + /* displace? */ + if (stormbringer && (randint(1000) > 666)) + { + py_attack(y, x, -1); + } + else if (cave_floor_bold(p_ptr->py, p_ptr->px) || + (mr_ptr->flags2 & RF2_PASS_WALL)) + { + msg_format("You push past %s.", m_name); + m_ptr->fy = p_ptr->py; + m_ptr->fx = p_ptr->px; + cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx; + c_ptr->m_idx = 0; + update_mon(cave[p_ptr->py][p_ptr->px].m_idx, TRUE); + } + else + { + msg_format("%^s is in your way!", m_name); + energy_use = 0; + oktomove = FALSE; + } + + /* now continue on to 'movement' */ + } + else + { + py_attack(y, x, -1); + oktomove = FALSE; + } + } + + else if ((c_ptr->feat == FEAT_DARK_PIT) && !p_ptr->ffall) + { + msg_print("You can't cross the chasm."); + running = 0; + oktomove = FALSE; + } + + /* Disarm a visible trap */ + else if (easy_disarm && disarm && (c_ptr->info & (CAVE_TRDT))) + { + (void)do_cmd_disarm_aux(y, x, tmp, do_pickup); + return; + } + + /* Don't step on known traps. */ + else if (disarm && (c_ptr->info & (CAVE_TRDT)) && !(p_ptr->confused || p_ptr->stun || p_ptr->image)) + { + msg_print("You stop to avoid triggering the trap."); + energy_use = 0; + oktomove = FALSE; + } + + /* Player can't enter ? soo bad for him/her ... */ + else if (!player_can_enter(c_ptr->feat)) + { + oktomove = FALSE; + + /* Disturb the player */ + disturb(0); + + if (p_ptr->prob_travel) + { + if (passwall(tmp, TRUE)) return; + } + + /* Notice things in the dark */ + if (!(c_ptr->info & (CAVE_MARK)) && !(c_ptr->info & (CAVE_SEEN))) + { + /* Rubble */ + if (c_ptr->feat == FEAT_RUBBLE) + { + msg_print("You feel some rubble blocking your way."); + c_ptr->info |= (CAVE_MARK); + lite_spot(y, x); + } + + /* Closed door */ + else if (c_ptr->feat < FEAT_SECRET) + { + msg_print("You feel a closed door blocking your way."); + c_ptr->info |= (CAVE_MARK); + lite_spot(y, x); + } + + /* Wall (or secret door) */ + else + { + int feat; + + if (c_ptr->mimic) feat = c_ptr->mimic; + else + feat = f_info[c_ptr->feat].mimic; + + msg_format("You feel %s.", f_text + f_info[feat].block); + c_ptr->info |= (CAVE_MARK); + lite_spot(y, x); + } + } + + /* Notice things */ + else + { + /* Rubble */ + if (c_ptr->feat == FEAT_RUBBLE) + { + if (!easy_tunnel) + { + msg_print("There is rubble blocking your way."); + + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + energy_use = 0; + /* + * Well, it makes sense that you lose time bumping into + * a wall _if_ you are confused, stunned or blind; but + * typing mistakes should not cost you a turn... + */ + } + else + { + do_cmd_tunnel_aux(y, x, dir); + return; + } + } + /* Closed doors */ + else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)) + { + if (easy_open) + { + if (easy_open_door(y, x)) return; + } + else + { + msg_print("There is a closed door blocking your way."); + + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + energy_use = 0; + } + } + + /* Wall (or secret door) */ + else + { + if (!easy_tunnel) + { + int feat; + + if (c_ptr->mimic) feat = c_ptr->mimic; + else + feat = f_info[c_ptr->feat].mimic; + + msg_format("There is %s.", f_text + f_info[feat].block); + + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + energy_use = 0; + } + else + { + do_cmd_tunnel_aux(y, x, dir); + return; + } + } + } + + /* Sound */ + sound(SOUND_HITWALL); + } + + /* Normal movement */ + if (!pattern_seq(p_ptr->py, p_ptr->px, y, x)) + { + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + { + energy_use = 0; + } + + disturb(0); /* To avoid a loop with running */ + + oktomove = FALSE; + } + + + /* + * Check trap detection status -- retrieve them here + * because they are used by the movement code as well + */ + old_dtrap = ((cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT) != 0); + new_dtrap = ((cave[y][x].info & CAVE_DETECT) != 0); + + /* Normal movement */ + if (oktomove && running && disturb_detect) + { + /* + * Disturb the player when about to leave the trap detected + * area + */ + if (old_dtrap && !new_dtrap) + { + /* Disturb player */ + disturb(0); + + /* but don't take a turn */ + energy_use = 0; + + /* Tell player why */ + cmsg_print(TERM_VIOLET, "You are about to leave a trap detected zone."); + /* Flush */ + /* msg_print(NULL); */ + + oktomove = FALSE; + } + } + + /* Normal movement */ + if (oktomove) + { + int oy, ox; + int feat; + + /* Rooted means no move */ + if (p_ptr->tim_roots) return; + + /* Save old location */ + oy = p_ptr->py; + ox = p_ptr->px; + + /* Move the player */ + p_ptr->py = y; + p_ptr->px = x; + + if (cave[p_ptr->py][p_ptr->px].mimic) feat = cave[p_ptr->py][p_ptr->px].mimic; + else + feat = cave[p_ptr->py][p_ptr->px].feat; + + /* Some hooks */ + if (process_hooks(HOOK_MOVED, "(d,d)", oy, ox)) return; + + /* Redraw new spot */ + lite_spot(p_ptr->py, p_ptr->px); + + /* Redraw old spot */ + lite_spot(oy, ox); + + /* Sound */ + /* sound(SOUND_WALK); */ + + /* Check for new panel (redraw map) */ + verify_panel(); + + /* Check detection status */ + if (old_dtrap && !new_dtrap) + { + cmsg_print(TERM_VIOLET, "You leave a trap detected zone."); + if (running) msg_print(NULL); + p_ptr->redraw |= (PR_DTRAP); + } + else if (!old_dtrap && new_dtrap) + { + cmsg_print(TERM_L_BLUE, "You enter a trap detected zone."); + if (running) msg_print(NULL); + p_ptr->redraw |= (PR_DTRAP); + } + + /* Update stuff */ + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); + + /* Update the monsters */ + p_ptr->update |= (PU_DISTANCE); + + /* Window stuff */ + if (!run) p_ptr->window |= (PW_OVERHEAD); + + /* Some feature descs */ + if (f_info[cave[p_ptr->py][p_ptr->px].feat].text > 1) + { + /* Mega-hack for dungeon branches */ + if ((feat == FEAT_MORE) && c_ptr->special) + { + msg_format("There is %s", d_text + d_info[c_ptr->special].text); + } + else + { + msg_print(f_text + f_info[feat].text); + } + + /* Flush message while running */ + if (running) msg_print(NULL); + } + + /* Spontaneous Searching */ + if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos))) + { + search(); + } + + /* Continuous Searching */ + if (p_ptr->searching) + { + search(); + } + + /* Handle "objects" */ + carry(do_pickup); + + /* Handle "store doors" */ + if (c_ptr->feat == FEAT_SHOP) + { + /* Disturb */ + disturb(0); + + /* Hack -- Enter store */ + command_new = '_'; + } + + else if (cave[y][x].feat >= FEAT_ALTAR_HEAD && + cave[y][x].feat <= FEAT_ALTAR_TAIL) + { + cptr name = f_name + f_info[cave[y][x].feat].name; + cptr pref = (is_a_vowel(name[0])) ? "an" : "a"; + + msg_format("You see %s %s.", pref, name); + + /* Flush message while running */ + if (running) msg_print(NULL); + } + + /* Discover invisible traps */ + else if ((c_ptr->t_idx != 0) && + !(f_info[cave[y][x].feat].flags1 & FF1_DOOR)) + { + /* Disturb */ + disturb(0); + + if (!(c_ptr->info & (CAVE_TRDT))) + { + /* Message */ + msg_print("You found a trap!"); + + /* Pick a trap */ + pick_trap(p_ptr->py, p_ptr->px); + } + + /* Hit the trap */ + hit_trap(); + } + + /* Execute the inscription */ + else if (c_ptr->inscription) + { + /* Disturb */ + disturb(0); + + msg_format("There is an inscription here: %s", + inscription_info[c_ptr->inscription].text); + if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK) + { + execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px); + } + } + } + + /* Update wilderness knowledge */ + if (p_ptr->wild_mode) + { + if (wizard) msg_format("y:%d, x:%d", p_ptr->py, p_ptr->px); + + /* Update the known wilderness */ + reveal_wilderness_around_player(p_ptr->py, p_ptr->px, 0, WILDERNESS_SEE_RADIUS); + + /* Walking the wild isnt meaningfull */ + p_ptr->did_nothing = TRUE; + } +} + +void move_player(int dir, int do_pickup, bool_ disarm) +{ + move_player_aux(dir, do_pickup, 0, disarm); +} + + +/* + * Hack -- Grid-based version of see_obstacle + */ +static int see_obstacle_grid(cave_type *c_ptr) +{ + /* + * Hack -- Avoid hitting detected traps, because we cannot rely on + * the CAVE_MARK check below, and traps can be set to nearly + * everything the player can move on to XXX XXX XXX + */ + if (c_ptr->info & (CAVE_TRDT)) return (TRUE); + + + /* Hack -- Handle special cases XXX XXX */ + switch (c_ptr->feat) + { + /* Require levitation */ + case FEAT_DARK_PIT: + case FEAT_DEEP_WATER: + case FEAT_ICE: + { + if (p_ptr->ffall || p_ptr->fly) return (FALSE); + } + + /* Require immunity */ + case FEAT_DEEP_LAVA: + case FEAT_SHAL_LAVA: + { + if (p_ptr->invuln || p_ptr->immune_fire) return (FALSE); + } + } + + + /* "Safe" floor grids aren't obstacles */ + if (f_info[c_ptr->feat].flags1 & FF1_CAN_RUN) return (FALSE); + + /* Must be known to the player */ + if (!(c_ptr->info & (CAVE_MARK))) return (FALSE); + + /* Default */ + return (TRUE); +} + + +/* + * Hack -- Check for a "known wall" or "dangerous" feature (see below) + */ +static int see_obstacle(int dir, int y, int x) +{ + /* Get the new location */ + y += ddy[dir]; + x += ddx[dir]; + + /* Illegal grids are not known walls */ + if (!in_bounds2(y, x)) return (FALSE); + + /* Analyse the grid */ + return (see_obstacle_grid(&cave[y][x])); +} + + +/* + * Hack -- Check for an "unknown corner" (see below) + */ +static int see_nothing(int dir, int y, int x) +{ + /* Get the new location */ + y += ddy[dir]; + x += ddx[dir]; + + /* Illegal grids are unknown */ + if (!in_bounds2(y, x)) return (TRUE); + + /* Memorized grids are always known */ + if (cave[y][x].info & (CAVE_MARK)) return (FALSE); + + /* Non-floor grids are unknown */ + if (!cave_floor_bold(y, x)) return (TRUE); + + /* Viewable door/wall grids are known */ + if (player_can_see_bold(y, x)) return (FALSE); + + /* Default */ + return (TRUE); +} + + + + + +/* + * The running algorithm: -CJS- + * + * In the diagrams below, the player has just arrived in the + * grid marked as '@', and he has just come from a grid marked + * as 'o', and he is about to enter the grid marked as 'x'. + * + * Of course, if the "requested" move was impossible, then you + * will of course be blocked, and will stop. + * + * Overview: You keep moving until something interesting happens. + * If you are in an enclosed space, you follow corners. This is + * the usual corridor scheme. If you are in an open space, you go + * straight, but stop before entering enclosed space. This is + * analogous to reaching doorways. If you have enclosed space on + * one side only (that is, running along side a wall) stop if + * your wall opens out, or your open space closes in. Either case + * corresponds to a doorway. + * + * What happens depends on what you can really SEE. (i.e. if you + * have no light, then running along a dark corridor is JUST like + * running in a dark room.) The algorithm works equally well in + * corridors, rooms, mine tailings, earthquake rubble, etc, etc. + * + * These conditions are kept in static memory: + * find_openarea You are in the open on at least one + * side. + * find_breakleft You have a wall on the left, and will + * stop if it opens + * find_breakright You have a wall on the right, and will + * stop if it opens + * + * To initialize these conditions, we examine the grids adjacent + * to the grid marked 'x', two on each side (marked 'L' and 'R'). + * If either one of the two grids on a given side is seen to be + * closed, then that side is considered to be closed. If both + * sides are closed, then it is an enclosed (corridor) run. + * + * LL L + * @x LxR + * RR @R + * + * Looking at more than just the immediate squares is + * significant. Consider the following case. A run along the + * corridor will stop just before entering the center point, + * because a choice is clearly established. Running in any of + * three available directions will be defined as a corridor run. + * Note that a minor hack is inserted to make the angled corridor + * entry (with one side blocked near and the other side blocked + * further away from the runner) work correctly. The runner moves + * diagonally, but then saves the previous direction as being + * straight into the gap. Otherwise, the tail end of the other + * entry would be perceived as an alternative on the next move. + * + * #.# + * ##.## + * .@x.. + * ##.## + * #.# + * + * Likewise, a run along a wall, and then into a doorway (two + * runs) will work correctly. A single run rightwards from @ will + * stop at 1. Another run right and down will enter the corridor + * and make the corner, stopping at the 2. + * + * #@x 1 + * ########### ###### + * 2 # + * ############# + * # + * + * After any move, the function area_affect is called to + * determine the new surroundings, and the direction of + * subsequent moves. It examines the current player location + * (at which the runner has just arrived) and the previous + * direction (from which the runner is considered to have come). + * + * Moving one square in some direction places you adjacent to + * three or five new squares (for straight and diagonal moves + * respectively) to which you were not previously adjacent, + * marked as '!' in the diagrams below. + * + * ...! ... + * .o@! .o.! + * ...! ..@! + * !!! + * + * You STOP if any of the new squares are interesting in any way: + * for example, if they contain visible monsters or treasure. + * + * You STOP if any of the newly adjacent squares seem to be open, + * and you are also looking for a break on that side. (that is, + * find_openarea AND find_break). + * + * You STOP if any of the newly adjacent squares do NOT seem to be + * open and you are in an open area, and that side was previously + * entirely open. + * + * Corners: If you are not in the open (i.e. you are in a corridor) + * and there is only one way to go in the new squares, then turn in + * that direction. If there are more than two new ways to go, STOP. + * If there are two ways to go, and those ways are separated by a + * square which does not seem to be open, then STOP. + * + * Otherwise, we have a potential corner. There are two new open + * squares, which are also adjacent. One of the new squares is + * diagonally located, the other is straight on (as in the diagram). + * We consider two more squares further out (marked below as ?). + * + * We assign "option" to the straight-on grid, and "option2" to the + * diagonal grid, and "check_dir" to the grid marked 's'. + * + * .s + * @x? + * #? + * + * If they are both seen to be closed, then it is seen that no + * benefit is gained from moving straight. It is a known corner. + * To cut the corner, go diagonally, otherwise go straight, but + * pretend you stepped diagonally into that next location for a + * full view next time. Conversely, if one of the ? squares is + * not seen to be closed, then there is a potential choice. We check + * to see whether it is a potential corner or an intersection/room entrance. + * If the square two spaces straight ahead, and the space marked with 's' + * are both blank, then it is a potential corner and enter if find_examine + * is set, otherwise must stop because it is not a corner. + */ + + + + +/* + * Hack -- allow quick "cycling" through the legal directions + */ +static byte cycle[] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 }; + +/* + * Hack -- map each direction into the "middle" of the "cycle[]" array + */ +static byte chome[] = { 0, 8, 9, 10, 7, 0, 11, 6, 5, 4 }; + +/* + * The direction we are running + */ +static byte find_current; + +/* + * The direction we came from + */ +static byte find_prevdir; + +/* + * We are looking for open area + */ +static bool_ find_openarea; + +/* + * We are looking for a break + */ +static bool_ find_breakright; +static bool_ find_breakleft; + + + +/* + * Initialize the running algorithm for a new direction. + * + * Diagonal Corridor -- allow diaginal entry into corridors. + * + * Blunt Corridor -- If there is a wall two spaces ahead and + * we seem to be in a corridor, then force a turn into the side + * corridor, must be moving straight into a corridor here. ??? + * + * Diagonal Corridor Blunt Corridor (?) + * # # # + * #x# @x# + * @p. p + */ +static void run_init(int dir) +{ + int row, col, deepleft, deepright; + + int i, shortleft, shortright; + + + /* Save the direction */ + find_current = dir; + + /* Assume running straight */ + find_prevdir = dir; + + /* Assume looking for open area */ + find_openarea = TRUE; + + /* Assume not looking for breaks */ + find_breakright = find_breakleft = FALSE; + + /* Assume no nearby walls */ + deepleft = deepright = FALSE; + shortright = shortleft = FALSE; + + /* Find the destination grid */ + row = p_ptr->py + ddy[dir]; + col = p_ptr->px + ddx[dir]; + + /* Extract cycle index */ + i = chome[dir]; + + /* Check for walls */ + if (see_obstacle(cycle[i + 1], p_ptr->py, p_ptr->px)) + { + find_breakleft = TRUE; + shortleft = TRUE; + } + else if (see_obstacle(cycle[i + 1], row, col)) + { + find_breakleft = TRUE; + deepleft = TRUE; + } + + /* Check for walls */ + if (see_obstacle(cycle[i - 1], p_ptr->py, p_ptr->px)) + { + find_breakright = TRUE; + shortright = TRUE; + } + else if (see_obstacle(cycle[i - 1], row, col)) + { + find_breakright = TRUE; + deepright = TRUE; + } + + /* Looking for a break */ + if (find_breakleft && find_breakright) + { + /* Not looking for open area */ + find_openarea = FALSE; + + /* Hack -- allow angled corridor entry */ + if (dir & 0x01) + { + if (deepleft && !deepright) + { + find_prevdir = cycle[i - 1]; + } + else if (deepright && !deepleft) + { + find_prevdir = cycle[i + 1]; + } + } + + /* Hack -- allow blunt corridor entry */ + else if (see_obstacle(cycle[i], row, col)) + { + if (shortleft && !shortright) + { + find_prevdir = cycle[i - 2]; + } + else if (shortright && !shortleft) + { + find_prevdir = cycle[i + 2]; + } + } + } +} + + +/* + * Update the current "run" path + * + * Return TRUE if the running should be stopped + */ +static bool_ run_test(void) +{ + int prev_dir, new_dir, check_dir = 0; + + int row, col; + + int i, max, inv; + + int option = 0, option2 = 0; + + cave_type *c_ptr; + + + /* Where we came from */ + prev_dir = find_prevdir; + + + /* Range of newly adjacent grids */ + max = (prev_dir & 0x01) + 1; + + + /* Look at every newly adjacent square. */ + for (i = -max; i <= max; i++) + { + s16b this_o_idx, next_o_idx = 0; + + + /* New direction */ + new_dir = cycle[chome[prev_dir] + i]; + + /* New location */ + row = p_ptr->py + ddy[new_dir]; + col = p_ptr->px + ddx[new_dir]; + + /* Access grid */ + c_ptr = &cave[row][col]; + + + /* Visible monsters abort running */ + if (c_ptr->m_idx) + { + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + + /* Visible monster */ + if (m_ptr->ml) return (TRUE); + } + + /* Visible objects abort running */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Visible object */ + if (o_ptr->marked) return (TRUE); + } + + + /* Assume unknown */ + inv = TRUE; + + /* Check memorized grids */ + if (c_ptr->info & (CAVE_MARK)) + { + bool_ notice = TRUE; + + /* + * Examine the terrain -- conditional disturbance + * If we had more flags, we could make these customisable too + */ + switch (c_ptr->feat) + { + case FEAT_DEEP_LAVA: + case FEAT_SHAL_LAVA: + { + /* Ignore */ + if (p_ptr->invuln || p_ptr->immune_fire) notice = FALSE; + + /* Done */ + break; + } + + case FEAT_DEEP_WATER: + case FEAT_ICE: + { + /* Ignore */ + if (p_ptr->ffall || p_ptr->fly) notice = FALSE; + + /* Done */ + break; + } + + /* Open doors */ + case FEAT_OPEN: + case FEAT_BROKEN: + { + /* Option -- ignore */ + if (find_ignore_doors) notice = FALSE; + + /* Done */ + break; + } + + /* + * Stairs - too many of them, should find better ways to + * handle them (not scripting!, because it can be called + * from within the running algo) XXX XXX XXX + */ + case FEAT_LESS: + case FEAT_MORE: + case FEAT_QUEST_ENTER: + case FEAT_QUEST_EXIT: + case FEAT_QUEST_DOWN: + case FEAT_QUEST_UP: + case FEAT_SHAFT_UP: + case FEAT_SHAFT_DOWN: + case FEAT_WAY_LESS: + case FEAT_WAY_MORE: + /* XXX */ + case FEAT_BETWEEN: + case FEAT_BETWEEN2: + { + /* Option -- ignore */ + if (find_ignore_stairs) notice = FALSE; + + /* Done */ + break; + } + } + + /* Check the "don't notice running" flag */ + if (f_info[c_ptr->feat].flags1 & FF1_DONT_NOTICE_RUNNING) + { + notice = FALSE; + } + + /* A detected trap is interesting */ + if (c_ptr->info & (CAVE_TRDT)) notice = TRUE; + + /* Interesting feature */ + if (notice) return (TRUE); + + /* The grid is "visible" */ + inv = FALSE; + } + + /* Mega-Hack -- Maze code removes CAVE_MARK XXX XXX XXX */ + if (c_ptr->info & (CAVE_TRDT)) return (TRUE); + + /* Analyze unknown grids and floors */ + if (inv || cave_floor_bold(row, col)) + { + /* Looking for open area */ + if (find_openarea) + { + /* Nothing */ + } + + /* The first new direction. */ + else if (!option) + { + option = new_dir; + } + + /* Three new directions. Stop running. */ + else if (option2) + { + return (TRUE); + } + + /* Two non-adjacent new directions. Stop running. */ + else if (option != cycle[chome[prev_dir] + i - 1]) + { + return (TRUE); + } + + /* Two new (adjacent) directions (case 1) */ + else if (new_dir & 0x01) + { + check_dir = cycle[chome[prev_dir] + i - 2]; + option2 = new_dir; + } + + /* Two new (adjacent) directions (case 2) */ + else + { + check_dir = cycle[chome[prev_dir] + i + 1]; + option2 = option; + option = new_dir; + } + } + + /* Obstacle, while looking for open area */ + else + { + if (find_openarea) + { + if (i < 0) + { + /* Break to the right */ + find_breakright = TRUE; + } + + else if (i > 0) + { + /* Break to the left */ + find_breakleft = TRUE; + } + } + } + } + + + /* Looking for open area */ + if (find_openarea) + { + /* Hack -- look again */ + for (i = -max; i < 0; i++) + { + new_dir = cycle[chome[prev_dir] + i]; + + row = p_ptr->py + ddy[new_dir]; + col = p_ptr->px + ddx[new_dir]; + + /* Access grid */ + c_ptr = &cave[row][col]; + + /* Unknown grids or non-obstacle */ + if (!see_obstacle_grid(c_ptr)) + { + /* Looking to break right */ + if (find_breakright) + { + return (TRUE); + } + } + + /* Obstacle */ + else + { + /* Looking to break left */ + if (find_breakleft) + { + return (TRUE); + } + } + } + + /* Hack -- look again */ + for (i = max; i > 0; i--) + { + new_dir = cycle[chome[prev_dir] + i]; + + row = p_ptr->py + ddy[new_dir]; + col = p_ptr->px + ddx[new_dir]; + + /* Access grid */ + c_ptr = &cave[row][col]; + + /* Unknown grid or non-obstacle */ + if (!see_obstacle_grid(c_ptr)) + { + /* Looking to break left */ + if (find_breakleft) + { + return (TRUE); + } + } + + /* Obstacle */ + else + { + /* Looking to break right */ + if (find_breakright) + { + return (TRUE); + } + } + } + } + + + /* Not looking for open area */ + else + { + /* No options */ + if (!option) + { + return (TRUE); + } + + /* One option */ + else if (!option2) + { + /* Primary option */ + find_current = option; + + /* No other options */ + find_prevdir = option; + } + + /* Two options, examining corners */ + else if (find_examine && !find_cut) + { + /* Primary option */ + find_current = option; + + /* Hack -- allow curving */ + find_prevdir = option2; + } + + /* Two options, pick one */ + else + { + /* Get next location */ + row = p_ptr->py + ddy[option]; + col = p_ptr->px + ddx[option]; + + /* Don't see that it is closed off. */ + /* This could be a potential corner or an intersection. */ + if (!see_obstacle(option, row, col) || !see_obstacle(check_dir, row, col)) + { + /* Can not see anything ahead and in the direction we */ + /* are turning, assume that it is a potential corner. */ + if (find_examine && + see_nothing(option, row, col) && + see_nothing(option2, row, col)) + { + find_current = option; + find_prevdir = option2; + } + + /* STOP: we are next to an intersection or a room */ + else + { + return (TRUE); + } + } + + /* This corner is seen to be enclosed; we cut the corner. */ + else if (find_cut) + { + find_current = option2; + find_prevdir = option2; + } + + /* This corner is seen to be enclosed, and we */ + /* deliberately go the long way. */ + else + { + find_current = option; + find_prevdir = option2; + } + } + } + + + /* About to hit a known wall, stop */ + if (see_obstacle(find_current, p_ptr->py, p_ptr->px)) + { + return (TRUE); + } + + + /* Failure */ + return (FALSE); +} + + + +/* + * Take one step along the current "run" path + */ +void run_step(int dir) +{ + /* Start running */ + if (dir) + { + /* Hack -- do not start silly run */ + if (see_obstacle(dir, p_ptr->py, p_ptr->px) && + (cave[p_ptr->py + ddy[dir]][p_ptr->px + ddx[dir]].feat != FEAT_TREES)) + { + /* Message */ + msg_print("You cannot run in that direction."); + + /* Disturb */ + disturb(0); + + /* Done */ + return; + } + + /* Calculate torch radius */ + p_ptr->update |= (PU_TORCH); + + /* Initialize */ + run_init(dir); + } + + /* Keep running */ + else + { + /* Update run */ + if (run_test()) + { + /* Disturb */ + disturb(0); + + /* Done */ + return; + } + } + + /* Decrease the run counter */ + if (--running <= 0) return; + + /* Take time */ + energy_use = 100; + + + /* Move the player, using the "pickup" flag */ + move_player_aux(find_current, always_pickup, 1, TRUE); +} + + +/* + * Take care of the various things that can happen when you step + * into a space. (Objects, traps, and stores.) + */ +void step_effects(int y, int x, int do_pickup) +{ + /* Handle "objects" */ + py_pickup_floor(do_pickup); + + /* Handle "store doors" */ + if (cave[y][x].feat == FEAT_SHOP) + { + /* Disturb */ + disturb(0); + + /* Hack -- Enter store */ + command_new = KTRL('V'); + } + + /* Discover/set off traps */ + else if (cave[y][x].t_idx != 0) + { + /* Disturb */ + disturb(0); + + if (!(cave[y][x].info & CAVE_TRDT)) + { + /* Message */ + msg_print("You found a trap!"); + + /* Pick a trap */ + pick_trap(y, x); + } + + /* Hit the trap */ + hit_trap(); + } +} + +/* + * Issue a pet command + */ +void do_cmd_pet(void) +{ + int i = 0; + + int num = 0; + + int powers[36]; + + char power_desc[36][80]; + + bool_ flag, redraw; + + int ask; + + char choice; + + char out_val[160]; + + int pets = 0, pet_ctr = 0; + + bool_ all_pets = FALSE; + + monster_type *m_ptr; + + + for (num = 0; num < 36; num++) + { + powers[num] = 0; + strcpy(power_desc[num], ""); + } + + num = 0; + + if (p_ptr->confused) + { + msg_print("You are too confused to command your pets."); + energy_use = 0; + return; + } + + /* Calculate pets */ + /* Process the monsters (backwards) */ + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + + if (m_ptr->status >= MSTATUS_FRIEND) pets++; + } + + if (pets == 0) + { + msg_print("You have no pets/companions."); + energy_use = 0; + return; + } + else + { + strcpy(power_desc[num], "dismiss pets"); + powers[num++] = 1; + strcpy(power_desc[num], "dismiss companions"); + powers[num++] = 10; + strcpy(power_desc[num], "call pets"); + powers[num++] = 2; + strcpy(power_desc[num], "follow me"); + powers[num++] = 6; + strcpy(power_desc[num], "seek and destroy"); + powers[num++] = 3; + if (p_ptr->pet_open_doors) + strcpy(power_desc[num], "disallow open doors"); + else + strcpy(power_desc[num], "allow open doors"); + powers[num++] = 4; + if (p_ptr->pet_pickup_items) + strcpy(power_desc[num], "disallow pickup items"); + else + strcpy(power_desc[num], "allow pickup items"); + powers[num++] = 5; + strcpy(power_desc[num], "give target to a friend"); + powers[num++] = 7; + strcpy(power_desc[num], "give target to all friends"); + powers[num++] = 8; + strcpy(power_desc[num], "friend forget target"); + powers[num++] = 9; + } + + /* Nothing chosen yet */ + flag = FALSE; + + /* No redraw yet */ + redraw = FALSE; + + /* Build a prompt (accept all spells) */ + if (num <= 26) + { + /* Build a prompt (accept all spells) */ + strnfmt(out_val, 78, + "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0), + I2A(num - 1)); + } + else + { + strnfmt(out_val, 78, + "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0), + '0' + num - 27); + } + + /* Get a command from the user */ + while (!flag && get_com(out_val, &choice)) + { + /* Request redraw */ + if ((choice == ' ') || (choice == '*') || (choice == '?')) + { + /* Show the list */ + if (!redraw) + { + byte y = 1, x = 0; + int ctr = 0; + char dummy[80]; + + strcpy(dummy, ""); + + /* Show list */ + redraw = TRUE; + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + prt("", y++, x); + + while (ctr < num) + { + strnfmt(dummy, 80, "%c) %s", I2A(ctr), power_desc[ctr]); + prt(dummy, y + ctr, x); + ctr++; + } + + if (ctr < 17) + { + prt("", y + ctr, x); + } + else + { + prt("", y + 17, x); + } + } + + /* Hide the list */ + else + { + /* Hide list */ + redraw = FALSE; + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + } + + /* Redo asking */ + continue; + } + + if (choice == '\r' && num == 1) + { + choice = 'a'; + } + + if (isalpha(choice)) + { + /* Note verify */ + ask = (isupper(choice)); + + /* Lowercase */ + if (ask) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + } + else + { + ask = FALSE; /* Can't uppercase digits */ + + i = choice - '0' + 26; + } + + /* Totally Illegal */ + if ((i < 0) || (i >= num)) + { + bell(); + continue; + } + + /* Verify it */ + if (ask) + { + char tmp_val[160]; + + /* Prompt */ + strnfmt(tmp_val, 78, "Use %s? ", power_desc[i]); + + /* Belay that order */ + if (!get_check(tmp_val)) continue; + } + + /* Stop the loop */ + flag = TRUE; + } + + /* Restore the screen */ + if (redraw) + { + Term_load(); + character_icky = FALSE; + } + + /* Abort if needed */ + if (!flag) + { + energy_use = 0; + return; + } + + switch (powers[i]) + { + /* forget target */ + case 9: + { + monster_type *m_ptr; + int ii, jj; + + msg_print("Select the friendly monster:"); + if (!tgt_pt(&ii, &jj)) return; + + if (cave[jj][ii].m_idx) + { + m_ptr = &m_list[cave[jj][ii].m_idx]; + + if (m_ptr->status < MSTATUS_PET) + { + msg_print("You cannot give orders to this monster."); + return; + } + + m_ptr->target = -1; + } + break; + } + /* Give target to all */ + case 8: + { + monster_type *m_ptr; + int ii, jj, i; + + + msg_print("Select the target monster:"); + if (!tgt_pt(&ii, &jj)) return; + + if (cave[jj][ii].m_idx) + { + msg_print("Target selected."); + + for (i = m_max - 1; i >= 1; i--) + { + /* Access the monster */ + m_ptr = &m_list[i]; + + if (!m_ptr->r_idx) continue; + + if (m_ptr->status < MSTATUS_PET) continue; + + m_ptr->target = cave[jj][ii].m_idx; + } + } + else + { + msg_print("This is not a correct target."); + return; + } + break; + } + case 1: /* Dismiss pets */ + { + int Dismissed = 0; + + if (get_check("Dismiss all pets? ")) all_pets = TRUE; + + /* Process the monsters (backwards) */ + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + monster_race *r_ptr; + + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + r_ptr = &r_info[m_ptr->r_idx]; + + if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_PET) || (m_ptr->status == MSTATUS_FRIEND))) /* Get rid of it! */ + { + bool_ checked = FALSE; + char command; + bool_ delete_this = FALSE; + + if (all_pets) + { + delete_this = TRUE; + } + else + { + char friend_name[80], check_friend[80]; + monster_desc(friend_name, m_ptr, 0x80); + strnfmt(check_friend, 80, "Dismiss %s? (Escape to cancel)", friend_name); + + while (!checked) + { + if (!get_com(check_friend, &command)) + { + /* get out of loop */ + checked = TRUE; + pet_ctr = 0; + } + else switch (command) + { + case 'Y': + case 'y': + delete_this = TRUE; + checked = TRUE; + break; + case 'n': + case 'N': + checked = TRUE; + break; + default: + bell(); + break; + } + } + } + + if (delete_this) + { + delete_monster_idx(pet_ctr); + Dismissed++; + } + } + } + + msg_format("You have dismissed %d pet%s.", Dismissed, + (Dismissed == 1 ? "" : "s")); + break; + } + case 10: /* Dismiss companions */ + { + int Dismissed = 0; + + if (get_check("Dismiss all companions? ")) all_pets = TRUE; + + /* Process the monsters (backwards) */ + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + monster_race *r_ptr; + + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + r_ptr = &r_info[m_ptr->r_idx]; + + if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_COMPANION))) /* Get rid of it! */ + { + bool_ delete_this = FALSE; + + if (all_pets) + delete_this = TRUE; + else + { + char friend_name[80], check_friend[80]; + monster_desc(friend_name, m_ptr, 0x80); + strnfmt(check_friend, 80, "Dismiss %s? ", friend_name); + + if (get_check(check_friend)) + delete_this = TRUE; + } + + if (delete_this) + { + delete_monster_idx(pet_ctr); + Dismissed++; + } + } + } + + msg_format("You have dismissed %d companion%s.", Dismissed, + (Dismissed == 1 ? "" : "s")); + break; + } + /* Call pets */ + case 2: + { + p_ptr->pet_follow_distance = 1; + break; + } + /* "Seek and destroy" */ + case 3: + { + p_ptr->pet_follow_distance = 255; + break; + } + /* flag - allow pets to open doors */ + case 4: + { + p_ptr->pet_open_doors = !p_ptr->pet_open_doors; + break; + } + /* flag - allow pets to pickup items */ + case 5: + { + p_ptr->pet_pickup_items = !p_ptr->pet_pickup_items; + + /* Drop objects being carried by pets */ + if (!p_ptr->pet_pickup_items) + { + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + + if (m_ptr->status >= MSTATUS_PET) + { + monster_drop_carried_objects(m_ptr); + } + } + } + + break; + } + /* "Follow Me" */ + case 6: + { + p_ptr->pet_follow_distance = 6; + break; + } + } +} + +/* + * Incarnate into a body + */ +bool_ do_cmd_integrate_body() +{ + cptr q, s; + + int item; + + object_type *o_ptr; + + + if (!p_ptr->disembodied) + { + msg_print("You are already in a body."); + return FALSE; + } + + /* Restrict choices to monsters */ + item_tester_tval = TV_CORPSE; + + /* Get an item */ + q = "Incarnate in which body? "; + s = "You have no corpse to incarnate in."; + if (!get_item(&item, q, s, (USE_FLOOR))) return FALSE; + + o_ptr = &o_list[0 - item]; + + if (o_ptr->sval != SV_CORPSE_CORPSE) + { + msg_print("You must select a corpse."); + return FALSE; + } + + p_ptr->body_monster = o_ptr->pval2; + p_ptr->chp = o_ptr->pval3; + + floor_item_increase(0 - item, -1); + floor_item_describe(0 - item); + floor_item_optimize(0 - item); + + msg_print("Your spirit is incarnated in your new body."); + p_ptr->wraith_form = FALSE; + p_ptr->disembodied = FALSE; + do_cmd_redraw(); + + return TRUE; +} + +/* + * Leave a body + */ +bool_ do_cmd_leave_body(bool_ drop_body) +{ + object_type *o_ptr, forge; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + int i; + + + if (p_ptr->disembodied) + { + msg_print("You are already disembodied."); + return FALSE; + } + + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + if (p_ptr->body_parts[i - INVEN_WIELD] && p_ptr->inventory[i].k_idx && + cursed_p(&p_ptr->inventory[i])) + { + msg_print("A cursed object is preventing you from leaving your body."); + return FALSE; + } + } + + if (drop_body) + { + if (magik(25 + get_skill_scale(SKILL_POSSESSION, 25) + get_skill(SKILL_PRESERVATION))) + { + o_ptr = &forge; + object_prep(o_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE)); + o_ptr->number = 1; + o_ptr->pval = 0; + o_ptr->pval2 = p_ptr->body_monster; + o_ptr->pval3 = p_ptr->chp; + o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1; + object_aware(o_ptr); + object_known(o_ptr); + o_ptr->ident |= IDENT_STOREB; + + /* Unique corpses are unique */ + if (r_ptr->flags1 & RF1_UNIQUE) + { + o_ptr->name1 = 201; + } + + drop_near(o_ptr, -1, p_ptr->py, p_ptr->px); + } + else + msg_print + ("You do not manage to keep the corpse from rotting away."); + } + + msg_print("Your spirit leaves your body."); + p_ptr->disembodied = TRUE; + + /* Turn into a lost soul(just for the picture) */ + p_ptr->body_monster = test_monster_name("Lost soul"); + do_cmd_redraw(); + + return (TRUE); +} + + +bool_ execute_inscription(byte i, byte y, byte x) +{ + cave_type *c_ptr = &cave[y][x]; + + + /* Not enough mana in the current grid */ + if (c_ptr->mana < inscription_info[i].mana) return (TRUE); + + + /* Reduce the grid mana -- note: it can't be restored */ + c_ptr->mana -= inscription_info[i].mana; + + /* Analyse inscription type */ + switch (i) + { + case INSCRIP_LIGHT: + { + msg_print("The inscription shines in a bright light!"); + lite_room(y, x); + + break; + } + + case INSCRIP_DARK: + { + msg_print("The inscription is enveloped in a dark aura!"); + unlite_room(y, x); + + break; + } + + case INSCRIP_STORM: + { + msg_print("The inscription releases a powerful storm!"); + project(0, 3, y, x, damroll(10, 10), + GF_ELEC, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | + PROJECT_KILL | PROJECT_JUMP); + + break; + } + + case INSCRIP_PROTECTION: + { + return (FALSE); + + break; + } + + case INSCRIP_DWARF_SUMMON: + { + int yy = y, xx = x; + + scatter(&yy, &xx, y, x, 3); + place_monster_one(yy, xx, test_monster_name("Dwarven Warrior"), + 0, FALSE, MSTATUS_FRIEND); + + break; + } + + case INSCRIP_CHASM: + { + monster_type *m_ptr; + monster_race *r_ptr; + cave_type *c_ptr; + int ii = x, ij = y; + + cave_set_feat(ij, ii, FEAT_DARK_PIT); + msg_print("A chasm appears in the floor!"); + + if (cave[ij][ii].m_idx) + { + m_ptr = &m_list[cave[ij][ii].m_idx]; + r_ptr = race_inf(m_ptr); + + if (r_ptr->flags7 & RF7_CAN_FLY) + { + msg_print("The monster simply flies over the chasm."); + } + else + { + if (!(r_ptr->flags1 & RF1_UNIQUE)) + { + msg_print("The monster falls in the chasm!"); + delete_monster_idx(cave[ij][ii].m_idx); + } + } + } + + if (cave[ij][ii].o_idx) + { + s16b this_o_idx, next_o_idx = 0; + + c_ptr = &cave[ij][ii]; + + /* Scan all objects in the grid */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; + this_o_idx = next_o_idx) + { + object_type * o_ptr; + bool_ plural = FALSE; + + char o_name[80]; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + if (o_ptr->number > 1) plural = TRUE; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Effect "observed" */ + if (o_ptr->marked) + { + object_desc(o_name, o_ptr, FALSE, 0); + } + + /* Artifacts get to resist */ + if (o_ptr->name1) + { + /* Observe the resist */ + if (o_ptr->marked) + { + msg_format("The %s %s simply fly over the chasm!", + o_name, (plural ? "are" : "is")); + } + } + + /* Kill it */ + else + { + /* Delete the object */ + delete_object_idx(this_o_idx); + + /* Redraw */ + lite_spot(ij, ii); + } + } + } + + break; + } + + case INSCRIP_BLACK_FIRE: + { + msg_print("The inscription releases a blast of hellfire!"); + project(0, 3, y, x, 200, + GF_HELL_FIRE, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | + PROJECT_KILL | PROJECT_JUMP); + + break; + } + } + + return (TRUE); +} + + +/* + * Choose an inscription and engrave it + */ +void do_cmd_engrave() +{ + char buf[41] = ""; + + byte i; + + strnfmt(buf, 41, "%s", inscription_info[cave[p_ptr->py][p_ptr->px].inscription].text); + + get_string("Engrave what? ", buf, 40); + + /* Silently do nothing when player his escape or enters an empty string */ + if (!buf[0]) return; + + for (i = 0; i < MAX_INSCRIPTIONS; i++) + { + if (!strcmp(inscription_info[i].text, buf)) + { + if (inscription_info[i].know) + { + /* Save the inscription */ + cave[p_ptr->py][p_ptr->px].inscription = i; + } + else + msg_print("You can't use this inscription for now."); + } + } + + /* Execute the inscription */ + if (inscription_info[cave[p_ptr->py][p_ptr->px].inscription].when & INSCRIP_EXEC_ENGRAVE) + { + execute_inscription(cave[p_ptr->py][p_ptr->px].inscription, p_ptr->py, p_ptr->px); + } + + energy_use += 300; +} + + +/* + * Let's do a spinning around attack: -- DG -- + * aDb + * y@k + * ooT + * Ah ... all of those will get hit. + */ +void do_spin() +{ + int i, j; + + + msg_print("You start spinning around..."); + + for (j = p_ptr->py - 1; j <= p_ptr->py + 1; j++) + { + for (i = p_ptr->px - 1; i <= p_ptr->px + 1; i++) + { + /* Avoid stupid bugs */ + if (in_bounds(j, i) && cave[j][i].m_idx) + py_attack(j, i, 1); + } + } +} |