diff options
Diffstat (limited to 'crawl-ref/source/mstuff2.cc')
-rw-r--r-- | crawl-ref/source/mstuff2.cc | 1665 |
1 files changed, 1665 insertions, 0 deletions
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc new file mode 100644 index 0000000000..38d0d8c885 --- /dev/null +++ b/crawl-ref/source/mstuff2.cc @@ -0,0 +1,1665 @@ +/* + * File: mstuff2.cc + * Summary: Misc monster related functions. + * Written by: Linley Henzell + * + * Change History (most recent first): + * + * <5> 31 July 2000 JDJ Fixed mon_throw to use lnchType. + * <4> 29 July 2000 JDJ Tweaked mons_throw so it doesn't index past + * the end of the array when monsters don't have + * a weapon equipped. + * <3> 25 July 2000 GDL Fixed Manticores + * <2> 28 July 2000 GDL Revised monster throwing + * <1> -/--/-- LRH Created + */ + +#include "AppHdr.h" +#include "mstuff2.h" + +#include <string> +#include <string.h> +#include <stdio.h> + +#include "externs.h" + +#include "beam.h" +#include "debug.h" +#include "effects.h" +#include "itemname.h" +#include "items.h" +#include "misc.h" +#include "monplace.h" +#include "monstuff.h" +#include "mon-util.h" +#include "player.h" +#include "spells2.h" +#include "spells4.h" +#include "spl-cast.h" +#include "stuff.h" +#include "view.h" +#include "wpn-misc.h" + +static unsigned char monster_abjuration(int pow, bool test); + +// XXX: must fix species abils to not use duration 15 +// -- ummm ... who wrote this? {dlb} + +// NB: only works because grid location already verified +// to be some sort of trap prior to function call: {dlb} +void mons_trap(struct monsters *monster) +{ + int temp_rand = 0; // probability determination {dlb} + + // single calculation permissible {dlb} + bool monsterNearby = mons_near(monster); + + // new function call {dlb} + int which_trap = trap_at_xy(monster->x, monster->y); + if (which_trap == -1) + return; + + bool trapKnown = (grd[monster->x][monster->y] != DNGN_UNDISCOVERED_TRAP); + bool revealTrap = false; // more sophisticated trap uncovering {dlb} + bool projectileFired = false; // <sigh> I had to do it, I swear {dlb} + int damage_taken = -1; // must initialize at -1 for this f(x) {dlb} + + struct bolt beem; + + + // flying monsters neatly avoid mechanical traps + // and may actually exit this function early: {dlb} + if (mons_flies(monster)) + { + if (trap_category(env.trap[which_trap].type) == DNGN_TRAP_MECHANICAL) + { + if (trapKnown) + simple_monster_message(monster, " flies safely over a trap."); + + return; // early return {dlb} + } + } + + // + // Trap damage to monsters is not a function of level, beacuse they + // are fairly stupid and tend to have fewer hp than players -- this + // choice prevents traps from easily killing large monsters fairly + // deep within the dungeon. + // + switch (env.trap[which_trap].type) + { + case TRAP_DART: + projectileFired = true; + strcpy(beem.beam_name, " dart"); + beem.damage = dice_def( 1, 4 ); + beem.colour = OBJ_MISSILES; + beem.type = MI_DART; + break; + case TRAP_NEEDLE: + projectileFired = true; + strcpy(beem.beam_name, " needle"); + beem.damage = dice_def( 1, 0 ); + beem.colour = OBJ_MISSILES; + beem.type = MI_NEEDLE; + break; + case TRAP_ARROW: + projectileFired = true; + strcpy(beem.beam_name, "n arrow"); + beem.damage = dice_def( 1, 7 ); + beem.colour = OBJ_MISSILES; + beem.type = MI_ARROW; + break; + case TRAP_SPEAR: + projectileFired = true; + strcpy(beem.beam_name, " spear"); + beem.damage = dice_def( 1, 10 ); + beem.colour = OBJ_WEAPONS; + beem.type = WPN_SPEAR; + break; + case TRAP_BOLT: + projectileFired = true; + strcpy(beem.beam_name, " bolt"); + beem.damage = dice_def( 1, 13 ); + beem.colour = OBJ_MISSILES; + beem.type = MI_BOLT; + break; + case TRAP_AXE: + projectileFired = true; + strcpy(beem.beam_name, "n axe"); + beem.damage = dice_def( 1, 15 ); + beem.colour = OBJ_WEAPONS; + beem.type = WPN_HAND_AXE; + break; + // teleport traps are *never* revealed through + // the triggering action of a monster, as any + // number of factors could have been in play: {dlb} + case TRAP_TELEPORT: + monster_teleport(monster, true); + break; + // amnesia traps do not affect monsters (yet) and + // only monsters of normal+ IQ will direct a msg + // to the player - also, *never* revealed: {dlb} + case TRAP_AMNESIA: + if (mons_intel(monster->type) > I_ANIMAL) + simple_monster_message(monster, + " seems momentarily disoriented."); + break; + // blade traps sometimes fail to trigger altogether, + // resulting in an "early return" from this f(x) for + // some - otherwise, blade *always* revealed: {dlb} + case TRAP_BLADE: + if (one_chance_in(5)) + { + if (trapKnown) + { + simple_monster_message(monster, + " fails to trigger a blade trap."); + } + return; // early return {dlb} + } + else if (random2(monster->evasion) > 8) + { + if (monsterNearby && !simple_monster_message(monster, + " avoids a huge, swinging blade.")) + { + mpr("A huge blade swings out!"); + } + + damage_taken = -1; // just to be certain {dlb} + } + else + { + if (monsterNearby) + { + strcpy(info, "A huge blade swings out"); + + if (player_monster_visible( monster )) + { + strcat(info, " and slices into "); + strcat(info, ptr_monam( monster, DESC_NOCAP_THE )); + } + + strcat(info, "!"); + mpr(info); + } + + damage_taken = 10 + random2avg(29, 2); + damage_taken -= random2(1 + monster->armour_class); + + if (damage_taken < 0) + damage_taken = 0; + } + + revealTrap = true; + break; + + // zot traps are out to get *the player*! Hostile monsters + // benefit and friendly monsters suffer - such is life - on + // rare occasion, the trap affects nearby players, triggering + // an "early return" - zot traps are *never* revealed - instead, + // enchantment messages serve as clues to the trap's presence: {dlb} + case TRAP_ZOT: + if (monsterNearby) + { + if (one_chance_in(5)) + { + mpr("The power of Zot is invoked against you!"); + miscast_effect( SPTYP_RANDOM, 10 + random2(30), + 75 + random2(100), 0, "the power of Zot" ); + return; // early return {dlb} + } + } + + // output triggering message to player, where appropriate: {dlb} + if (!silenced(monster->x, monster->y) + && !silenced(you.x_pos, you.y_pos)) + { + if (monsterNearby) + strcpy(info, "You hear a loud \"Zot\"!"); + else + strcpy(info, "You hear a distant \"Zot\"!"); + mpr(info); + } + + // determine trap effects upon monster, based upon + // whether it is naughty or nice to the player: {dlb} + + // NB: beem[].colour values are mislabeled as colours (?) - + // cf. mons_ench_f2() [which are also mislabeled] {dlb} + temp_rand = random2(16); + + beem.thrower = KILL_MON; // probably unnecessary + beem.aux_source = NULL; + + if (mons_friendly(monster)) + { + beem.colour = ((temp_rand < 3) ? CYAN : //paralyze - 3 in 16 + (temp_rand < 7) ? RED // confuse - 4 in 16 + : BLACK); // slow - 9 in 16 + } + else + { + beem.colour = ((temp_rand < 3) ? BLUE : //haste - 3 in 16 {dlb} + (temp_rand < 7) ? MAGENTA //invis - 4 in 16 {dlb} + : GREEN); // heal - 9 in 16 {dlb} + } + + mons_ench_f2(monster, beem); + damage_taken = 0; // just to be certain {dlb} + break; + } + + + // go back and handle projectile traps: {dlb} + bool apply_poison = false; + + if (projectileFired) + { + // projectile traps *always* revealed after "firing": {dlb} + revealTrap = true; + + // determine whether projectile hits, calculate damage: {dlb} + if (((20 + (you.your_level * 2)) * random2(200)) / 100 + >= monster->evasion) + { + damage_taken = roll_dice( beem.damage ); + damage_taken -= random2(1 + monster->armour_class); + + if (damage_taken < 0) + damage_taken = 0; + + if (beem.colour == OBJ_MISSILES + && beem.type == MI_NEEDLE + && random2(100) < 50 - (3*monster->armour_class/2)) + { + apply_poison = true; + } + } + else + { + damage_taken = -1; // negative damage marks a miss + } + + if (monsterNearby) + { + snprintf( info, INFO_SIZE, "A%s %s %s%s!", + beem.beam_name, + (damage_taken >= 0) ? "hits" : "misses", + ptr_monam( monster, DESC_NOCAP_THE ), + (damage_taken == 0) ? ", but does no damage" : "" ); + + mpr(info); + } + + if (apply_poison) + poison_monster( monster, false ); + + // generate "fallen" projectile, where appropriate: {dlb} + if (random2(10) < 7) + { + beem.target_x = monster->x; + beem.target_y = monster->y; + itrap(beem, which_trap); + } + } + + + // reveal undiscovered traps, where appropriate: {dlb} + if (monsterNearby && !trapKnown && revealTrap) + { + grd[monster->x][monster->y] = trap_category(env.trap[which_trap].type); + } + + // apply damage and handle death, where appropriate: {dlb} + if (damage_taken > 0) + { + hurt_monster(monster, damage_taken); + + if (monster->hit_points < 1) + { + monster_die(monster, KILL_MISC, 0); + monster->speed_increment = 1; + } + } + + return; +} // end mons_trap() + +void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) +{ + // always do setup. It might be done already, but it doesn't + // hurt to do it again (cheap). + setup_mons_cast(monster, pbolt, spell_cast); + + // single calculation permissible {dlb} + bool monsterNearby = mons_near(monster); + + int sumcount = 0; + int sumcount2; + int summonik = 0; + int duration = 0; + +#if DEBUG_DIAGNOSTICS + snprintf( info, INFO_SIZE, "Mon #%d casts %s (#%d)", monster_index(monster), + mons_spell_name( spell_cast ), spell_cast ); + + mpr( info, MSGCH_DIAGNOSTICS ); +#endif + + if (spell_cast == MS_HELLFIRE_BURST || spell_cast == MS_BRAIN_FEED + || spell_cast == MS_SMITE || spell_cast == MS_MUTATION) + { // etc. + if (monster->foe == MHITYOU || monster->foe == MHITNOT) + { + if (monsterNearby) + direct_effect( pbolt ); + return; + } + + mons_direct_effect( pbolt, monster_index(monster) ); + return; + } + + switch (spell_cast) + { + case MS_VAMPIRE_SUMMON: + sumcount2 = 3 + random2(3) + monster->hit_dice / 5; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + int mons = MONS_GIANT_BAT; + + if (!one_chance_in(3)) + { + switch (random2(4)) + { + case 0: + mons = MONS_ORANGE_RAT; + break; + + case 1: + mons = MONS_GREEN_RAT; + break; + + case 2: + mons = MONS_GREY_RAT; + break; + + case 3: + default: + mons = MONS_RAT; + break; + } + } + + create_monster( mons, ENCH_ABJ_V, SAME_ATTITUDE(monster), + monster->x, monster->y, monster->foe, 250 ); + } + return; + + case MS_LEVEL_SUMMON: // summon anything appropriate for level + if (!mons_friendly(monster) && monsterNearby + && monster_abjuration(1, true) > 0 && coinflip()) + { + monster_abjuration( monster->hit_dice * 10, false ); + return; + } + + sumcount2 = 1 + random2(4) + random2( monster->hit_dice / 7 + 1 ); + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( RANDOM_MONSTER, ENCH_ABJ_V, SAME_ATTITUDE(monster), + monster->x, monster->y, monster->foe, 250 ); + } + return; + + case MS_FAKE_RAKSHASA_SUMMON: + sumcount2 = (coinflip() ? 2 : 3); + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( MONS_RAKSHASA_FAKE, ENCH_ABJ_III, + SAME_ATTITUDE(monster), monster->x, monster->y, + monster->foe, 250 ); + } + return; + + case MS_SUMMON_DEMON: // class 2-4 demons + if (!mons_friendly(monster) && monsterNearby + && monster_abjuration(1, true) > 0 && coinflip()) + { + monster_abjuration(monster->hit_dice * 10, false); + return; + } + + sumcount2 = 1 + random2(2) + random2( monster->hit_dice / 10 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 10; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( summon_any_demon(DEMON_COMMON), duration, + SAME_ATTITUDE(monster), monster->x, monster->y, + monster->foe, 250 ); + } + return; + + case MS_ANIMATE_DEAD: + // see special handling in monstuff::handle_spell {dlb} + animate_dead( 5 + random2(5), SAME_ATTITUDE(monster), monster->foe, 1 ); + return; + + case MS_SUMMON_DEMON_LESSER: // class 5 demons + sumcount2 = 1 + random2(3) + random2( monster->hit_dice / 5 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 5; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( summon_any_demon(DEMON_LESSER), duration, + SAME_ATTITUDE(monster), monster->x, monster->y, + monster->foe, 250 ); + } + return; + + case MS_SUMMON_UFETUBUS: + sumcount2 = 2 + random2(2) + random2( monster->hit_dice / 5 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 5; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( MONS_UFETUBUS, duration, SAME_ATTITUDE(monster), + monster->x, monster->y, monster->foe, 250 ); + } + return; + + case MS_SUMMON_BEAST: // Geryon + create_monster( MONS_BEAST, ENCH_ABJ_IV, SAME_ATTITUDE(monster), + monster->x, monster->y, monster->foe, 250 ); + return; + + case MS_SUMMON_UNDEAD: // summon undead around player + if (!mons_friendly(monster) && monsterNearby + && monster_abjuration(1, true) > 0 && coinflip()) + { + monster_abjuration( monster->hit_dice * 10, false ); + return; + } + + sumcount2 = 2 + random2(2) + random2( monster->hit_dice / 4 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 5; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + do + { + summonik = random2(241); // hmmmm ... {dlb} + } + while (mons_holiness(summonik) != MH_UNDEAD); + + create_monster(summonik, duration, SAME_ATTITUDE(monster), + you.x_pos, you.y_pos, monster->foe, 250); + } + return; + + case MS_TORMENT: + if (!monsterNearby || mons_friendly(monster)) + return; + + simple_monster_message(monster, " calls on the powers of Hell!"); + + torment(monster->x, monster->y); + return; + + case MS_SUMMON_DEMON_GREATER: + if (!mons_friendly(monster) && !monsterNearby && + monster_abjuration(1, true) > 0 && coinflip()) + { + monster_abjuration(monster->hit_dice * 10, false); + return; + } + + sumcount2 = 1 + random2( monster->hit_dice / 10 + 1 ); + + duration = ENCH_ABJ_II + monster->hit_dice / 10; + if (duration > ENCH_ABJ_VI) + duration = ENCH_ABJ_VI; + + for (sumcount = 0; sumcount < sumcount2; sumcount++) + { + create_monster( summon_any_demon(DEMON_GREATER), duration, + SAME_ATTITUDE(monster), monster->x, monster->y, + monster->foe, 250 ); + } + return; + + case MS_CANTRIP: + // Monster spell of uselessness, just prints a message. + // This spell exists so that some monsters with really strong + // spells (ie orc priest) can be toned down a bit. -- bwr + // + // XXX: Needs expansion, and perhaps different priest/mage flavours. + switch (random2(7)) + { + case 0: + simple_monster_message( monster, " glows brightly for a moment.", + MSGCH_MONSTER_ENCHANT ); + break; + case 1: + mpr( "You feel troubled." ); + break; + case 2: + mpr( "You feel a wave of unholy energy pass over you." ); + break; + case 3: + simple_monster_message( monster, " looks stronger.", + MSGCH_MONSTER_ENCHANT ); + break; + case 4: + simple_monster_message( monster, " becomes somewhat translucent.", + MSGCH_MONSTER_ENCHANT ); + break; + case 5: + simple_monster_message( monster, "'s eyes start to glow.", + MSGCH_MONSTER_ENCHANT ); + break; + case 6: + default: + if (one_chance_in(20)) + mpr( "You resist (whatever that was supposed to do)." ); + else + mpr( "You resist." ); + break; + } + return; + } + + fire_beam( pbolt ); +} // end mons_cast() + + +/* + * setup bolt structure for monster spell casting. + * + */ + +void setup_mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast) +{ + // always set these -- used by things other than fire_beam() + pbolt.ench_power = 12 * monster->hit_dice; + + if (spell_cast == MS_TELEPORT) + pbolt.ench_power = 2000; + else if (spell_cast == MS_PAIN) // this is cast by low HD monsters + pbolt.ench_power *= 2; + + pbolt.beam_source = monster_index(monster); + + // set bolt type + if (spell_cast == MS_HELLFIRE_BURST + || spell_cast == MS_BRAIN_FEED + || spell_cast == MS_SMITE || spell_cast == MS_MUTATION) + { // etc. + switch (spell_cast) + { + case MS_HELLFIRE_BURST: + pbolt.type = DMNBM_HELLFIRE; + break; + case MS_BRAIN_FEED: + pbolt.type = DMNBM_BRAIN_FEED; + break; + case MS_SMITE: + pbolt.type = DMNBM_SMITING; + break; + case MS_MUTATION: + pbolt.type = DMNBM_MUTATION; + break; + } + return; + } + + // the below are no-ops since they don't involve direct_effect, + // fire_tracer, or beam. + switch (spell_cast) + { + case MS_VAMPIRE_SUMMON: + case MS_LEVEL_SUMMON: // summon anything appropriate for level + case MS_FAKE_RAKSHASA_SUMMON: + case MS_SUMMON_DEMON: + case MS_ANIMATE_DEAD: + case MS_SUMMON_DEMON_LESSER: + case MS_SUMMON_UFETUBUS: + case MS_SUMMON_BEAST: // Geryon + case MS_SUMMON_UNDEAD: // summon undead around player + case MS_TORMENT: + case MS_SUMMON_DEMON_GREATER: + case MS_CANTRIP: + return; + default: + break; + } + + // Need to correct this for power of spellcaster + int power = 12 * monster->hit_dice; + + struct SBeam theBeam = mons_spells(spell_cast, power); + + pbolt.colour = theBeam.colour; + pbolt.range = theBeam.range; + pbolt.rangeMax = theBeam.rangeMax; + pbolt.hit = theBeam.hit; + pbolt.damage = theBeam.damage; + pbolt.ench_power = theBeam.ench_power; + pbolt.type = theBeam.type; + pbolt.flavour = theBeam.flavour; + pbolt.thrower = theBeam.thrown; + pbolt.aux_source = NULL; + strcpy( pbolt.beam_name, theBeam.name.c_str() ); + pbolt.isBeam = theBeam.isBeam; + pbolt.source_x = monster->x; + pbolt.source_y = monster->y; + pbolt.isTracer = false; + + if (pbolt.beam_name[0] && pbolt.beam_name[0] != '0') + pbolt.aux_source = pbolt.beam_name; + else + pbolt.aux_source = NULL; + + if (spell_cast == MS_HASTE + || spell_cast == MS_INVIS + || spell_cast == MS_HEAL || spell_cast == MS_TELEPORT) + { + pbolt.target_x = monster->x; + pbolt.target_y = monster->y; + } +} // end setup_mons_cast() + + +void monster_teleport(struct monsters *monster, bool instan) +{ + if (!instan) + { + if (mons_del_ench(monster, ENCH_TP_I, ENCH_TP_IV)) + { + simple_monster_message(monster, " seems more stable."); + } + else + mons_add_ench(monster, (coinflip() ? ENCH_TP_III : ENCH_TP_IV )); + + return; + } + + simple_monster_message(monster, " disappears!"); + + // pick the monster up + mgrd[monster->x][monster->y] = NON_MONSTER; + + char ogrid = monster_habitat(monster->type); + + int newx, newy; + while(true) + { + newx = 10 + random2(GXM - 20); + newy = 10 + random2(GYM - 20); + + // don't land on top of another monster + if (mgrd[newx][newy] != NON_MONSTER) + continue; + + // monsters going to the same habitat + if (ogrid == grd[newx][newy]) + break; + + // DEEP_WATER monsters can be teleported to SHALLOW_WATER + if (ogrid == DNGN_DEEP_WATER && grd[newx][newy] == DNGN_SHALLOW_WATER) + break; + } + + monster->x = newx; + monster->y = newy; + + mgrd[monster->x][monster->y] = monster_index(monster); + + /* Mimics change form/colour when t'ported */ + if (mons_is_mimic( monster->type )) + { + monster->type = MONS_GOLD_MIMIC + random2(5); + monster->number = get_mimic_colour( monster ); + } +} // end monster_teleport() + +void setup_dragon(struct monsters *monster, struct bolt &pbolt) +{ + strcpy(pbolt.beam_name, ptr_monam( monster, DESC_PLAIN )); + + switch (monster->type) + { + case MONS_FIREDRAKE: + case MONS_HELL_HOUND: + case MONS_DRAGON: + case MONS_LINDWURM: + case MONS_XTAHUA: + strcat(pbolt.beam_name, "'s blast of flame"); + pbolt.flavour = BEAM_FIRE; + pbolt.colour = RED; + pbolt.aux_source = "blast of flame"; + break; + + case MONS_ICE_DRAGON: + strcat(pbolt.beam_name, "'s blast of cold"); + pbolt.flavour = BEAM_COLD; + pbolt.colour = WHITE; + pbolt.aux_source = "blast of cold"; + break; + + default: + DEBUGSTR("Bad monster class in setup_dragon()"); + } + + pbolt.range = 4; + pbolt.rangeMax = 13; + pbolt.damage = dice_def( 3, (monster->hit_dice * 2) ); + pbolt.type = SYM_ZAP; + pbolt.hit = 30; + pbolt.beam_source = monster_index(monster); + pbolt.thrower = KILL_MON; + pbolt.isBeam = true; +} // end setup_dragon(); + +void setup_generic_throw(struct monsters *monster, struct bolt &pbolt) +{ + pbolt.range = 9; + pbolt.rangeMax = 9; + pbolt.beam_source = monster_index(monster); + + pbolt.type = SYM_MISSILE; + pbolt.flavour = BEAM_MISSILE; + pbolt.thrower = KILL_MON_MISSILE; + pbolt.aux_source = NULL; + pbolt.isBeam = false; +} + +// decide if something is launched or thrown +// pass -1 for launcher class & 0 for type if no weapon is weilded + +void throw_type( int lnchClass, int lnchType, int wepClass, int wepType, + bool &launched, bool &thrown ) +{ + if (wepClass == OBJ_MISSILES + && lnchClass == OBJ_WEAPONS + && launches_things(lnchType) && wepType == launched_by(lnchType)) + { + launched = true; + } + + if (wepClass == OBJ_WEAPONS) + { + if (wepType == WPN_DAGGER || wepType == WPN_HAND_AXE || wepType == WPN_SPEAR) + { + thrown = true; + } + } + + if (wepClass == OBJ_MISSILES) + { + if (wepType == MI_DART || wepType == MI_STONE || wepType == MI_LARGE_ROCK) + { + thrown = true; + } + } + + // launched overrides thrown + if (launched == true) + thrown = false; +} + +bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used) +{ + // this was assumed during cleanup down below: + ASSERT( hand_used == monster->inv[MSLOT_MISSILE] ); + + // XXX: ugly hack, but avoids adding dynamic allocation to this code + static char throw_buff[ ITEMNAME_SIZE ]; + + int baseHit = 0, baseDam = 0; // from thrown or ammo + int ammoHitBonus = 0, ammoDamBonus = 0; // from thrown or ammo + int lnchHitBonus = 0, lnchDamBonus = 0; // special add from launcher + int exHitBonus = 0, exDamBonus = 0; // 'extra' bonus from skill/dex/str + + int hitMult = 0; + int damMult = 0; + + bool launched = false; // item is launched + bool thrown = false; // item is sensible thrown item + + // some initial convenience & initializations + int wepClass = mitm[hand_used].base_type; + int wepType = mitm[hand_used].sub_type; + + int weapon = monster->inv[MSLOT_WEAPON]; + + int lnchClass = (weapon != NON_ITEM) ? mitm[weapon].base_type : -1; + int lnchType = (weapon != NON_ITEM) ? mitm[weapon].sub_type : 0; + + item_def item = mitm[hand_used]; // copy changed for venom launchers + item.quantity = 1; + + pbolt.range = 9; + pbolt.beam_source = monster_index(monster); + + pbolt.type = SYM_MISSILE; + pbolt.colour = item.colour; + pbolt.flavour = BEAM_MISSILE; + pbolt.thrower = KILL_MON_MISSILE; + pbolt.aux_source = NULL; + + // figure out if we're thrown or launched + throw_type( lnchClass, lnchType, wepClass, wepType, launched, thrown ); + + // extract launcher bonuses due to magic + if (launched) + { + lnchHitBonus = mitm[ weapon ].plus; + lnchDamBonus = mitm[ weapon ].plus2; + } + + // extract weapon/ammo bonuses due to magic + ammoHitBonus = item.plus; + ammoDamBonus = item.plus2; + + if (thrown) + { + // Darts are easy. + if (wepClass == OBJ_MISSILES && wepType == MI_DART) + { + baseHit = 5; + hitMult = 40; + damMult = 25; + } + else + { + baseHit = 0; + hitMult = 30; + damMult = 25; + } + + baseDam = property( item, PWPN_DAMAGE ); + + if (wepClass == OBJ_MISSILES) // throw missile + // ammo damage needs adjusting here - OBJ_MISSILES + // don't get separate tohit/damage bonuses! + ammoDamBonus = ammoHitBonus; + + // give monster "skill" bonuses based on HD + exHitBonus = (hitMult * monster->hit_dice) / 10 + 1; + exDamBonus = (damMult * monster->hit_dice) / 10 + 1; + } + + if (launched) + { + switch (lnchType) + { + case WPN_BLOWGUN: + baseHit = 2; + hitMult = 60; + damMult = 0; + lnchDamBonus = 0; + break; + case WPN_BOW: + baseHit = 0; + hitMult = 60; + damMult = 35; + // monsters get half the launcher damage bonus, + // which is about as fair as I can figure it. + lnchDamBonus = (lnchDamBonus + 1) / 2; + break; + case WPN_CROSSBOW: + baseHit = 4; + hitMult = 70; + damMult = 30; + break; + case WPN_HAND_CROSSBOW: + baseHit = 2; + hitMult = 50; + damMult = 20; + break; + case WPN_SLING: + baseHit = 1; + hitMult = 40; + damMult = 20; + // monsters get half the launcher damage bonus, + // which is about as fair as I can figure it. + lnchDamBonus /= 2; + break; + } + + baseDam = property( item, PWPN_HIT ); + + // missiles don't have pluses2; use hit bonus + ammoDamBonus = ammoHitBonus; + + exHitBonus = (hitMult * monster->hit_dice) / 10 + 1; + exDamBonus = (damMult * monster->hit_dice) / 10 + 1; + + // elven bow w/ elven arrow, also orcish + if (get_equip_race( mitm[monster->inv[MSLOT_WEAPON]] ) + == get_equip_race( mitm[monster->inv[MSLOT_MISSILE]] )) + { + baseHit++; + baseDam++; + + if (cmp_equip_race(mitm[monster->inv[MSLOT_WEAPON]], ISFLAG_ELVEN)) + pbolt.hit++; + } + + // monsters no longer gain unfair advantages with weapons of fire/ice + // and incorrect ammo. They now have same restriction as players. + + const int bow_brand = get_weapon_brand(mitm[monster->inv[MSLOT_WEAPON]]); + + const int ammo_brand = get_ammo_brand( item ); + + bool poison = (ammo_brand == SPMSL_POISONED + || ammo_brand == SPMSL_POISONED_II); + + // POISON brand launchers poison ammo + if (bow_brand == SPWPN_VENOM && ammo_brand == SPMSL_NORMAL) + set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED ); + + + // WEAPON or AMMO of FIRE + if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME) + && bow_brand != SPWPN_FROST && ammo_brand != SPMSL_ICE) + { + baseHit += 2; + exDamBonus += 6; + pbolt.flavour = BEAM_FIRE; + strcpy(pbolt.beam_name, "bolt of "); + + if (poison) + strcat(pbolt.beam_name, "poison "); + + strcat(pbolt.beam_name, "flame"); + pbolt.colour = RED; + pbolt.type = SYM_ZAP; + } + + // WEAPON or AMMO of FROST + if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE) + && bow_brand != SPWPN_FLAME && ammo_brand != SPMSL_FLAME) + { + baseHit += 2; + exDamBonus += 6; + pbolt.flavour = BEAM_COLD; + strcpy(pbolt.beam_name, "bolt of "); + + if (poison) + strcat(pbolt.beam_name, "poison "); + + strcat(pbolt.beam_name, "frost"); + pbolt.colour = WHITE; + pbolt.type = SYM_ZAP; + } + + // Note: we already have 10 energy taken off. -- bwr + if (lnchType == WPN_CROSSBOW) + monster->speed_increment += ((bow_brand == SPWPN_SPEED) ? 4 : -2); + else if (bow_brand == SPWPN_SPEED) + monster->speed_increment += 5; + } + + // monster intelligence bonus + if (mons_intel(monster->type) == I_HIGH) + exHitBonus += 10; + + // now, if a monster is, for some reason, throwing something really + // stupid, it will have baseHit of 0 and damage of 0. Ah well. + strcpy(info, ptr_monam( monster, DESC_CAP_THE) ); + + strcat(info, (launched) ? " shoots " : " throws "); + + if (strlen(pbolt.beam_name) > 0) + { + strcat(info, "a "); + strcat(info, pbolt.beam_name); + } + else + { + // build shoot message + char str_pass[ ITEMNAME_SIZE ]; + item_name( item, DESC_NOCAP_A, str_pass ); + strcat(info, str_pass); + + // build beam name + item_name( item, DESC_PLAIN, str_pass ); + strcpy(pbolt.beam_name, str_pass); + } + + strcat(info, "."); + mpr(info); + + + if (launched) + { + snprintf( throw_buff, sizeof(throw_buff), "Shot with a%s %s by %s", + (is_vowel(pbolt.beam_name[0]) ? "n" : ""), pbolt.beam_name, + ptr_monam( monster, DESC_NOCAP_A ) ); + } + else + { + snprintf( throw_buff, sizeof(throw_buff), "Hit by a%s %s thrown by %s", + (is_vowel(pbolt.beam_name[0]) ? "n" : ""), pbolt.beam_name, + ptr_monam( monster, DESC_NOCAP_A ) ); + } + + pbolt.aux_source = throw_buff; + + // add everything up. + pbolt.hit = baseHit + random2avg(exHitBonus, 2) + ammoHitBonus; + pbolt.damage = dice_def( 1, baseDam + random2avg(exDamBonus, 2) + ammoDamBonus ); + + if (launched) + { + pbolt.damage.size += lnchDamBonus; + pbolt.hit += lnchHitBonus; + } + + // decrease inventory + fire_beam( pbolt, &item ); + + if (dec_mitm_item_quantity( hand_used, 1 )) + monster->inv[MSLOT_MISSILE] = NON_ITEM; + + + return (true); +} // end mons_throw() + +// should really do something about mons_hit, but can't be bothered +void spore_goes_pop(struct monsters *monster) +{ + struct bolt beam; + int type = monster->type; + + if (monster == NULL) + return; + + beam.isTracer = false; + beam.beam_source = monster_index(monster); + beam.type = SYM_BURST; + beam.target_x = monster->x; + beam.target_y = monster->y; + beam.thrower = KILL_MON; // someone else's explosion + beam.aux_source = NULL; + + if (type == MONS_GIANT_SPORE) + { + beam.flavour = BEAM_SPORE; + strcpy(beam.beam_name, "explosion of spores"); + beam.colour = LIGHTGREY; + beam.damage = dice_def( 3, 15 ); + beam.ex_size = 2; + strcpy( info, "The giant spore explodes!" ); + } + else + { + beam.flavour = BEAM_ELECTRICITY; + strcpy(beam.beam_name, "blast of lightning"); + beam.colour = LIGHTCYAN; + beam.damage = dice_def( 3, 20 ); + beam.ex_size = coinflip() ? 3 : 2; + strcpy( info, "The ball lightning explodes!" ); + } + + if (mons_near(monster)) + { + viewwindow(1, false); + mpr( info ); + } + + explosion(beam); +} // end spore_goes_pop() + +struct SBeam mons_spells( int spell_cast, int power ) +{ + ASSERT(power > 0); + + struct SBeam beam; + + beam.name = "****"; // initialize to some bogus values so we can catch problems + beam.colour = 1000; + beam.range = -1; + beam.hit = -1; + beam.damage = dice_def( 1, 0 ); + beam.ench_power = -1; + beam.type = -1; + beam.flavour = -1; + beam.thrown = -1; + + switch (spell_cast) + { + case MS_MMISSILE: + beam.colour = LIGHTMAGENTA; //inv_colour [throw_2]; + beam.name = "magic dart"; // inv_name [throw_2]); + beam.range = 6; + beam.rangeMax = 10; + beam.damage = dice_def( 3, 4 + (power / 100) ); + beam.hit = 1500; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; + beam.isBeam = false; + break; + + case MS_FLAME: + beam.colour = RED; + beam.name = "puff of flame"; + beam.range = 6; + beam.rangeMax = 10; + + // should this be the same as magic missile? + // No... magic missile is special in that it has a really + // high to-hit value, so these should do more damage -- bwr + beam.damage = dice_def( 3, 5 + (power / 40) ); + + beam.hit = 60; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_FIRE; + beam.isBeam = false; + break; + + case MS_FROST: + beam.colour = WHITE; + beam.name = "puff of frost"; + beam.range = 6; + beam.rangeMax = 10; + + // should this be the same as magic missile? + // see MS_FLAME -- bwr + beam.damage = dice_def( 3, 5 + (power / 40) ); + + beam.hit = 60; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_COLD; + beam.isBeam = false; + break; + + case MS_PARALYSIS: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_PARALYSIS; + beam.thrown = KILL_MON_MISSILE; + beam.isBeam = true; + break; + + case MS_SLOW: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_SLOW; + beam.thrown = KILL_MON_MISSILE; + beam.isBeam = true; + break; + + case MS_HASTE: // (self) + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_HASTE; + beam.thrown = KILL_MON_MISSILE; + beam.isBeam = true; + break; + + case MS_CONFUSE: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_CONFUSION; + beam.thrown = KILL_MON_MISSILE; + beam.isBeam = true; + break; + + case MS_VENOM_BOLT: + beam.name = "bolt of poison"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 6 + power / 13 ); + beam.colour = LIGHTGREEN; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_POISON; + beam.hit = 7 + random2(power) / 80; + beam.isBeam = true; + break; + + case MS_FIRE_BOLT: + beam.name = "bolt of fire"; + beam.range = 4; + beam.rangeMax = 13; + beam.damage = dice_def( 3, 8 + power / 11 ); + beam.colour = RED; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_FIRE; + beam.hit = 8 + random2(power) / 80; // hit + beam.isBeam = true; + break; + + case MS_COLD_BOLT: + beam.name = "bolt of cold"; + beam.range = 4; + beam.rangeMax = 13; + beam.damage = dice_def( 3, 8 + power / 11 ); + beam.colour = WHITE; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_COLD; + beam.hit = 8 + random2(power) / 80; // hit + beam.isBeam = true; + break; + + case MS_LIGHTNING_BOLT: + beam.name = "bolt of lightning"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 10 + power / 9 ); + beam.colour = LIGHTCYAN; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_ELECTRICITY; + beam.hit = 10 + random2(power) / 40; + beam.isBeam = true; + break; + + case MS_INVIS: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_INVISIBILITY; + beam.thrown = KILL_MON; + beam.isBeam = true; + break; + + case MS_FIREBALL: + beam.colour = RED; + beam.name = "fireball"; + beam.range = 6; + beam.rangeMax = 10; + beam.damage = dice_def( 3, 7 + power / 10 ); + beam.hit = 40; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_EXPLOSION; // why not BEAM_FIRE? {dlb} + beam.isBeam = false; + break; + + case MS_HEAL: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_HEALING; + beam.thrown = KILL_MON; + beam.hit = 5 + (power / 5); + beam.isBeam = true; + break; + + case MS_TELEPORT: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_TELEPORT; // 6 is used by digging + beam.thrown = KILL_MON; + beam.isBeam = true; + break; + + case MS_TELEPORT_OTHER: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_TELEPORT; // 6 is used by digging + beam.thrown = KILL_MON; + beam.isBeam = true; + break; + + case MS_BLINK: + beam.isBeam = false; + break; + + case MS_CRYSTAL_SPEAR: // was splinters + beam.name = "crystal spear"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 12 + power / 10 ); + beam.colour = WHITE; + beam.type = SYM_MISSILE; + beam.thrown = KILL_MON; + beam.flavour = BEAM_MMISSILE; + beam.hit = 6; // + random2(power) / 10; + beam.isBeam = false; + break; + + case MS_DIG: + beam.name = "0"; + beam.range = 3; + beam.rangeMax = 7 + random2(power) / 10; + beam.type = 0; + beam.flavour = BEAM_DIGGING; + beam.thrown = KILL_MON; + beam.isBeam = true; + break; + + case MS_NEGATIVE_BOLT: // negative energy + beam.name = "bolt of negative energy"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 6 + power / 13 ); + beam.colour = DARKGREY; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_NEG; + beam.hit = 7 + random2(power) / 80; + beam.isBeam = true; + break; + + // 20, 21 are used + + case MS_ORB_ENERGY: // mystic blast + beam.colour = LIGHTMAGENTA; + beam.name = "orb of energy"; + beam.range = 6; + beam.rangeMax = 10; + beam.damage = dice_def( 3, 7 + (power / 14) ); + beam.hit = 10 + (power / 20); + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; + beam.isBeam = false; + break; + + // 23 is brain feed + + case MS_STEAM_BALL: + beam.colour = LIGHTGREY; + beam.name = "ball of steam"; + beam.range = 6; + beam.rangeMax = 10; + beam.damage = dice_def( 3, 6 ); + beam.hit = 11; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_FIRE; // fire - I think this is appropriate + beam.isBeam = false; + break; + + // 27 is summon devils + // 28 is animate dead + + case MS_PAIN: + beam.name = "0"; + beam.range = 7; + beam.rangeMax = 14; + beam.type = 0; + beam.flavour = BEAM_PAIN; // pain + beam.thrown = KILL_MON; + // beam.damage = dice_def( 1, 50 ); + beam.damage = dice_def( 1, 7 + (power / 20) ); + beam.ench_power = 50; + beam.isBeam = true; + break; + + // 30 is smiting + + case MS_STICKY_FLAME: + beam.colour = RED; + beam.name = "sticky flame"; + beam.range = 6; + beam.rangeMax = 10; + beam.damage = dice_def( 3, 3 + power / 50 ); + beam.hit = 8 + power / 15; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_FIRE; + beam.isBeam = false; + break; + + case MS_POISON_BLAST: // demon + beam.name = "blast of poison"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 3 + power / 25 ); + beam.colour = LIGHTGREEN; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_POISON; + beam.hit = 7 + random2(power) / 80; + beam.isBeam = true; + break; + + case MS_PURPLE_BLAST: // purple bang thing + beam.colour = LIGHTMAGENTA; + beam.name = "orb of energy"; + beam.range = 6; + beam.rangeMax = 10; + beam.damage = dice_def( 3, 10 + power / 15 ); + beam.hit = 10 + power / 20; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_EXPLOSION; // an exploding magical missile + beam.isBeam = false; + break; + + case MS_ENERGY_BOLT: // eye of devastation + beam.colour = YELLOW; + beam.name = "bolt of energy"; + beam.range = 9; + beam.rangeMax = 23; + beam.damage = dice_def( 3, 20 ); + beam.hit = 9; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_NUKE; // a magical missile which destroys walls + beam.isBeam = true; + break; + + case MS_STING: // sting + beam.colour = GREEN; + beam.name = "sting"; + beam.range = 8; + beam.rangeMax = 12; + beam.damage = dice_def( 1, 6 + power / 25 ); + beam.hit = 60; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_POISON; + beam.isBeam = false; + break; + + case MS_IRON_BOLT: + beam.colour = LIGHTCYAN; + beam.name = "iron bolt"; + beam.range = 4; + beam.rangeMax = 8; + beam.damage = dice_def( 3, 8 + (power / 9) ); + beam.hit = 6 + (power / 25); + beam.type = SYM_MISSILE; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; // similarly unresisted thing + beam.isBeam = false; + break; + + case MS_STONE_ARROW: + beam.colour = LIGHTGREY; + beam.name = "stone arrow"; + beam.range = 8; + beam.rangeMax = 12; + beam.damage = dice_def( 3, 5 + (power / 10) ); + beam.hit = 5 + power / 47; + beam.type = SYM_MISSILE; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; // similarly unresisted thing + beam.isBeam = false; + break; + + case MS_POISON_SPLASH: + beam.colour = GREEN; + beam.name = "splash of poison"; + beam.range = 5; + beam.rangeMax = 10; + beam.damage = dice_def( 1, 4 + power / 10 ); + beam.hit = 9; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_POISON; + beam.isBeam = false; + break; + + case MS_DISINTEGRATE: + beam.name = "0"; + beam.range = 7; + beam.rangeMax = 14; + beam.type = 0; + beam.flavour = BEAM_DISINTEGRATION; + beam.thrown = KILL_MON; + beam.ench_power = 50; + // beam.hit = 30 + (power / 10); + beam.damage = dice_def( 1, 30 + (power / 10) ); + beam.isBeam = true; + break; + + case MS_MARSH_GAS: // swamp drake + beam.name = "foul vapour"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 2 + power / 25 ); + beam.colour = GREEN; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_POISON; + beam.hit = 7 + random2(power) / 80; + beam.isBeam = false; + break; + + case MS_QUICKSILVER_BOLT: // Quicksilver dragon + beam.colour = random_colour(); + beam.name = "bolt of energy"; + beam.range = 9; + beam.rangeMax = 23; + beam.damage = dice_def( 3, 25 ); + beam.hit = 9; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; + beam.isBeam = false; + break; + + case MS_HELLFIRE: // fiend's hellfire + beam.name = "hellfire"; + beam.colour = RED; + beam.range = 4; + beam.rangeMax = 13; + beam.damage = dice_def( 3, 25 ); + beam.hit = 20; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_EXPLOSION; // hellfire - not BEAM_HELLFIRE? {dlb} + beam.isBeam = true; + break; + + case MS_METAL_SPLINTERS: + beam.name = "spray of metal splinters"; + beam.range = 7; + beam.rangeMax = 16; + beam.damage = dice_def( 3, 20 + power / 20 ); + beam.colour = CYAN; + beam.type = SYM_ZAP; + beam.thrown = KILL_MON; + beam.flavour = BEAM_FRAG; + beam.hit = 15 + random2(power) / 50; + beam.isBeam = true; + break; + + case MS_BANISHMENT: + beam.name = "0"; + beam.range = 5; + beam.rangeMax = 9; + beam.type = 0; + beam.flavour = BEAM_BANISH; + beam.thrown = KILL_MON_MISSILE; + beam.isBeam = true; + break; + + default: + DEBUGSTR("Unknown spell"); + } + + return (beam); +} // end mons_spells() + +static unsigned char monster_abjuration(int pow, bool test) +{ + + unsigned char result = 0; + struct monsters *monster = 0; // NULL {dlb} + + if (!test) + mpr("Send 'em back where they came from!"); + + for (int ab = 0; ab < MAX_MONSTERS; ab++) + { + int abjLevel; + + monster = &menv[ab]; + + if (monster->type == -1 || !mons_near(monster)) + continue; + + if (!mons_friendly(monster)) + continue; + + abjLevel = mons_has_ench(monster, ENCH_ABJ_I, ENCH_ABJ_VI); + if (abjLevel == ENCH_NONE) + continue; + + result++; + + if (test) + continue; + + if (pow > 60) + pow = 60; + + abjLevel -= 1 + (random2(pow) / 3); + + if (abjLevel < ENCH_ABJ_I) + monster_die(monster, KILL_RESET, 0); + else + { + simple_monster_message(monster, " shudders."); + mons_del_ench(monster, ENCH_ABJ_I, ENCH_ABJ_VI); + mons_add_ench(monster, abjLevel); + } + } + + return result; +} // end monster_abjuration() |