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
OP6CTAKWCAU64JXQ3USQYR5E5IFHQHNCACII5UMVRXUTZXJQOAZAC
442VGKMARB6LTQUEBIB5P447EI34BRJL6JALZKXLWPDHWCM6KKCQC
L4PKJZERR7WADKWHY3MR6J6OZFREVPL3CB43I6MLJ2BVKWCUTE7AC
CDKRLJIGVWQE2PMHCSLJBLYQEK7JYC4LQM7H2X3O6NMJMCCDRVIAC
YLZ5G5CR7526RPCDPMBJ6DSADFWVASB4T3DWENT3HUUCD422QITAC
X3RDT655FEYO6XEVPIUAPEPJZAFE55KZBH2AZOLK3NGHINMVIGFQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
RC6L3CIBLJEH4GWRFD7UQNGI6PZT74FRUVOYHSAN2XCC74NZUASQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
Y46M2XO74VYDTBTFFUUCI275UGELTXUXS4GEIBBXCY5USQKJ5O6AC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
OREY5XZ7FHN4UHDW4E6EQKGZHQUGK26LOGVHLKFN3YJI3B2734BAC
FUEEIUKGHHFPIRZCN3N753GONWAZTWQ2ZWR53IBJAAZ6FZUNGOMAC
74LQ7JXVLAFSHLI7LCBKFX47CNTYSKGUQSXNX5FCIUIGCC2JTR3QC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
AOAJ6D3OKSELEYKAT55XCVU5LYJ7SMCZKC6DIEGLLB3TF2LEENWQC
}
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);
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);
}
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:
// 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;
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!" );
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;
}
}
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);
}
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.can_go_berserk(intentional))
return (false);
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);