diff options
Diffstat (limited to 'stone_soup/crawl-ref/source/mon-util.cc')
-rw-r--r-- | stone_soup/crawl-ref/source/mon-util.cc | 2209 |
1 files changed, 0 insertions, 2209 deletions
diff --git a/stone_soup/crawl-ref/source/mon-util.cc b/stone_soup/crawl-ref/source/mon-util.cc deleted file mode 100644 index 7ce15b9660..0000000000 --- a/stone_soup/crawl-ref/source/mon-util.cc +++ /dev/null @@ -1,2209 +0,0 @@ -/* - * File: mon-util.cc - * Summary: Misc monster related functions. - * Written by: Linley Henzell - * - * Modified for Crawl Reference by $Author$ on $Date$ - * - * Change History (most recent first): - * - * <2> 11/04/99 cdl added a break to spell selection - * for kobold Summoners - * print just "[Ii]t" for invisible undead - * renamed params to monam() - * <1> -/--/-- LRH Created - */ - -// $pellbinder: (c) D.G.S.E 1998 -// some routines snatched from former monsstat.cc - -#include "AppHdr.h" -#include "mon-util.h" -#include "monstuff.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - -#include "externs.h" - -#include "debug.h" -#include "itemname.h" -#include "itemprop.h" -#include "mstuff2.h" -#include "player.h" -#include "randart.h" -#include "stuff.h" -#include "view.h" - -//jmf: moved from inside function -static FixedVector < int, NUM_MONSTERS > mon_entry; - -// really important extern -- screen redraws suck w/o it {dlb} -FixedVector < unsigned short, 1000 > mcolour; - -static struct monsterentry mondata[] = { -#include "mon-data.h" -}; - -#define MONDATASIZE (sizeof(mondata)/sizeof(struct monsterentry)) - -static int mspell_list[][7] = { -#include "mon-spll.h" -}; - -#if DEBUG_DIAGNOSTICS -static const char *monster_spell_name[] = { - "Magic Missile", - "Throw Flame", - "Throw Frost", - "Paralysis", - "Slow", - "Haste", - "Confuse", - "Venom Bolt", - "Fire Bolt", - "Cold Bolt", - "Lightning Bolt", - "Invisibility", - "Fireball", - "Heal", - "Teleport", - "Teleport Other", - "Blink", - "Crystal Spear", - "Dig", - "Negative Bolt", - "Hellfire Burst", - "Vampire Summon", - "Orb Energy", - "Brain Feed", - "Level Summon", - "Fake Rakshasa Summon", - "Steam Ball", - "Summon Demon", - "Animate Dead", - "Pain", - "Smite", - "Sticky Flame", - "Poison Blast", - "Summon Demon Lesser", - "Summon Ufetubus", - "Purple Blast", - "Summon Beast", - "Energy Bolt", - "Sting", - "Iron Bolt", - "Stone Arrow", - "Poison Splash", - "Summon Undead", - "Mutation", - "Cantrip", - "Disintegrate", - "Marsh Gas", - "Quicksilver Bolt", - "Torment", - "Hellfire", - "Metal Splinters", - "Summon Demon Greater", - "Banishment", -}; -#endif - -static int mons_exp_mod(int mclass); -static monsterentry *seekmonster(int p_monsterid); - -// macro that saves some typing, nothing more -#define smc seekmonster(mc) - -/* ******************** BEGIN PUBLIC FUNCTIONS ******************** */ -void init_monsters(FixedVector < unsigned short, 1000 > &colour) -{ - unsigned int x; // must be unsigned to match size_t {dlb} - - for (x = 0; x < MONDATASIZE; x++) - colour[mondata[x].mc] = mondata[x].colour; - - //unsigned int x = 0; // must be unsigned to match size_t {dlb} - - // first, fill static array with dummy values {dlb}; - for (x = 0; x < NUM_MONSTERS; x++) - mon_entry[x] = -1; - - // next, fill static array with location of entry in mondata[] {dlb}: - for (x = 0; x < MONDATASIZE; x++) - mon_entry[mondata[x].mc] = x; - - // finally, monsters yet with dummy entries point to TTTSNB(tm) {dlb}: - for (x = 0; x < NUM_MONSTERS; x++) - { - if (mon_entry[x] == -1) - mon_entry[x] = mon_entry[MONS_PROGRAM_BUG]; - } - //return (monsterentry *) 0; // return value should not matter here {dlb} -} // end mons_init() - -unsigned long get_mons_class_resists(int mc) -{ - return (smc->resists); -} - -unsigned long mons_class_resist(int mc, unsigned long flags) -{ - return (smc->resists & flags); -} - -bool mons_class_flag(int mc, int bf) -{ - return ((smc->bitfields & bf) != 0); -} // end mons_class_flag() - -static int scan_mon_inv_randarts( struct monsters *mon, int ra_prop ) -{ - int ret = 0; - - if (mons_itemuse( mon->type ) >= MONUSE_STARTING_EQUIPMENT) - { - const int weapon = mon->inv[MSLOT_WEAPON]; - const int second = mon->inv[MSLOT_MISSILE]; // ettins/two-head oges - const int armour = mon->inv[MSLOT_ARMOUR]; - - if (weapon != NON_ITEM && mitm[weapon].base_type == OBJ_WEAPONS - && is_random_artefact( mitm[weapon] )) - { - ret += randart_wpn_property( mitm[weapon], ra_prop ); - } - - if (second != NON_ITEM && mitm[second].base_type == OBJ_WEAPONS - && is_random_artefact( mitm[second] )) - { - ret += randart_wpn_property( mitm[second], ra_prop ); - } - - if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR - && is_random_artefact( mitm[armour] )) - { - ret += randart_wpn_property( mitm[armour], ra_prop ); - } - } - - return (ret); -} - -mon_holy_type mons_holiness(const monsters *mon) -{ - return (mons_class_holiness(mon->type)); -} - -mon_holy_type mons_class_holiness(int mc) -{ - return (smc->holiness); -} // end mons_holiness() - -bool mons_is_stationary(const monsters *mons) -{ - return (mons->type == MONS_OKLOB_PLANT - || mons->type == MONS_PLANT - || mons->type == MONS_FUNGUS - || mons->type == MONS_CURSE_SKULL - || (mons->type >= MONS_CURSE_TOE - && mons->type <= MONS_POTION_MIMIC)); -} - -bool invalid_monster(const monsters *mons) -{ - return (!mons || mons->type == -1); -} - -bool mons_is_mimic( int mc ) -{ - return (mons_species( mc ) == MONS_GOLD_MIMIC); -} - -bool mons_is_demon( int mc ) -{ - const int show_char = mons_char( mc ); - - // Not every demonic monster is a demon (ie hell hog, hell hound) - if (mons_class_holiness( mc ) == MH_DEMONIC - && (isdigit( show_char ) || show_char == '&')) - { - return (true); - } - - return (false); -} - -// Used for elven Glamour ability. -- bwr -bool mons_is_humanoid( int mc ) -{ - switch (mons_char( mc)) - { - case 'o': // orcs - case 'e': // elvens (deep) - case 'c': // centaurs - case 'C': // giants - case 'O': // ogres - case 'K': // kobolds - case 'N': // nagas - case '@': // adventuring humans - case 'T': // trolls - return (true); - - case 'g': // goblines, hobgoblins, gnolls, boggarts -- but not gargoyles - if (mc != MONS_GARGOYLE - && mc != MONS_METAL_GARGOYLE - && mc != MONS_MOLTEN_GARGOYLE) - { - return (true); - } - - default: - break; - } - - return (false); -} - -int mons_zombie_size(int mc) -{ - return (smc->zombie_size); -} // end mons_zombie_size() - - -int mons_weight(int mc) -{ - return (smc->weight); -} // end mons_weight() - -corpse_effect_type mons_corpse_effect(int mc) -{ - return (smc->corpse_thingy); -} // end mons_corpse_effect() - - -monster_type mons_species( int mc ) -{ - return (smc->species); -} // end mons_species() - -monster_type mons_genus( int mc ) -{ - return (smc->genus); -} - -monster_type draco_subspecies( const monsters *mon ) -{ - ASSERT( mons_genus( mon->type ) == MONS_DRACONIAN ); - - monster_type ret = mons_species( mon->type ); - - if (ret == MONS_DRACONIAN && mon->type != MONS_DRACONIAN) - ret = static_cast<monster_type>( mon->number ); - - return (ret); -} - -int mons_shouts(int mc) -{ - int u = smc->shouts; - - if (u == -1) - u = random2(12); - - return (u); -} // end mons_shouts() - -bool mons_is_unique( int mc ) -{ - if (mc <= MONS_PROGRAM_BUG - || (mc >= MONS_NAGA_MAGE && mc <= MONS_ROYAL_JELLY) - || (mc >= MONS_DRACONIAN && mc <= MONS_DRACONIAN_SCORCHER) - || (mc >= MONS_ANCIENT_LICH - && (mc != MONS_PLAYER_GHOST && mc != MONS_PANDEMONIUM_DEMON))) - { - return (false); - } - - return (true); -} - -char mons_see_invis( struct monsters *mon ) -{ - if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON) - return (ghost.values[ GVAL_SEE_INVIS ]); - else if (((seekmonster(mon->type))->bitfields & M_SEE_INVIS) != 0) - return (1); - else if (scan_mon_inv_randarts( mon, RAP_EYESIGHT ) > 0) - return (1); - - return (0); -} // end mons_see_invis() - - -// This does NOT do line of sight! It checks the targ's visibility -// with respect to mon's perception, but doesn't do walls or range. -bool mons_monster_visible( struct monsters *mon, struct monsters *targ ) -{ - if (mons_has_ench(targ, ENCH_SUBMERGED) - || (mons_has_ench(targ, ENCH_INVIS) && !mons_see_invis(mon))) - { - return (false); - } - - return (true); -} - -// This does NOT do line of sight! It checks the player's visibility -// with respect to mon's perception, but doesn't do walls or range. -bool mons_player_visible( struct monsters *mon ) -{ - if (you.invis) - { - if (player_in_water()) - return (true); - - if (mons_see_invis( mon )) - return (true); - - return (false); - } - - return (true); -} - -unsigned char mons_char(int mc) -{ - return (unsigned char) smc->showchar; -} // end mons_char() - - -char mons_itemuse(int mc) -{ - return (smc->gmon_use); -} // end mons_itemuse() - -unsigned char mons_colour(int mc) -{ - return (smc->colour); -} // end mons_colour() - - -int mons_damage(int mc, int rt) -{ - ASSERT(rt >= 0); - ASSERT(rt <= 3); - - if (rt < 0 || rt > 3) // make it fool-proof - return (0); - - if (rt == 0 && (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)) - return (ghost.values[ GVAL_DAMAGE ]); - - return (smc->damage[rt]); -} // end mons_damage() - -int mons_resist_magic( struct monsters *mon ) -{ - int u = (seekmonster(mon->type))->resist_magic; - - // negative values get multiplied with mhd - if (u < 0) - u = mon->hit_dice * (-u * 2); - - u += scan_mon_inv_randarts( mon, RAP_MAGIC ); - - // ego armour resistance - const int armour = mon->inv[MSLOT_ARMOUR]; - - if (armour != NON_ITEM - && get_armour_ego_type( mitm[armour] ) == SPARM_MAGIC_RESISTANCE ) - { - u += 30; - } - - return (u); -} // end mon_resist_magic() - - -// Returns true if the monster made its save against hostile -// enchantments/some other magics. -bool check_mons_resist_magic( struct monsters *monster, int pow ) -{ - int mrs = mons_resist_magic(monster); - - if (mrs == 5000) - return (true); - - // Evil, evil hack to make weak one hd monsters easier for first - // level characters who have resistable 1st level spells. Six is - // a very special value because mrs = hd * 2 * 3 for most monsters, - // and the weak, low level monsters have been adjusted so that the - // "3" is typically a 1. There are some notable one hd monsters - // that shouldn't fall under this, so we do < 6, instead of <= 6... - // or checking monster->hit_dice. The goal here is to make the - // first level easier for these classes and give them a better - // shot at getting to level two or three and spells that can help - // them out (or building a level or two of their base skill so they - // aren't resisted as often). -- bwr - if (mrs < 6 && coinflip()) - return (false); - - pow = stepdown_value( pow, 30, 40, 100, 120 ); - - const int mrchance = (100 + mrs) - pow; - const int mrch2 = random2(100) + random2(101); - -#if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, - "Power: %d, monster's MR: %d, target: %d, roll: %d", - pow, mrs, mrchance, mrch2 ); - - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - return ((mrch2 < mrchance) ? true : false); -} // end check_mons_resist_magic() - - -int mons_res_elec( struct monsters *mon ) -{ - int mc = mon->type; - - if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) - return (ghost.values[ GVAL_RES_ELEC ]); - - /* this is a variable, not a player_xx() function, so can be above 1 */ - int u = 0; - - if (mons_class_resist( mon->type, MR_RES_ELEC)) - u++; - - // don't bother checking equipment if the monster can't use it - if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) - { - u += scan_mon_inv_randarts( mon, RAP_ELECTRICITY ); - - // no ego armour, but storm dragon. - const int armour = mon->inv[MSLOT_ARMOUR]; - if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR - && mitm[armour].sub_type == ARM_STORM_DRAGON_ARMOUR) - { - u += 1; - } - } - - return (u); -} // end mons_res_elec() - -bool mons_res_asphyx( const monsters *mon ) -{ - const int holiness = mons_holiness( mon ); - return (holiness == MH_UNDEAD - || holiness == MH_DEMONIC - || holiness == MH_NONLIVING); -} - -int mons_res_poison( struct monsters *mon ) -{ - int mc = mon->type; - - int u = 0, f = get_mons_class_resists(mc); - - if (f & MR_RES_POISON) - u++; - - if (f & MR_VUL_POISON) - u--; - - if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) - { - u += scan_mon_inv_randarts( mon, RAP_POISON ); - - const int armour = mon->inv[MSLOT_ARMOUR]; - if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR) - { - // intrinsic armour abilities - switch (mitm[armour].sub_type) - { - case ARM_SWAMP_DRAGON_ARMOUR: u += 1; break; - case ARM_GOLD_DRAGON_ARMOUR: u += 1; break; - default: break; - } - - // ego armour resistance - if (get_armour_ego_type( mitm[armour] ) == SPARM_POISON_RESISTANCE) - u += 1; - } - } - - return (u); -} // end mons_res_poison() - - -int mons_res_fire( struct monsters *mon ) -{ - int mc = mon->type; - - if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) - return (ghost.values[ GVAL_RES_FIRE ]); - - int u = 0, f = get_mons_class_resists(mc); - - // 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--; - - if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) - { - u += scan_mon_inv_randarts( mon, RAP_POISON ); - - const int armour = mon->inv[MSLOT_ARMOUR]; - if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR) - { - // intrinsic armour abilities - switch (mitm[armour].sub_type) - { - case ARM_DRAGON_ARMOUR: u += 2; break; - case ARM_GOLD_DRAGON_ARMOUR: u += 1; break; - case ARM_ICE_DRAGON_ARMOUR: u -= 1; break; - default: break; - } - - // check ego resistance - const int ego = get_armour_ego_type( mitm[armour] ); - if (ego == SPARM_FIRE_RESISTANCE || ego == SPARM_RESISTANCE) - u += 1; - } - } - - return (u); -} // end mons_res_fire() - - -int mons_res_cold( struct monsters *mon ) -{ - int mc = mon->type; - - if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) - return (ghost.values[ GVAL_RES_COLD ]); - - int u = 0, f = get_mons_class_resists(mc); - - // 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--; - - if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) - { - u += scan_mon_inv_randarts( mon, RAP_POISON ); - - const int armour = mon->inv[MSLOT_ARMOUR]; - if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR) - { - // intrinsic armour abilities - switch (mitm[armour].sub_type) - { - case ARM_ICE_DRAGON_ARMOUR: u += 2; break; - case ARM_GOLD_DRAGON_ARMOUR: u += 1; break; - case ARM_DRAGON_ARMOUR: u -= 1; break; - default: break; - } - - // check ego resistance - const int ego = get_armour_ego_type( mitm[armour] ); - if (ego == SPARM_COLD_RESISTANCE || ego == SPARM_RESISTANCE) - u += 1; - } - } - - return (u); -} // end mons_res_cold() - -int mons_res_negative_energy( struct monsters *mon ) -{ - int mc = mon->type; - - if (mons_holiness(mon) == MH_UNDEAD - || mons_holiness(mon) == MH_DEMONIC - || mons_holiness(mon) == MH_NONLIVING - || mons_holiness(mon) == MH_PLANT - || mon->type == MONS_SHADOW_DRAGON - || mon->type == MONS_DEATH_DRAKE) - { - return (3); // to match the value for players - } - - int u = 0; - - if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) - { - u += scan_mon_inv_randarts( mon, RAP_NEGATIVE_ENERGY ); - - const int armour = mon->inv[MSLOT_ARMOUR]; - if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR) - { - // check for ego resistance - if (get_armour_ego_type( mitm[armour] ) == SPARM_POSITIVE_ENERGY) - u += 1; - } - } - - return (u); -} // end mons_res_negative_energy() - -bool mons_is_evil( const monsters *mon ) -{ - return (mons_class_flag( mon->type, M_EVIL )); -} - -bool mons_is_unholy( const monsters *mon ) -{ - const mon_holy_type holy = mons_holiness( mon ); - - return (holy == MH_UNDEAD || holy == MH_DEMONIC); -} - -bool mons_has_lifeforce( const monsters *mon ) -{ - const int holy = mons_holiness( mon ); - - return (holy == MH_NATURAL || holy == MH_PLANT); - // && !mons_has_ench( mon, ENCH_PETRIFY )); -} - -int mons_skeleton(int mc) -{ - if (mons_zombie_size(mc) == 0 - || mons_weight(mc) == 0 || ((smc->bitfields & M_NO_SKELETON) != 0)) - { - return (0); - } - - return (1); -} // end mons_skeleton() - -char mons_class_flies(int mc) -{ - if (mc == MONS_PANDEMONIUM_DEMON) - return (ghost.values[ GVAL_DEMONLORD_FLY ]); - - int f = smc->bitfields; - - if (f & M_FLIES) - return (1); - - if (f & M_LEVITATE) - return (2); - - return (0); -} - -char mons_flies( struct monsters *mon ) -{ - char ret = mons_class_flies( mon->type ); - - return (ret ? ret : (scan_mon_inv_randarts(mon, RAP_LEVITATE) > 0) ? 2 : 0); -} // end mons_flies() - - -// this nice routine we keep in exactly the way it was -int hit_points(int hit_dice, int min_hp, int rand_hp) -{ - int hrolled = 0; - - for (int hroll = 0; hroll < hit_dice; hroll++) - { - hrolled += random2(1 + rand_hp); - hrolled += min_hp; - } - - return (hrolled); -} // end hit_points() - -// This function returns the standard number of hit dice for a type -// of monster, not a pacticular monsters current hit dice. -- bwr -int mons_type_hit_dice( int type ) -{ - struct monsterentry *mon_class = seekmonster( type ); - - if (mon_class) - return (mon_class->hpdice[0]); - - return (0); -} - - -int exper_value( struct monsters *monster ) -{ - long x_val = 0; - - // these three are the original arguments: - const int mclass = monster->type; - const int mHD = monster->hit_dice; - const int maxhp = monster->max_hit_points; - - // these are some values we care about: - const int speed = mons_speed(mclass); - const int modifier = mons_exp_mod(mclass); - const int item_usage = mons_itemuse(mclass); - - // XXX: shapeshifters can qualify here, even though they can't cast: - const bool spellcaster = mons_class_flag( mclass, M_SPELLCASTER ); - - // early out for no XP monsters - if (mons_class_flag(mclass, M_NO_EXP_GAIN)) - return (0); - - // These undead take damage to maxhp, so we use only HD. -- bwr - if (mclass == MONS_ZOMBIE_SMALL - || mclass == MONS_ZOMBIE_LARGE - || mclass == MONS_SIMULACRUM_SMALL - || mclass == MONS_SIMULACRUM_LARGE - || mclass == MONS_SKELETON_SMALL - || mclass == MONS_SKELETON_LARGE) - { - x_val = (16 + mHD * 4) * (mHD * mHD) / 10; - } - else - { - x_val = (16 + maxhp) * (mHD * mHD) / 10; - } - - - // Let's calculate a simple difficulty modifier -- bwr - int diff = 0; - - // Let's look for big spells: - if (spellcaster) - { - const int msecc = ((mclass == MONS_HELLION) ? MST_BURNING_DEVIL : - (mclass == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : monster->number); - - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - - mons_spell_list( msecc, hspell_pass ); - - for (int i = 0; i < 6; i++) - { - switch (hspell_pass[i]) - { - case MS_PARALYSIS: - case MS_SMITE: - case MS_HELLFIRE_BURST: - case MS_HELLFIRE: - case MS_TORMENT: - case MS_IRON_BOLT: - diff += 25; - break; - - case MS_LIGHTNING_BOLT: - case MS_NEGATIVE_BOLT: - case MS_VENOM_BOLT: - case MS_STICKY_FLAME: - case MS_DISINTEGRATE: - case MS_SUMMON_DEMON_GREATER: - case MS_BANISHMENT: - case MS_CRYSTAL_SPEAR: - case MS_TELEPORT: - case MS_TELEPORT_OTHER: - diff += 10; - break; - - default: - break; - } - } - } - - // let's look at regeneration - if (monster_descriptor( mclass, MDSC_REGENERATES )) - diff += 15; - - // Monsters at normal or fast speed with big melee damage - if (speed >= 10) - { - int max_melee = 0; - for (int i = 0; i < 4; i++) - max_melee += mons_damage( mclass, i ); - - if (max_melee > 30) - diff += (max_melee / ((speed == 10) ? 2 : 1)); - } - - // Monsters who can use equipment (even if only the equipment - // they are given) can be considerably enhanced because of - // the way weapons work for monsters. -- bwr - if (item_usage == MONUSE_STARTING_EQUIPMENT - || item_usage == MONUSE_WEAPONS_ARMOUR) - { - diff += 30; - } - - // Set a reasonable range on the difficulty modifier... - // Currently 70% - 200% -- bwr - if (diff > 100) - diff = 100; - else if (diff < -30) - diff = -30; - - // Apply difficulty - x_val *= (100 + diff); - x_val /= 100; - - // Basic speed modification - if (speed > 0) - { - x_val *= speed; - x_val /= 10; - } - - // Slow monsters without spells and items often have big HD which - // cause the experience value to be overly large... this tries - // to reduce the inappropriate amount of XP that results. -- bwr - if (speed < 10 && !spellcaster && item_usage < MONUSE_STARTING_EQUIPMENT) - { - x_val /= 2; - } - - // Apply the modifier in the monster's definition - if (modifier > 0) - { - x_val *= modifier; - x_val /= 10; - } - - // Reductions for big values. -- bwr - if (x_val > 100) - x_val = 100 + ((x_val - 100) * 3) / 4; - if (x_val > 1000) - x_val = 1000 + (x_val - 1000) / 2; - - // guarantee the value is within limits - if (x_val <= 0) - x_val = 1; - else if (x_val > 15000) - x_val = 15000; - - return (x_val); -} // end exper_value() - -void mons_spell_list(const monsters *monster, int splist[6]) -{ - int msecc = ((monster->type == MONS_HELLION) ? MST_BURNING_DEVIL : - (monster->type == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : monster->number); - return mons_spell_list(msecc, splist); -} - -// this needs to be rewritten a la the monsterseek rewrite {dlb}: -void mons_spell_list( int sec, int splist[6] ) -{ - unsigned int x; - - for (x = 0; x < NUM_MSTYPES; x++) - { - if (mspell_list[x][0] == sec) - break; - } - - if (x >= NUM_MSTYPES) - return; - - // I *KNOW* this can easily be done in a loop - splist[0] = mspell_list[x][1]; // bolt spell - splist[1] = mspell_list[x][2]; // enchantment - splist[2] = mspell_list[x][3]; // self_ench - splist[3] = mspell_list[x][4]; // misc - splist[4] = mspell_list[x][5]; // misc2 - splist[5] = mspell_list[x][6]; // emergency - - if (sec == MST_GHOST) /* ghost */ - { - for (x = 0; x < 6; x++) - splist[x] = ghost.values[ GVAL_SPELL_1 + x ]; - } -} // end mons_spell_list() - -#if DEBUG_DIAGNOSTICS -const char *mons_spell_name( int spell ) -{ - if (spell == MS_NO_SPELL || spell >= NUM_MONSTER_SPELLS || spell < 0) - return ("No spell"); - - return (monster_spell_name[ spell ]); -} -#endif - -// generate a shiny new and unscarred monster -void define_monster(int k) -{ - int temp_rand = 0; // probability determination {dlb} - int m2_class = menv[k].type; - int m2_HD, m2_hp, m2_hp_max, m2_AC, m2_ev, m2_speed; - int m2_sec = menv[k].number; - struct monsterentry *m = seekmonster(m2_class); - - m2_HD = m->hpdice[0]; - - // misc - m2_AC = m->AC; - m2_ev = m->ev; - - // speed - m2_speed = m->speed; - - // some monsters are randomized: - // did I get them all? // I don't think so {dlb} - if (mons_is_mimic( m2_class )) - m2_sec = get_mimic_colour( &menv[k] ); - else - { - switch (m2_class) - { - case MONS_ABOMINATION_SMALL: - m2_HD = 4 + random2(4); - m2_AC = 3 + random2(7); - m2_ev = 7 + random2(6); - m2_speed = 7 + random2avg(9, 2); - - if (m2_sec == 250) - m2_sec = random_colour(); - break; - - case MONS_ZOMBIE_SMALL: - m2_HD = (coinflip() ? 1 : 2); - break; - - case MONS_ZOMBIE_LARGE: - m2_HD = 3 + random2(5); - break; - - case MONS_ABOMINATION_LARGE: - m2_HD = 8 + random2(4); - m2_AC = 5 + random2avg(9, 2); - m2_ev = 3 + random2(5); - m2_speed = 6 + random2avg(7, 2); - - if (m2_sec == 250) - m2_sec = random_colour(); - break; - - case MONS_BEAST: - m2_HD = 4 + random2(4); - m2_AC = 2 + random2(5); - m2_ev = 7 + random2(5); - m2_speed = 8 + random2(5); - break; - - case MONS_HYDRA: - m2_sec = 4 + random2(5); - break; - - case MONS_DEEP_ELF_FIGHTER: - case MONS_DEEP_ELF_KNIGHT: - case MONS_DEEP_ELF_SOLDIER: - case MONS_ORC_WIZARD: - m2_sec = MST_ORC_WIZARD_I + random2(3); - break; - - case MONS_LICH: - case MONS_ANCIENT_LICH: - m2_sec = MST_LICH_I + random2(4); - break; - - case MONS_HELL_KNIGHT: - m2_sec = (coinflip() ? MST_HELL_KNIGHT_I : MST_HELL_KNIGHT_II); - break; - - case MONS_NECROMANCER: - m2_sec = (coinflip() ? MST_NECROMANCER_I : MST_NECROMANCER_II); - break; - - case MONS_WIZARD: - case MONS_OGRE_MAGE: - case MONS_EROLCHA: - case MONS_DEEP_ELF_MAGE: - m2_sec = MST_WIZARD_I + random2(5); - break; - - case MONS_DEEP_ELF_CONJURER: - m2_sec = (coinflip()? MST_DEEP_ELF_CONJURER_I : MST_DEEP_ELF_CONJURER_II); - break; - - case MONS_BUTTERFLY: - case MONS_SPATIAL_VORTEX: - case MONS_KILLER_KLOWN: - m2_sec = random_colour(); - break; - - case MONS_GILA_MONSTER: - temp_rand = random2(7); - - m2_sec = (temp_rand >= 5 ? LIGHTRED : // 2/7 - temp_rand >= 3 ? LIGHTMAGENTA : // 2/7 - temp_rand == 2 ? RED : // 1/7 - temp_rand == 1 ? MAGENTA // 1/7 - : YELLOW); // 1/7 - break; - - case MONS_HUMAN: - case MONS_ELF: - // these are supposed to only be created by polymorph - m2_HD += random2(10); - m2_AC += random2(5); - m2_ev += random2(5); - break; - - default: - break; - } - } - - // some calculations - m2_hp = hit_points(m2_HD, m->hpdice[1], m->hpdice[2]); - m2_hp += m->hpdice[3]; - m2_hp_max = m2_hp; - - if (m2_sec == 250) - m2_sec = m->sec; - - // so let it be written, so let it be done - menv[k].hit_dice = m2_HD; - menv[k].hit_points = m2_hp; - menv[k].max_hit_points = m2_hp_max; - menv[k].armour_class = m2_AC; - menv[k].evasion = m2_ev; - menv[k].speed = m2_speed; - menv[k].speed_increment = 70; - menv[k].number = m2_sec; - menv[k].flags = 0; - - // reset monster enchantments - for (int i = 0; i < NUM_MON_ENCHANTS; i++) - menv[k].enchantment[i] = ENCH_NONE; -} // end define_monster() - - -/* ------------------------- monam/moname ------------------------- */ -const char *ptr_monam( struct monsters *mon, char desc ) -{ - // We give an item type description for mimics now, note that - // since gold mimics only have one description (to match the - // examine code in direct.cc), we won't bother going through - // this for them. -- bwr - if (mons_is_mimic( mon->type ) && mon->type != MONS_GOLD_MIMIC) - { - static char mimic_name_buff[ ITEMNAME_SIZE ]; - - item_def item; - get_mimic_item( mon, item ); - item_name( item, desc, mimic_name_buff ); - - return (mimic_name_buff); - } - - return (monam( mon->number, mon->type, player_monster_visible( mon ), - desc, mon->inv[MSLOT_WEAPON] )); -} - -const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn ) -{ - static char gmo_n[ ITEMNAME_SIZE ]; - char gmo_n2[ ITEMNAME_SIZE ] = ""; - - gmo_n[0] = '\0'; - - // If you can't see the critter, let moname() print [Ii]t. - if (!vis) - { - moname( mons, vis, desc, gmo_n ); - return (gmo_n); - } - - // These need their description level handled here instead of - // in monam(). - if (mons == MONS_SPECTRAL_THING) - { - switch (desc) - { - case DESC_CAP_THE: - strcpy(gmo_n, "The"); - break; - case DESC_NOCAP_THE: - strcpy(gmo_n, "the"); - break; - case DESC_CAP_A: - strcpy(gmo_n, "A"); - break; - case DESC_NOCAP_A: - strcpy(gmo_n, "a"); - break; - case DESC_PLAIN: /* do nothing */ ; - break; - //default: DEBUGSTR("bad desc flag"); - } - } - - switch (mons) - { - case MONS_ZOMBIE_SMALL: - case MONS_ZOMBIE_LARGE: - moname(mons_num, vis, desc, gmo_n); - strcat(gmo_n, " zombie"); - break; - - case MONS_SKELETON_SMALL: - case MONS_SKELETON_LARGE: - moname(mons_num, vis, desc, gmo_n); - strcat(gmo_n, " skeleton"); - break; - - case MONS_SIMULACRUM_SMALL: - case MONS_SIMULACRUM_LARGE: - moname(mons_num, vis, desc, gmo_n); - strcat(gmo_n, " simulacrum"); - break; - - case MONS_SPECTRAL_THING: - strcat(gmo_n, " spectral "); - moname(mons_num, vis, DESC_PLAIN, gmo_n2); - strcat(gmo_n, gmo_n2); - break; - - case MONS_DANCING_WEAPON: - // safety check -- if we don't have/know the weapon use default name - if (mons_wpn == NON_ITEM) - moname( mons, vis, desc, gmo_n ); - else - { - item_def item = mitm[mons_wpn]; - unset_ident_flags( item, ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES ); - item_name( item, desc, gmo_n ); - } - break; - - case MONS_PLAYER_GHOST: - strcpy(gmo_n, ghost.name); - strcat(gmo_n, "'s ghost"); - break; - - case MONS_PANDEMONIUM_DEMON: - strcpy(gmo_n, ghost.name); - break; - - default: - moname(mons, vis, desc, gmo_n); - break; - } - - return (gmo_n); -} // end monam() - -const char *moname(int mons_num, bool vis, char descrip, char glog[ ITEMNAME_SIZE ]) -{ - glog[0] = '\0'; - - char gmon_name[ ITEMNAME_SIZE ] = ""; - strcpy( gmon_name, seekmonster( mons_num )->name ); - - if (!vis) - { - switch (descrip) - { - case DESC_CAP_THE: - case DESC_CAP_A: - strcpy(glog, "It"); - break; - case DESC_NOCAP_THE: - case DESC_NOCAP_A: - case DESC_PLAIN: - strcpy(glog, "it"); - break; - } - - strcpy(gmon_name, glog); - return (glog); - } - - if (!mons_is_unique( mons_num )) - { - switch (descrip) - { - case DESC_CAP_THE: - strcpy(glog, "The "); - break; - case DESC_NOCAP_THE: - strcpy(glog, "the "); - break; - case DESC_CAP_A: - strcpy(glog, "A"); - break; - case DESC_NOCAP_A: - strcpy(glog, "a"); - break; - case DESC_PLAIN: - break; - // default: DEBUGSTR("bad monster descrip flag"); - } - - if (descrip == DESC_CAP_A || descrip == DESC_NOCAP_A) - { - switch (toupper(gmon_name[0])) - { - case 'A': - case 'E': - case 'I': - case 'O': - case 'U': - strcat(glog, "n "); - break; - - default: - strcat(glog, " "); - break; - } - } - } - - strcat(glog, gmon_name); - - return (glog); -} // end moname() - -/* ********************* END PUBLIC FUNCTIONS ********************* */ - -// see mons_init for initialization of mon_entry array. -static monsterentry *seekmonster(int p_monsterid) -{ - int me = mon_entry[p_monsterid]; - - if (me >= 0) // PARANOIA - return (&mondata[me]); - else - return (NULL); -} // end seekmonster() - -static int mons_exp_mod(int mc) -{ - return (smc->exp_mod); -} // end mons_exp_mod() - - -int mons_speed(int mc) -{ - return (smc->speed); -} // end mons_speed() - - -int mons_intel(int mc) //jmf: "fixed" to work with new I_ types -{ - switch (smc->intel) - { - case I_PLANT: - return (I_PLANT); - case I_INSECT: - case I_REPTILE: - return (I_INSECT); - case I_ANIMAL: - case I_ANIMAL_LIKE: - return (I_ANIMAL); - case I_NORMAL: - return (I_NORMAL); - case I_HIGH: - return (I_HIGH); - default: - return (I_NORMAL); - } -} // ens mons_intel() - - -int mons_intel_type(int mc) //jmf: new, used by my spells -{ - return (smc->intel); -} // end mons_intel_type() - -int mons_power(int mc) -{ - // for now, just return monster hit dice. - return (smc->hpdice[0]); -} - -bool mons_aligned(int m1, int m2) -{ - bool fr1, fr2; - struct monsters *mon1, *mon2; - - if (m1 == MHITNOT || m2 == MHITNOT) - return (true); - - if (m1 == MHITYOU) - fr1 = true; - else - { - mon1 = &menv[m1]; - fr1 = (mon1->attitude == ATT_FRIENDLY) || mons_has_ench(mon1, ENCH_CHARM); - } - - if (m2 == MHITYOU) - fr2 = true; - else - { - mon2 = &menv[m2]; - fr2 = (mon2->attitude == ATT_FRIENDLY) || mons_has_ench(mon2, ENCH_CHARM); - } - - return (fr1 == fr2); -} - -bool mons_friendly(const monsters *m) -{ - return (m->attitude == ATT_FRIENDLY || mons_has_ench(m, ENCH_CHARM)); -} - -bool mons_is_paralysed(const monsters *m) -{ - return (m->speed_increment == 0); -} - -bool mons_is_confused(const monsters *m) -{ - return (mons_has_ench(m, ENCH_CONFUSION) && - !mons_class_flag(m->type, M_CONFUSED)); -} - -bool mons_is_fleeing(const monsters *m) -{ - return (m->behaviour == BEH_FLEE); -} - -bool mons_is_sleeping(const monsters *m) -{ - return (m->behaviour == BEH_SLEEP); -} - -bool mons_is_batty(const monsters *m) -{ - return testbits(m->flags, MF_BATTY); -} - -bool mons_looks_stabbable(const monsters *m) -{ - // Make sure oklob plants are never highlighted. That'll defeat the - // point of making them look like normal plants. - return (!mons_class_flag(m->type, M_NO_EXP_GAIN) - && m->type != MONS_OKLOB_PLANT - && !mons_is_mimic(m->type) - && !mons_friendly(m) - && mons_is_sleeping(m)); -} - -bool mons_looks_distracted(const monsters *m) -{ - return (!mons_class_flag(m->type, M_NO_EXP_GAIN) - && m->type != MONS_OKLOB_PLANT - && !mons_is_mimic(m->type) - && !mons_friendly(m) - && ((m->foe != MHITYOU && !mons_is_batty(m)) - || mons_is_confused(m) - || mons_is_fleeing(m))); -} - -/* ****************************************************************** - -// In the name of England, I declare this function wasteful! {dlb} - -static monsterentry *seekmonster( int mc ) -{ - - ASSERT(mc >= 0); - - int x = 0; - - while (x < mondatasize) - { - if (mondata[x].mc == mc) - return &mondata[x]; - - x++; - } - - ASSERT(false); - - return seekmonster(MONS_PROGRAM_BUG); // see the disasters coming if there is no 250? - -} // end seekmonster() -****************************************************************** */ - - -/* ****************************************************************** - -// only used once, and internal to this file, to boot {dlb}: - -// These are easy to implement here. The difficult (dull!) work of converting -// the data structures is finally finished now! -inline char *mons_name( int mc ) -{ - - return smc->name; - -} // end mons_name() -****************************************************************** */ - -/***************************************************************** - - Used to determine whether or not a monster should fire a beam (MUST be - called _after_ fire_tracer() for meaningful result. - -*/ - -bool mons_should_fire(struct bolt &beam) -{ - // use of foeRatio: - // the higher this number, the more monsters - // will _avoid_ collateral damage to their friends. - // setting this to zero will in fact have all - // monsters ignore their friends when considering - // collateral damage. - - // quick check - did we in fact get any foes? - if (beam.foe_count == 0) - return (false); - - // if we either hit no friends, or monster too dumb to care - if (beam.fr_count == 0 || !beam.smart_monster) - return (true); - - // only fire if they do acceptably low collateral damage - // the default for this is 50%; in other words, don't - // hit a foe unless you hit 2 or fewer friends. - if (beam.foe_power >= (beam.foe_ratio * beam.fr_power) / 100) - return (true); - - return (false); -} - -int mons_has_ench(const monsters *mon, unsigned int ench, unsigned int ench2) -{ - // silliness - if (ench == ENCH_NONE) - return (ench); - - if (ench2 == ENCH_NONE) - ench2 = ench; - - for (int p = 0; p < NUM_MON_ENCHANTS; p++) - { - if (mon->enchantment[p] >= ench && mon->enchantment[p] <= ench2) - return (mon->enchantment[p]); - } - - return (ENCH_NONE); -} - -// Returning the deleted enchantment is important! See abjuration. -- bwr -int mons_del_ench( struct monsters *mon, unsigned int ench, unsigned int ench2, - bool quiet ) -{ - unsigned int p; - int ret_val = ENCH_NONE; - - // silliness - if (ench == ENCH_NONE) - return (ENCH_NONE); - - if (ench2 == ENCH_NONE) - ench2 = ench; - - for (p = 0; p < NUM_MON_ENCHANTS; p++) - { - if (mon->enchantment[p] >= ench && mon->enchantment[p] <= ench2) - break; - } - - if (p == NUM_MON_ENCHANTS) - return (ENCH_NONE); - - ret_val = mon->enchantment[p]; - mon->enchantment[p] = ENCH_NONE; - - // check for slow/haste - if (ench == ENCH_HASTE) - { - if (mon->speed >= 100) - mon->speed = 100 + ((mon->speed - 100) / 2); - else - mon->speed /= 2; - } - - if (ench == ENCH_SLOW) - { - if (mon->speed >= 100) - mon->speed = 100 + ((mon->speed - 100) * 2); - else - mon->speed *= 2; - } - - if (ench == ENCH_FEAR) - { - if (!quiet) - simple_monster_message(mon, " seems to regain its courage."); - - // reevaluate behaviour - behaviour_event(mon, ME_EVAL); - } - - if (ench == ENCH_CONFUSION) - { - if (!quiet) - simple_monster_message(mon, " seems less confused."); - - // reevaluate behaviour - behaviour_event(mon, ME_EVAL); - } - - if (ench == ENCH_INVIS) - { - // invisible monsters stay invisible - if (mons_class_flag(mon->type, M_INVIS)) - { - mon->enchantment[p] = ENCH_INVIS; - } - else if (mons_near(mon) && !player_see_invis() - && !mons_has_ench( mon, ENCH_SUBMERGED )) - { - if (!quiet) - { - strcpy( info, ptr_monam( mon, DESC_CAP_A ) ); - strcat( info, " appears!" ); - mpr( info ); - } - } - } - - if (ench == ENCH_CHARM) - { - if (!quiet) - simple_monster_message(mon, " is no longer charmed."); - - // reevaluate behaviour - behaviour_event(mon, ME_EVAL); - } - - if (ench == ENCH_BACKLIGHT_I) - { - if (!quiet) - simple_monster_message(mon, " stops glowing."); - } - - if (ench == ENCH_STICKY_FLAME_I || ench == ENCH_YOUR_STICKY_FLAME_I) - { - if (!quiet) - simple_monster_message(mon, " stops burning."); - } - - if (ench == ENCH_POISON_I || ench == ENCH_YOUR_POISON_I) - { - if (!quiet) - simple_monster_message(mon, " looks more healthy."); - } - - if (ench == ENCH_YOUR_ROT_I) - { - if (!quiet) - simple_monster_message(mon, " is no longer rotting."); - } - - return (ret_val); -} - -bool mons_add_ench(struct monsters *mon, unsigned int ench) -{ - // silliness - if (ench == ENCH_NONE) - return (false); - - int newspot = -1; - - // don't double-add - for (int p = 0; p < NUM_MON_ENCHANTS; p++) - { - if (mon->enchantment[p] == ench) - return (true); - - if (mon->enchantment[p] == ENCH_NONE && newspot < 0) - newspot = p; - } - - if (newspot < 0) - return (false); - - mon->enchantment[newspot] = ench; -// if ench == ENCH_FEAR //mv: withou this fear & repel undead spell doesn't work - - - // check for slow/haste - if (ench == ENCH_HASTE) - { - if (mon->speed >= 100) - mon->speed = 100 + ((mon->speed - 100) * 2); - else - mon->speed *= 2; - } - - if (ench == ENCH_SLOW) - { - if (mon->speed >= 100) - mon->speed = 100 + ((mon->speed - 100) / 2); - else - mon->speed /= 2; - } - - return (true); -} - -// used to determine whether or not a monster should always -// fire this spell if selected. If not, we should use a -// tracer. - -// note - this function assumes that the monster is "nearby" -// its target! - -bool ms_requires_tracer(int monspell) -{ - bool requires = false; - - switch(monspell) - { - case MS_BANISHMENT: - case MS_COLD_BOLT: - 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_VAMPIRE_SUMMON: - case MS_CANTRIP: - - // meaningless, but sure, why not? - case MS_NO_SPELL: - break; - - default: - break; - - } - - return (requires); -} - -// returns true if the spell is something you wouldn't want done if -// you had a friendly target.. only returns a meaningful value for -// non-beam spells - -bool ms_direct_nasty(int monspell) -{ - bool nasty = true; - - 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_VAMPIRE_SUMMON: - nasty = false; - break; - - case MS_BRAIN_FEED: - case MS_HELLFIRE_BURST: - case MS_MUTATION: - case MS_SMITE: - case MS_TORMENT: - - // meaningless, but sure, why not? - case MS_NO_SPELL: - break; - - default: - break; - - } - - return (nasty); -} - -// Spells a monster may want to cast if fleeing from the player, and -// the player is not in sight. -bool ms_useful_fleeing_out_of_sight( struct monsters *mon, int monspell ) -{ - if (ms_waste_of_time( mon, monspell )) - return (false); - - switch (monspell) - { - case MS_HASTE: - case MS_INVIS: - case MS_HEAL: - case MS_ANIMATE_DEAD: - return (true); - - case MS_VAMPIRE_SUMMON: - case MS_SUMMON_UFETUBUS: - case MS_FAKE_RAKSHASA_SUMMON: - case MS_LEVEL_SUMMON: - case MS_SUMMON_DEMON: - case MS_SUMMON_DEMON_LESSER: - case MS_SUMMON_BEAST: - case MS_SUMMON_UNDEAD: - case MS_SUMMON_DEMON_GREATER: - if (one_chance_in(10)) // only summon friends some of the time - return (true); - break; - - default: - break; - } - - return (false); -} - -bool ms_low_hitpoint_cast( struct monsters *mon, int monspell ) -{ - bool ret = false; - - bool targ_adj = false; - - if (mon->foe == MHITYOU || mon->foe == MHITNOT) - { - if (adjacent(you.x_pos, you.y_pos, mon->x, mon->y)) - targ_adj = true; - } - else if (adjacent( menv[mon->foe].x, menv[mon->foe].y, mon->x, mon->y )) - { - targ_adj = true; - } - - switch (monspell) - { - case MS_TELEPORT: - case MS_TELEPORT_OTHER: - case MS_HEAL: - ret = true; - break; - - case MS_BLINK: - if (targ_adj) - ret = true; - break; - - case MS_VAMPIRE_SUMMON: - case MS_SUMMON_UFETUBUS: - case MS_FAKE_RAKSHASA_SUMMON: - if (!targ_adj) - ret = true; - break; - } - - return (ret); -} - -// Checks to see if a particular spell is worth casting in the first place. -bool ms_waste_of_time( struct monsters *mon, int monspell ) -{ - bool ret = false; - 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_HASTE: - if (mons_has_ench( mon, ENCH_HASTE )) - ret = true; - break; - - case MS_INVIS: - if (mons_has_ench( mon, ENCH_INVIS )) - ret = true; - break; - - case MS_HEAL: - if (mon->hit_points > mon->max_hit_points / 2) - ret = true; - break; - - case MS_TELEPORT: - // Monsters aren't smart enough to know when to cancel teleport. - if (mons_has_ench( mon, ENCH_TP_I, ENCH_TP_IV )) - ret = true; - break; - - case MS_TELEPORT_OTHER: - // Monsters aren't smart enough to know when to cancel teleport. - if (mon->foe == MHITYOU) - { - if (you.duration[DUR_TELEPORT]) - return (true); - } - else if (mon->foe != MHITNOT) - { - if (mons_has_ench( &menv[mon->foe], ENCH_TP_I, ENCH_TP_IV)) - return (true); - } - // intentional fall-through - - case MS_SLOW: - case MS_CONFUSE: - case MS_PAIN: - case MS_BANISHMENT: - case MS_DISINTEGRATE: - case MS_PARALYSIS: - // occasionally we don't estimate... just fire and see: - if (one_chance_in(5)) - return (false); - - // Only intelligent monsters estimate. - intel = mons_intel( mon->type ); - if (intel != I_NORMAL && intel != I_HIGH) - return (false); - - // We'll estimate the target's resistance to magic, by first getting - // the actual value and then randomizing it. - est_magic_resist = (mon->foe == MHITNOT) ? 10000 : 0; - - if (mon->foe != MHITNOT) - { - if (mon->foe == MHITYOU) - est_magic_resist = player_res_magic(); - else - { - targ = &menv[ mon->foe ]; - est_magic_resist = mons_resist_magic( targ ); - } - - // now randomize (normal intels less accurate than high): - if (intel == I_NORMAL) - est_magic_resist += random2(80) - 40; - else - est_magic_resist += random2(30) - 15; - } - - power = 12 * mon->hit_dice * (monspell == MS_PAIN ? 2 : 1); - power = stepdown_value( power, 30, 40, 100, 120 ); - - // Determine the amount of chance allowed by the benefit from - // the spell. The estimated difficulty is the probability - // of rolling over 100 + diff on 2d100. -- bwr - diff = (monspell == MS_PAIN - || monspell == MS_SLOW - || monspell == MS_CONFUSE) ? 0 : 50; - - if (est_magic_resist - power > diff) - ret = true; - - break; - - default: - break; - } - - return (ret); -} - -static bool ms_ranged_spell( int monspell ) -{ - switch (monspell) - { - case MS_HASTE: - case MS_HEAL: - case MS_TELEPORT: - case MS_INVIS: - case MS_BLINK: - return (false); - - default: - break; - } - - return (true); -} - -bool mons_has_ranged_spell( struct monsters *mon ) -{ - const int mclass = mon->type; - - if (mons_class_flag( mclass, M_SPELLCASTER )) - { - const int msecc = ((mclass == MONS_HELLION) ? MST_BURNING_DEVIL : - (mclass == MONS_PANDEMONIUM_DEMON) ? MST_GHOST - : mon->number); - - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - - mons_spell_list( msecc, hspell_pass ); - - for (int i = 0; i < 6; i++) - { - if (ms_ranged_spell( hspell_pass[i] )) - return (true); - } - } - - return (false); -} - -bool mons_has_ranged_attack( struct monsters *mon ) -{ - const int weapon = mon->inv[MSLOT_WEAPON]; - const int ammo = mon->inv[MSLOT_MISSILE]; - - const int lnchClass = (weapon != NON_ITEM) ? mitm[weapon].base_type : -1; - const int lnchType = (weapon != NON_ITEM) ? mitm[weapon].sub_type : 0; - - const int ammoClass = (ammo != NON_ITEM) ? mitm[ammo].base_type : -1; - const int ammoType = (ammo != NON_ITEM) ? mitm[ammo].sub_type : 0; - - bool launched = false; - bool thrown = false; - - throw_type( lnchClass, lnchType, ammoClass, ammoType, launched, thrown ); - - if (launched || thrown) - return (true); - - return (false); -} - - -// use of variant: -// 0 : She is tap dancing. -// 1 : It seems she is tap dancing. (lower case pronoun) -// 2 : Her sword explodes! (upper case possessive) -// 3 : It sticks to her sword! (lower case possessive) -// ... as needed - -const char *mons_pronoun(int mon_type, int variant) -{ - int gender = GENDER_NEUTER; - - if (mons_is_unique( mon_type )) - { - switch(mon_type) - { - case MONS_JESSICA: - case MONS_PSYCHE: - case MONS_JOSEPHINE: - case MONS_AGNES: - case MONS_MAUD: - case MONS_LOUISE: - case MONS_FRANCES: - case MONS_MARGERY: - case MONS_EROLCHA: - case MONS_ERICA: - gender = GENDER_FEMALE; - break; - default: - gender = GENDER_MALE; - break; - } - } - - switch(variant) - { - case PRONOUN_CAP: - return ((gender == 0) ? "It" : - (gender == 1) ? "He" : "She"); - - case PRONOUN_NOCAP: - return ((gender == 0) ? "it" : - (gender == 1) ? "he" : "she"); - - case PRONOUN_CAP_POSSESSIVE: - return ((gender == 0) ? "Its" : - (gender == 1) ? "His" : "Her"); - - case PRONOUN_NOCAP_POSSESSIVE: - return ((gender == 0) ? "its" : - (gender == 1) ? "his" : "her"); - - case PRONOUN_REFLEXIVE: // awkward at start of sentence, always lower - return ((gender == 0) ? "itself" : - (gender == 1) ? "himself" : "herself"); - } - - return (""); -} - -/* - * Checks if the monster can use smiting/torment to attack without unimpeded - * LOS to the player. - */ -static bool mons_can_smite(const monsters *monster) -{ - if (monster->type == MONS_FIEND) - return (true); - - int hspell_pass[6] = { MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL, - MS_NO_SPELL, MS_NO_SPELL, MS_NO_SPELL }; - mons_spell_list(monster, hspell_pass); - for (unsigned i = 0; i < sizeof(hspell_pass) / sizeof(hspell_pass[0]); ++i) - if (hspell_pass[i] == MS_TORMENT || hspell_pass[i] == MS_SMITE) - return (true); - - return (false); -} - -/* - * Determines if a monster is smart and pushy enough to displace other monsters. - * A shover should not cause damage to the shovee by displacing it, so monsters - * that trail clouds of badness are ineligible. The shover should also benefit - * from shoving, so monsters that can smite/torment are ineligible. - * - * (Smiters would be eligible for shoving when fleeing if the AI allowed for - * smart monsters to flee.) - */ -bool monster_shover(const monsters *m) -{ - const monsterentry *me = seekmonster(m->type); - if (!me) - return (false); - - // Efreet and fire elementals are disqualified because they leave behind - // clouds of flame. - if (m->type == MONS_EFREET || m->type == MONS_FIRE_ELEMENTAL - || m->type == MONS_ROTTING_DEVIL) - return (false); - - if (mons_can_smite(m)) - return (false); - - int mchar = me->showchar; - // Somewhat arbitrary: giants and dragons are too big to get past anything, - // beetles are too dumb (arguable), dancing weapons can't communicate, eyes - // aren't pushers and shovers, zombies are zombies. Worms and elementals - // are on the list because all 'w' are currently unrelated. - return (mchar != 'C' && mchar != 'B' && mchar != '(' && mchar != 'D' - && mchar != 'G' && mchar != 'Z' && mchar != 'z' - && mchar != 'w' && mchar != '#'); -} - -// Returns true if m1 and m2 are related, and m1 is higher up the totem pole -// than m2. The criteria for being related are somewhat loose, as you can see -// below. -bool monster_senior(const monsters *m1, const monsters *m2) -{ - const monsterentry *me1 = seekmonster(m1->type), - *me2 = seekmonster(m2->type); - - if (!me1 || !me2) - return (false); - - int mchar1 = me1->showchar, - mchar2 = me2->showchar; - - // If both are demons, the smaller number is the nastier demon. - if (isdigit(mchar1) && isdigit(mchar2)) - return (mchar1 < mchar2); - - // &s are the evillest demons of all, well apart from Geryon, who really - // profits from *not* pushing past beasts. - if (mchar1 == '&' && isdigit(mchar2) && m1->type != MONS_GERYON) - return (m1->hit_dice > m2->hit_dice); - - // Skeletal warriors can push past zombies large and small. - if (m1->type == MONS_SKELETAL_WARRIOR && (mchar2 == 'z' || mchar2 == 'Z')) - return (m1->hit_dice > m2->hit_dice); - - if (m1->type == MONS_QUEEN_BEE - && (m2->type == MONS_KILLER_BEE - || m2->type == MONS_KILLER_BEE_LARVA)) - return (true); - - if (m1->type == MONS_KILLER_BEE && m2->type == MONS_KILLER_BEE_LARVA) - return (true); - - // Special-case gnolls so they can't get past (hob)goblins - if (m1->type == MONS_GNOLL && m2->type != MONS_GNOLL) - return (false); - - return (mchar1 == mchar2 && m1->hit_dice > m2->hit_dice); -} |