/*
 *  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)
#ifdef JP
                simple_monster_message(monster, "は罠の上を安全に飛んでいる。");
#else
                simple_monster_message(monster, " flies safely over a trap.");
#endif

            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;
#ifdef JP
        strcpy(beem.beam_name, "投げ矢");
#else
        strcpy(beem.beam_name, " dart");
#endif
        beem.damage = dice_def( 1, 4 );
        beem.colour = OBJ_MISSILES;
        beem.type = MI_DART;
        break;
    case TRAP_NEEDLE:
        projectileFired = true;
#ifdef JP
        strcpy(beem.beam_name, "吹き矢針");
#else
        strcpy(beem.beam_name, " needle");
#endif
        beem.damage = dice_def( 1, 0 );
        beem.colour = OBJ_MISSILES;
        beem.type = MI_NEEDLE;
        break;
    case TRAP_ARROW:
        projectileFired = true;
#ifdef JP
        strcpy(beem.beam_name, "矢");
#else
        strcpy(beem.beam_name, "n arrow");
#endif
        beem.damage = dice_def( 1, 7 );
        beem.colour = OBJ_MISSILES;
        beem.type = MI_ARROW;
        break;
    case TRAP_SPEAR:
        projectileFired = true;
#ifdef JP
        strcpy(beem.beam_name, "槍");
#else
        strcpy(beem.beam_name, " spear");
#endif
        beem.damage = dice_def( 1, 10 );
        beem.colour = OBJ_WEAPONS;
        beem.type = WPN_SPEAR;
        break;
    case TRAP_BOLT:
        projectileFired = true;
#ifdef JP
        strcpy(beem.beam_name, "クロスボウの矢");
#else
        strcpy(beem.beam_name, " bolt");
#endif
        beem.damage = dice_def( 1, 13 );
        beem.colour = OBJ_MISSILES;
        beem.type = MI_BOLT;
        break;
    case TRAP_AXE:
        projectileFired = true;
#ifdef JP
        strcpy(beem.beam_name, "斧");
#else
        strcpy(beem.beam_name, "n axe");
#endif
        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,
#ifdef JP
                                   "は一瞬困惑したようだ。");
#else
                                   " seems momentarily disoriented.");
#endif
        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,
#ifdef JP
                                        "は刃の罠を発動しそこねた。");
#else
                                        " fails to trigger a blade trap.");
#endif
            }
            return;             // early return {dlb}
        }
        else if (random2(monster->evasion) > 8)
        {
            if (monsterNearby && !simple_monster_message(monster,
#ifdef JP
                                           "は左右に揺れる巨大な刃を避けた。"))
#else
                                           " avoids a huge, swinging blade."))
#endif
            {
#ifdef JP
                mpr("巨大な刃が左右に揺れた！");
#else
                mpr("A huge blade swings out!");
#endif
            }

            damage_taken = -1;  // just to be certain {dlb}
        }
        else
        {
            if (monsterNearby)
            {
#ifdef JP
                strcpy(info, "巨大な刃が左右に揺れて");
#else
                strcpy(info, "A huge blade swings out");
#endif

                if (player_monster_visible( monster ))
                {
#ifdef JP

                    strcat(info, ptr_monam( monster, DESC_PLAIN ));
                    strcat(info, "に突き刺さった");
#else
                    strcat(info, " and slices into ");
                    strcat(info, ptr_monam( monster, DESC_NOCAP_THE ));
#endif
                }

#ifdef JP
                strcat(info, "！");
#else
                strcat(info, "!");
#endif
                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))
            {
#ifdef JP
                mpr("ゾットの力があなたに向けて発動した！");
#else
                mpr("The power of Zot is invoked against you!");
#endif
                miscast_effect( SPTYP_RANDOM, 10 + random2(30),
#ifdef JP
                                75 + random2(100), 0, "ゾットの力" );
#else
                                75 + random2(100), 0, "the power of Zot" );
#endif
                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)
#ifdef JP
                strcpy(info, "あなたは『ゾット』の怒声を耳にした！");
#else
                strcpy(info, "You hear a loud \"Zot\"!");
#endif
            else
#ifdef JP
                strcpy(info, "あなたは遠くに『ゾット』の声を聞いた！");
#else
                strcpy(info, "You hear a distant \"Zot\"!");
#endif
            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)
        {
#ifdef JP
            snprintf( info, INFO_SIZE, "%sは%s%s%s！",
                      beem.beam_name,
                      ptr_monam( monster, DESC_NOCAP_THE ),
                      (damage_taken >= 0) ? "に命中した" : "から外れた",
                      (damage_taken == 0) ? "が、損傷を与えなかった" : "" );
#else
            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" : "" );
#endif

            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
#ifdef JP
    snprintf( info, INFO_SIZE, "Mon #%d casts %s (#%d)", monster_index(monster),
#else
    snprintf( info, INFO_SIZE, "Mon #%d casts %s (#%d)", monster_index(monster),
#endif
             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;

#ifdef JP
        simple_monster_message(monster, "は地獄の力に呼びかけた！");
#else
        simple_monster_message(monster, " calls on the powers of Hell!");
#endif

        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:
#ifdef JP
            simple_monster_message( monster, "は一瞬だけ明るく輝いた。",
#else
            simple_monster_message( monster, " glows brightly for a moment.",
#endif
                                    MSGCH_MONSTER_ENCHANT );
            break;
        case 1:
#ifdef JP
            mpr( "あなたは背筋が寒くなった。" );
#else
            mpr( "You feel troubled." );
#endif
            break;
        case 2:
#ifdef JP
            mpr( "あなたは邪悪なエネルギーの波動が傍らを通り抜けるのを感じた。" );
#else
            mpr( "You feel a wave of unholy energy pass over you." );
#endif
            break;
        case 3:
#ifdef JP
            simple_monster_message( monster, "は強大に見える。",
#else
            simple_monster_message( monster, " looks stronger.",
#endif
                                    MSGCH_MONSTER_ENCHANT );
            break;
        case 4:
#ifdef JP
            simple_monster_message( monster, "は少しだけ半透明になった。",
#else
            simple_monster_message( monster, " becomes somewhat translucent.",
#endif
                                    MSGCH_MONSTER_ENCHANT );
            break;
        case 5:
#ifdef JP
            simple_monster_message( monster, "の眼が爛々と輝きだした。",
#else
            simple_monster_message( monster, "'s eyes start to glow.",
#endif
                                    MSGCH_MONSTER_ENCHANT );
            break;
        case 6:
        default:
            if (one_chance_in(20))
#ifdef JP
                mpr( "何だったにせよ、あなたは抵抗した。" );
#else
                mpr( "You resist (whatever that was supposed to do)." );
#endif
            else
#ifdef JP
                mpr( "あなたは抵抗した。" );
#else
                mpr( "You resist." );
#endif
            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))
        {
#ifdef JP
            simple_monster_message(monster, "の存在は安定したようだ。");
#else
            simple_monster_message(monster, " seems more stable.");
#endif
        }
        else
            mons_add_ench(monster, (coinflip() ? ENCH_TP_III : ENCH_TP_IV ));

        return;
    }

#ifdef JP
    simple_monster_message(monster, "は姿を消した！");
#else
    simple_monster_message(monster, " disappears!");
#endif

    // 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:
#ifdef JP
        strcat(pbolt.beam_name, "の炎の塊");
#else
        strcat(pbolt.beam_name, "'s blast of flame");
#endif
        pbolt.flavour = BEAM_FIRE;
        pbolt.colour = RED;
#ifdef JP
        pbolt.aux_source = "炎の塊";
#else
        pbolt.aux_source = "blast of flame";
#endif
        break;

    case MONS_ICE_DRAGON:
#ifdef JP
        strcat(pbolt.beam_name, "の冷気の塊");
#else
        strcat(pbolt.beam_name, "'s blast of cold");
#endif
        pbolt.flavour = BEAM_COLD;
        pbolt.colour = WHITE;
#ifdef JP
        pbolt.aux_source = "冷気の塊";
#else
        pbolt.aux_source = "blast of cold";
#endif
        break;

    default:
#ifdef JP
        DEBUGSTR("Bad monster class in setup_dragon()");
#else
        DEBUGSTR("Bad monster class in setup_dragon()");
#endif
    }

    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;
#ifdef JP
            strcpy(pbolt.beam_name, "");
#else
            strcpy(pbolt.beam_name, "bolt of ");
#endif

            if (poison)
#ifdef JP
                strcat(pbolt.beam_name, "毒の塗られた");
#else
                strcat(pbolt.beam_name, "poison ");
#endif

#ifdef JP
            strcat(pbolt.beam_name, "炎の矢");
#else
            strcat(pbolt.beam_name, "flame");
#endif
            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;
#ifdef JP
            strcpy(pbolt.beam_name, "");
#else
            strcpy(pbolt.beam_name, "bolt of ");
#endif

            if (poison)
#ifdef JP
                strcat(pbolt.beam_name, "毒の塗られた");
#else
                strcat(pbolt.beam_name, "poison ");
#endif

#ifdef JP
            strcat(pbolt.beam_name, "冷気の矢");
#else
            strcat(pbolt.beam_name, "frost");
#endif
            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) );

#ifdef JP
    strcat(info, "は");
    if (strlen(pbolt.beam_name) > 0)
    {
        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, (launched) ? "を撃った" : "を投げた");
#else
    strcat(info, (launched) ? " shoots " : " throws ");
#endif

#ifdef JP
#else
    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);
    }
#endif

#ifdef JP
    strcat(info, "。");
#else
    strcat(info, ".");
#endif
    mpr(info);


    if (launched)
    {
#ifdef JP
        snprintf( throw_buff, sizeof(throw_buff), "%sが撃った%sが命中した",
                  ptr_monam( monster, DESC_PLAIN ),
                  pbolt.beam_name );
        char aux_buff[80];
        snprintf( aux_buff, sizeof(aux_buff), "%sの射撃",
                  pbolt.beam_name );
        pbolt.aux_source = aux_buff;
#else
        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 ) );
#endif
    }
    else
    {
#ifdef JP
        snprintf( throw_buff, sizeof(throw_buff), "%sが投げた%sが命中した",
                  ptr_monam( monster, DESC_PLAIN),
                  pbolt.beam_name );
        char aux_buff[80];
        snprintf( aux_buff, sizeof(aux_buff), "%sの投擲",
                  pbolt.beam_name );
    pbolt.aux_source = aux_buff;
    }
#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;
#endif


    // 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;
#ifdef JP
        strcpy(beam.beam_name, "胞子の爆発");
#else
        strcpy(beam.beam_name, "explosion of spores");
#endif
        beam.colour = LIGHTGREY;
        beam.damage = dice_def( 3, 15 );
        beam.ex_size = 2;
#ifdef JP
        strcpy( info, "巨大胞子は爆発した！" );
#else
        strcpy( info, "The giant spore explodes!" );
#endif
    }
    else
    {
        beam.flavour = BEAM_ELECTRICITY;
#ifdef JP
        strcpy(beam.beam_name, "稲妻の放射");
#else
        strcpy(beam.beam_name, "blast of lightning");
#endif
        beam.colour = LIGHTCYAN;
        beam.damage = dice_def( 3, 20 );
        beam.ex_size = coinflip() ? 3 : 2;
#ifdef JP
        strcpy( info, "球雷が爆発した！" );
#else
        strcpy( info, "The ball lightning explodes!" );
#endif
    }

    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];
#ifdef JP
        beam.name = "魔法の矢";       // inv_name [throw_2]);
#else
        beam.name = "magic dart";       // inv_name [throw_2]);
#endif
        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;
#ifdef JP
        beam.name = "炎の塊";
#else
        beam.name = "puff of flame";
#endif
        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;
#ifdef JP
        beam.name = "冷気の塊";
#else
        beam.name = "puff of frost";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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)
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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:
#ifdef JP
        beam.name = "毒液の矢";
#else
        beam.name = "bolt of poison";
#endif
        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:
#ifdef JP
        beam.name = "猛火の矢";
#else
        beam.name = "bolt of fire";
#endif
        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:
#ifdef JP
        beam.name = "凍結の矢";
#else
        beam.name = "bolt of cold";
#endif
        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:
#ifdef JP
        beam.name = "稲妻の矢";
#else
        beam.name = "bolt of lightning";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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;
#ifdef JP
        beam.name = "ファイアボール";
#else
        beam.name = "fireball";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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
#ifdef JP
        beam.name = "水晶の槍";
#else
        beam.name = "crystal spear";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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
#ifdef JP
        beam.name = "負のエネルギーの矢";
#else
        beam.name = "bolt of negative energy";
#endif
        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;
#ifdef JP
        beam.name = "エネルギーの爆裂球";
#else
        beam.name = "orb of energy";
#endif
        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;
#ifdef JP
        beam.name = "蒸気の爆裂球";
#else
        beam.name = "ball of steam";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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;
#ifdef JP
        beam.name = "焼夷の炎";
#else
        beam.name = "sticky flame";
#endif
        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
#ifdef JP
        beam.name = "毒素の放射";
#else
        beam.name = "blast of poison";
#endif
        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;
#ifdef JP
        beam.name = "エネルギーの爆裂球";
#else
        beam.name = "orb of energy";
#endif
        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;
#ifdef JP
        beam.name = "エネルギーの矢";
#else
        beam.name = "bolt of energy";
#endif
        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;
#ifdef JP
        beam.name = "毒針";
#else
        beam.name = "sting";
#endif
        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;
#ifdef JP
        beam.name = "鉄塊の矢";
#else
        beam.name = "iron bolt";
#endif
        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;
#ifdef JP
        beam.name = "石錐の矢";
#else
        beam.name = "stone arrow";
#endif
        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;
#ifdef JP
        beam.name = "毒の飛沫";
#else
        beam.name = "splash of poison";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        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
#ifdef JP
        beam.name = "穢れた蒸気";
#else
        beam.name = "foul vapour";
#endif
        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();
#ifdef JP
        beam.name = "エネルギーの矢";
#else
        beam.name = "bolt of energy";
#endif
        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
#ifdef JP
        beam.name = "地獄の業火";
#else
        beam.name = "hellfire";
#endif
        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:
#ifdef JP
        beam.name = "金属片の雨";
#else
        beam.name = "spray of metal splinters";
#endif
        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:
#ifdef JP
        beam.name = "0";
#else
        beam.name = "0";
#endif
        beam.range = 5;
        beam.rangeMax = 9;
        beam.type = 0;
        beam.flavour = BEAM_BANISH;
        beam.thrown = KILL_MON_MISSILE;
        beam.isBeam = true;
        break;

    default:
#ifdef JP
        DEBUGSTR("知られざる呪文");
#else
        DEBUGSTR("Unknown spell");
#endif
    }

    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)
#ifdef JP
        mpr("送還の呪文が発動した！");
#else
        mpr("Send 'em back where they came from!");
#endif

    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
        {
#ifdef JP
            simple_monster_message(monster, "は身震いした。");
#else
            simple_monster_message(monster, " shudders.");
#endif
            mons_del_ench(monster, ENCH_ABJ_I, ENCH_ABJ_VI);
            mons_add_ench(monster, abjLevel);
        }
    }

    return result;
}                               // end monster_abjuration()
