From 5d21ab53cd4c5c73b5aa0c69376619a4992c9414 Mon Sep 17 00:00:00 2001 From: dshaligram Date: Sat, 7 Apr 2007 14:54:30 +0000 Subject: Moth of wrath angering attack was lost in 0.2, reintroduced. Monsters can now go berserk (the rage spell, or by moth of wrath attack). Rupert is now a crusading-style berserker. Moths of wrath can now enrage nearby monsters of similar attitude. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1256 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/defines.h | 2 + crawl-ref/source/direct.cc | 6 + crawl-ref/source/enum.h | 5 +- crawl-ref/source/externs.h | 22 ++- crawl-ref/source/fight.cc | 17 ++ crawl-ref/source/misc.cc | 26 +-- crawl-ref/source/mon-data.h | 8 +- crawl-ref/source/mon-spll.h | 9 + crawl-ref/source/mon-util.cc | 379 ++++++++++++++++++++++++++++++------------- crawl-ref/source/monspeak.cc | 4 + crawl-ref/source/monstuff.cc | 11 +- crawl-ref/source/mstuff2.cc | 69 ++++++++ crawl-ref/source/mstuff2.h | 1 + crawl-ref/source/player.cc | 34 ++++ crawl-ref/source/stuff.cc | 8 + crawl-ref/source/travel.h | 2 +- crawl-ref/source/view.cc | 3 + 17 files changed, 459 insertions(+), 147 deletions(-) (limited to 'crawl-ref') diff --git a/crawl-ref/source/defines.h b/crawl-ref/source/defines.h index cca786c9d0..4758daf6be 100644 --- a/crawl-ref/source/defines.h +++ b/crawl-ref/source/defines.h @@ -75,6 +75,8 @@ // max y-bound for level generation {dlb} #define GYM 70 +#define INFINITE_DISTANCE 30000 + // this is the size of the border around the playing area (see in_bounds()) #define BOUNDARY_BORDER 1 diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 1f745b1a78..ce747fd17d 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -1392,6 +1392,9 @@ static void describe_mons_enchantment(const monsters &mons, // internally valid. if (paralysed && (ench.ench == ENCH_SLOW || ench.ench == ENCH_HASTE)) return; + + if (ench.ench == ENCH_HASTE && mons.has_ench(ENCH_BERSERK)) + return; strcpy(info, mons_pronoun(mons.type, PRONOUN_CAP)); @@ -1412,6 +1415,9 @@ static void describe_mons_enchantment(const monsters &mons, case ENCH_SLOW: strcat(info, " is moving slowly."); break; + case ENCH_BERSERK: + strcat(info, " is berserk!"); + break; case ENCH_HASTE: strcat(info, " is moving very quickly."); break; diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 149ae24fd4..1f3338384a 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1194,6 +1194,7 @@ enum enchant_type ENCH_PARALYSIS, // 20 ENCH_SICK, ENCH_SLEEPY, // Monster can't wake until this wears off. + ENCH_FATIGUE, // Post-berserk fatigue. NUM_ENCHANTMENTS }; @@ -2300,7 +2301,8 @@ enum mon_attack_flavour AF_ROT, AF_VAMPIRIC, AF_KLOWN, - AF_DISTORT + AF_DISTORT, + AF_RAGE }; enum mon_attitude_type @@ -2581,6 +2583,7 @@ enum mon_spellbook_type MST_DRAC_CALLER, MST_DRAC_SHIFTER, MST_CURSE_TOE, + MST_RUPERT, NUM_MSTYPES, MST_NO_SPELLS = 250 }; diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 0560a0e2be..cb4e1f6735 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -142,6 +142,7 @@ public: // (statues have only indirect attacks). virtual bool cannot_fight() const = 0; virtual void attacking(actor *other) = 0; + virtual bool can_go_berserk() const = 0; virtual void go_berserk(bool intentional) = 0; virtual void hurt(actor *attacker, int amount) = 0; virtual void heal(int amount, bool max_too = false) = 0; @@ -238,6 +239,8 @@ struct coord_def { set(0, 0); } + + int distance_from(const coord_def &b) const; bool operator == (const coord_def &other) const { return x == other.x && y == other.y; @@ -746,6 +749,8 @@ public: bool cannot_fight() const; void attacking(actor *other); + bool can_go_berserk() const; + bool can_go_berserk(bool verbose) const; void go_berserk(bool intentional); void banish(const std::string &who = ""); void blink(); @@ -907,16 +912,22 @@ public: public: kill_category kill_alignment() const; + + int foe_distance() const; + bool needs_berserk(bool check_spells = true) const; - bool has_ench(enchant_type ench, - enchant_type ench2 = ENCH_NONE) const; + bool has_ench(enchant_type ench) const; + bool has_ench(enchant_type ench, enchant_type ench2) const; mon_enchant get_ench(enchant_type ench, enchant_type ench2 = ENCH_NONE) const; - bool add_ench(const mon_enchant &); - void update_ench(const mon_enchant &); - bool del_ench(enchant_type ench, bool quiet = false); + bool add_ench(const mon_enchant &); + void update_ench(const mon_enchant &); + bool del_ench(enchant_type ench, bool quiet = false); + + void scale_hp(int num, int den); void lose_ench_levels(const mon_enchant &e, int levels); + void add_enchantment_effect(const mon_enchant &me, bool quiet = false); void remove_enchantment_effect(const mon_enchant &me, bool quiet = false); void apply_enchantments(int speed); void apply_enchantment(mon_enchant me, int speed); @@ -963,6 +974,7 @@ public: int skill(skill_type skill, bool skill_bump = false) const; void attacking(actor *other); + bool can_go_berserk() const; void go_berserk(bool intentional); void banish(const std::string &who = ""); void expose_to_element(beam_type element, int strength = 0); diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 4ccd1cb207..a8de265865 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -2494,6 +2494,10 @@ int melee_attack::mons_calc_damage(const mon_attack_def &attk) damage_max += attk.damage; damage += 1 + random2(attk.damage); + // Berserk monsters get bonus damage. + if (atk->has_ench(ENCH_BERSERK)) + damage = damage * 3 / 2; + if (water_attack) damage *= 2; @@ -2887,6 +2891,19 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk) case AF_DISTORT: distortion_affects_defender(); break; + + case AF_RAGE: + if (!one_chance_in(3) || !defender->can_go_berserk()) + break; + + if (needs_message) + mprf("%s %s %s!", + attacker->name(DESC_CAP_THE).c_str(), + attacker->conj_verb("infuriate").c_str(), + defender->name(DESC_NOCAP_THE).c_str()); + + defender->go_berserk(false); + break; } } diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 1369f7ed76..9fb999beee 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -1678,32 +1678,12 @@ void weird_colours(unsigned char coll, char wc[30]) bool go_berserk(bool intentional) { - if (you.berserker) - { - if (intentional) - mpr("You're already berserk!"); - // or else you won't notice -- no message here. - return false; - } - - if (you.exhausted) - { - if (intentional) - mpr("You're too exhausted to go berserk."); - // or else they won't notice -- no message here - return false; - } - - if (you.is_undead) - { - if (intentional) - mpr("You cannot raise a blood rage in your lifeless body."); - // or else you won't notice -- no message here - return false; - } + if (!you.can_go_berserk(intentional)) + return (false); if (Options.tutorial_left) Options.tut_berserk_counter++; + mpr("A red film seems to cover your vision as you go berserk!"); mpr("You feel yourself moving faster!"); mpr("You feel mighty!"); diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index bf3d0b56a9..c400468b40 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -3221,7 +3221,7 @@ 0, 20, MONS_HUMAN, MONS_HUMAN, MH_NATURAL, -5, { {AT_HIT, AF_PLAIN, 21}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} }, { 16, 0, 0, 123 }, - 0, 10, 10, 7, MST_WIZARD_IV, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, + 0, 10, 10, 7, MST_RUPERT, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, MONUSE_WEAPONS_ARMOUR, SIZE_MEDIUM } , @@ -3891,12 +3891,12 @@ { MONS_MOTH_OF_WRATH, 'y', BROWN, "moth of wrath", - M_FLIES, + M_FLIES | M_SPECIAL_ABILITY, MR_NO_FLAGS, 0, 10, MONS_MOTH_OF_WRATH, MONS_MOTH_OF_WRATH, MH_NATURAL, -3, - { {AT_BITE, AF_PLAIN, 25}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} }, + { {AT_BITE, AF_RAGE, 25}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} }, { 9, 3, 5, 0 }, - 0, 10, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SHOUT, I_NORMAL, + 0, 10, 12, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SHOUT, I_HIGH, MONUSE_NOTHING, SIZE_MEDIUM } , diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h index 8498265dde..4131fb405e 100644 --- a/crawl-ref/source/mon-spll.h +++ b/crawl-ref/source/mon-spll.h @@ -784,4 +784,13 @@ MS_SUMMON_UNDEAD, MS_TORMENT }, + // Crusader theme. + { MST_RUPERT, + MS_PARALYSIS, + MS_CONFUSE, + MS_BERSERK_RAGE, + MS_NO_SPELL, + MS_NO_SPELL, + MS_BERSERK_RAGE }, + #endif diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index e8cc6205fd..420c536589 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -1865,75 +1865,75 @@ bool ms_requires_tracer(int monspell) switch(monspell) { - case MS_BANISHMENT: - case MS_COLD_BOLT: - case MS_ICE_BOLT: - case MS_SHOCK: - case MS_MAGMA: - case MS_CONFUSE: - case MS_CRYSTAL_SPEAR: - case MS_DISINTEGRATE: - case MS_ENERGY_BOLT: - case MS_FIRE_BOLT: - case MS_FIREBALL: - case MS_FLAME: - case MS_FROST: - case MS_HELLFIRE: - case MS_IRON_BOLT: - case MS_LIGHTNING_BOLT: - case MS_MARSH_GAS: - case MS_MIASMA: - case MS_METAL_SPLINTERS: - case MS_MMISSILE: - case MS_NEGATIVE_BOLT: - case MS_ORB_ENERGY: - case MS_PAIN: - case MS_PARALYSIS: - case MS_POISON_BLAST: - case MS_POISON_ARROW: - case MS_POISON_SPLASH: - case MS_QUICKSILVER_BOLT: - case MS_SLOW: - case MS_STEAM_BALL: - case MS_STICKY_FLAME: - case MS_STING: - case MS_STONE_ARROW: - case MS_TELEPORT_OTHER: - case MS_VENOM_BOLT: - requires = true; - break; - - // self-niceties and direct effects - case MS_ANIMATE_DEAD: - case MS_BLINK: - case MS_BRAIN_FEED: - case MS_DIG: - case MS_FAKE_RAKSHASA_SUMMON: - case MS_HASTE: - case MS_HEAL: - case MS_HELLFIRE_BURST: - case MS_INVIS: - case MS_LEVEL_SUMMON: - case MS_MUTATION: - case MS_SMITE: - case MS_SUMMON_BEAST: - case MS_SUMMON_DEMON_LESSER: - case MS_SUMMON_DEMON: - case MS_SUMMON_DEMON_GREATER: - case MS_SUMMON_UFETUBUS: - case MS_TELEPORT: - case MS_TORMENT: - case MS_SUMMON_SMALL_MAMMALS: - case MS_VAMPIRE_SUMMON: - case MS_CANTRIP: + case MS_BANISHMENT: + case MS_COLD_BOLT: + case MS_ICE_BOLT: + case MS_SHOCK: + case MS_MAGMA: + case MS_CONFUSE: + case MS_CRYSTAL_SPEAR: + case MS_DISINTEGRATE: + case MS_ENERGY_BOLT: + case MS_FIRE_BOLT: + case MS_FIREBALL: + case MS_FLAME: + case MS_FROST: + case MS_HELLFIRE: + case MS_IRON_BOLT: + case MS_LIGHTNING_BOLT: + case MS_MARSH_GAS: + case MS_MIASMA: + case MS_METAL_SPLINTERS: + case MS_MMISSILE: + case MS_NEGATIVE_BOLT: + case MS_ORB_ENERGY: + case MS_PAIN: + case MS_PARALYSIS: + case MS_POISON_BLAST: + case MS_POISON_ARROW: + case MS_POISON_SPLASH: + case MS_QUICKSILVER_BOLT: + case MS_SLOW: + case MS_STEAM_BALL: + case MS_STICKY_FLAME: + case MS_STING: + case MS_STONE_ARROW: + case MS_TELEPORT_OTHER: + case MS_VENOM_BOLT: + requires = true; + break; - // meaningless, but sure, why not? - case MS_NO_SPELL: - break; + // self-niceties and direct effects + case MS_ANIMATE_DEAD: + case MS_BLINK: + case MS_BRAIN_FEED: + case MS_DIG: + case MS_FAKE_RAKSHASA_SUMMON: + case MS_HASTE: + case MS_HEAL: + case MS_HELLFIRE_BURST: + case MS_INVIS: + case MS_LEVEL_SUMMON: + case MS_MUTATION: + case MS_SMITE: + case MS_SUMMON_BEAST: + case MS_SUMMON_DEMON_LESSER: + case MS_SUMMON_DEMON: + case MS_SUMMON_DEMON_GREATER: + case MS_SUMMON_UFETUBUS: + case MS_TELEPORT: + case MS_TORMENT: + case MS_SUMMON_SMALL_MAMMALS: + case MS_VAMPIRE_SUMMON: + case MS_CANTRIP: + case MS_BERSERK_RAGE: - default: - break; + // meaningless, but sure, why not? + case MS_NO_SPELL: + break; + default: + break; } return (requires); @@ -1949,39 +1949,39 @@ bool ms_direct_nasty(int monspell) switch(monspell) { - // self-niceties/summonings - case MS_ANIMATE_DEAD: - case MS_BLINK: - case MS_DIG: - case MS_FAKE_RAKSHASA_SUMMON: - case MS_HASTE: - case MS_HEAL: - case MS_INVIS: - case MS_LEVEL_SUMMON: - case MS_SUMMON_BEAST: - case MS_SUMMON_DEMON_LESSER: - case MS_SUMMON_DEMON: - case MS_SUMMON_DEMON_GREATER: - case MS_SUMMON_UFETUBUS: - case MS_TELEPORT: - case MS_SUMMON_SMALL_MAMMALS: - case MS_VAMPIRE_SUMMON: - nasty = false; - break; + // self-niceties/summonings + case MS_ANIMATE_DEAD: + case MS_BLINK: + case MS_DIG: + case MS_FAKE_RAKSHASA_SUMMON: + case MS_HASTE: + case MS_HEAL: + case MS_INVIS: + case MS_LEVEL_SUMMON: + case MS_SUMMON_BEAST: + case MS_SUMMON_DEMON_LESSER: + case MS_SUMMON_DEMON: + case MS_SUMMON_DEMON_GREATER: + case MS_SUMMON_UFETUBUS: + case MS_TELEPORT: + case MS_SUMMON_SMALL_MAMMALS: + case MS_VAMPIRE_SUMMON: + nasty = false; + break; - case MS_BRAIN_FEED: - case MS_HELLFIRE_BURST: - case MS_MUTATION: - case MS_SMITE: - case MS_TORMENT: + case MS_BRAIN_FEED: + case MS_HELLFIRE_BURST: + case MS_MUTATION: + case MS_SMITE: + case MS_TORMENT: + case MS_BERSERK_RAGE: // meaningless, but sure, why not? - case MS_NO_SPELL: - break; - - default: - break; + case MS_NO_SPELL: + break; + default: + break; } return (nasty); @@ -2072,12 +2072,16 @@ bool ms_waste_of_time( struct monsters *mon, int monspell ) int intel, est_magic_resist, power, diff; struct monsters *targ; - // Eventually, we'll probably want to be able to have monsters // learn which of their elemental bolts were resisted and have those // handled here as well. -- bwr switch (monspell) { + case MS_BERSERK_RAGE: + if (!mon->needs_berserk(false)) + ret = true; + break; + case MS_HASTE: if (mon->has_ench(ENCH_HASTE)) ret = true; @@ -2620,6 +2624,24 @@ void monsters::attacking(actor * /* other */) void monsters::go_berserk(bool /* intentional */) { + if (holiness() != MH_NATURAL || has_ench(ENCH_FATIGUE)) + return; + + if (has_ench(ENCH_SLOW)) + { + del_ench(ENCH_SLOW); + simple_monster_message( + this, + make_stringf(" shakes off %s lethargy.", + name(DESC_NOCAP_YOUR).c_str()).c_str()); + } + del_ench(ENCH_HASTE); + del_ench(ENCH_FATIGUE); + + const int duration = 20 + random2avg(20, 2); + add_ench(mon_enchant(ENCH_BERSERK, duration)); + add_ench(mon_enchant(ENCH_HASTE, duration)); + simple_monster_message( this, " goes berserk!" ); } void monsters::expose_to_element(beam_type, int) @@ -3043,19 +3065,20 @@ void monsters::load_spells(int book) } } -bool monsters::has_ench(enchant_type ench, - enchant_type ench2) const +bool monsters::has_ench(enchant_type ench) const +{ + return (enchantments.find(ench) != enchantments.end()); +} + +bool monsters::has_ench(enchant_type ench, enchant_type ench2) const { if (ench2 == ENCH_NONE) ench2 = ench; for (int i = ench; i <= ench2; ++i) { - if (enchantments.find( static_cast(i) ) != - enchantments.end()) - { + if (has_ench(static_cast(i))) return (true); - } } return (false); } @@ -3083,8 +3106,10 @@ void monsters::update_ench(const mon_enchant &ench) { mon_enchant_list::iterator i = enchantments.find(ench); if (i != enchantments.end()) + { enchantments.erase(i); - enchantments.insert(ench); + enchantments.insert(ench); + } } } @@ -3095,8 +3120,12 @@ bool monsters::add_ench(const mon_enchant &ench) return (false); mon_enchant_list::iterator i = enchantments.find(ench); + bool new_enchantment = false; if (i == enchantments.end()) + { + new_enchantment = true; enchantments.insert(ench); + } else { mon_enchant new_ench = *i + ench; @@ -3104,24 +3133,39 @@ bool monsters::add_ench(const mon_enchant &ench) enchantments.insert(new_ench); } + if (new_enchantment) + add_enchantment_effect(ench); + + return (true); +} + +void monsters::add_enchantment_effect(const mon_enchant &ench, bool) +{ // check for slow/haste - if (ench.ench == ENCH_HASTE) + switch (ench.ench) { + case ENCH_BERSERK: + // Inflate hp. + scale_hp(3, 2); + break; + + case ENCH_HASTE: if (speed >= 100) speed = 100 + ((speed - 100) * 2); else speed *= 2; - } + break; - if (ench.ench == ENCH_SLOW) - { + case ENCH_SLOW: if (speed >= 100) speed = 100 + ((speed - 100) / 2); else speed /= 2; - } + break; - return (true); + default: + break; + } } bool monsters::del_ench(enchant_type ench, bool quiet) @@ -3141,6 +3185,10 @@ void monsters::remove_enchantment_effect(const mon_enchant &me, bool quiet) { switch (me.ench) { + case ENCH_BERSERK: + scale_hp(2, 3); + break; + case ENCH_HASTE: if (speed >= 100) speed = 100 + ((speed - 100) / 2); @@ -3290,11 +3338,23 @@ void monsters::timeout_enchantments(int levels) lose_ench_levels(*i, levels); break; + case ENCH_BERSERK: + del_ench(i->ench); + del_ench(ENCH_HASTE); + break; + + case ENCH_FATIGUE: + del_ench(i->ench); + del_ench(ENCH_SLOW); + break; + case ENCH_TP: + del_ench(i->ench); teleport(true); break; case ENCH_CONFUSION: + del_ench(i->ench); blink(); break; @@ -3317,13 +3377,38 @@ void monsters::apply_enchantment(mon_enchant me, int spd) { switch (me.ench) { + case ENCH_BERSERK: + lose_ench_levels(me, 1); + if (me.degree <= 1) + { + simple_monster_message(this, " is no longer berserk."); + del_ench(ENCH_HASTE); + const int duration = random_range(7, 13); + add_ench(mon_enchant(ENCH_FATIGUE, duration)); + add_ench(mon_enchant(ENCH_SLOW, duration)); + } + break; + + case ENCH_FATIGUE: + lose_ench_levels(me, 1); + if (me.degree <= 1) + { + simple_monster_message(this, " looks more energetic."); + del_ench(ENCH_SLOW); + } + break; + case ENCH_SLOW: - if (random2(250) <= mod_speed( hit_dice + 10, spd )) + if (me.degree > 0) + lose_ench_levels(me, 1); + else if (random2(250) <= mod_speed( hit_dice + 10, spd )) del_ench(me.ench); break; case ENCH_HASTE: - if (random2(1000) < mod_speed( 25, spd )) + if (me.degree > 0) + lose_ench_levels(me, 1); + else if (random2(1000) < mod_speed( 25, spd )) del_ench(ENCH_HASTE); break; @@ -3608,6 +3693,19 @@ void monsters::apply_enchantments(int spd) } } +void monsters::scale_hp(int num, int den) +{ + hit_points = hit_points * num / den; + max_hit_points = max_hit_points * num / den; + + if (hit_points < 1) + hit_points = 1; + if (max_hit_points < 1) + max_hit_points = 1; + if (hit_points > max_hit_points) + hit_points = max_hit_points; +} + kill_category monsters::kill_alignment() const { return (attitude == ATT_FRIENDLY? KC_FRIENDLY : KC_OTHER); @@ -3704,6 +3802,63 @@ void monsters::check_speed() } } +int monsters::foe_distance() const +{ + // early out -- no foe! + if (foe == MHITNOT) + return (INFINITE_DISTANCE); + + if (foe == MHITYOU) + return grid_distance(x, y, you.x_pos, you.y_pos); + + // must be a monster + const monsters *my_foe = &menv[foe]; + if (my_foe->alive()) + return grid_distance(x, y, my_foe->x, my_foe->y); + + return (INFINITE_DISTANCE); +} + +bool monsters::can_go_berserk() const +{ + if (holiness() != MH_NATURAL) + return (false); + + if (has_ench(ENCH_FATIGUE) || has_ench(ENCH_BERSERK)) + return (false); + + // If we have no melee attack, going berserk is pointless. + const mon_attack_def attk = mons_attack_spec(this, 0); + if (attk.type == AT_NONE || attk.damage == 0) + return (false); + + return (true); +} + +bool monsters::needs_berserk(bool check_spells) const +{ + if (!can_go_berserk()) + return (false); + + if (has_ench(ENCH_HASTE) || has_ench(ENCH_TP)) + return (false); + + if (foe_distance() > 3) + return (false); + + if (check_spells) + { + for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i) + { + const int spell = spells[i]; + if (spell != MS_NO_SPELL && spell != MS_BERSERK_RAGE) + return (false); + } + } + + return (true); +} + ///////////////////////////////////////////////////////////////////////// // mon_enchant @@ -3712,7 +3867,7 @@ static const char *enchant_names[] = "none", "slow", "haste", "fear", "conf", "inv", "pois", "bers", "rot", "summon", "abj", "backlit", "charm", "fire", "gloshifter", "shifter", "tp", "wary", "submerged", - "short lived", "paralysis", "sick", "sleep", "bug" + "short lived", "paralysis", "sick", "sleep", "fatigue", "bug" }; const char *mons_enchantment_name(enchant_type ench) diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc index e473939cea..948061e48e 100644 --- a/crawl-ref/source/monspeak.cc +++ b/crawl-ref/source/monspeak.cc @@ -224,6 +224,10 @@ bool mons_speaks(const monsters *monster) if (monster->has_ench(ENCH_CHARM)) return false; + // berserk monsters just want your hide. + if (monster->has_ench(ENCH_BERSERK)) + return false; + if (monster->has_ench(ENCH_CONFUSION)) { if (mons_holiness( monster ) == MH_DEMONIC diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index fd0cf3f637..cccb5c4036 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -2233,6 +2233,11 @@ static bool handle_special_ability(struct monsters *monster, bolt & beem) break; + case MONS_MOTH_OF_WRATH: + if (one_chance_in(3)) + used = moth_incite_monsters(monster); + break; + case MONS_PIT_FIEND: if (one_chance_in(3)) break; @@ -3568,7 +3573,11 @@ static void handle_monster_move(int i, monsters *monster) beem.target_y = monster->target_y; if (monster->behaviour != BEH_SLEEP - && monster->behaviour != BEH_WANDER) + && monster->behaviour != BEH_WANDER + + // berserking monsters are limited to running up and + // hitting their foes. + && !monster->has_ench(ENCH_BERSERK)) { // prevents unfriendlies from nuking you from offscreen. // How nice! diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 9eafee035f..618cbdeb28 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -366,6 +366,10 @@ void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) switch (spell_cast) { + case MS_BERSERK_RAGE: + monster->go_berserk(true); + return; + case MS_SUMMON_SMALL_MAMMALS: case MS_VAMPIRE_SUMMON: if ( spell_cast == MS_SUMMON_SMALL_MAMMALS ) @@ -719,6 +723,8 @@ void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cas case MS_TORMENT: case MS_SUMMON_DEMON_GREATER: case MS_CANTRIP: + case MS_BERSERK_RAGE: + case MS_MIGHT: return; default: break; @@ -1946,3 +1952,66 @@ bool orange_statue_effects(monsters *mons) return (false); } + +static bool make_monster_angry(const monsters *mon, monsters *targ) +{ + if (mon->attitude != targ->attitude) + return (false); + + // targ is guaranteed to have a foe (needs_berserk checks this). + // Now targ needs to be closer to *its* foe than mon is (otherwise + // mon might be in the way). + + coord_def victim; + if (targ->foe == MHITYOU) + victim = you.pos(); + else if (targ->foe != MHITNOT) + { + const monsters *vmons = &menv[targ->foe]; + if (!vmons->alive()) + return (false); + victim = vmons->pos(); + } + else + { + // Should be impossible. needs_berserk should find this case. + ASSERT(false); + return (false); + } + + // If mon may be blocking targ from its victim, don't try. + if (victim.distance_from(targ->pos()) > victim.distance_from(mon->pos())) + return (false); + + const bool need_message = mons_near(mon) && player_monster_visible(mon); + if (need_message) + mprf("%s goads %s on!", mon->name(DESC_CAP_THE).c_str(), + targ->name(DESC_NOCAP_THE).c_str()); + + targ->go_berserk(false); + + return (true); +} + +bool moth_incite_monsters(const monsters *mon) +{ + int goaded = 0; + for (int i = 0; i < MAX_MONSTERS; ++i) + { + monsters *targ = &menv[i]; + if (targ == mon || !targ->alive() || !targ->needs_berserk()) + continue; + + if (mon->pos().distance_from(targ->pos()) > 3) + continue; + + // Cannot goad other moths of wrath! + if (targ->type == MONS_MOTH_OF_WRATH) + continue; + + if (make_monster_angry(mon, targ) && !one_chance_in(3 * ++goaded)) + return (true); + } + + return (false); +} diff --git a/crawl-ref/source/mstuff2.h b/crawl-ref/source/mstuff2.h index aedb2cdcd0..d584406762 100644 --- a/crawl-ref/source/mstuff2.h +++ b/crawl-ref/source/mstuff2.h @@ -102,5 +102,6 @@ void throw_type(int lnchClass, int lnchType, int wepClass, int wepType, bool orange_statue_effects(monsters *mons); bool silver_statue_effects(monsters *mons); +bool moth_incite_monsters(const monsters *mon); #endif diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index abf7f089e0..836b7d877f 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -4934,6 +4934,40 @@ void player::go_berserk(bool intentional) ::go_berserk(intentional); } +bool player::can_go_berserk() const +{ + return can_go_berserk(false); +} + +bool player::can_go_berserk(bool verbose) const +{ + if (you.berserker) + { + if (verbose) + mpr("You're already berserk!"); + // or else you won't notice -- no message here. + return (false); + } + + if (you.exhausted) + { + if (verbose) + mpr("You're too exhausted to go berserk."); + // or else they won't notice -- no message here + return (false); + } + + if (you.is_undead) + { + if (verbose) + mpr("You cannot raise a blood rage in your lifeless body."); + // or else you won't notice -- no message here + return (false); + } + + return (true); +} + void player::god_conduct(int thing_done, int level) { ::did_god_conduct(thing_done, level); diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc index 74c9250bdd..5e1898cd00 100644 --- a/crawl-ref/source/stuff.cc +++ b/crawl-ref/source/stuff.cc @@ -1090,3 +1090,11 @@ void zap_los_monsters() } } } + +////////////////////////////////////////////////////////////////////////// +// coord_def +int coord_def::distance_from(const coord_def &other) const +{ + return (grid_distance(x, y, other.x, other.y)); +} + diff --git a/crawl-ref/source/travel.h b/crawl-ref/source/travel.h index d60311b07c..53ba304588 100644 --- a/crawl-ref/source/travel.h +++ b/crawl-ref/source/travel.h @@ -482,7 +482,7 @@ protected: protected: static const int UNFOUND_DIST = -30000; - static const int INFINITE_DIST = 30000; + static const int INFINITE_DIST = INFINITE_DISTANCE; protected: run_mode_type runmode; diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index 9e1c719c5f..601d6d8cdf 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -522,6 +522,9 @@ static int get_mons_colour(const monsters *mons) { int col = mons->colour; + if (mons->has_ench(ENCH_BERSERK)) + col = RED; + if (mons_friendly(mons)) { col |= COLFLAG_FRIENDLY_MONSTER; -- cgit v1.2.3-54-g00ecf