summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/mon-util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/mon-util.cc')
-rw-r--r--crawl-ref/source/mon-util.cc270
1 files changed, 174 insertions, 96 deletions
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 8fad0cfef9..8a9eef2355 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -34,6 +34,8 @@
#include "debug.h"
#include "delay.h"
#include "dgnevent.h"
+#include "fight.h"
+#include "ghost.h"
#include "insult.h"
#include "itemname.h"
#include "itemprop.h"
@@ -256,14 +258,18 @@ void init_monster_symbols()
}
}
-unsigned long get_mons_class_resists(int mc)
+const mon_resist_def &get_mons_class_resists(int mc)
{
- return (smc->resists);
+ const monsterentry *me = get_monster_data(mc);
+ return (me? me->resists : get_monster_data(MONS_PROGRAM_BUG)->resists);
}
-unsigned long get_mons_resists(const monsters *mon)
+mon_resist_def get_mons_resists(const monsters *mon)
{
- unsigned long resists = get_mons_class_resists(mon->type);
+ if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON)
+ return (mon->ghost->resists);
+
+ mon_resist_def resists = get_mons_class_resists(mon->type);
if ((mons_genus(mon->type) == MONS_DRACONIAN &&
mon->type != MONS_DRACONIAN) ||
mon->type == MONS_TIAMAT)
@@ -290,11 +296,6 @@ int mons_piety(const monsters *mon)
return (mon->hit_dice * 14);
}
-unsigned long mons_resist(const monsters *mon, unsigned long flags)
-{
- return (get_mons_resists(mon) & flags);
-}
-
bool mons_class_flag(int mc, int bf)
{
const monsterentry *me = smc;
@@ -394,7 +395,7 @@ bool mons_class_is_slowable(int mc)
bool mons_is_wall_shielded(int mc)
{
- return (mons_habitat(mc) == HT_ROCK);
+ return (mons_habitat_by_type(mc) == HT_ROCK);
}
// returns whether a monster is non-solid
@@ -605,7 +606,7 @@ bool mons_sense_invis(const monsters *mon)
bool mons_see_invis(const monsters *mon)
{
if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_SEE_INVIS ]);
+ return (mon->ghost->see_invis);
else if (mons_class_flag(mon->type, M_SEE_INVIS))
return (true);
else if (scan_mon_inv_randarts( mon, RAP_EYESIGHT ) > 0)
@@ -739,7 +740,7 @@ mon_attack_def mons_attack_spec(const monsters *mons, int attk_number)
if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
{
if (attk_number == 0)
- return mon_attack_def::attk(mons->ghost->values[GVAL_DAMAGE]);
+ return mon_attack_def::attk(mons->ghost->damage);
else
return mon_attack_def::attk(0, AT_NONE);
}
@@ -852,14 +853,10 @@ int mons_res_elec( const monsters *mon )
{
int mc = mon->type;
- if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_RES_ELEC ]);
-
/* this is a variable, not a player_xx() function, so can be above 1 */
int u = 0;
- if (mons_resist(mon, MR_RES_ELEC))
- u++;
+ u += get_mons_resists(mon).elec;
// don't bother checking equipment if the monster can't use it
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
@@ -885,26 +882,19 @@ bool mons_res_asphyx( const monsters *mon )
|| holiness == MH_DEMONIC
|| holiness == MH_NONLIVING
|| holiness == MH_PLANT
- || mons_resist(mon, MR_RES_ASPHYX));
+ || get_mons_resists(mon).asphyx > 0);
}
int mons_res_acid( const monsters *mon )
{
- const unsigned long f = get_mons_resists(mon);
- return ((f & MR_RES_ACID) != 0);
+ return get_mons_resists(mon).acid;
}
int mons_res_poison( const monsters *mon )
{
int mc = mon->type;
- unsigned long u = 0, f = get_mons_resists(mon);
-
- if (f & MR_RES_POISON)
- u++;
-
- if (f & MR_VUL_POISON)
- u--;
+ int u = get_mons_resists(mon).poison;
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -930,26 +920,27 @@ int mons_res_poison( const monsters *mon )
return (u);
} // end mons_res_poison()
+bool mons_res_sticky_flame( const monsters *mon )
+{
+ return (get_mons_resists(mon).sticky_flame
+ || mon->has_equipped(EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR));
+}
+
+int mons_res_steam( const monsters *mon )
+{
+ int res = get_mons_resists(mon).steam;
+ if (mon->has_equipped(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR))
+ res += 3;
+ return (res + mons_res_fire(mon) / 2);
+}
+
int mons_res_fire( const monsters *mon )
{
int mc = mon->type;
- if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_RES_FIRE ]);
-
- int u = 0, f = get_mons_resists(mon);
-
- // no Big Prize (tm) here either if you set all three flags. It's a pity uh?
- //
- // Note that natural monster resistance is two levels, this is duplicate
- // the fact that having this flag used to be a lot better than armour
- // for monsters (it used to make them immune in a lot of cases) -- bwr
- if (f & MR_RES_HELLFIRE)
- u += 3;
- else if (f & MR_RES_FIRE)
- u += 2;
- else if (f & MR_VUL_FIRE)
- u--;
+ const mon_resist_def res = get_mons_resists(mon);
+
+ int u = std::min(res.fire + res.hellfire * 3, 3);
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -982,18 +973,7 @@ int mons_res_cold( const monsters *mon )
{
int mc = mon->type;
- if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_RES_COLD ]);
-
- int u = 0, f = get_mons_resists(mon);
-
- // Note that natural monster resistance is two levels, this is duplicate
- // the fact that having this flag used to be a lot better than armour
- // for monsters (it used to make them immune in a lot of cases) -- bwr
- if (f & MR_RES_COLD)
- u += 2;
- else if (f & MR_VUL_COLD)
- u--;
+ int u = get_mons_resists(mon).cold;
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -1104,9 +1084,9 @@ flight_type mons_class_flies(int mc)
flight_type mons_flies(const monsters *mon)
{
if (mon->type == MONS_PANDEMONIUM_DEMON
- && mon->ghost->values[ GVAL_DEMONLORD_FLY ])
+ && mon->ghost->fly)
{
- return (FL_FLY);
+ return (mon->ghost->fly);
}
const flight_type ret = mons_class_flies( mon->type );
@@ -1686,9 +1666,16 @@ mon_intel_type mons_intel(int mc)
return (smc->intel);
}
-habitat_type mons_habitat(int mc)
+habitat_type mons_habitat_by_type(int mc)
+{
+ const monsterentry *me = get_monster_data(mc);
+ return (me? me->habitat : HT_LAND);
+}
+
+habitat_type mons_habitat(const monsters *m)
{
- return (smc->habitat);
+ return mons_habitat_by_type(
+ mons_is_zombified(m)? mons_zombie_base(m) : m->type );
}
bool intelligent_ally(const monsters *monster)
@@ -1761,7 +1748,7 @@ int mons_offhand_weapon_index(const monsters *m)
int mons_base_damage_brand(const monsters *m)
{
if (m->type == MONS_PLAYER_GHOST || m->type == MONS_PANDEMONIUM_DEMON)
- return m->ghost->values[ GVAL_BRAND ];
+ return m->ghost->brand;
return (SPWPN_NORMAL);
}
@@ -1986,6 +1973,15 @@ bool ms_waste_of_time( const monsters *mon, spell_type monspell )
int intel, est_magic_resist, power, diff;
const actor *foe = mon->get_foe();
+
+ // Keep friendly summoners from spamming summons constantly.
+ if (mons_friendly(mon)
+ && (!foe || foe == &you)
+ && spell_typematch(monspell, SPTYP_SUMMONING))
+ {
+ return (true);
+ }
+
// 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
@@ -2337,6 +2333,11 @@ monsters::monsters()
{
}
+// Empty destructor to keep auto_ptr happy with incomplete ghost_demon type.
+monsters::~monsters()
+{
+}
+
monsters::monsters(const monsters &mon)
{
init_with(mon);
@@ -2395,7 +2396,7 @@ coord_def monsters::target_pos() const
bool monsters::swimming() const
{
const dungeon_feature_type grid = grd[x][y];
- return (grid_is_watery(grid) && mons_habitat(type) == HT_WATER);
+ return (grid_is_watery(grid) && mons_habitat(this) == HT_WATER);
}
bool monsters::submerged() const
@@ -2409,7 +2410,7 @@ bool monsters::floundering() const
return (grid_is_water(grid)
// Can't use monster_habitable_grid because that'll return true
// for non-water monsters in shallow water.
- && mons_habitat(type) != HT_WATER
+ && mons_habitat(this) != HT_WATER
&& !mons_amphibious(type)
&& !mons_flies(this));
}
@@ -2693,6 +2694,10 @@ bool monsters::can_use_missile(const item_def &item) const
// grabbing missiles.
if (has_spell_of_type(SPTYP_CONJURATION | SPTYP_SUMMONING))
return (false);
+
+ // Blademasters don't want to throw stuff.
+ if (type == MONS_DEEP_ELF_BLADEMASTER)
+ return (false);
if (item.base_type == OBJ_WEAPONS)
return (is_throwable(item));
@@ -3392,6 +3397,11 @@ int monsters::id() const
return (type);
}
+int monsters::get_experience_level() const
+{
+ return (hit_dice);
+}
+
bool monsters::fumbles_attack(bool verbose)
{
if (floundering() && one_chance_in(4))
@@ -3434,7 +3444,7 @@ void monsters::go_berserk(bool /* intentional */)
simple_monster_message(
this,
make_stringf(" shakes off %s lethargy.",
- name(DESC_NOCAP_YOUR).c_str()).c_str());
+ pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str()).c_str());
}
del_ench(ENCH_HASTE);
del_ench(ENCH_FATIGUE, true); // give no additional message
@@ -3556,6 +3566,11 @@ int monsters::res_fire() const
return (mons_res_fire(this));
}
+int monsters::res_steam() const
+{
+ return (mons_res_steam(this));
+}
+
int monsters::res_cold() const
{
return (mons_res_cold(this));
@@ -3707,12 +3722,12 @@ void monsters::set_ghost(const ghost_demon &g)
void monsters::pandemon_init()
{
- hit_dice = ghost->values[ GVAL_DEMONLORD_HIT_DICE ];
- hit_points = ghost->values[ GVAL_MAX_HP ];
- max_hit_points = ghost->values[ GVAL_MAX_HP ];
- ac = ghost->values[ GVAL_AC ];
- ev = ghost->values[ GVAL_EV ];
- speed = (one_chance_in(3) ? 10 : 6 + roll_dice(2, 9));
+ hit_dice = ghost->xl;
+ hit_points = ghost->max_hp;
+ max_hit_points = ghost->max_hp;
+ ac = ghost->ac;
+ ev = ghost->ev;
+ speed = (one_chance_in(3) ? 10 : 8 + roll_dice(2, 9));
speed_increment = 70;
if (you.char_direction == GDT_ASCENDING && you.level_type == LEVEL_DUNGEON)
colour = LIGHTRED;
@@ -3724,12 +3739,12 @@ void monsters::pandemon_init()
void monsters::ghost_init()
{
type = MONS_PLAYER_GHOST;
- hit_dice = ghost->values[ GVAL_EXP_LEVEL ];
- hit_points = ghost->values[ GVAL_MAX_HP ];
- max_hit_points = ghost->values[ GVAL_MAX_HP ];
- ac = ghost->values[ GVAL_AC];
- ev = ghost->values[ GVAL_EV ];
- speed = ghost->values[ GVAL_SPEED ];
+ hit_dice = ghost->xl;
+ hit_points = ghost->max_hp;
+ max_hit_points = ghost->max_hp;
+ ac = ghost->ac;
+ ev = ghost->ev;
+ speed = ghost->speed;
speed_increment = 70;
attitude = ATT_HOSTILE;
behaviour = BEH_WANDER;
@@ -3896,16 +3911,7 @@ void monsters::load_spells(mon_spellbook_type book)
#endif
if (book == MST_GHOST)
- {
- for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
- {
- spells[i] =
- static_cast<spell_type>( ghost->values[ GVAL_SPELL_1 + i ] );
-#if DEBUG_DIAGNOSTICS
- mprf( MSGCH_DIAGNOSTICS, "spell #%d: %d", i, spells[i] );
-#endif
- }
- }
+ spells = ghost->spells;
else
{
for (unsigned int i = 0; i < ARRAYSIZE(mspell_list); ++i)
@@ -3918,6 +3924,11 @@ void monsters::load_spells(mon_spellbook_type book)
}
}
}
+#if DEBUG_DIAGNOSTICS
+ for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
+ mprf( MSGCH_DIAGNOSTICS, "Spell #%d: %d (%s)",
+ i, spells[i], spell_title(spells[i]) );
+#endif
}
bool monsters::has_ench(enchant_type ench) const
@@ -4588,12 +4599,12 @@ void monsters::apply_enchantment(const mon_enchant &me)
}
// Now we handle the others:
- int grid = grd[x][y];
+ const dungeon_feature_type grid = grd[x][y];
// Badly injured monsters prefer to stay submerged...
// electrical eels and lava snakes have ranged attacks
// and are more likely to surface. -- bwr
- if (!monster_can_submerge(type, grid))
+ if (!monster_can_submerge(this, grid))
del_ench(ENCH_SUBMERGED); // forced to surface
else if (hit_points <= max_hit_points / 2)
break;
@@ -4658,10 +4669,8 @@ void monsters::apply_enchantment(const mon_enchant &me)
// assumption: mons_res_fire has already been checked
case ENCH_STICKY_FLAME:
{
- int dam = roll_dice( 2, 4 ) - 1;
-
- if (mons_res_fire( this ) < 0)
- dam += roll_dice( 2, 5 ) - 1;
+ int dam =
+ resist_adjust_damage(this, res_fire(), roll_dice( 2, 4 ) - 1);
if (dam > 0)
{
@@ -5002,7 +5011,7 @@ void monsters::apply_location_effects()
mons_check_pool(this);
if (alive() && has_ench(ENCH_SUBMERGED)
- && !monster_can_submerge(type, grd[x][y]))
+ && !monster_can_submerge(this, grd[x][y]))
{
del_ench(ENCH_SUBMERGED);
}
@@ -5430,7 +5439,7 @@ std::string do_mon_str_replacements(const std::string &in_msg,
if (mons_shouts(monster->type) >= NUM_SHOUTS)
{
mpr("Invalid @says@ type.", MSGCH_DIAGNOSTICS);
- msg = replace_all(msg, "@says@", "bugilly says");
+ msg = replace_all(msg, "@says@", "buggily says");
}
else
msg = replace_all(msg, "@says@",
@@ -5448,7 +5457,7 @@ static mon_body_shape get_ghost_shape(const monsters *mon)
{
const ghost_demon &ghost = *(mon->ghost);
- switch(ghost.values[GVAL_SPECIES])
+ switch (ghost.species)
{
case SP_NAGA:
return (MON_SHAPE_NAGA);
@@ -5472,9 +5481,10 @@ static mon_body_shape get_ghost_shape(const monsters *mon)
case SP_UNK1_DRACONIAN:
case SP_BASE_DRACONIAN:
return (MON_SHAPE_HUMANOID_TAILED);
- }
- return (MON_SHAPE_HUMANOID);
+ default:
+ return (MON_SHAPE_HUMANOID);
+ }
}
mon_body_shape get_mon_shape(const monsters *mon)
@@ -5706,3 +5716,71 @@ std::string get_mon_shape_str(const mon_body_shape shape)
return (shape_names[shape]);
}
+
+/////////////////////////////////////////////////////////////////////////
+// mon_resist_def
+
+mon_resist_def::mon_resist_def()
+ : elec(0), poison(0), fire(0), steam(0), cold(0), hellfire(0),
+ asphyx(0), acid(0), sticky_flame(false), pierce(0),
+ slice(0), bludgeon(0)
+{
+}
+
+mon_resist_def::mon_resist_def(int flags, short level)
+ : elec(0), poison(0), fire(0), steam(0), cold(0), hellfire(0),
+ asphyx(0), acid(0), sticky_flame(false), pierce(0),
+ slice(0), bludgeon(0)
+{
+ for (int i = 0; i < 32; ++i)
+ {
+ switch (flags & (1 << i))
+ {
+ case MR_RES_STEAM: steam = 3; break;
+ case MR_RES_ELEC: elec = level; break;
+ case MR_RES_POISON: poison = level; break;
+ case MR_RES_FIRE: fire = level; break;
+ case MR_RES_HELLFIRE: hellfire = level; break;
+ case MR_RES_COLD: cold = level; break;
+ case MR_RES_ASPHYX: asphyx = level; break;
+ case MR_RES_ACID: acid = level; break;
+ case MR_VUL_ELEC: elec = -level; break;
+ case MR_VUL_POISON: poison = -level; break;
+ case MR_VUL_FIRE: fire = -level; break;
+ case MR_VUL_COLD: cold = -level; break;
+
+ case MR_RES_PIERCE: pierce = level; break;
+ case MR_RES_SLICE: slice = level; break;
+ case MR_RES_BLUDGEON: bludgeon = level; break;
+ case MR_VUL_PIERCE: pierce = -level; break;
+ case MR_VUL_SLICE: slice = -level; break;
+ case MR_VUL_BLUDGEON: bludgeon = -level; break;
+
+ case MR_RES_STICKY_FLAME: sticky_flame = true; break;
+
+ default: break;
+ }
+ }
+}
+
+const mon_resist_def &mon_resist_def::operator |= (const mon_resist_def &o)
+{
+ elec += o.elec;
+ poison += o.poison;
+ fire += o.fire;
+ cold += o.cold;
+ hellfire += o.hellfire;
+ asphyx += o.asphyx;
+ acid += o.acid;
+ pierce += o.pierce;
+ slice += o.slice;
+ bludgeon += o.bludgeon;
+ sticky_flame = sticky_flame || o.sticky_flame;
+ return (*this);
+}
+
+mon_resist_def mon_resist_def::operator | (const mon_resist_def &o) const
+{
+ mon_resist_def c(*this);
+ return (c |= o);
+}