player vs monster. Specifically, hydras get their as many attacks as they have heads vs other monsters, and other monsters can chop off hydra heads (at 25% of the probability that the player has of chopping off heads).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@936 c06c8d41-db1a-0410-9941-cceddc491573
33ZMPQC6OXTESW7SRW765GRNJUEJRSYONRVZVIEUDAUEJ2PPMB4AC
/////////////////////////////////////////////////////////////////////////////
// Actor stuff - generic functions that can be called with a monster pointer,
// or NULL for the player.
std::string pronoun_you(description_level_type desc)
{
return (desc == DESC_CAP_A || desc == DESC_CAP_THE? "You" : "you");
}
std::string actor_name(const monsters *actor, description_level_type desc)
{
return (actor? ptr_monam(actor, desc) : pronoun_you(desc));
}
// actor_verb(NULL, "chop") == chop, as in "You chop"
// actor_verb(monster, "chop") == chops, as in "The skeletal warrior chops".
std::string actor_verb(const monsters *actor, const std::string &verb)
{
// Simplistic - we really should be conjugating the verb
// appropriately. We'll special-case as necessary.
return (actor? verb + "s" : verb);
}
int actor_damage_type(const monsters *actor)
{
return (actor? mons_damage_type(actor) : player_damage_type());
}
int actor_damage_brand(const monsters *actor)
{
return (actor? mons_damage_brand(actor) : player_damage_brand());
}
(you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS
|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
|| you.mutation[MUT_CLAWS]
|| you.species == SP_TROLL
|| you.species == SP_GHOUL))
you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS)
}
bool mons_wields_two_weapons(const monsters *m)
{
return (m->type == MONS_TWO_HEADED_OGRE || m->type == MONS_ETTIN);
}
// Does not check whether the monster can dual-wield - that is the
// caller's responsibility.
int mons_offhand_weapon_index(const monsters *m)
{
return (m->inv[1]);
}
int mons_weapon_index(const monsters *m)
{
// This randomly picks one of the wielded weapons for monsters that can use
// two weapons. Not ideal, but better than nothing. fight.cc does it right,
// for various values of right.
int weap = m->inv[MSLOT_WEAPON];
if (mons_wields_two_weapons(m))
{
const int offhand = mons_offhand_weapon_index(m);
if (offhand != NON_ITEM && (weap == NON_ITEM || coinflip()))
weap = offhand;
}
return (weap);
}
int mons_base_damage_type(const monsters *m)
{
return (mons_class_flag(m->type, M_CLAWS)? DVORP_CLAWING : DVORP_CRUSHING);
}
int mons_damage_type(const monsters *m)
{
const int mweap = mons_weapon_index(m);
if (mweap == NON_ITEM)
return (mons_base_damage_type(m));
return (get_vorpal_type(mitm[mweap]));
}
// Returns true if a head got lopped off.
static bool chop_hydra_head( const monsters *attacker,
monsters *defender,
int damage_done,
int dam_type,
int wpn_brand )
{
const bool defender_visible = mons_near(defender);
// Monster attackers have only a 25% chance of making the
// chop-check to prevent runaway head inflation.
if (attacker && !one_chance_in(4))
return (false);
if ((dam_type == DVORP_SLICING || dam_type == DVORP_CHOPPING
|| dam_type == DVORP_CLAWING)
&& damage_done > 0
&& (damage_done >= 4 || wpn_brand == SPWPN_VORPAL || coinflip()))
{
defender->number--;
const char *verb = NULL;
if (dam_type == DVORP_CLAWING)
{
static const char *claw_verbs[] = { "rip", "tear", "claw" };
verb =
claw_verbs[
random2( sizeof(claw_verbs) / sizeof(*claw_verbs) ) ];
}
else
{
static const char *slice_verbs[] =
{
"slice", "lop", "chop", "hack"
};
verb =
slice_verbs[
random2( sizeof(slice_verbs) / sizeof(*slice_verbs) ) ];
}
if (defender->number < 1)
{
if (defender_visible)
mprf( "%s %s %s's last head off!",
actor_name(attacker, DESC_CAP_THE).c_str(),
actor_verb(attacker, verb).c_str(),
str_monam(defender, DESC_NOCAP_THE).c_str() );
defender->hit_points = -1;
}
else
{
if (defender_visible)
mprf( "%s %s one of %s's heads off!",
actor_name(attacker, DESC_CAP_THE).c_str(),
actor_verb(attacker, verb).c_str(),
str_monam(defender, DESC_NOCAP_THE).c_str() );
if (wpn_brand == SPWPN_FLAMING)
{
if (defender_visible)
mpr( "The flame cauterises the wound!" );
}
else if (defender->number < 19)
{
simple_monster_message( defender, " grows two more!" );
defender->number += 2;
heal_monster( defender, 8 + random2(8), true );
}
}
return (true);
}
return (false);
static bool actor_decapitates_hydra(monsters *attacker, monsters *defender,
int damage_done)
{
if (defender->type == MONS_HYDRA)
{
const int dam_type = actor_damage_type(attacker);
const int wpn_brand = actor_damage_brand(attacker);
return chop_hydra_head(attacker, defender, damage_done,
dam_type, wpn_brand);
}
return (false);
}
static bool player_fumbles_attack()
{
// fumbling in shallow water <early return>:
if (player_in_water() && !player_is_swimming())
{
if (random2(you.dex) < 4 || one_chance_in(5))
{
mpr("Unstable footing causes you to fumble your attack.");
return (true);
}
}
return (false);
}
// fumbling in shallow water <early return>:
if (player_in_water() && !player_is_swimming())
{
if (random2(you.dex) < 4 || one_chance_in(5))
{
mpr("Unstable footing causes you to fumble your attack.");
return (false);
}
}
if (player_fumbles_attack())
return (false);
// wet merfolk
if (player_is_swimming()
// monster not a water creature, but is in water
&& monster_floundering(defender))
{
water_attack = true;
}
// Swimmers get bonus against non-swimmers wading in water.
water_attack = player_is_swimming() && monster_floundering(defender);
if (defender->type == MONS_JELLY
|| defender->type == MONS_BROWN_OOZE
|| defender->type == MONS_ACID_BLOB
|| defender->type == MONS_ROYAL_JELLY)
{
weapon_acid(5);
}
if (defender->type == MONS_JELLY
|| defender->type == MONS_BROWN_OOZE
|| defender->type == MONS_ACID_BLOB
|| defender->type == MONS_ROYAL_JELLY)
{
weapon_acid(5);
}
if (defender->type == MONS_HYDRA)
{
const int dam_type = player_damage_type();
const int wpn_brand = player_damage_brand();
// If decapitation, extra damage is bypassed.
if (actor_decapitates_hydra(NULL, defender, damage_done))
goto mons_dies;
if ((dam_type == DVORP_SLICING || dam_type == DVORP_CHOPPING)
&& damage_done > 0
&& (damage_done >= 4 || wpn_brand == SPWPN_VORPAL || coinflip()))
{
defender->number--;
temp_rand = random2(4);
const char *const verb = (temp_rand == 0) ? "slice" :
(temp_rand == 1) ? "lop" :
(temp_rand == 2) ? "chop" : "hack";
if (defender->number < 1)
{
snprintf( info, INFO_SIZE, "You %s %s's last head off!",
verb, ptr_monam(defender, DESC_NOCAP_THE) );
mpr( info );
defender->hit_points = -1;
}
else
{
snprintf( info, INFO_SIZE, "You %s one of %s's heads off!",
verb, ptr_monam(defender, DESC_NOCAP_THE) );
mpr( info );
if (wpn_brand == SPWPN_FLAMING)
mpr( "The flame cauterises the wound!" );
else if (defender->number < 19)
{
simple_monster_message( defender, " grows two more!" );
defender->number += 2;
heal_monster( defender, 8 + random2(8), true );
}
}
// if the hydra looses a head:
// - it's dead if it has none remaining (HP set to -1)
// - flame used to cauterise doesn't do extra damage
// - ego weapons do their additional damage to the
// hydra's decapitated head, so it's ignored.
//
// ... and so we skip the special damage.
goto mons_dies;
}
}
static void mons_lose_attack_energy(monsters *attacker, int wpn_speed,
int which_attack)
{
// speed adjustment for weapon using monsters
if (wpn_speed > 0)
{
// only get one third penalty/bonus for second weapons.
if (which_attack > 0)
wpn_speed = (20 + wpn_speed) / 3;
attacker->speed_increment -= (wpn_speed - 10) / 2;
}
}
// adjust time taken if monster used weapon
if (wpn_speed > 0)
{
// only get one third penalty/bonus for second weapons.
if (runthru > 0)
wpn_speed = (20 + wpn_speed) / 3;
attacker->speed_increment -= (wpn_speed - 10) / 2;
}
mons_lose_attack_energy(attacker, wpn_speed, runthru);
// speed adjustment for weapon using monsters
if (wpn_speed > 0)
{
// only get one third penalty/bonus for second weapons.
if (runthru > 0)
wpn_speed = (20 + wpn_speed) / 3;
attacker->speed_increment -= (wpn_speed - 10) / 2;
}
mons_lose_attack_energy(attacker, wpn_speed, runthru);