etc.), enchantment is stored as a struct (ENCH_which, degree, whose_enchantment). This allows us to do such things as award experience for monsters poisoned by friends, or confused monsters bashing themselves or falling into lava (not implemented, but could be :-)).
Doing monster_die() correctly is a little tricky for friendly-enchantment-deaths because the original monster may not be around to answer questions, so we fake the killer's stats for indirect friendly kills (which means that some gods may not award piety for minion-cast enchantments, f'r'instance). I think we can live with that for now.
Breaks save file compatibility as usual.
Clouds could also use similar treatment - poison clouds created by a friendly are not correctly tracked yet.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1075 c06c8d41-db1a-0410-9941-cceddc491573
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
Y3P2I7OBISB7VGCDWDDVTXPVKY3KA7KXMD7F2RAGZ5KG4IQMKH7QC
IDTLZ6PEPJP67PO7K5TODLXTBFAEVIE4C3HZCXIQK6OIAQKRVDUQC
ENN23K4IMPAAICA5IXCZBOMWAUXGXWR46AIEWUEDVSHSSJNQB63QC
44BRJ7DD6BJ4RBUNE2PZDODHNGYRLNFPGF7OZWTRNDFW5PKAVSPQC
SJOTTUZMA3UTGT5D6LKUTBDP2CZFXT24FB5IAWCUFHSHLLACM75QC
3Q3LKWVLZ52PG62XVAWU5Y2CA4TGOYC7HZWXUVQWOWOFLGFKQMFQC
QL5YCGGNXVNGD5WACTFKXQOUCCXOKMUQGRHDJAUEIW25LQUSTD2QC
YZXHBEKWQPY4BTKG5FFGLP3AIRBQSTKQVLJJHWKQEA3HTN4UHDYQC
6L4EP4ZRWWYLT55PD5KTTJON5J2JB5VV5MWNHF5VPZQZ5BKEYZ4QC
DODCHP2S4I6VZKQAVXX6D76OPNFI2YWZ4XH3HZTMAJZXA2RJ3XRQC
FUEEIUKGHHFPIRZCN3N753GONWAZTWQ2ZWR53IBJAAZ6FZUNGOMAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
3HMZIQCJSWUYTWQDWRQYPI5VOSRIAGGBOM7LIMF7362RJ2LRMZAAC
74LQ7JXVLAFSHLI7LCBKFX47CNTYSKGUQSXNX5FCIUIGCC2JTR3QC
TXFN7TLLKYEM2W5HVB2UXKXUH7MTXOU77PXBPMP7NJKXSLVX5PMQC
AY7Z23L4WPEFEL2HMF3KX3IM6OYMLB2QWY3AIHHQ37CMRVCXRCMAC
AOAJ6D3OKSELEYKAT55XCVU5LYJ7SMCZKC6DIEGLLB3TF2LEENWQC
NFOXLH722RGWYY5D63VV6SF2XEJBEOQEFQME6FSA4HZRK3CPLSRQC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
IPXXB4VRVZWOU5DKQ5ZTD37LS3QNK2R6APNZUO672YEEJT6OFAYQC
RC6L3CIBLJEH4GWRFD7UQNGI6PZT74FRUVOYHSAN2XCC74NZUASQC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
RISMOCQM6BKK4XSIRKYLOBB2UPDYJNDAL6OGIIR5GGNZQAK5YSZAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
CAGCTYIUYWDHQAJOLVLKOEV5HG6K5ZG7IDHONLIG6BDNCWZJAK4AC
for (int j = 0; j < NUM_MON_ENCHANTS; j++)
marshallByte(th, m.enchantment[j]);
marshallShort(th, m.enchantments.size());
for (mon_enchant_list::const_iterator i = m.enchantments.begin();
i != m.enchantments.end(); ++i)
{
marshall_mon_enchant(th, *i);
}
if (abjLevel < ENCH_ABJ_I)
{
mons_add_ench(monster, ENCH_ABJ_I);
monster_die(monster, KILL_RESET, 0);
}
else
{
simple_monster_message(monster, " shudders.");
mons_add_ench(monster, abjLevel);
}
}
mon_enchant abj = monster->get_ench(ENCH_ABJ);
if (abj.ench != ENCH_NONE)
monster->lose_ench_levels(abj, 1 + (random2(pow) / 8));
if (monster->has_ench(ENCH_ABJ))
simple_monster_message(monster, " shudders.");
kill_category player::kill_alignment() const
{
return (KC_YOU);
}
// duplicate enchantments
for ( int i = 0; i < NUM_MON_ENCHANTS; ++i )
child->enchantment[i] = parent->enchantment[i];
child->enchantments = parent->enchantments;
// If old monster is visible to the player, and is interesting,
// then note why the interesting monster went away.
if (player_monster_visible(monster) && mons_near(monster)
// If old monster is visible to the player, and is interesting,
// then note why the interesting monster went away.
if (player_monster_visible(monster) && mons_near(monster)
take_note(Note(NOTE_POLY_MONSTER, monster->type, 0,
ptr_monam(monster, DESC_NOCAP_A, true)));
}
take_note(Note(NOTE_POLY_MONSTER, monster->type, 0,
ptr_monam(monster, DESC_NOCAP_A, true)));
}
int abj = mons_has_ench( monster, ENCH_ABJ_I, ENCH_ABJ_VI );
int shifter = mons_has_ench( monster, ENCH_GLOWING_SHAPESHIFTER,
ENCH_SHAPESHIFTER );
mon_enchant abj = monster->get_ench(ENCH_ABJ);
mon_enchant shifter = monster->get_ench(ENCH_GLOWING_SHAPESHIFTER,
ENCH_SHAPESHIFTER);
for (int p = 0; p < NUM_MON_ENCHANTS && !died; p++)
{
switch (monster->enchantment[p])
{
case ENCH_SLOW:
if (random2(250) <= mod_speed( monster->hit_dice + 10, speed ))
mons_del_ench(monster, ENCH_SLOW);
break;
case ENCH_HASTE:
if (random2(1000) < mod_speed( 25, speed ))
mons_del_ench(monster, ENCH_HASTE);
break;
case ENCH_FEAR:
if (random2(150) <= mod_speed( monster->hit_dice + 5, speed ))
mons_del_ench(monster, ENCH_FEAR);
break;
case ENCH_PARALYSIS:
if (random2(120) < mod_speed( monster->hit_dice + 5, speed ))
mons_del_ench(monster, ENCH_PARALYSIS);
break;
case ENCH_CONFUSION:
if (random2(120) < mod_speed( monster->hit_dice + 5, speed ))
{
// don't delete perma-confusion
if (!mons_class_flag(monster->type, M_CONFUSED))
mons_del_ench(monster, ENCH_CONFUSION);
}
break;
case ENCH_INVIS:
if (random2(1000) < mod_speed( 25, speed ))
{
// don't delete perma-invis
if (!mons_class_flag( monster->type, M_INVIS ))
mons_del_ench(monster, ENCH_INVIS);
}
break;
case ENCH_SUBMERGED:
// not even air elementals unsubmerge into clouds
if (env.cgrid[monster->x][monster->y] != EMPTY_CLOUD)
break;
// Air elementals are a special case, as their
// submerging in air isn't up to choice. -- bwr
if (monster->type == MONS_AIR_ELEMENTAL)
{
heal_monster( monster, 1, one_chance_in(5) );
if (one_chance_in(5))
mons_del_ench( monster, ENCH_SUBMERGED );
break;
}
// Now we handle the others:
grid = grd[monster->x][monster->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(monster->type, grid))
mons_del_ench( monster, ENCH_SUBMERGED ); // forced to surface
else if (monster->hit_points <= monster->max_hit_points / 2)
break;
else if (((monster->type == MONS_ELECTRICAL_EEL
|| monster->type == MONS_LAVA_SNAKE)
&& (random2(1000) < mod_speed( 20, speed )
|| (mons_near(monster)
&& monster->hit_points == monster->max_hit_points
&& !one_chance_in(10))))
|| random2(2000) < mod_speed(10, speed)
|| (mons_near(monster)
&& monster->hit_points == monster->max_hit_points
&& !one_chance_in(5)))
{
mons_del_ench( monster, ENCH_SUBMERGED );
}
break;
case ENCH_POISON_I:
case ENCH_POISON_II:
case ENCH_POISON_III:
case ENCH_POISON_IV:
case ENCH_YOUR_POISON_I:
case ENCH_YOUR_POISON_II:
case ENCH_YOUR_POISON_III:
case ENCH_YOUR_POISON_IV:
poisonval = monster->enchantment[p] - ENCH_POISON_I;
if (poisonval < 0 || poisonval > 3)
poisonval = monster->enchantment[p] - ENCH_YOUR_POISON_I;
dam = (poisonval >= 3) ? 1 : 0;
if (coinflip())
dam += roll_dice( 1, poisonval + 2 );
if (mons_res_poison(monster) < 0)
dam += roll_dice( 2, poisonval ) - 1;
// We adjust damage for monster speed (since this is applied
// only when the monster moves), and we handle the factional
// part as well (so that speed 30 creatures will take damage).
dam *= 10;
dam = (dam / speed) + ((random2(speed) < (dam % speed)) ? 1 : 0);
if (dam > 0)
{
hurt_monster( monster, dam );
#if DEBUG_DIAGNOSTICS
// for debugging, we don't have this silent.
simple_monster_message( monster, " takes poison damage.",
MSGCH_DIAGNOSTICS );
snprintf( info, INFO_SIZE, "poison damage: %d", dam );
mpr( info, MSGCH_DIAGNOSTICS );
#endif
if (monster->hit_points < 1)
{
monster_die(monster,
((monster->enchantment[p] < ENCH_POISON_I)
? KILL_YOU : KILL_MISC), 0);
died = true;
}
}
// chance to get over poison (1 in 8, modified for speed)
if (random2(1000) < mod_speed( 125, speed ))
{
if (monster->enchantment[p] == ENCH_POISON_I)
mons_del_ench(monster, ENCH_POISON_I);
else if (monster->enchantment[p] == ENCH_YOUR_POISON_I)
mons_del_ench(monster, ENCH_YOUR_POISON_I);
else
monster->enchantment[p]--;
}
break;
case ENCH_YOUR_ROT_I:
if (random2(1000) < mod_speed( 250, speed ))
mons_del_ench(monster, ENCH_YOUR_ROT_I);
else if (monster->hit_points > 1
&& random2(1000) < mod_speed( 333, speed ))
{
hurt_monster(monster, 1);
}
break;
//jmf: FIXME: if (undead) make_small_rot_cloud();
case ENCH_YOUR_ROT_II:
case ENCH_YOUR_ROT_III:
case ENCH_YOUR_ROT_IV:
if (monster->hit_points > 1
&& random2(1000) < mod_speed( 333, speed ))
{
hurt_monster(monster, 1);
}
if (random2(1000) < mod_speed( 250, speed ))
monster->enchantment[p]--;
break;
case ENCH_BACKLIGHT_I:
if (random2(1000) < mod_speed( 100, speed ))
mons_del_ench( monster, ENCH_BACKLIGHT_I );
break;
case ENCH_BACKLIGHT_II:
case ENCH_BACKLIGHT_III:
case ENCH_BACKLIGHT_IV:
if (random2(1000) < mod_speed( 200, speed ))
monster->enchantment[p]--;
break;
// assumption: mons_res_fire has already been checked
case ENCH_STICKY_FLAME_I:
case ENCH_STICKY_FLAME_II:
case ENCH_STICKY_FLAME_III:
case ENCH_STICKY_FLAME_IV:
case ENCH_YOUR_STICKY_FLAME_I:
case ENCH_YOUR_STICKY_FLAME_II:
case ENCH_YOUR_STICKY_FLAME_III:
case ENCH_YOUR_STICKY_FLAME_IV:
dam = roll_dice( 2, 4 ) - 1;
if (mons_res_fire( monster ) < 0)
dam += roll_dice( 2, 5 ) - 1;
// We adjust damage for monster speed (since this is applied
// only when the monster moves), and we handle the factional
// part as well (so that speed 30 creatures will take damage).
dam *= 10;
dam = (dam / speed) + ((random2(speed) < (dam % speed)) ? 1 : 0);
if (dam > 0)
{
hurt_monster( monster, dam );
simple_monster_message(monster, " burns!");
#if DEBUG_DIAGNOSTICS
snprintf( info, INFO_SIZE, "sticky flame damage: %d", dam );
mpr( info, MSGCH_DIAGNOSTICS );
#endif
if (monster->hit_points < 1)
{
monster_die(monster,
((monster->enchantment[p] < ENCH_STICKY_FLAME_I)
? KILL_YOU : KILL_MISC), 0);
died = true;
}
}
// chance to get over sticky flame (1 in 5, modified for speed)
if (random2(1000) < mod_speed( 200, speed ))
{
if (monster->enchantment[p] == ENCH_STICKY_FLAME_I)
mons_del_ench( monster, ENCH_STICKY_FLAME_I );
else if (monster->enchantment[p] == ENCH_YOUR_STICKY_FLAME_I)
mons_del_ench( monster, ENCH_YOUR_STICKY_FLAME_I );
else
monster->enchantment[p]--;
}
break;
case ENCH_SHORT_LIVED:
// This should only be used for ball lightning -- bwr
if (random2(1000) < mod_speed( 200, speed ))
monster->hit_points = -1;
break;
// 19 is taken by summoning:
// If these are changed, must also change abjuration
case ENCH_ABJ_I:
case ENCH_ABJ_II:
case ENCH_ABJ_III:
case ENCH_ABJ_IV:
if (random2(1000) < mod_speed( 100, speed ))
monster->enchantment[p]--;
if (monster->enchantment[p] < ENCH_ABJ_I)
{
monster->enchantment[p] = ENCH_ABJ_I;
monster_die(monster, KILL_RESET, 0);
died = true;
}
break;
case ENCH_ABJ_V:
if (random2(1000) < mod_speed( 20, speed ))
monster->enchantment[p] = ENCH_ABJ_IV;
break;
case ENCH_ABJ_VI:
if (random2(1000) < mod_speed( 10, speed ))
monster->enchantment[p] = ENCH_ABJ_V;
break;
case ENCH_CHARM:
if (random2(500) <= mod_speed( monster->hit_dice + 10, speed ))
mons_del_ench(monster, ENCH_CHARM);
break;
case ENCH_GLOWING_SHAPESHIFTER: // this ench never runs out
// number of actions is fine for shapeshifters
if (monster->type == MONS_GLOWING_SHAPESHIFTER
|| random2(1000) < mod_speed( 250, speed ))
{
monster_polymorph(monster, RANDOM_MONSTER, 0);
}
break;
case ENCH_SHAPESHIFTER: // this ench never runs out
if (monster->type == MONS_SHAPESHIFTER
|| random2(1000) < mod_speed( 1000 / ((15 * monster->hit_dice) / 5), speed ))
{
monster_polymorph(monster, RANDOM_MONSTER, 0);
}
break;
case ENCH_TP_I:
mons_del_ench( monster, ENCH_TP_I );
monster_teleport( monster, true );
break;
case ENCH_TP_II:
case ENCH_TP_III:
case ENCH_TP_IV:
tmp = mod_speed( 1000, speed );
if (tmp < 1000 && random2(1000) < tmp)
monster->enchantment[p]--;
else if (monster->enchantment[p] - tmp / 1000 >= ENCH_TP_I)
{
monster->enchantment[p] -= tmp / 1000;
tmp %= 1000;
if (random2(1000) < tmp)
{
if (monster->enchantment[p] > ENCH_TP_I)
monster->enchantment[p]--;
else
{
mons_del_ench( monster, ENCH_TP_I, ENCH_TP_IV );
monster_teleport( monster, true );
}
}
}
else
{
mons_del_ench( monster, ENCH_TP_I, ENCH_TP_IV );
monster_teleport( monster, true );
}
break;
case ENCH_SLEEP_WARY:
if (random2(1000) < mod_speed( 50, speed ))
mons_del_ench(monster, ENCH_SLEEP_WARY);
break;
}
}
return (died);
monster->apply_enchantments(speed);
return (!monster->alive());
// dur should always be ENCH_ABJ_xx
if (dur >= ENCH_ABJ_I && dur <= ENCH_ABJ_VI)
mons_add_ench(&menv[id], dur );
// dur should always be 1-6 for monsters that can be abjured.
if (dur >= 1 && dur <= 6)
menv[id].add_ench( mon_enchant(ENCH_ABJ, dur) );
int mons_has_ench( const monsters *mon, unsigned int ench,
unsigned int ench2 = ENCH_NONE );
int mons_del_ench( struct monsters *mon, unsigned int ench,
unsigned int ench2 = ENCH_NONE, bool quiet = false );
bool mons_add_ench( struct monsters *mon, unsigned int ench );
}
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_PARALYSIS)
{
if (!quiet)
simple_monster_message(mon, " is no longer paralysed.");
behaviour_event(mon, ME_EVAL);
}
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 );
}
seen_monster(mon);
}
}
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);
}
}
return (false);
}
mon_enchant monsters::get_ench(enchant_type ench1,
enchant_type ench2) const
{
if (ench2 == ENCH_NONE)
ench2 = ench1;
for (int e = ench1; e <= ench2; ++e)
{
mon_enchant_list::const_iterator i =
enchantments.find(static_cast<enchant_type>(e));
if (i != enchantments.end())
return (*i);
}
return (mon_enchant());
}
void monsters::update_ench(const mon_enchant &ench)
{
if (ench.ench != ENCH_NONE)
{
mon_enchant_list::iterator i = enchantments.find(ench);
if (i != enchantments.end())
enchantments.erase(i);
enchantments.insert(ench);
}
}
bool monsters::add_ench(const mon_enchant &ench)
{
// silliness
if (ench.ench == ENCH_NONE)
return (false);
mon_enchant_list::iterator i = enchantments.find(ench);
if (i == enchantments.end())
enchantments.insert(ench);
else
{
mon_enchant new_ench = *i + ench;
enchantments.erase(i);
enchantments.insert(new_ench);
}
// check for slow/haste
if (ench.ench == ENCH_HASTE)
{
if (speed >= 100)
speed = 100 + ((speed - 100) * 2);
else
speed *= 2;
bool monsters::del_ench(enchant_type ench, bool quiet)
{
mon_enchant_list::iterator i = enchantments.find(ench);
if (i == enchantments.end())
return (false);
mon_enchant me = *i;
enchantments.erase(i);
remove_enchantment_effect(me, quiet);
return (true);
}
void monsters::remove_enchantment_effect(const mon_enchant &me, bool quiet)
{
switch (me.ench)
{
case ENCH_HASTE:
if (speed >= 100)
speed = 100 + ((speed - 100) / 2);
else
speed /= 2;
break;
case ENCH_SLOW:
if (speed >= 100)
speed = 100 + ((speed - 100) * 2);
else
speed *= 2;
break;
case ENCH_PARALYSIS:
if (!quiet)
simple_monster_message(this, " is no longer paralysed.");
behaviour_event(this, ME_EVAL);
break;
case ENCH_FEAR:
if (!quiet)
simple_monster_message(this, " seems to regain its courage.");
// reevaluate behaviour
behaviour_event(this, ME_EVAL);
break;
case ENCH_CONFUSION:
if (!quiet)
simple_monster_message(this, " seems less confused.");
// reevaluate behaviour
behaviour_event(this, ME_EVAL);
break;
case ENCH_INVIS:
// invisible monsters stay invisible
if (mons_class_flag(type, M_INVIS))
add_ench( mon_enchant(ENCH_INVIS) );
else if (mons_near(this) && !player_see_invis()
&& !has_ench( ENCH_SUBMERGED ))
{
if (!quiet)
mprf("%s appears!", name(DESC_CAP_A).c_str() );
seen_monster(this);
}
break;
case ENCH_CHARM:
if (!quiet)
simple_monster_message(this, " is no longer charmed.");
// reevaluate behaviour
behaviour_event(this, ME_EVAL);
break;
case ENCH_BACKLIGHT:
if (!quiet)
simple_monster_message(this, " stops glowing.");
break;
case ENCH_STICKY_FLAME:
if (!quiet)
simple_monster_message(this, " stops burning.");
break;
case ENCH_POISON:
if (!quiet)
simple_monster_message(this, " looks more healthy.");
break;
case ENCH_ROT:
if (!quiet)
simple_monster_message(this, " is no longer rotting.");
break;
case ENCH_ABJ:
case ENCH_SHORT_LIVED:
add_ench( mon_enchant(ENCH_ABJ) );
monster_die( this, quiet? KILL_DISMISSED : KILL_RESET, 0 );
break;
default:
break;
}
}
void monsters::lose_ench_levels(const mon_enchant &e, int levels)
{
if (!levels)
return;
mon_enchant me(e);
me.degree -= levels;
if (me.degree < 1)
del_ench(e.ench);
else
update_ench(me);
}
//---------------------------------------------------------------
//
// timeout_enchantments
//
// Update a monster's enchantments when the player returns
// to the level.
//
// Management for enchantments... problems with this are the oddities
// (monster dying from poison several thousands of turns later), and
// game balance.
//
// Consider: Poison/Sticky Flame a monster at range and leave, monster
// dies but can't leave level to get to player (implied game balance of
// the delayed damage is that the monster could be a danger before
// it dies). This could be fixed by keeping some monsters active
// off level and allowing them to take stairs (a very serious change).
//
// Compare this to the current abuse where the player gets
// effectively extended duration of these effects (although only
// the actual effects only occur on level, the player can leave
// and heal up without having the effect disappear).
//
// This is a simple compromise between the two... the enchantments
// go away, but the effects don't happen off level. -- bwr
//
//---------------------------------------------------------------
void monsters::timeout_enchantments(int levels)
{
for (mon_enchant_list::iterator i = enchantments.begin();
i != enchantments.end(); )
{
mon_enchant_list::iterator cur = i++;
switch (cur->ench)
{
case ENCH_POISON: case ENCH_ROT: case ENCH_BACKLIGHT:
case ENCH_STICKY_FLAME: case ENCH_ABJ: case ENCH_SHORT_LIVED:
case ENCH_SLOW: case ENCH_HASTE: case ENCH_FEAR:
case ENCH_INVIS: case ENCH_CHARM: case ENCH_SLEEP_WARY:
lose_ench_levels(*cur, levels);
break;
case ENCH_TP:
teleport(true);
break;
case ENCH_CONFUSION:
blink();
break;
default:
break;
}
}
}
// used to adjust time durations in handle_enchantment() for monster speed
static inline int mod_speed( int val, int speed )
{
return (speed ? (val * 10) / speed : val);
}
void monsters::apply_enchantment(mon_enchant me, int spd)
{
switch (me.ench)
{
case ENCH_SLOW:
if (random2(250) <= mod_speed( hit_dice + 10, spd ))
del_ench(me.ench);
break;
case ENCH_HASTE:
if (random2(1000) < mod_speed( 25, spd ))
del_ench(ENCH_HASTE);
break;
case ENCH_FEAR:
if (random2(150) <= mod_speed( hit_dice + 5, spd ))
del_ench(ENCH_FEAR);
break;
case ENCH_PARALYSIS:
if (random2(120) < mod_speed( hit_dice + 5, spd ))
del_ench(ENCH_PARALYSIS);
break;
case ENCH_CONFUSION:
if (random2(120) < mod_speed( hit_dice + 5, spd ))
{
// don't delete perma-confusion
if (!mons_class_flag(type, M_CONFUSED))
del_ench(ENCH_CONFUSION);
}
break;
case ENCH_INVIS:
if (random2(1000) < mod_speed( 25, spd ))
{
// don't delete perma-invis
if (!mons_class_flag( type, M_INVIS ))
del_ench(ENCH_INVIS);
}
break;
case ENCH_SUBMERGED:
{
// not even air elementals unsubmerge into clouds
if (env.cgrid[x][y] != EMPTY_CLOUD)
break;
// Air elementals are a special case, as their
// submerging in air isn't up to choice. -- bwr
if (type == MONS_AIR_ELEMENTAL)
{
heal_monster( this, 1, one_chance_in(5) );
if (one_chance_in(5))
del_ench(ENCH_SUBMERGED);
break;
}
// Now we handle the others:
int 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))
del_ench(ENCH_SUBMERGED); // forced to surface
else if (hit_points <= max_hit_points / 2)
break;
else if (((type == MONS_ELECTRICAL_EEL
|| type == MONS_LAVA_SNAKE)
&& (random2(1000) < mod_speed( 20, spd )
|| (mons_near(this)
&& hit_points == max_hit_points
&& !one_chance_in(10))))
|| random2(2000) < mod_speed(10, spd)
|| (mons_near(this)
&& hit_points == max_hit_points
&& !one_chance_in(5)))
{
del_ench(ENCH_SUBMERGED);
}
break;
}
case ENCH_POISON:
{
int poisonval = me.degree;
int dam = (poisonval >= 4) ? 1 : 0;
if (coinflip())
dam += roll_dice( 1, poisonval + 1 );
if (mons_res_poison(this) < 0)
dam += roll_dice( 2, poisonval ) - 1;
// We adjust damage for monster speed (since this is applied
// only when the monster moves), and we handle the factional
// part as well (so that speed 30 creatures will take damage).
dam *= 10;
dam = (dam / spd) + ((random2(spd) < (dam % spd)) ? 1 : 0);
if (dam > 0)
{
hurt_monster( this, dam );
#if DEBUG_DIAGNOSTICS
// for debugging, we don't have this silent.
simple_monster_message( this, " takes poison damage.",
MSGCH_DIAGNOSTICS );
snprintf( info, INFO_SIZE, "poison damage: %d", dam );
mpr( info, MSGCH_DIAGNOSTICS );
#endif
if (hit_points < 1)
{
monster_die(this, me.killer(), me.kill_agent());
break;
}
}
// chance to get over poison (1 in 8, modified for speed)
if (random2(1000) < mod_speed( 125, spd ))
lose_ench_levels(me, 1);
break;
}
case ENCH_ROT:
if (hit_points > 1
&& random2(1000) < mod_speed( 333, spd ))
{
hurt_monster(this, 1);
}
if (random2(1000) < mod_speed( me.degree == 1? 250 : 333, spd ))
lose_ench_levels(me, 1);
break;
case ENCH_BACKLIGHT:
if (random2(1000) < mod_speed( me.degree == 1? 100 : 200, spd ))
lose_ench_levels(me, 1);
break;
// 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;
// We adjust damage for monster speed (since this is applied
// only when the monster moves), and we handle the factional
// part as well (so that speed 30 creatures will take damage).
dam *= 10;
dam = (dam / spd) + ((random2(spd) < (dam % spd)) ? 1 : 0);
if (dam > 0)
{
hurt_monster( this, dam );
simple_monster_message(this, " burns!");
#if DEBUG_DIAGNOSTICS
mprf( MSGCH_DIAGNOSTICS, "sticky flame damage: %d", dam );
#endif
if (hit_points < 1)
{
monster_die(this, me.killer(), me.kill_agent());
break;
}
}
// chance to get over sticky flame (1 in 5, modified for speed)
if (random2(1000) < mod_speed( 200, spd ))
lose_ench_levels(me, 1);
break;
}
case ENCH_SHORT_LIVED:
// This should only be used for ball lightning -- bwr
if (random2(1000) < mod_speed( 200, spd ))
hit_points = -1;
break;
// 19 is taken by summoning:
// If these are changed, must also change abjuration
case ENCH_ABJ:
{
const int mspd =
me.degree == 6? 10 :
me.degree == 5? 20 : 100;
if (random2(1000) < mod_speed( mspd, spd ))
lose_ench_levels(me, 1);
break;
}
case ENCH_CHARM:
if (random2(500) <= mod_speed( hit_dice + 10, spd ))
del_ench(ENCH_CHARM);
break;
case ENCH_GLOWING_SHAPESHIFTER: // this ench never runs out
// number of actions is fine for shapeshifters
if (type == MONS_GLOWING_SHAPESHIFTER
|| random2(1000) < mod_speed( 250, spd ))
{
monster_polymorph(this, RANDOM_MONSTER, 0);
}
break;
case ENCH_SHAPESHIFTER: // this ench never runs out
if (type == MONS_SHAPESHIFTER
|| random2(1000) < mod_speed( 1000 / ((15 * hit_dice) / 5), spd ))
{
monster_polymorph(this, RANDOM_MONSTER, 0);
}
break;
case ENCH_TP:
if (me.degree <= 1)
{
del_ench(ENCH_TP);
monster_teleport( this, true );
}
else
{
int tmp = mod_speed( 1000, spd );
if (tmp < 1000 && random2(1000) < tmp)
lose_ench_levels(me, 1);
else if (me.degree - tmp / 1000 >= 1)
{
lose_ench_levels(me, tmp / 1000);
tmp %= 1000;
if (random2(1000) < tmp)
{
if (me.degree > 1)
lose_ench_levels(me, 1);
else
{
del_ench( ENCH_TP );
monster_teleport( this, true );
}
}
}
else
{
del_ench( ENCH_TP );
monster_teleport( this, true );
}
}
break;
case ENCH_SLEEP_WARY:
if (random2(1000) < mod_speed( 50, spd ))
del_ench(ENCH_SLEEP_WARY);
break;
default:
break;
}
}
void monsters::apply_enchantments(int spd)
{
for (mon_enchant_list::iterator i = enchantments.begin();
i != enchantments.end(); )
{
mon_enchant_list::iterator cur = i++;
apply_enchantment(*cur, spd);
}
}
kill_category monsters::kill_alignment() const
{
return (attitude == ATT_FRIENDLY? KC_FRIENDLY : KC_OTHER);
}
/////////////////////////////////////////////////////////////////////////
// mon_enchant
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", "bug"
};
const char *mons_enchantment_name(enchant_type ench)
{
ASSERT(NUM_ENCHANTMENTS ==
(sizeof(enchant_names) / sizeof(*enchant_names)) - 1);
if (ench > NUM_ENCHANTMENTS)
ench = NUM_ENCHANTMENTS;
return (enchant_names[ench]);
}
mon_enchant::operator std::string () const
{
return make_stringf("%s (%d%s)",
mons_enchantment_name(ench),
degree,
kill_category_desc(who));
}
const char *mon_enchant::kill_category_desc(kill_category k) const
{
return (k == KC_YOU? "you" :
k == KC_FRIENDLY? "pet" : "");
}
void mon_enchant::merge_killer(kill_category k)
{
who = who < k? who : k;
}
void mon_enchant::cap_degree()
{
// Hard cap to simulate old enum behaviour, we should really throw this
// out entirely.
const int max = ench == ENCH_ABJ? 6 : 4;
if (degree > max)
degree = max;
}
mon_enchant &mon_enchant::operator += (const mon_enchant &other)
{
if (ench == other.ench)
{
degree += other.degree;
cap_degree();
merge_killer(other.who);
}
return (*this);
}
mon_enchant mon_enchant::operator + (const mon_enchant &other) const
{
mon_enchant tmp(*this);
tmp += other;
return (tmp);
}
int mon_enchant::killer() const
{
return (who == KC_YOU? KILL_YOU :
who == KC_FRIENDLY? KILL_MON :
KILL_MISC);
}
int mon_enchant::kill_agent() const
{
return (who == KC_FRIENDLY? ANON_FRIENDLY_MONSTER : 0);
}
}
static bool remove_enchant_levels( struct monsters *mon, int slot, int min,
int levels )
{
const int new_level = mon->enchantment[slot] - levels;
if (new_level < min)
{
mons_del_ench( mon,
mon->enchantment[slot], mon->enchantment[slot], true );
return (true);
}
else
{
mon->enchantment[slot] = new_level;
}
return (false);
//
// update_enchantments
//
// Update a monster's enchantments when the player returns
// to the level.
//
// Management for enchantments... problems with this are the oddities
// (monster dying from poison several thousands of turns later), and
// game balance.
//
// Consider: Poison/Sticky Flame a monster at range and leave, monster
// dies but can't leave level to get to player (implied game balance of
// the delayed damage is that the monster could be a danger before
// it dies). This could be fixed by keeping some monsters active
// off level and allowing them to take stairs (a very serious change).
//
// Compare this to the current abuse where the player gets
// effectively extended duration of these effects (although only
// the actual effects only occur on level, the player can leave
// and heal up without having the effect disappear).
// This is a simple compromise between the two... the enchantments
// go away, but the effects don't happen off level. -- bwr
//
//---------------------------------------------------------------
static void update_enchantments( struct monsters *mon, int levels )
{
int i;
for (i = 0; i < NUM_MON_ENCHANTS; i++)
{
switch (mon->enchantment[i])
{
case ENCH_YOUR_POISON_I:
case ENCH_YOUR_POISON_II:
case ENCH_YOUR_POISON_III:
case ENCH_YOUR_POISON_IV:
remove_enchant_levels( mon, i, ENCH_YOUR_POISON_I, levels );
break;
case ENCH_YOUR_ROT_I:
case ENCH_YOUR_ROT_II:
case ENCH_YOUR_ROT_III:
case ENCH_YOUR_ROT_IV:
remove_enchant_levels( mon, i, ENCH_YOUR_ROT_I, levels );
break;
case ENCH_BACKLIGHT_I:
case ENCH_BACKLIGHT_II:
case ENCH_BACKLIGHT_III:
case ENCH_BACKLIGHT_IV:
remove_enchant_levels( mon, i, ENCH_BACKLIGHT_I, levels );
break;
case ENCH_YOUR_STICKY_FLAME_I:
case ENCH_YOUR_STICKY_FLAME_II:
case ENCH_YOUR_STICKY_FLAME_III:
case ENCH_YOUR_STICKY_FLAME_IV:
remove_enchant_levels( mon, i, ENCH_YOUR_STICKY_FLAME_I, levels );
break;
case ENCH_POISON_I:
case ENCH_POISON_II:
case ENCH_POISON_III:
case ENCH_POISON_IV:
remove_enchant_levels( mon, i, ENCH_POISON_I, levels );
break;
case ENCH_STICKY_FLAME_I:
case ENCH_STICKY_FLAME_II:
case ENCH_STICKY_FLAME_III:
case ENCH_STICKY_FLAME_IV:
remove_enchant_levels( mon, i, ENCH_STICKY_FLAME_I, levels );
break;
case ENCH_ABJ_I:
case ENCH_ABJ_II:
case ENCH_ABJ_III:
case ENCH_ABJ_IV:
case ENCH_ABJ_V:
case ENCH_ABJ_VI:
if (remove_enchant_levels( mon, i, ENCH_ABJ_I, levels ))
{
// Re-add ABJ_I so that monster_die doesn't try to send the
// monster to the Abyss on KILL_RESET.
mons_add_ench( mon, ENCH_ABJ_I );
monster_die( mon, KILL_RESET, 0 );
}
break;
case ENCH_SHORT_LIVED:
mons_add_ench( mon, ENCH_ABJ_I );
monster_die( mon, KILL_RESET, 0 );
break;
case ENCH_TP_I:
case ENCH_TP_II:
case ENCH_TP_III:
case ENCH_TP_IV:
monster_teleport( mon, true );
break;
case ENCH_CONFUSION:
monster_blink( mon );
break;
case ENCH_GLOWING_SHAPESHIFTER:
case ENCH_SHAPESHIFTER:
case ENCH_CREATED_FRIENDLY:
case ENCH_SUBMERGED:
default:
// don't touch these
break;
case ENCH_SLOW:
case ENCH_HASTE:
case ENCH_FEAR:
case ENCH_INVIS:
case ENCH_CHARM:
case ENCH_SLEEP_WARY:
// delete enchantment (using function to get this done cleanly)
mons_del_ench(mon, mon->enchantment[i], mon->enchantment[i], true);
break;
}
}
}
//---------------------------------------------------------------
//
int degree;
mon_enchant(enchant_type e = ENCH_NONE, int deg = 0,
kill_category whose = KC_OTHER)
: ench(e), degree(deg), who(whose)
{
}
int killer() const;
int kill_agent() const;
operator std::string () const;
const char *kill_category_desc(kill_category) const;
void merge_killer(kill_category who);
void cap_degree();
bool operator < (const mon_enchant &other) const
{
return (ench < other.ench);
}
kill_category kill_alignment() const;
bool has_ench(enchant_type ench,
enchant_type ench2 = ENCH_NONE) 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);
void lose_ench_levels(const mon_enchant &e, int levels);
void remove_enchantment_effect(const mon_enchant &me, bool quiet = false);
void apply_enchantments(int speed);
void apply_enchantment(mon_enchant me, int speed);
void timeout_enchantments(int levels);
ENCH_HASTE, // 2
ENCH_FEAR = 4, // 4
ENCH_CONFUSION, // 5
ENCH_INVIS,
ENCH_YOUR_POISON_I,
ENCH_YOUR_POISON_II,
ENCH_YOUR_POISON_III,
ENCH_YOUR_POISON_IV, // 10
ENCH_BERSERK_I,
ENCH_BERSERK_II,
ENCH_BERSERK_III,
ENCH_BERSERK_IV,
ENCH_YOUR_ROT_I, // 15 //jmf: rotting effect for monsters
ENCH_YOUR_ROT_II,
ENCH_YOUR_ROT_III,
ENCH_YOUR_ROT_IV,
ENCH_SUMMON = 19, // 19
ENCH_ABJ_I, // 20
ENCH_ABJ_II,
ENCH_ABJ_III,
ENCH_ABJ_IV,
ENCH_ABJ_V,
ENCH_ABJ_VI, // 25
ENCH_BACKLIGHT_I, //jmf: backlight for Corona spell
ENCH_BACKLIGHT_II,
ENCH_BACKLIGHT_III,
ENCH_BACKLIGHT_IV,
ENCH_CHARM = 30, // 30
ENCH_YOUR_STICKY_FLAME_I,
ENCH_YOUR_STICKY_FLAME_II,
ENCH_YOUR_STICKY_FLAME_III,
ENCH_YOUR_STICKY_FLAME_IV, // 34
ENCH_GLOWING_SHAPESHIFTER = 38, // 38
ENCH_SHAPESHIFTER,
ENCH_TP_I, // 40
ENCH_TP_II,
ENCH_TP_III,
ENCH_TP_IV, // 43
ENCH_POISON_I = 57, // 57
ENCH_POISON_II,
ENCH_POISON_III,
ENCH_POISON_IV, // 60
ENCH_STICKY_FLAME_I,
ENCH_STICKY_FLAME_II,
ENCH_STICKY_FLAME_III,
ENCH_STICKY_FLAME_IV,
ENCH_FRIEND_ABJ_I, // no longer used
ENCH_FRIEND_ABJ_II, // no longer used
ENCH_FRIEND_ABJ_III, // no longer used
ENCH_FRIEND_ABJ_IV, // no longer used
ENCH_FRIEND_ABJ_V, // no longer used
ENCH_FRIEND_ABJ_VI, // no longer used
ENCH_CREATED_FRIENDLY, // no longer used
ENCH_HASTE,
ENCH_FEAR,
ENCH_CONFUSION,
ENCH_INVIS, // 5
ENCH_POISON,
ENCH_BERSERK,
ENCH_ROT,
ENCH_SUMMON,
ENCH_ABJ, // 10
ENCH_BACKLIGHT,
ENCH_CHARM,
ENCH_STICKY_FLAME,
ENCH_GLOWING_SHAPESHIFTER,
ENCH_SHAPESHIFTER, // 15
ENCH_TP,
static void describe_mons_enchantment(const monsters &mons,
const mon_enchant &ench,
bool paralysed)
{
// Suppress silly-looking combinations, even if they're
// internally valid.
if (paralysed && (ench.ench == ENCH_SLOW || ench.ench == ENCH_HASTE))
return;
strcpy(info, mons_pronoun(mons.type, PRONOUN_CAP));
switch (ench.ench)
{
case ENCH_POISON:
strcat(info, " is poisoned.");
break;
case ENCH_ROT:
strcat(info, " is rotting away."); //jmf: "covered in sores"?
break;
case ENCH_BACKLIGHT:
strcat(info, " is softly glowing.");
break;
case ENCH_SLOW:
strcat(info, " is moving slowly.");
break;
case ENCH_HASTE:
strcat(info, " is moving very quickly.");
break;
case ENCH_CONFUSION:
strcat(info, " appears to be bewildered and confused.");
break;
case ENCH_INVIS:
strcat(info, " is slightly transparent.");
break;
case ENCH_CHARM:
strcat(info, " is in your thrall.");
break;
case ENCH_STICKY_FLAME:
strcat(info, " is covered in liquid flames.");
break;
default:
info[0] = 0;
break;
} // end switch
if (info[0])
mpr(info);
}
const unsigned ench = menv[i].enchantment[p];
// Suppress silly-looking combinations, even if they're
// internally valid.
if (paralysed && (ench == ENCH_SLOW || ench == ENCH_HASTE))
continue;
strcpy(info, mons_pronoun(menv[i].type, PRONOUN_CAP));
switch (ench)
{
case ENCH_YOUR_ROT_I:
case ENCH_YOUR_ROT_II:
case ENCH_YOUR_ROT_III:
case ENCH_YOUR_ROT_IV:
strcat(info, " is rotting away."); //jmf: "covered in sores"?
break;
case ENCH_BACKLIGHT_I:
case ENCH_BACKLIGHT_II:
case ENCH_BACKLIGHT_III:
case ENCH_BACKLIGHT_IV:
strcat(info, " is softly glowing.");
break;
case ENCH_SLOW:
strcat(info, " is moving slowly.");
break;
case ENCH_HASTE:
strcat(info, " is moving very quickly.");
break;
case ENCH_CONFUSION:
strcat(info, " appears to be bewildered and confused.");
break;
case ENCH_INVIS:
strcat(info, " is slightly transparent.");
break;
case ENCH_CHARM:
strcat(info, " is in your thrall.");
break;
case ENCH_YOUR_STICKY_FLAME_I:
case ENCH_YOUR_STICKY_FLAME_II:
case ENCH_YOUR_STICKY_FLAME_III:
case ENCH_YOUR_STICKY_FLAME_IV:
case ENCH_STICKY_FLAME_I:
case ENCH_STICKY_FLAME_II:
case ENCH_STICKY_FLAME_III:
case ENCH_STICKY_FLAME_IV:
strcat(info, " is covered in liquid flames.");
break;
default:
info[0] = 0;
break;
} // end switch
if (info[0])
mpr(info);
describe_mons_enchantment(menv[i], *e, paralysed);
static const char *enchant_names[] =
{
"None",
"Slow", "Haste", "*BUG-3*", "Fear", "Conf", "Invis",
"YPois-1", "YPois-2", "YPois-3", "YPois-4",
"YShug-1", "YShug-2", "YShug-3", "YShug-4",
"YRot-1", "YRot-2", "YRot-3", "YRot-4",
"Summon", "Abj-1", "Abj-2", "Abj-3", "Abj-4", "Abj-5", "Abj-6",
"Corona-1", "Corona-2", "Corona-3", "Corona-4",
"Charm", "YSticky-1", "YSticky-2", "YSticky-3", "YSticky-4",
"*BUG-35*", "*BUG-36*", "*BUG-37*",
"GlowShapeshifter", "Shapeshifter",
"Tele-1", "Tele-2", "Tele-3", "Tele-4",
"*BUG-44*", "*BUG-45*", "*BUG-46*", "*BUG-47*", "*BUG-48*", "*BUG-49*",
"*BUG-50*", "*BUG-51*", "*BUG-52*", "*BUG-53*", "*BUG-54*", "*BUG-55*",
"*BUG-56*",
"Pois-1", "Pois-2", "Pois-3", "Pois-4",
"Sticky-1", "Sticky-2", "Sticky-3", "Sticky-4",
"OldAbj-1", "OldAbj-2", "OldAbj-3", "OldAbj-4", "OldAbj-5", "OldAbj-6",
"OldCreatedFriendly", "SleepWary", "Submerged", "Short Lived",
"*BUG-too big*"
};
// print enchantments
strncpy( info, "ench: ", INFO_SIZE );
for (j = 0; j < 6; j++)
{
if (menv[i].enchantment[j] >= NUM_ENCHANTMENTS)
strncat( info, enchant_names[ NUM_ENCHANTMENTS ], INFO_SIZE );
else
strncat( info, enchant_names[ menv[i].enchantment[j] ], INFO_SIZE );
if (strlen( info ) <= 70)
strncat( info, " ", INFO_SIZE );
else if (j < 5)
{
mpr( info, MSGCH_DIAGNOSTICS );
strncpy( info, "ench: ", INFO_SIZE );
}
}
}
static kill_category whose_kill(const bolt &beam)
{
if (YOU_KILL(beam.thrower))
return (KC_YOU);
else if (MON_KILL(beam.thrower))
{
if (beam.beam_source >= 0 && beam.beam_source < MAX_MONSTERS)
{
const monsters &mons = menv[beam.beam_source];
if (mons.attitude == ATT_FRIENDLY)
return (KC_FRIENDLY);
}
}
return (KC_OTHER);
// who gets the credit if monster dies of poison?
ench = mons_has_ench( monster, ENCH_POISON_I, ENCH_POISON_IV );
if (ench != ENCH_NONE)
{
old_strength = ench - ENCH_POISON_I;
}
else
{
ench = mons_has_ench(monster, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV);
if (ench != ENCH_NONE)
{
old_strength = ench - ENCH_YOUR_POISON_I;
yourPoison = true;
}
}
// delete old poison
mons_del_ench( monster, ENCH_POISON_I, ENCH_POISON_IV, true );
mons_del_ench( monster, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV, true );
const mon_enchant old_pois = monster->get_ench(ENCH_POISON);
monster->add_ench( mon_enchant(ENCH_POISON, levels, from_whom) );
const mon_enchant new_pois = monster->get_ench(ENCH_POISON);
// Calculate new strength:
int new_strength = old_strength + levels;
if (new_strength > 3)
new_strength = 3;
// now, if player poisons the monster at ANY TIME, they should
// get credit for the kill if the monster dies from poison. This
// really isn't that abusable -- GDL.
if (fromPlayer || yourPoison)
ench = ENCH_YOUR_POISON_I + new_strength;
else
ench = ENCH_POISON_I + new_strength;
// who gets the credit if monster dies of napalm?
currentFlame = mons_has_ench( monster, ENCH_STICKY_FLAME_I,
ENCH_STICKY_FLAME_IV );
if (currentFlame != ENCH_NONE)
{
currentStrength = currentFlame - ENCH_STICKY_FLAME_I;
yourFlame = false;
}
else
{
currentFlame = mons_has_ench( monster, ENCH_YOUR_STICKY_FLAME_I,
ENCH_YOUR_STICKY_FLAME_IV );
if (currentFlame != ENCH_NONE)
{
currentStrength = currentFlame - ENCH_YOUR_STICKY_FLAME_I;
yourFlame = true;
}
else
currentStrength = -1; // no flame yet!
}
// delete old flame
mons_del_ench( monster, ENCH_STICKY_FLAME_I, ENCH_STICKY_FLAME_IV, true );
mons_del_ench( monster, ENCH_YOUR_STICKY_FLAME_I, ENCH_YOUR_STICKY_FLAME_IV,
true );
// increase sticky flame strength, cap at 3 (level is 0..3)
currentStrength += levels;
if (currentStrength > 3)
currentStrength = 3;
// now, if player flames the monster at ANY TIME, they should
// get credit for the kill if the monster dies from napalm. This
// really isn't that abusable -- GDL.
if (fromPlayer || yourFlame)
currentStrength += ENCH_YOUR_STICKY_FLAME_I;
else
currentStrength += ENCH_STICKY_FLAME_I;
// actually do flame
if (mons_add_ench( monster, currentStrength ))
if (monster->add_ench(mon_enchant(ENCH_STICKY_FLAME, levels, who)))
{
menv[i].type = -1;
menv[i].speed_increment = 10;
menv[i].flags = 0;
menv[i].behaviour = BEH_SLEEP;
menv[i].foe = NON_MONSTER;
menv[i].attitude = ATT_HOSTILE;
for (j = 0; j < NUM_MON_ENCHANTS; j++)
menv[i].enchantment[j] = ENCH_NONE;
menv[i].reset();