/*
 *  File:       spells4.cc
 *  Summary:    new spells, focusing on transmigration, divination and
 *              other neglected areas of Crawl magic ;^)
 *  Written by: Copyleft Josh Fishman 1999-2000, All Rights Preserved
 *
 *  Change History (most recent first):
 *
 *   <2> 29jul2000  jdj  Made a zillion functions static.
 *   <1> 06jan2000  jmf  Created
 */

#include "AppHdr.h"

#include <string>
#include <stdio.h>

#include "externs.h"

#include "abyss.h"
#include "beam.h"
#include "cloud.h"
#include "debug.h"
#include "delay.h"
#include "describe.h"
#include "direct.h"
#include "dungeon.h"
#include "effects.h"
#include "it_use2.h"
#include "itemname.h"
#include "items.h"
#include "invent.h"
#include "misc.h"
#include "monplace.h"
#include "monstuff.h"
#include "mon-util.h"
#include "mstuff2.h"
#include "ouch.h"
#include "player.h"
#include "randart.h"
#include "religion.h"
#include "skills.h"
#include "spells1.h"
#include "spells4.h"
#include "spl-cast.h"
#include "spl-util.h"
#include "stuff.h"
#include "view.h"

enum DEBRIS                 // jmf: add for shatter, dig, and Giants to throw
{
    DEBRIS_METAL,           //    0
    DEBRIS_ROCK,
    DEBRIS_STONE,
    DEBRIS_WOOD,
    DEBRIS_CRYSTAL,
    NUM_DEBRIS
};          // jmf: ...and I'll actually implement the items Real Soon Now...

static bool mons_can_host_shuggoth(int type);
// static int make_a_random_cloud(int x, int y, int pow, int ctype);
static int make_a_rot_cloud(int x, int y, int pow, int ctype);
static int quadrant_blink(int x, int y, int pow, int garbage);

//void cast_animate_golem(int pow); // see actual function for reasoning {dlb}
//void cast_detect_magic(int pow);  //jmf: as above...
//void cast_eringyas_surprising_bouquet(int powc);
void do_monster_rot(int mon);

//jmf: FIXME: put somewhere else (misc.cc?)
// A feeble attempt at Nethack-like completeness for cute messages.
const char *your_hand( bool plural )
{
    static char hand_buff[80];

    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    default:
#ifdef JP 
        mpr("ERROR: unknown transformation in your_hand() (spells4.cc)");
#else
        mpr("ERROR: unknown transformation in your_hand() (spells4.cc)");
#endif
    case TRAN_NONE:
    case TRAN_STATUE:
        if (you.species == SP_TROLL || you.species == SP_GHOUL)
        {
#ifdef JP 
            strcpy(hand_buff, "爪");
#else
            strcpy(hand_buff, "claw");
#endif
            break;
        }
        // or fall-through
    case TRAN_ICE_BEAST:
    case TRAN_LICH:
#ifdef JP 
        strcpy(hand_buff, "手");
#else
        strcpy(hand_buff, "hand");
#endif
        break;
    case TRAN_SPIDER:
#ifdef JP 
        strcpy(hand_buff, "前脚");
#else
        strcpy(hand_buff, "front leg");
#endif
        break;
    case TRAN_SERPENT_OF_HELL:
    case TRAN_DRAGON:
#ifdef JP 
        strcpy(hand_buff, "前脚の爪");
#else
        strcpy(hand_buff, "foreclaw");
#endif
        break;
    case TRAN_BLADE_HANDS:
#ifdef JP 
        strcpy(hand_buff, "鎌状の刃");
#else
        strcpy(hand_buff, "scythe-like blade");
#endif
        break;
    case TRAN_AIR:
#ifdef JP 
        strcpy(hand_buff, "霧の蔓");
#else
        strcpy(hand_buff, "misty tendril");
#endif
        break;
    }

    if (plural)
#ifdef JP 
        strcat(hand_buff, "");
#else
        strcat(hand_buff, "s");
#endif

    return (hand_buff);
}

// I need to make some debris for metal, crystal and stone.
// They could go in OBJ_MISSILES, but I think I'd rather move
// MI_LARGE_ROCK into OBJ_DEBRIS and code giants to throw any
// OBJ_DEBRIS they get their meaty mits on.
static void place_debris(int x, int y, int debris_type)
{
#ifdef USE_DEBRIS_CODE
    switch (debris_type)
    {
    // hate to say this, but the first parameter only allows specific quantity
    // for *food* and nothing else -- and I would hate to see that parameter
    // (force_unique) abused any more than it already has been ... {dlb}:
    case DEBRIS_STONE:
        large = items( random2(3), OBJ_MISSILES, MI_LARGE_ROCK, true, 1, 250 );
        small = items( 3 + random2(6) + random2(6) + random2(6),
                       OBJ_MISSILES, MI_STONE, true, 1, 250 );
        break;
    case DEBRIS_METAL:
    case DEBRIS_WOOD:
    case DEBRIS_CRYSTAL:
        break;
    }

    if (small != NON_ITEM)
        move_item_to_grid( &small, x, y );

    if (large != NON_ITEM)
        move_item_to_grid( &large, x, y );

#else
    UNUSED( x );
    UNUSED( y );
    UNUSED( debris_type );
    return;
#endif
}                               // end place_debris()

// just to avoid typing this over and over
// now returns true if monster died -- bwr
inline bool player_hurt_monster(int monster, int damage)
{
    ASSERT( monster != NON_MONSTER );

    if (damage > 0)
    {
        hurt_monster( &menv[monster], damage );

        if (menv[monster].hit_points > 0)
            print_wounds( &menv[monster] );
        else
        {
            monster_die( &menv[monster], KILL_YOU, 0 );
            return (true);
        }
    }

    return (false);
}                               // end player_hurt_monster()


// Here begin the actual spells:
static int shatter_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    dice_def   dam_dice( 0, 5 + pow / 4 );  // number of dice set below
    const int  monster = mgrd[x][y];

    if (monster == NON_MONSTER)
        return (0);

    // Removed a lot of silly monsters down here... people, just because
    // it says ice, rock, or iron in the name doesn't mean it's actually
    // made out of the substance. -- bwr
    switch (menv[monster].type)
    {
    case MONS_ICE_BEAST:        // 3/2 damage
    case MONS_SIMULACRUM_SMALL:
    case MONS_SIMULACRUM_LARGE:
        dam_dice.num = 4;
        break;

    case MONS_SKELETON_SMALL: // double damage
    case MONS_SKELETON_LARGE:
    case MONS_CURSE_SKULL:
    case MONS_CLAY_GOLEM:
    case MONS_STONE_GOLEM:
    case MONS_IRON_GOLEM:
    case MONS_CRYSTAL_GOLEM:
    case MONS_EARTH_ELEMENTAL:
    case MONS_GARGOYLE:
    case MONS_SKELETAL_DRAGON:
    case MONS_SKELETAL_WARRIOR:
        dam_dice.num = 6;
        break;

    case MONS_VAPOUR:
    case MONS_INSUBSTANTIAL_WISP:
    case MONS_AIR_ELEMENTAL:
    case MONS_FIRE_ELEMENTAL:
    case MONS_WATER_ELEMENTAL:
    case MONS_SPECTRAL_WARRIOR:
    case MONS_FREEZING_WRAITH:
    case MONS_WRAITH:
    case MONS_PHANTOM:
    case MONS_PLAYER_GHOST:
    case MONS_SHADOW:
    case MONS_HUNGRY_GHOST:
    case MONS_FLAYED_GHOST:
    case MONS_SMOKE_DEMON:      //jmf: I hate these bastards...
        dam_dice.num = 0;
        break;

    case MONS_PULSATING_LUMP:
    case MONS_JELLY:
    case MONS_SLIME_CREATURE:
    case MONS_BROWN_OOZE:
    case MONS_AZURE_JELLY:
    case MONS_DEATH_OOZE:
    case MONS_ACID_BLOB:
    case MONS_ROYAL_JELLY:
    case MONS_OOZE:
    case MONS_SPECTRAL_THING:
    case MONS_JELLYFISH:
        dam_dice.num = 1;
        dam_dice.size /= 2;
        break;

    case MONS_DANCING_WEAPON:     // flies, but earth based
    case MONS_MOLTEN_GARGOYLE:
    case MONS_QUICKSILVER_DRAGON:
        // Soft, earth creatures... would normally resist to 1 die, but
        // are sensitive to this spell. -- bwr
        dam_dice.num = 2;
        break;

    default:                    // normal damage
        if (mons_flies( &menv[monster] ))
            dam_dice.num = 1;
        else
            dam_dice.num = 3;
        break;
    }

    int damage = roll_dice( dam_dice ) - random2( menv[monster].armour_class );

    if (damage > 0)
        player_hurt_monster( monster, damage );
    else 
        damage = 0;

    return (damage);
}                               // end shatter_monsters()

static int shatter_items(int x, int y, int pow, int garbage)
{
    UNUSED( pow );
    UNUSED( garbage );

    int broke_stuff = 0, next, obj = igrd[x][y];

    if (obj == NON_ITEM)
        return 0;

    while (obj != NON_ITEM)
    {
        next = mitm[obj].link;

        switch (mitm[obj].base_type)
        {
        case OBJ_POTIONS:
            if (!one_chance_in(10))
            {
                broke_stuff++;
                destroy_item(obj);
            }
            break;

        default:
            break;
        }

        obj = next;
    }

    if (broke_stuff)
    {
        if (!silenced(x, y) && !silenced(you.x_pos, you.y_pos))
#ifdef JP 
            mpr("あなたはガラスが割れる音を耳にした。");
#else
            mpr("You hear glass break.");
#endif

        return 1;
    }

    return 0;
}                               // end shatter_items()

static int shatter_walls(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    int  chance = 0;
    int  stuff = 0;

    // if not in-bounds then we can't really shatter it -- bwr
    if (x <= 5 || x >= GXM - 5 || y <= 5 || y >= GYM - 5)
        return (0);

    switch (grd[x][y])
    {
    case DNGN_SECRET_DOOR:
        if (see_grid(x, y))
#ifdef JP 
            mpr("秘密のドアが砕け散った！");
#else
            mpr("A secret door shatters!");
#endif
        grd[x][y] = DNGN_FLOOR;
        stuff = DEBRIS_WOOD;
        chance = 100;
        break;

    case DNGN_CLOSED_DOOR:
    case DNGN_OPEN_DOOR:
        if (see_grid(x, y))
#ifdef JP 
            mpr("ドアが砕け散った！");
#else
            mpr("A door shatters!");
#endif
        grd[x][y] = DNGN_FLOOR;
        stuff = DEBRIS_WOOD;
        chance = 100;
        break;

    case DNGN_METAL_WALL:
    case DNGN_SILVER_STATUE:
        stuff = DEBRIS_METAL;
        chance = pow / 10;
        break;

    case DNGN_ORCISH_IDOL:
    case DNGN_GRANITE_STATUE:
        chance = 50;
        stuff = DEBRIS_STONE;
        break;

    case DNGN_STONE_WALL:
        chance = pow / 6;
        stuff = DEBRIS_STONE;
        break;

    case DNGN_ROCK_WALL:
        chance = pow / 4;
        stuff = DEBRIS_ROCK;
        break;

    case DNGN_ORANGE_CRYSTAL_STATUE:
        chance = pow / 6;
        stuff = DEBRIS_CRYSTAL;
        break;

    case DNGN_GREEN_CRYSTAL_WALL:
        chance = 50;
        stuff = DEBRIS_CRYSTAL;
        break;

    default:
        break;
    }

    if (stuff && random2(100) < chance)
    {
        if (!silenced( x, y ))
            noisy( 30, x, y );

        grd[x][y] = DNGN_FLOOR;
        place_debris(x, y, stuff);
        return (1);
    }

    return (0);
}                               // end shatter_walls()

void cast_shatter(int pow)
{
    int damage = 0;
    const bool sil = silenced( you.x_pos, you.y_pos );

    if (!sil)
        noisy( 30, you.x_pos, you.y_pos );

#ifdef JP 
    snprintf(info, INFO_SIZE, "ダンジョンが%s！", (sil ? "揺れ動いた" : "轟音をあげた"));
#else
    snprintf(info, INFO_SIZE, "The dungeon %s!", (sil ? "shakes" : "rumbles"));
#endif
    mpr(info);

    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_NONE:
    case TRAN_SPIDER:
    case TRAN_LICH:
    case TRAN_DRAGON:
    case TRAN_AIR:
    case TRAN_SERPENT_OF_HELL:
        break;

    case TRAN_STATUE:           // full damage
        damage = 15 + random2avg( (pow / 5), 4 );
        break;

    case TRAN_ICE_BEAST:        // 1/2 damage
        damage = 10 + random2avg( (pow / 5), 4 ) / 2;
        break;

    case TRAN_BLADE_HANDS:      // 2d3 damage
#ifdef JP 
        mpr("あなたの鎌状の刃はひどく振動した！");
#else
        mpr("Your scythe-like blades vibrate painfully!");
#endif
        damage = 2 + random2avg(5, 2);
        break;

    default:
#ifdef JP 
        mpr("cast_shatter(): unknown transformation in spells4.cc");
#else
        mpr("cast_shatter(): unknown transformation in spells4.cc");
#endif
    }

    if (damage)
        ouch(damage, 0, KILLED_BY_TARGETTING);

    int rad = 3 + (you.skills[SK_EARTH_MAGIC] / 5);

    apply_area_within_radius(shatter_items, you.x_pos, you.y_pos, pow, rad, 0);
    apply_area_within_radius(shatter_monsters, you.x_pos, you.y_pos, pow, rad, 0);
    int dest = apply_area_within_radius( shatter_walls, you.x_pos, you.y_pos, 
                                         pow, rad, 0 );

    if (dest && !sil)
#ifdef JP 
        mpr("ドッカーン！");
#else
        mpr("Ka-crash!");
#endif
}                               // end cast_shatter()

// cast_forescry: raises evasion (by 8 currently) via divination
void cast_forescry(int pow)
{
    if (!you.duration[DUR_FORESCRY])
#ifdef JP 
        mpr("あなたはごく近い未来の予見を垣間見ている……。");
#else
        mpr("You begin to receive glimpses of the immediate future...");
#endif

    you.duration[DUR_FORESCRY] += 5 + random2(pow);

    if (you.duration[DUR_FORESCRY] > 30)
        you.duration[DUR_FORESCRY] = 30;

    you.redraw_evasion = 1;
}                               // end cast_forescry()

void cast_see_invisible(int pow)
{
    if (player_see_invis())
#ifdef JP 
        mpr("何も起こらなかったようだ。");
#else
        mpr("Nothing seems to happen.");
#endif
    else
#ifdef JP 
        mpr("あなたの視覚は鋭くなった。");
#else
        mpr("Your vision seems to sharpen.");
#endif

    // no message if you already are under the spell
    you.duration[DUR_SEE_INVISIBLE] += 10 + random2(2 + (pow / 2));

    if (you.duration[DUR_SEE_INVISIBLE] > 100)
        you.duration[DUR_SEE_INVISIBLE] = 100;
}                               // end cast_see_invisible()

#if 0
// FIXME: This would be kinda cool if implemented right.
//        The idea is that, like detect_secret_doors, the spell gathers all
//        sorts of information about a thing and then tells the caster a few
//        cryptic hints. So for a (+3,+5) Mace of Flaming, one might detect
//        "enchantment and heat", but for a cursed ring of hunger, one might
//        detect "enchantment and ice" (since it gives you a 'deathly cold'
//        feeling when you put it on) or "necromancy" (since it's evil).
//        A weapon of Divine Wrath and a randart that makes you angry might
//        both give similar messages. The key would be to not tell more than
//        hints about whether an item is benign or cursed, but give info
//        on how strong its enchantment is (and therefore how valuable it
//        probably is).
static void cast_detect_magic(int pow)
{
    struct dist bmove;
    int x, y;
    int monster = 0, item = 0, next;    //int max;
    FixedVector < int, NUM_SPELL_TYPES > found;
    int strong = 0;             // int curse = 0;

    for (next = 0; next < NUM_SPELL_TYPES; next++)
    {
        found[next] = 0;
    }

#ifdef JP 
    mpr("どの方向？", MSGCH_PROMPT);
#else
    mpr("Which direction?", MSGCH_PROMPT);
#endif
    direction( bmove, DIR_DIR );

    if (!bmove.isValid)
    {
        canned_msg(MSG_SPELL_FIZZLES);
        return;
    }

    if (bmove.dx == 0 && bmove.dy == 0)
    {
#ifdef JP 
        mpr("あなたは直感的察知を行った。");
#else
        mpr("You detect a divination in progress.");
#endif
        return;
    }

    x = you.x_pos + bmove.dx;
    y = you.y_pos + bmove.dy;

    monster = mgrd[x][y];
    if (monster == NON_MONSTER)
        goto do_items;
    else
        goto all_done;

  do_items:
    item = igrd[x][y];

    if (item == NON_ITEM)
        goto all_done;

    while (item != NON_ITEM)
    {
        next = mitm[item].link;
        if (is_dumpable_artifact
            (mitm[item].base_type, mitm[item].sub_type, mitm[item].plus,
             mitm[item].plus2, mitm[item].special, 0, 0))
        {
            strong++;
            //FIXME: do checks for randart properties
        }
        else
        {
            switch (mitm[item].base_type)
            {
            case OBJ_WEAPONS:
                found[SPTYP_ENCHANTMENT] += (mitm[item].plus > 50);
                found[SPTYP_ENCHANTMENT] += (mitm[item].plus2 > 50);
                break;

            case OBJ_MISSILES:
                found[SPTYP_ENCHANTMENT] += (mitm[item].plus > 50);
                found[SPTYP_ENCHANTMENT] += (mitm[item].plus2 > 50);
                break;

            case OBJ_ARMOUR:
                found[SPTYP_ENCHANTMENT] += mitm[item].plus;
            }
        }
    }

  all_done:
    if (monster)
    {
#ifdef JP 
        mpr("あなたはモンスターらしき形態形成フィールドを察知した。");
#else
        mpr("You detect a morphogenic field, such as a monster might have.");
#endif
    }
    if (strong)
    {
#ifdef JP 
        mpr("あなたは非常に強力な魔力を察知した。");
#else
        mpr("You detect very strong enchantments.");
#endif
        return;
    }
    else
    {
        //FIXME:
    }
    return;
}
#endif

// The description idea was okay, but this spell just isn't that exciting.
// So I'm converting it to the more practical expose secret doors. -- bwr
void cast_detect_secret_doors(int pow)
{
    int found = 0;

    for (int x = you.x_pos - 8; x <= you.x_pos + 8; x++)
    {
        for (int y = you.y_pos - 8; y <= you.y_pos + 8; y++)
        {
            if (x < 5 || x > GXM - 5 || y < 5 || y > GYM - 5)
                continue;

            if (!see_grid(x, y)) 
                continue;

            if (grd[x][y] == DNGN_SECRET_DOOR && random2(pow) > random2(15))
            {
                grd[x][y] = DNGN_CLOSED_DOOR;
                found++;
            }
        }
    }

    if (found)
    {
        redraw_screen();

#ifdef JP 
        snprintf( info, INFO_SIZE, "あなたは秘密のドアを察知した。" );
#else
        snprintf( info, INFO_SIZE, "You detect %s secret door%s.", 
                 (found > 1) ? "some" : "a", (found > 1) ? "s" : "" );
#endif
        mpr( info );
    }
}                               // end cast_detect_secret_doors()

void cast_summon_butterflies(int pow)
{
    // explicitly limiting the number
    int num = 4 + random2(3) + random2( pow ) / 10;
    if (num > 16)
        num = 16;

    for (int scount = 1; scount < num; scount++)
    {
        create_monster( MONS_BUTTERFLY, ENCH_ABJ_III, BEH_FRIENDLY,
                        you.x_pos, you.y_pos, MHITYOU, 250 );
    }
}

void cast_summon_large_mammal(int pow)
{
    int mon;
    int temp_rand = random2(pow);

    if (temp_rand < 10)
        mon = MONS_JACKAL;
    else if (temp_rand < 15)
        mon = MONS_HOUND;
    else
    {
        switch (temp_rand % 7)
        {
        case 0:
            if (you.species == SP_HILL_ORC && one_chance_in(3))
                mon = MONS_WARG;
            else
                mon = MONS_WOLF;
            break;
        case 1:
        case 2:
            mon = MONS_WAR_DOG;
            break;
        case 3:
        case 4:
            mon = MONS_HOUND;
            break;
        default:
            mon = MONS_JACKAL;
            break;
        }
    }

    create_monster( mon, ENCH_ABJ_III, BEH_FRIENDLY, you.x_pos, you.y_pos, 
                    you.pet_target, 250 );
}

void cast_sticks_to_snakes(int pow)
{
    int mon, i, behaviour;

    int how_many = 0;

    int max = 1 + random2( 1 + you.skills[SK_TRANSMIGRATION] ) / 4;

    int dur = ENCH_ABJ_III + random2(pow) / 20;
    if (dur > ENCH_ABJ_V)
        dur = ENCH_ABJ_V;

    const int weapon = you.equip[EQ_WEAPON];

    if (weapon == -1)
    {
#ifdef JP 
        snprintf( info, INFO_SIZE, "あなたの%sが蛇のように這いはじめた！", your_hand(true));
#else
        snprintf( info, INFO_SIZE, "Your %s feel slithery!", your_hand(true));
#endif
        mpr(info);
        return;
    }

    behaviour = item_cursed( you.inv[ weapon ] ) ? BEH_HOSTILE
                                                 : BEH_FRIENDLY;

    if ((you.inv[ weapon ].base_type == OBJ_MISSILES
         && (you.inv[ weapon ].sub_type == MI_ARROW)))
    {
        if (you.inv[ weapon ].quantity < max)
            max = you.inv[ weapon ].quantity;

        for (i = 0; i <= max; i++)
        {
            //jmf: perhaps also check for poison ammo?
            if (pow > 50 || (pow > 25 && one_chance_in(3)))
                mon = MONS_SNAKE;
            else
                mon = MONS_SMALL_SNAKE;

            if (create_monster( mon, dur, behaviour, you.x_pos, you.y_pos, 
                                MHITYOU, 250 ) != -1)
            {
                how_many++;
            }
        }
    }

    if (you.inv[ weapon ].base_type == OBJ_WEAPONS
        && (you.inv[ weapon ].sub_type == WPN_CLUB
            || you.inv[ weapon ].sub_type == WPN_SPEAR
            || you.inv[ weapon ].sub_type == WPN_QUARTERSTAFF
            || you.inv[ weapon ].sub_type == WPN_SCYTHE
            || you.inv[ weapon ].sub_type == WPN_GIANT_CLUB
            || you.inv[ weapon ].sub_type == WPN_GIANT_SPIKED_CLUB
            || you.inv[ weapon ].sub_type == WPN_BOW
            || you.inv[ weapon ].sub_type == WPN_ANCUS
            || you.inv[ weapon ].sub_type == WPN_HALBERD
            || you.inv[ weapon ].sub_type == WPN_GLAIVE
            || you.inv[ weapon ].sub_type == WPN_BLOWGUN))
    {
        how_many = 1;

        // Upsizing Snakes to Brown Snakes as the base class for using
        // the really big sticks (so bonus applies really only to trolls,
        // ogres, and most importantly ogre magi).  Still it's unlikely
        // any character is strong enough to bother lugging a few of
        // these around.  -- bwr
        if (mass_item( you.inv[ weapon ] ) < 500)
            mon = MONS_SNAKE;
        else
            mon = MONS_BROWN_SNAKE;

        if (pow > 90 && one_chance_in(3))
            mon = MONS_GREY_SNAKE;

        if (pow > 70 && one_chance_in(3))
            mon = MONS_BLACK_SNAKE;

        if (pow > 40 && one_chance_in(3))
            mon = MONS_YELLOW_SNAKE;

        if (pow > 20 && one_chance_in(3))
            mon = MONS_BROWN_SNAKE;

        create_monster(mon, dur, behaviour, you.x_pos, you.y_pos, MHITYOU, 250);
    }

#ifdef USE_DEBRIS_CODE
    if (you.inv[ weapon ].base_type == OBJ_DEBRIS
        && (you.inv[ weapon ].sub_type == DEBRIS_WOOD))
    {
        // this is how you get multiple big snakes
        how_many = 1;
#ifdef JP 
        mpr("FIXME: implement OBJ_DEBRIS conversion! (spells4.cc)");
#else
        mpr("FIXME: implement OBJ_DEBRIS conversion! (spells4.cc)");
#endif
    }
#endif // USE_DEBRIS_CODE

    if (how_many > you.inv[you.equip[EQ_WEAPON]].quantity)
        how_many = you.inv[you.equip[EQ_WEAPON]].quantity;

    if (how_many)
    {
        dec_inv_item_quantity( you.equip[EQ_WEAPON], how_many );

#ifdef USE_TILE
        if (Options.use_tile)
            TilePlayerRefresh();
#endif

#ifdef JP 
        snprintf( info, INFO_SIZE, "あなたは蛇を造りだした！" );
#else
        snprintf( info, INFO_SIZE, "You create %s snake%s!",
                how_many > 1 ? "some" : "a", how_many > 1 ? "s" : "");
#endif
    }
    else
    {
#ifdef JP 
        snprintf( info, INFO_SIZE, "あなたの%sが蛇になったように感じる！", your_hand(true));
#else
        snprintf( info, INFO_SIZE, "Your %s feel slithery!", your_hand(true));
#endif
    }

    mpr(info);
    return;
}                               // end cast_sticks_to_snakes()

void cast_summon_dragon(int pow)
{
    int happy;

    // Removed the chance of multiple dragons... one should be more
    // than enough, and if it isn't, the player can cast again...
    // especially since these aren't on the Abjuration plan... they'll
    // last until they die (maybe that should be changed, but this is
    // a very high level spell so it might be okay).  -- bwr
    happy = (random2(pow) > 5);

    if (create_monster( MONS_DRAGON, ENCH_ABJ_III,
                        (happy ? BEH_FRIENDLY : BEH_HOSTILE),
                        you.x_pos, you.y_pos, MHITYOU, 250 ) != -1)
    {
#ifdef JP 
        strcpy(info, "ドラゴンが現れた。");
#else
        strcpy(info, "A dragon appears.");
#endif

        if (!happy)
#ifdef JP 
            strcat(info, "ドラゴンはあまり機嫌が良さそうには見えない。");
#else
            strcat(info, " It doesn't look very happy.");
#endif
    }
    else
#ifdef JP 
        strcpy(info, "何も起きなかった。");
#else
        strcpy(info, "Nothing happens.");
#endif

    mpr(info);
}                               // end cast_summon_dragon()

void cast_conjure_ball_lightning( int pow )
{
    int num = 3 + random2( 2 + pow / 50 );

    // but restricted so that the situation doesn't get too gross.
    // Each of these will explode for 3d20 damage. -- bwr
    if (num > 8)
        num = 8;

    bool summoned = false;

    for (int i = 0; i < num; i++)
    {
        int tx = -1, ty = -1;

        for (int j = 0; j < 10; j++)
        {
            if (!random_near_space( you.x_pos, you.y_pos, tx, ty, true, true)
                && distance( you.x_pos, you.y_pos, tx, ty ) <= 5)
            {
                break;
            }
        }

        // if we fail, we'll try the ol' summon next to player trick.
        if (tx == -1 || ty == -1)
        {
            tx = you.x_pos;
            ty = you.y_pos;
        }

        int mon = mons_place( MONS_BALL_LIGHTNING, BEH_FRIENDLY, MHITNOT,
                              true, tx, ty );

        // int mon = create_monster( MONS_BALL_LIGHTNING, 0, BEH_FRIENDLY, 
        //                           tx, ty, MHITNOT, 250 );

        if (mon != -1)
        {
            mons_add_ench( &menv[mon], ENCH_SHORT_LIVED );
            summoned = true;
        }
    }

    if (summoned)
#ifdef JP 
        mpr( "あなたは幾つかの球雷をつくりだした！" );
#else
        mpr( "You create some ball lightning!" );
#endif
    else
        canned_msg( MSG_NOTHING_HAPPENS );
}

static int sleep_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );
    int mnstr = mgrd[x][y];

    if (mnstr == NON_MONSTER)                                   return 0;
    if (mons_holiness( menv[mnstr].type ) != MH_NATURAL)        return 0;
    if (check_mons_resist_magic( &menv[mnstr], pow ))           return 0;

    // Why shouldn't we be able to sleep friendly monsters? -- bwr
    // if (mons_friendly( &menv[mnstr] ))                          return 0;

    //jmf: now that sleep == hibernation:
    if (mons_res_cold( &menv[mnstr] ) > 0 && coinflip())        return 0;
    if (mons_has_ench( &menv[mnstr], ENCH_SLEEP_WARY ))         return 0;

    menv[mnstr].behaviour = BEH_SLEEP;
    mons_add_ench( &menv[mnstr], ENCH_SLEEP_WARY );

    if (mons_flag( menv[mnstr].type, M_COLD_BLOOD ) && coinflip())
        mons_add_ench( &menv[mnstr], ENCH_SLOW );

    return 1;
}                               // end sleep_monsters()

void cast_mass_sleep(int pow)
{
    apply_area_visible(sleep_monsters, pow);
}                               // end cast_mass_sleep()

static int tame_beast_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );
    int which_mons = mgrd[x][y];

    if (which_mons == NON_MONSTER)                             return 0;

    struct monsters *monster = &menv[which_mons];

    if (mons_holiness(monster->type) != MH_NATURAL)            return 0;
    if (mons_intel_type(monster->type) != I_ANIMAL)            return 0;
    if (mons_friendly(monster))                                return 0;

    // 50% bonus for dogs, add cats if they get implemented
    if (monster->type == MONS_HOUND || monster->type == MONS_WAR_DOG
		 || monster->type == MONS_BLACK_BEAR)
    {
        pow += (pow / 2);
    }

    if (you.species == SP_HILL_ORC && monster->type == MONS_WARG)
        pow += (pow / 2);

    if (check_mons_resist_magic(monster, pow))
        return 0;

    // I'd like to make the monsters affected permanently, but that's
    // pretty powerful. Maybe a small (pow/10) chance of being permanently
    // tamed, large chance of just being enslaved.
#ifdef JP 
    simple_monster_message(monster, "を飼いならした！");
#else
    simple_monster_message(monster, " is tamed!");
#endif

    if (random2(100) < random2(pow / 10))
        monster->attitude = ATT_FRIENDLY;       // permanent, right?
    else
        mons_add_ench(monster, ENCH_CHARM);

    return 1;
}                               // end tame_beast_monsters()

void cast_tame_beasts(int pow)
{
    apply_area_visible(tame_beast_monsters, pow);
}                               // end cast_tame_beasts()

static int ignite_poison_objects(int x, int y, int pow, int garbage)
{
    UNUSED( pow );
    UNUSED( garbage );

    int obj = igrd[x][y], next, strength = 0;

    if (obj == NON_ITEM)
        return (0);

    while (obj != NON_ITEM)
    {
        next = mitm[obj].link;
        if (mitm[obj].base_type == OBJ_POTIONS)
        {
            switch (mitm[obj].sub_type)
            {
                // intentional fall-through all the way down
            case POT_STRONG_POISON:
                strength += 20;
            case POT_DEGENERATION:
                strength += 10;
            case POT_POISON:
                strength += 10;
                destroy_item(obj);
            default:
                break;
            }
        }

        // FIXME: impliment burning poisoned ammo
        // else if ( it's ammo that's poisoned) {
        //   strength += number_of_ammo;
        //   destroy_item(ammo);
        //  }
        obj = next;
    }

    if (strength > 0)
        place_cloud(CLOUD_FIRE, x, y, strength + roll_dice(3, strength / 4) );

    return (strength);
}                               // end ignite_poison_objects()

static int ignite_poison_clouds( int x, int y, int pow, int garbage )
{
    UNUSED( pow );
    UNUSED( garbage );

    bool did_anything = false;

    const int cloud = env.cgrid[x][y];

    if (cloud != EMPTY_CLOUD)
    {
        if (env.cloud[ cloud ].type == CLOUD_STINK
            || env.cloud[ cloud ].type == CLOUD_STINK_MON)
        {
            did_anything = true;
            env.cloud[ cloud ].type = CLOUD_FIRE;

            env.cloud[ cloud ].decay /= 2;

            if (env.cloud[ cloud ].decay < 1)
                env.cloud[ cloud ].decay = 1;
        }
        else if (env.cloud[ cloud ].type == CLOUD_POISON
                 || env.cloud[ cloud ].type == CLOUD_POISON_MON)
        {
            did_anything = true;
            env.cloud[ cloud ].type = CLOUD_FIRE;
        }
    }

    return ((int) did_anything);
}                               // end ignite_poison_clouds()

static int ignite_poison_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    struct bolt beam;
    beam.flavour = BEAM_FIRE;   // this is dumb, only used for adjust!

    dice_def  dam_dice( 0, 5 + pow / 7 );  // dice added below if applicable

    const int mon_index = mgrd[x][y];
    if (mon_index == NON_MONSTER)
        return (0);

    struct monsters *const mon = &menv[ mon_index ];

    // Monsters which have poison corpses or poisonous attacks:
    if (mons_corpse_thingy( mon->type ) == CE_POISONOUS
        || mon->type == MONS_GIANT_ANT
        || mon->type == MONS_SMALL_SNAKE
        || mon->type == MONS_SNAKE
        || mon->type == MONS_JELLYFISH
        || mons_is_mimic( mon->type ))
    {
        dam_dice.num = 3;
    }

    // Monsters which are poisoned:
    int strength = 0;

    // first check for player poison:
    int ench = mons_has_ench( mon, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV );
    if (ench != ENCH_NONE)
        strength += ench - ENCH_YOUR_POISON_I + 1;

    // ... now monster poison:
    ench = mons_has_ench( mon, ENCH_POISON_I, ENCH_POISON_IV );
    if (ench != ENCH_NONE)
        strength += ench - ENCH_POISON_I + 1;
    
    // strength is now the sum of both poison types (although only 
    // one should actually be present at a given time):
    dam_dice.num += strength;

    int damage = roll_dice( dam_dice );
    if (damage > 0)
    {
        damage = mons_adjust_flavoured( mon, beam, damage );

#if DEBUG_DIAGNOSTICS
#ifdef JP 
        snprintf( info, INFO_SIZE, "Dice: %dd%d; Damage: %d", 
#else
        snprintf( info, INFO_SIZE, "Dice: %dd%d; Damage: %d", 
#endif
                  dam_dice.num, dam_dice.size, damage );    

        mpr( info, MSGCH_DIAGNOSTICS );
#endif

        if (!player_hurt_monster( mon_index, damage ))
        {
            // Monster survived, remove any poison.
            mons_del_ench( mon, ENCH_POISON_I, ENCH_POISON_IV );
            mons_del_ench( mon, ENCH_YOUR_POISON_I, ENCH_YOUR_POISON_IV );
        }

        return (1);
    }

    return (0);
}

void cast_ignite_poison(int pow)
{
    int damage = 0, strength = 0, pcount = 0, acount = 0, totalstrength = 0;
    char item;
    bool wasWielding = false;
    char str_pass[ ITEMNAME_SIZE ];

    // temp weapon of venom => temp fire brand
    const int wpn = you.equip[EQ_WEAPON];

    if (wpn != -1 
        && you.duration[DUR_WEAPON_BRAND]
        && get_weapon_brand( you.inv[wpn] ) == SPWPN_VENOM)
    {
        if (set_item_ego_type( you.inv[wpn], OBJ_WEAPONS, SPWPN_FLAMING ))
        {
            in_name( wpn, DESC_CAP_YOUR, str_pass );
            strcpy( info, str_pass );
#ifdef JP 
            strcat( info, "は炎を吹き上げた！" );
#else
            strcat( info, " bursts into flame!" );
#endif
            mpr(info);

            you.wield_change = true;
            you.duration[DUR_WEAPON_BRAND] += 1 + you.duration[DUR_WEAPON_BRAND] / 2;
            if (you.duration[DUR_WEAPON_BRAND] > 80)
                you.duration[DUR_WEAPON_BRAND] = 80;
        }
    }

    totalstrength = 0;

    for (item = 0; item < ENDOFPACK; item++)
    {
        if (!you.inv[item].quantity)
            continue;

        strength = 0;

        if (you.inv[item].base_type == OBJ_MISSILES)
        {
            if (you.inv[item].special == 3)
            {                   // burn poison ammo
                strength = you.inv[item].quantity;
                acount += you.inv[item].quantity;
            }
        }

        if (you.inv[item].base_type == OBJ_POTIONS)
        {
            switch (you.inv[item].sub_type)
            {
            case POT_STRONG_POISON:
                strength += 20 * you.inv[item].quantity;
                break;
            case POT_DEGENERATION:
            case POT_POISON:
                strength += 10 * you.inv[item].quantity;
                break;
            default:
                break;
            } // end switch

            if (strength)
                pcount += you.inv[item].quantity;
        }

        if (strength)
        {
            you.inv[item].quantity = 0;
            if (item == you.equip[EQ_WEAPON])
            {
                you.equip[EQ_WEAPON] = -1;
                wasWielding = true;
            }
        }

        totalstrength += strength;
    }

    if (acount > 0)
#ifdef JP 
        mpr("あなたの飛び道具が炎をあげた！");
#else
        mpr("Some ammo you are carrying burns!");
#endif

    if (pcount > 0)
    {
#ifdef JP 
        snprintf( info, INFO_SIZE, "あなたの持っているポーションが爆発した！" );
#else
        snprintf( info, INFO_SIZE, "%s potion%s you are carrying explode%s!",
            pcount > 1 ? "Some" : "A",
            pcount > 1 ? "s" : "",
            pcount > 1 ? "" : "s");
#endif
        mpr(info);
    }

    if (wasWielding == true)
        canned_msg( MSG_EMPTY_HANDED );

    if (totalstrength)
    {
        place_cloud(CLOUD_FIRE, you.x_pos, you.y_pos,
                    random2(totalstrength / 4 + 1) + random2(totalstrength / 4 + 1) +
                    random2(totalstrength / 4 + 1) + random2(totalstrength / 4 + 1) + 1);
    }

    // player is poisonous
    if (you.mutation[MUT_SPIT_POISON] || you.mutation[MUT_STINGER]
        || you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER // poison attack
        || (!player_is_shapechanged()
            && (you.species == SP_GREEN_DRACONIAN       // poison breath
                || you.species == SP_KOBOLD             // poisonous corpse
                || you.species == SP_NAGA)))            // spit poison
    {
        damage = roll_dice( 3, 5 + pow / 7 );
    }

    // player is poisoned
    damage += roll_dice( you.poison, 6 );

    if (damage)
    {
        const int resist = player_res_fire();

        if (resist > 0)
        {
#ifdef JP 
            mpr("あなたは血液が沸騰するのを感じた！");
#else
            mpr("You feel like your blood is boiling!");
#endif
            damage = damage / 3;
        }
        else if (resist < 0)
        {
            damage *= 3;
#ifdef JP 
            mpr("あなたの体内の毒が激しく燃えあがった！");
#else
            mpr("The poison in your system burns terribly!");
#endif
        }
        else
        {
#ifdef JP 
            mpr("あなたの体内の毒が燃えあがった！");
#else
            mpr("The poison in your system burns!");
#endif
        }

        ouch( damage, 0, KILLED_BY_TARGETTING );

        if (you.poison > 0)
        {
#ifdef JP 
            mpr( "あなたは毒が体内から消え失せたのを感じた。" );
#else
            mpr( "You feel that the poison has left your system." );
#endif
            you.poison = 0;
        }
    }

    apply_area_visible(ignite_poison_clouds, pow);
    apply_area_visible(ignite_poison_objects, pow);
    apply_area_visible(ignite_poison_monsters, pow);
}                               // end cast_ignite_poison()

void cast_silence(int pow)
{
    if (!you.attribute[ATTR_WAS_SILENCED])
#ifdef JP 
        mpr("深い静寂があなたをすっぽり包み込んだ。");
#else
        mpr("A profound silence engulfs you.");
#endif

    you.attribute[ATTR_WAS_SILENCED] = 1;

    you.duration[DUR_SILENCE] += 10 + random2avg( pow, 2 );

    if (you.duration[DUR_SILENCE] > 100)
        you.duration[DUR_SILENCE] = 100;
}                               // end cast_silence()


/* ******************************************************************
// no hooks for this anywhere {dlb}:

void cast_animate_golem(int pow)
{
  // must have more than 20 max_hitpoints

  // must be wielding a Scroll of Paper (for chem)

  // must be standing on a pile of <foo> (for foo in: wood, metal, rock, stone)

  // Will cost you 5-10% of max_hitpoints, or 20 + some, whichever is more
  mpr("You imbue the inanimate form with a portion of your life force.");

  naughty(NAUGHTY_CREATED_LIFE, 10);
}

****************************************************************** */

static int discharge_monsters( int x, int y, int pow, int garbage )
{
    UNUSED( garbage );

    const int mon = mgrd[x][y];
    int damage = 0;

    struct bolt beam;
    beam.flavour = BEAM_ELECTRICITY; // used for mons_adjust_flavoured

    if (x == you.x_pos && y == you.y_pos)
    {
#ifdef JP 
        mpr( "あなたは稲妻に撃たれた。" );
#else
        mpr( "You are struck by lightning." );
#endif
        damage = 3 + random2( 5 + pow / 10 );
        damage = check_your_resists( damage, BEAM_ELECTRICITY );
        ouch( damage, 0, KILLED_BY_WILD_MAGIC );
    } 
    else if (mon == NON_MONSTER)
        return (0);
    else if (mons_res_elec(&menv[mon]) > 0 || mons_flies(&menv[mon]))
        return (0);
    else
    {
        damage = 3 + random2( 5 + pow / 10 );
        damage = mons_adjust_flavoured( &menv[mon], beam, damage );

        if (damage)
        {
            strcpy( info, ptr_monam( &(menv[mon]), DESC_CAP_THE ) );
#ifdef JP 
            strcat( info, "は稲妻に撃たれた。" );
#else
            strcat( info, " is struck by lightning." );
#endif
            mpr( info );

            player_hurt_monster( mon, damage );
        }
    }

    // Recursion to give us chain-lightning -- bwr
    // Low power slight chance added for low power characters -- bwr
    if ((pow >= 10 && !one_chance_in(3)) || (pow >= 3 && one_chance_in(10)))
    {
#ifdef JP 
        mpr( "稲妻が電弧を成した！" );
#else
        mpr( "The lightning arcs!" );
#endif
        pow /= (coinflip() ? 2 : 3);
        damage += apply_random_around_square( discharge_monsters, x, y, 
                                              true, pow, 1 ); 
    }
    else if (damage > 0)
    {
        // Only printed if we did damage, so that the messages in 
        // cast_discharge() are clean. -- bwr
#ifdef JP 
        mpr( "稲妻は地面を直撃した。" );
#else
        mpr( "The lightning grounds out." );
#endif
    }

    return (damage);
}                               // end discharge_monsters() 

void cast_discharge( int pow )
{
    int num_targs = 1 + random2( 1 + pow / 25 );
    int dam;

    dam = apply_random_around_square( discharge_monsters, you.x_pos, you.y_pos,
                                      true, pow, num_targs );

#if DEBUG_DIAGNOSTICS
#ifdef JP 
    snprintf( info, INFO_SIZE, "Arcs: %d Damage: %d", num_targs, dam );
#else
    snprintf( info, INFO_SIZE, "Arcs: %d Damage: %d", num_targs, dam );
#endif
    mpr( info, MSGCH_DIAGNOSTICS );
#endif

    if (dam == 0) 
    {
        if (coinflip())
#ifdef JP 
            mpr("あなたの周りの大気が電荷にバチバチと音を立てた。");
#else
            mpr("The air around you crackles with electrical energy.");
#endif
        else 
        {
            bool plural = coinflip();
#ifdef JP 
            snprintf( info, INFO_SIZE, "%s蒼い電弧があなたには害を及ぼすことなく%s地面に落ちた。",
                plural ? "幾つかの" : "",
                plural ? "周りの" : (coinflip() ? "側方の" :
                                     coinflip() ? "後方の" : "前方の")
                );
#else
            snprintf( info, INFO_SIZE, "%s blue arc%s ground%s harmlessly %s you.",
                plural ? "Some" : "A",
                plural ? "s" : "",
                plural ? " themselves" : "s itself",
                plural ? "around" : (coinflip() ? "beside" :
                                     coinflip() ? "behind" : "before")
                );
#endif

            mpr(info);
        }
    }
}                               // end cast_discharge()

// NB: this must be checked against the same effects
// in fight.cc for all forms of attack !!! {dlb}
// This function should be currently unused (the effect is too powerful)
static int distortion_monsters(int x, int y, int pow, int message)
{
    int specdam = 0;
    int monster_attacked = mgrd[x][y];

    if (monster_attacked == NON_MONSTER)
        return 0;

    struct monsters *defender = &menv[monster_attacked];

    if (pow > 100)
        pow = 100;

    if (x == you.x_pos && y == you.y_pos)
    {
        if (you.skills[SK_TRANSLOCATIONS] < random2(8))
        {
            miscast_effect( SPTYP_TRANSLOCATION, pow / 9 + 1, pow, 100, 
#ifdef JP 
                            "歪曲の効果" );
#else
                            "a distortion effect" );
#endif
        }
        else
        {
            miscast_effect( SPTYP_TRANSLOCATION, 1, 1, 100, 
#ifdef JP 
                            "歪曲の効果" );
#else
                            "a distortion effect" );
#endif
        }

        return 1;
    }

    if (defender->type == MONS_BLINK_FROG)      // any others resist?
    {
        int hp = defender->hit_points;
        int max_hp = defender->max_hit_points;

#ifdef JP 
        mpr("またたきガエルは転位エネルギーに満たされた。");
#else
        mpr("The blink frog basks in the translocular energy.");
#endif

        if (hp < max_hp)
            hp += 1 + random2(1 + pow / 4) + random2(1 + pow / 7);

        if (hp > max_hp)
            hp = max_hp;

        defender->hit_points = hp;
        return 1;
    }
    else if (coinflip())
    {
#ifdef JP 
        strcpy(info, "");
#else
        strcpy(info, "Space bends around ");
#endif
        strcat(info, ptr_monam(defender, DESC_NOCAP_THE));
#ifdef JP 
        strcat(info, "の周りで空間が曲がった。");
#else
        strcat(info, ".");
#endif
        mpr(info);
        specdam += 1 + random2avg( 7, 2 ) + random2(pow) / 40;
    }
    else if (coinflip())
    {
#ifdef JP 
        strcpy(info, "");
#else
        strcpy(info, "Space warps horribly around ");
#endif
        strcat(info, ptr_monam( defender, DESC_NOCAP_THE ));
#ifdef JP 
        strcat(info, "の周りで空間が恐ろしく歪んだ！");
#else
        strcat(info, "!");
#endif
        mpr(info);

        specdam += 3 + random2avg( 12, 2 ) + random2(pow) / 25;
    }
    else if (one_chance_in(3))
    {
        monster_blink(defender);
        return 1;
    }
    else if (one_chance_in(3))
    {
        monster_teleport(defender, coinflip());
        return 1;
    }
    else if (one_chance_in(3))
    {
        monster_die(defender, KILL_RESET, 0);
        return 1;
    }
    else if (message)
    {
#ifdef JP 
        mpr("何も起こらなかったようだ。");
#else
        mpr("Nothing seems to happen.");
#endif
        return 1;
    }

    player_hurt_monster(monster_attacked, specdam);

    return (specdam);
}                               // end distortion_monsters()

void cast_bend(int pow)
{
    apply_one_neighbouring_square( distortion_monsters, pow );
}                               // end cast_bend()

// Really this is just applying the best of Band/Warp weapon/Warp field
// into a spell that gives the "make monsters go away" benefit without
// the insane damage potential.  -- bwr
int disperse_monsters(int x, int y, int pow, int message)
{
    UNUSED( message );

    const int monster_attacked = mgrd[x][y];

    if (monster_attacked == NON_MONSTER)
        return 0;

    struct monsters *defender = &menv[monster_attacked];

    if (defender->type == MONS_BLINK_FROG)
    {
#ifdef JP 
        simple_monster_message(defender, "は呪文に抵抗した。");
#else
        simple_monster_message(defender, " resists.");
#endif
        return 1;
    }
    else if (check_mons_resist_magic(defender, pow))
    {
        if (coinflip())
        {
#ifdef JP 
            simple_monster_message(defender, "は幾らか呪文に抵抗した。");
#else
            simple_monster_message(defender, " partially resists.");
#endif
            monster_blink(defender);
        }
        else
#ifdef JP 
            simple_monster_message(defender, "は呪文に抵抗した。");
#else
            simple_monster_message(defender, " resists.");
#endif

        return 1;
    }
    else
    {
        monster_teleport( defender, true );
        return 1;
    }

    return 0;
}

void cast_dispersal(int pow)
{
    if (apply_area_around_square( disperse_monsters, 
                                  you.x_pos, you.y_pos, pow ) == 0)
    {
#ifdef JP 
        mpr( "あなたの周りの大気が短い間ちらちらと光った。" );
#else
        mpr( "There is a brief shimmering in the air around you." );
#endif
    }
}

static int spell_swap_func(int x, int y, int pow, int message)
{
    UNUSED( message );

    int monster_attacked = mgrd[x][y];

    if (monster_attacked == NON_MONSTER)
        return 0;

    struct monsters *defender = &menv[monster_attacked];

    if (defender->type == MONS_BLINK_FROG
        || check_mons_resist_magic( defender, pow ))
    {
#ifdef JP 
        simple_monster_message( defender, "は呪文に抵抗した。" );
#else
        simple_monster_message( defender, " resists." );
#endif
    }
    else
    {
        // Swap doesn't seem to actually swap, but just sets the
        // monster's location equal to the players... this being because
        // the acr.cc call is going to move the player afterwards (for
        // the regular friendly monster swap).  So we'll go through
        // standard swap procedure here... since we really want to apply
        // the same swap_places function as with friendly monsters...
        // see note over there. -- bwr
        int old_x = defender->x;
        int old_y = defender->y;

        if (swap_places( defender ))
        {
            you.x_pos = old_x;
            you.y_pos = old_y;
        }
    }

    return 1;
}

void cast_swap(int pow)
{
    apply_one_neighbouring_square( spell_swap_func, pow );
}

static int make_a_rot_cloud(int x, int y, int pow, int ctype)
{
    int next = 0, obj = mgrd[x][y];

    if (obj == NON_MONSTER)
        return 0;

    while (obj != NON_ITEM)
    {
        next = mitm[obj].link;

        if (mitm[obj].base_type == OBJ_CORPSES
            && mitm[obj].sub_type == CORPSE_BODY)
        {
            if (!mons_skeleton(mitm[obj].plus))
                destroy_item(obj);
            else
            {
                mitm[obj].sub_type = CORPSE_SKELETON;
                mitm[obj].special = 200;
                mitm[obj].colour = LIGHTGREY;
            }

            place_cloud(ctype, x, y,
                        (3 + random2(pow / 4) + random2(pow / 4) +
                         random2(pow / 4)));
            return 1;
        }

        obj = next;
    }

    return 0;
}                               // end make_a_rot_cloud()

int make_a_normal_cloud(int x, int y, int pow, int ctype)
{
    place_cloud( ctype, x, y, 
                (3 + random2(pow / 4) + random2(pow / 4) + random2(pow / 4)) );

    return 1;
}                               // end make_a_normal_cloud()

#if 0

static int make_a_random_cloud(int x, int y, int pow, int ctype)
{
    if (ctype == CLOUD_NONE)
        ctype = CLOUD_BLACK_SMOKE;

    unsigned char cloud_material;

    switch (random2(9))
    {
    case 0:
        cloud_material = CLOUD_FIRE;
        break;
    case 1:
        cloud_material = CLOUD_STINK;
        break;
    case 2:
        cloud_material = CLOUD_COLD;
        break;
    case 3:
        cloud_material = CLOUD_POISON;
        break;
    case 4:
        cloud_material = CLOUD_BLUE_SMOKE;
        break;
    case 5:
        cloud_material = CLOUD_STEAM;
        break;
    case 6:
        cloud_material = CLOUD_PURP_SMOKE;
        break;
    default:
        cloud_material = ctype;
        break;
    }

    // that last bit is equivalent to "random2(pow/4) + random2(pow/4)
    // + random2(pow/4)" {dlb}
    // can you see the pattern? {dlb}
    place_cloud(cloud_material, x, y, 3 + random2avg(3 * (pow / 4) - 2, 3));

    return 1;
}                               // end make_a_random_cloud()

#endif

static int passwall(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    char dx, dy, nx = x, ny = y;
    int howdeep = 0;
    bool done = false;
    int shallow = 1 + (you.skills[SK_EARTH_MAGIC] / 8);

    // allow statues as entry points?
    if (grd[x][y] != DNGN_ROCK_WALL)
        // Irony: you can start on a secret door but not a door.
        // Worked stone walls are out, they're not diggable and
        // are used for impassable walls... I'm not sure we should
        // even allow statues (should be contiguous rock) -- bwr
    {
#ifdef JP 
        mpr("その壁は通過が不可能だ。");
#else
        mpr("That's not a passable wall.");
#endif
        return 0;
    }

    dx = x - you.x_pos;
    dy = y - you.y_pos;

    while (!done)
    {
        // I'm trying to figure proper borders out {dlb}
        // FIXME: dungeon border?
        if (nx > (GXM - 1) || ny > (GYM - 1) || nx < 2 || ny < 2)
        {
#ifdef JP 
            mpr("あなたは圧倒的な体積の岩を知覚した。");
#else
            mpr("You sense an overwhelming volume of rock.");
#endif
            return 0;
        }

        switch (grd[nx][ny])
        {
        default:
            done = true;
            break;
        case DNGN_ROCK_WALL:
        case DNGN_ORCISH_IDOL:
        case DNGN_GRANITE_STATUE:
        case DNGN_SECRET_DOOR:
            nx += dx;
            ny += dy;
            howdeep++;
            break;
        }
    }

    int range = shallow + random2(pow) / 25;

    if (howdeep > shallow)
    {
#ifdef JP 
        mpr("この岩は非常に厚いようだ。");
#else
        mpr("This rock feels deep.");
#endif

#ifdef JP 
        if (yesno("どうしても実行しますか？"))
#else
        if (yesno("Try anyway?"))
#endif
        {
            if (howdeep > range)
            {
                ouch(1 + you.hp, 0, KILLED_BY_PETRIFICATION);
                //jmf: not return; if wizard, successful transport is option
            }
        }
        else
        {
            if (one_chance_in(30))
#ifdef JP 
                mpr("意気地なし。");
#else
                mpr("Wuss.");
#endif
            else
                canned_msg(MSG_OK);
            return 1;
        }
    }

    // Note that the delay was (1 + howdeep * 2), but now that the
    // delay is stopped when the player is attacked it can be much
    // shorter since its harder to use for quick escapes. -- bwr
    start_delay( DELAY_PASSWALL, 2 + howdeep, nx, ny );

    return 1;
}                               // end passwall()

void cast_passwall(int pow)
{
    apply_one_neighbouring_square(passwall, pow);
}                               // end cast_passwall()

static int intoxicate_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( pow );
    UNUSED( garbage );

    int mon = mgrd[x][y];

    if (mon == NON_MONSTER)
        return 0;
    if (mons_intel(menv[mon].type) < I_NORMAL)
        return 0;
    if (mons_holiness(menv[mon].type) != MH_NATURAL)
        return 0;
    if (mons_res_poison(&menv[mon]) > 0)
        return 0;

    mons_add_ench(&menv[mon], ENCH_CONFUSION);
    return 1;
}                               // end intoxicate_monsters()

void cast_intoxicate(int pow)
{
    potion_effect( POT_CONFUSION, 10 + (100 - pow) / 10);

    if (one_chance_in(20) && lose_stat( STAT_INTELLIGENCE, 1 + random2(3) ))
#ifdef JP 
        mpr("あなたの頭はクルクル回った！");
#else
        mpr("Your head spins!");
#endif

    apply_area_visible(intoxicate_monsters, pow);
}                               // end cast_intoxicate()

// intended as a high-level Elven (a)bility
static int glamour_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    int mon = mgrd[x][y];

    // Power in this function is already limited by a function of
    // experience level (10 + level / 2) since it's only an ability,
    // never an actual spell. -- bwr

    if (mon == NON_MONSTER)
        return (0);

    if (one_chance_in(5))
        return (0);

    if (mons_intel(menv[mon].type) < I_NORMAL)
        return (0);

    if (mons_holiness(mon) != MH_NATURAL)
        return (0);

    if (!mons_is_humanoid( menv[mon].type ))
        return (0);

    const char show_char = mons_char( menv[mon].type );

    // gargoyles are immune.
    if (menv[mon].type == MONS_GARGOYLE
        || menv[mon].type == MONS_METAL_GARGOYLE
        || menv[mon].type == MONS_MOLTEN_GARGOYLE)
    {
        return (0);
    }

    // orcs resist thru hatred of elves
    // elves resist cause they're elves
    // boggarts are malevolent highly magical wee-folk
    if (show_char == 'o' || show_char == 'e' || menv[mon].type == MONS_BOGGART)
        pow = (pow / 2) + 1;

    if (check_mons_resist_magic(&menv[mon], pow))
        return (0);

    switch (random2(6))
    {
    case 0:
        mons_add_ench(&menv[mon], ENCH_FEAR);
        break;
    case 1:
    case 4:
        mons_add_ench(&menv[mon], ENCH_CONFUSION);
        break;
    case 2:
    case 5:
        mons_add_ench(&menv[mon], ENCH_CHARM);
        break;
    case 3:
        menv[mon].behaviour = BEH_SLEEP;
        break;
    }

    // why no, there's no message as to which effect happened >:^)
    if (!one_chance_in(4))
    {
        strcpy(info, ptr_monam( &(menv[mon]), DESC_CAP_THE));

        switch (random2(4))
        {
        case 0:
#ifdef JP 
            strcat(info, "は茫然とした様子だ。");
#else
            strcat(info, " looks dazed.");
#endif
            break;
        case 1:
#ifdef JP 
            strcat(info, "パチパチとまばたきした。");
#else
            strcat(info, " blinks several times.");
#endif
            break;
        case 2:
#ifdef JP 
            strcat(info, "目をこすった");
#else
            strcat(info, " rubs its eye");
#endif
            if (menv[mon].type != MONS_CYCLOPS)
#ifdef JP 
                strcat(info, "");
            strcat(info, "。");
#else
                strcat(info, "s");
            strcat(info, ".");
#endif
            break;
        case 4:
#ifdef JP 
            strcat(info, "首を傾げた。");
#else
            strcat(info, " tilts its head.");
#endif
            break;
        }

        mpr(info);
    }

    return (1);
}                               // end glamour_monsters()

void cast_glamour(int pow)
{
    apply_area_visible(glamour_monsters, pow);
}                               // end cast_glamour()

bool backlight_monsters(int x, int y, int pow, int garbage)
{
    UNUSED( pow );
    UNUSED( garbage );

    int mon = mgrd[x][y];

    if (mon == NON_MONSTER)
        return (false);

    switch (menv[mon].type)
    {
    //case MONS_INSUBSTANTIAL_WISP: //jmf: I'm not sure if these glow or not
    //case MONS_VAPOUR:
    case MONS_UNSEEN_HORROR:    // consider making this visible? probably not.
        return (false);

    case MONS_FIRE_VORTEX:
    case MONS_ANGEL:
    case MONS_FIEND:
    case MONS_SHADOW:
    case MONS_EFREET:
    case MONS_HELLION:
    case MONS_GLOWING_SHAPESHIFTER:
    case MONS_FIRE_ELEMENTAL:
    case MONS_AIR_ELEMENTAL:
    case MONS_SHADOW_FIEND:
    case MONS_SPECTRAL_WARRIOR:
    case MONS_ORANGE_RAT:
    case MONS_BALRUG:
    case MONS_SPATIAL_VORTEX:
    case MONS_PIT_FIEND:
    case MONS_SHINING_EYE:
    case MONS_DAEVA:
    case MONS_SPECTRAL_THING:
    case MONS_ORB_OF_FIRE:
    case MONS_EYE_OF_DEVASTATION:
        return (false);               // already glowing or invisible
    default:
        break;
    }

    int lvl = mons_has_ench( &menv[mon], ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_IV );

    if (lvl == ENCH_NONE)
#ifdef JP 
        simple_monster_message( &menv[mon], "は光で照らし出された。" );
#else
        simple_monster_message( &menv[mon], " is outlined in light." );
#endif
    else if (lvl == ENCH_BACKLIGHT_IV)
#ifdef JP 
        simple_monster_message( &menv[mon], "は一瞬、より明るく輝いた。" );
#else
        simple_monster_message( &menv[mon], " glows brighter for a moment." );
#endif
    else 
    {
        // remove old level
        mons_del_ench( &menv[mon], ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_III, true );
#ifdef JP 
        simple_monster_message( &menv[mon], "は、より明るく輝いた。" );
#else
        simple_monster_message( &menv[mon], " glows brighter." );
#endif
    }

    // this enchantment wipes out invisibility (neat)
    mons_del_ench( &menv[mon], ENCH_INVIS );
    mons_add_ench( &menv[mon], ENCH_BACKLIGHT_IV );

    return (true);
}                               // end backlight_monsters()

void cast_evaporate(int pow)
{
    // experimenting with allowing the potion to be thrown... we're
    // still making it have to be "in hands" at this point. -- bwr
    struct dist spelld;
    struct bolt beem;

#ifdef JP 
    const int potion = prompt_invent_item( "どのポーションを投げますか？", OBJ_POTIONS );
#else
    const int potion = prompt_invent_item( "Throw which potion?", OBJ_POTIONS );
#endif

    if (potion == -1)
    {
#ifdef JP 
        snprintf( info, INFO_SIZE, "あなたの%sから蒸気が噴出した！", 
#else
        snprintf( info, INFO_SIZE, "Wisps of steam play over your %s!", 
#endif
                  your_hand(true) );

        mpr(info);
        return;
    } 
    else if (you.inv[potion].base_type != OBJ_POTIONS)
    {
#ifdef JP 
        mpr( "この呪文はポーションにのみ作用する！" );
#else
        mpr( "This spell works only on potions!" );
#endif
        canned_msg(MSG_SPELL_FIZZLES);
        return;
    }

    mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT );

    message_current_target();

    direction( spelld, DIR_NONE, TARG_ENEMY );

    if (!spelld.isValid)
    {
        canned_msg(MSG_SPELL_FIZZLES);
        return;
    }

    beem.target_x = spelld.tx;
    beem.target_y = spelld.ty;

    beem.source_x = you.x_pos;
    beem.source_y = you.y_pos;

#ifdef JP 
    strcpy( beem.beam_name, "ポーション" );
#else
    strcpy( beem.beam_name, "potion" );
#endif
    beem.colour = you.inv[potion].colour;
    beem.range = 9;
    beem.rangeMax = 9;
    beem.type = SYM_FLASK;
    beem.beam_source = MHITYOU;
    beem.thrower = KILL_YOU_MISSILE;
    beem.aux_source = NULL;
    beem.isBeam = false;
    beem.isTracer = false;

    beem.hit = you.dex / 2 + roll_dice( 2, you.skills[SK_THROWING] / 2 + 1 );
    beem.damage = dice_def( 1, 0 );  // no damage, just producing clouds
    beem.ench_power = pow;           // used for duration only?

    beem.flavour = BEAM_POTION_STINKING_CLOUD;

    switch (you.inv[potion].sub_type)
    {
    case POT_STRONG_POISON:
        beem.flavour = BEAM_POTION_POISON;
        beem.ench_power *= 2;
        break;

    case POT_DEGENERATION:
        beem.flavour = (coinflip() ? BEAM_POTION_POISON : BEAM_POTION_MIASMA);
        beem.ench_power *= 2;
        break;
            
    case POT_POISON:
        beem.flavour = BEAM_POTION_POISON;
        break;

    case POT_DECAY:
        beem.flavour = BEAM_POTION_MIASMA;
        beem.ench_power *= 2;
        break;

    case POT_PARALYSIS:
        beem.ench_power *= 2;
        // fall through
    case POT_CONFUSION:
    case POT_SLOWING:
        beem.flavour = BEAM_POTION_STINKING_CLOUD;
        break;

    case POT_WATER:
    case POT_PORRIDGE:
        beem.flavour = BEAM_POTION_STEAM;
        break;

    case POT_BERSERK_RAGE:
        beem.flavour = (coinflip() ? BEAM_POTION_FIRE : BEAM_POTION_STEAM);
        break;

    case POT_MUTATION:
    case POT_GAIN_STRENGTH:
    case POT_GAIN_DEXTERITY:
    case POT_GAIN_INTELLIGENCE:
    case POT_EXPERIENCE:
    case POT_MAGIC:
        switch (random2(5))
        {
        case 0:   beem.flavour = BEAM_POTION_FIRE;            break;
        case 1:   beem.flavour = BEAM_POTION_COLD;            break;
        case 2:   beem.flavour = BEAM_POTION_POISON;          break;
        case 3:   beem.flavour = BEAM_POTION_MIASMA;          break;
        default:  beem.flavour = BEAM_POTION_RANDOM;          break;
        }
        break;

    default:
        switch (random2(12))
        {
        case 0:   beem.flavour = BEAM_POTION_FIRE;            break;
        case 1:   beem.flavour = BEAM_POTION_STINKING_CLOUD;  break;
        case 2:   beem.flavour = BEAM_POTION_COLD;            break;
        case 3:   beem.flavour = BEAM_POTION_POISON;          break;
        case 4:   beem.flavour = BEAM_POTION_RANDOM;          break;
        case 5:   beem.flavour = BEAM_POTION_BLUE_SMOKE;      break;
        case 6:   beem.flavour = BEAM_POTION_BLACK_SMOKE;     break;
        case 7:   beem.flavour = BEAM_POTION_PURP_SMOKE;      break;
        default:  beem.flavour = BEAM_POTION_STEAM;           break;
        }
        break;
    }

    if (coinflip())
        exercise( SK_THROWING, 1 );

    fire_beam(beem);

    // both old and new code use up a potion:
    dec_inv_item_quantity( potion, 1 );

    return;
}                               // end cast_evaporate()

// The intent of this spell isn't to produce helpful potions 
// for drinking, but rather to provide ammo for the Evaporate
// spell out of corpses, thus potentially making it useful.  
// Producing helpful potions would break game balance here...
// and producing more than one potion from a corpse, or not 
// using up the corpse might also lead to game balance problems. -- bwr
void cast_fulsome_distillation( int powc )
{
    char str_pass[ ITEMNAME_SIZE ];

    if (powc > 50)
        powc = 50;

    int corpse = -1;

    // Search items at the players location for corpses.
    // XXX: Turn this into a separate function and merge with 
    // the messes over in butchery, animating, and maybe even 
    // item pickup from stacks (which would make it easier to 
    // create a floor stack menu system later) -- bwr
    for (int curr_item = igrd[you.x_pos][you.y_pos]; 
             curr_item != NON_ITEM; 
             curr_item = mitm[curr_item].link) 
    {
        if (mitm[curr_item].base_type == OBJ_CORPSES
            && mitm[curr_item].sub_type == CORPSE_BODY)
        {
            it_name( curr_item, DESC_NOCAP_THE, str_pass );
#ifdef JP 
            snprintf( info, INFO_SIZE, "%sからポーションを精製しますか？", str_pass );
#else
            snprintf( info, INFO_SIZE, "Distill a potion from %s?", str_pass );
#endif

            if (yesno( info, true, false ))
            {
                corpse = curr_item;
                break;
            }
        }
    }

    if (corpse == -1)
    {
        canned_msg(MSG_SPELL_FIZZLES);
        return;
    }

    const bool rotten = (mitm[corpse].special < 100);
    const bool big_monster = (mons_type_hit_dice( mitm[corpse].plus ) >= 5);
    const bool power_up = (rotten && big_monster);

    int potion_type = POT_WATER;

    switch (mitm[corpse].plus)
    {
    case MONS_GIANT_BAT:             // extracting batty behaviour : 1
    case MONS_UNSEEN_HORROR:         // extracting batty behaviour : 7
    case MONS_GIANT_BLOWFLY:         // extracting batty behaviour : 5
        potion_type = POT_CONFUSION;
        break;

    case MONS_RED_WASP:              // paralysis attack : 8
    case MONS_YELLOW_WASP:           // paralysis attack : 4
        potion_type = POT_PARALYSIS;
        break;

    case MONS_SNAKE:                 // clean meat, but poisonous attack : 2
    case MONS_GIANT_ANT:             // clean meat, but poisonous attack : 3
        potion_type = (power_up ? POT_POISON : POT_CONFUSION);
        break;

    case MONS_ORANGE_RAT:            // poisonous meat, but draining attack : 3
        potion_type = (power_up ? POT_DECAY : POT_POISON);
        break;

    case MONS_SPINY_WORM:            // 12
        potion_type = (power_up ? POT_DECAY : POT_STRONG_POISON);
        break;

    default:
        switch (mons_corpse_thingy( mitm[corpse].plus ))
        {
        case CE_CLEAN:
            potion_type = (power_up ? POT_CONFUSION : POT_WATER);
            break;

        case CE_CONTAMINATED:
            potion_type = (power_up ? POT_DEGENERATION : POT_POISON);
            break;

        case CE_POISONOUS:
            potion_type = (power_up ? POT_STRONG_POISON : POT_POISON);
            break;

        case CE_MUTAGEN_RANDOM: 
        case CE_MUTAGEN_GOOD:   // unused
        case CE_RANDOM:         // unused
            potion_type = POT_MUTATION;
            break;

        case CE_MUTAGEN_BAD:    // unused
        case CE_ROTTEN:         // actually this only occurs via mangling 
        case CE_HCL:            // necrophage
            potion_type = (power_up ? POT_DECAY : POT_STRONG_POISON);
            break;

        case CE_NOCORPSE:       // shouldn't occur
        default:
            break;
        }
        break;
    }

    // If not powerful enough, we downgrade the potion
    if (random2(50) > powc + 10 * rotten)
    {
        switch (potion_type)
        {
        case POT_DECAY: 
        case POT_DEGENERATION: 
        case POT_STRONG_POISON: 
            potion_type = POT_POISON;
            break;

        case POT_MUTATION: 
        case POT_POISON: 
            potion_type = POT_CONFUSION;
            break;

        case POT_PARALYSIS: 
            potion_type = POT_SLOWING;
            break;

        case POT_CONFUSION: 
        case POT_SLOWING: 
        default:
            potion_type = POT_WATER;
            break;
        }
    }

    // We borrow the corpse's object to make our potion:
    mitm[corpse].base_type = OBJ_POTIONS;
    mitm[corpse].sub_type = potion_type;
    mitm[corpse].quantity = 1;
    mitm[corpse].plus = 0;
    mitm[corpse].plus2 = 0;
    item_colour( mitm[corpse] );  // sets special as well

    it_name( corpse, DESC_NOCAP_A, str_pass );
#ifdef JP 
    snprintf( info, INFO_SIZE, "あなたは死体から%sを抽出した。",
#else
    snprintf( info, INFO_SIZE, "You extract %s from the corpse.",
#endif
              str_pass );
    mpr( info );

    // try to move the potion to the player (for convenience)
    if (move_item_to_player( corpse, 1 ) != 1)
    {
#ifdef JP 
        mpr( "残念ながら、あなたはこれ以上持ち運ぶことができない！" );
#else
        mpr( "Unfortunately, you can't carry it right now!" );
#endif
    }
}

void make_shuggoth(int x, int y, int hp)
{
    int mon = create_monster( MONS_SHUGGOTH, 100 + random2avg(58, 3),
                              BEH_HOSTILE, x, y, MHITNOT, 250 );

    if (mon != -1)
    {
        menv[mon].hit_points = hp;
        menv[mon].max_hit_points = hp;
    }

    return;
}                               // end make_shuggoth()

static int rot_living(int x, int y, int pow, int message)
{
    UNUSED( message );

    int mon = mgrd[x][y];
    int ench;

    if (mon == NON_MONSTER)
        return 0;

    if (mons_holiness(menv[mon].type) != MH_NATURAL)
        return 0;

    if (check_mons_resist_magic(&menv[mon], pow))
        return 0;

    ench = ((random2(pow) + random2(pow) + random2(pow) + random2(pow)) / 4);

    if (ench >= 50)
        ench = ENCH_YOUR_ROT_IV;
    else if (ench >= 35)
        ench = ENCH_YOUR_ROT_III;
    else if (ench >= 20)
        ench = ENCH_YOUR_ROT_II;
    else
        ench = ENCH_YOUR_ROT_I;

    mons_add_ench(&menv[mon], ench);

    return 1;
}                               // end rot_living()

static int rot_undead(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    int mon = mgrd[x][y];
    int ench;

    if (mon == NON_MONSTER)
        return 0;

    if (mons_holiness(menv[mon].type) != MH_UNDEAD)
        return 0;

    if (check_mons_resist_magic(&menv[mon], pow))
        return 0;

    // this does not make sense -- player mummies are
    // immune to rotting (or have been) -- so what is
    // the schema in use here to determine rotting??? {dlb}

    //jmf: up for discussion. it is clearly unfair to
    //     rot player mummies.
    //     the `shcema' here is: corporeal non-player undead
    //     rot, discorporeal undead don't rot. if you wanna
    //     insist that monsters get the same treatment as
    //     players, I demand my player mummies get to worship
    //     the evil mummy & orc god.
    switch (menv[mon].type)
    {
    case MONS_NECROPHAGE:
    case MONS_ZOMBIE_SMALL:
    case MONS_LICH:
    case MONS_MUMMY:
    case MONS_VAMPIRE:
    case MONS_ZOMBIE_LARGE:
    case MONS_WIGHT:
    case MONS_GHOUL:
    case MONS_BORIS:
    case MONS_ANCIENT_LICH:
    case MONS_VAMPIRE_KNIGHT:
    case MONS_VAMPIRE_MAGE:
    case MONS_GUARDIAN_MUMMY:
    case MONS_GREATER_MUMMY:
    case MONS_MUMMY_PRIEST:
        break;
    case MONS_ROTTING_HULK:
    default:
        return 0;               // immune (no flesh) or already rotting
    }

    ench = ((random2(pow) + random2(pow) + random2(pow) + random2(pow)) / 4);

    if (ench >= 50)
        ench = ENCH_YOUR_ROT_IV;
    else if (ench >= 35)
        ench = ENCH_YOUR_ROT_III;
    else if (ench >= 20)
        ench = ENCH_YOUR_ROT_II;
    else
        ench = ENCH_YOUR_ROT_I;

    mons_add_ench(&menv[mon], ench);

    return 1;
}                               // end rot_undead()

static int rot_corpses(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    return make_a_rot_cloud(x, y, pow, CLOUD_MIASMA);
}                               // end rot_corpses()

void cast_rotting(int pow)
{
    apply_area_visible(rot_living, pow);
    apply_area_visible(rot_undead, pow);
    apply_area_visible(rot_corpses, pow);
    return;
}                               // end cast_rotting()

void do_monster_rot(int mon)
{
    int damage = 1 + random2(3);

    if (mons_holiness(menv[mon].type) == MH_UNDEAD && random2(5))
    {
        apply_area_cloud(make_a_normal_cloud, menv[mon].x, menv[mon].y,
                         10, 1, CLOUD_MIASMA);
    }

    player_hurt_monster( mon, damage );
    return;
}                               // end do_monster_rot()

static int snake_charm_monsters(int x, int y, int pow, int message)
{
    UNUSED( message );

    int mon = mgrd[x][y];

    if (mon == NON_MONSTER)                             return 0;
    if (mons_friendly(&menv[mon]))                      return 0;
    if (one_chance_in(4))                               return 0;
    if (mons_char(menv[mon].type) != 'S')               return 0;
    if (check_mons_resist_magic(&menv[mon], pow))       return 0;

    menv[mon].attitude = ATT_FRIENDLY;
#ifdef JP 
    snprintf( info, INFO_SIZE, "%sは前後にふらついた。", ptr_monam( &(menv[mon]), DESC_CAP_THE ));
#else
    snprintf( info, INFO_SIZE, "%s sways back and forth.", ptr_monam( &(menv[mon]), DESC_CAP_THE ));
#endif
    mpr(info);

    return 1;
}

void cast_snake_charm(int pow)
{
    // powc = (you.experience_level * 2) + (you.skills[SK_INVOCATIONS] * 3);
    apply_one_neighbouring_square(snake_charm_monsters, pow);
}

void cast_fragmentation(int pow)        // jmf: ripped idea from airstrike
{
    struct dist beam;
    struct bolt blast;
    int debris = 0;
    int trap;
    bool explode = false;
    bool hole = true;
    const char *what = NULL;

#ifdef JP 
    mpr("何を砕きますか？(例:壁など)", MSGCH_PROMPT);
#else
    mpr("Fragment what (e.g. a wall)?", MSGCH_PROMPT);
#endif
    direction( beam, DIR_TARGET, TARG_ENEMY );

    if (!beam.isValid)
    {
        canned_msg(MSG_SPELL_FIZZLES);
        return;
    }

    //FIXME: if (player typed '>' to attack floor) goto do_terrain;
    blast.beam_source = MHITYOU;
    blast.thrower = KILL_YOU;
    blast.aux_source = NULL;
    blast.ex_size = 1;              // default
    blast.type = '#';
    blast.colour = 0;
    blast.target_x = beam.tx;
    blast.target_y = beam.ty;
    blast.isTracer = false;
    blast.flavour = BEAM_FRAG;

    // Number of dice vary... 3 is easy/common, but it can get as high as 6.
    blast.damage = dice_def( 0, 5 + pow / 10 ); 

    const int grid = grd[beam.tx][beam.ty];
    const int mon  = mgrd[beam.tx][beam.ty];

    const bool okay_to_dest = ((beam.tx > 5 && beam.tx < GXM - 5)
                                && (beam.ty > 5 && beam.ty < GYM - 5));

    if (mon != NON_MONSTER)
    {
        // This needs its own hand_buff... we also need to do it first
        // in case the target dies. -- bwr
        char explode_msg[80];

#ifdef JP 
        snprintf( explode_msg, sizeof( explode_msg ), "%sは爆発した！",
#else
        snprintf( explode_msg, sizeof( explode_msg ), "%s explodes!",
#endif
                  ptr_monam( &(menv[mon]), DESC_CAP_THE ) );

        switch (menv[mon].type)
        {
        case MONS_ICE_BEAST: // blast of ice fragments
        case MONS_SIMULACRUM_SMALL:
        case MONS_SIMULACRUM_LARGE:
            explode = true;
#ifdef JP 
            strcpy(blast.beam_name, "氷の爆風");
#else
            strcpy(blast.beam_name, "icy blast");
#endif
            blast.colour = WHITE;
            blast.damage.num = 2;
            blast.flavour = BEAM_ICE;
            if (player_hurt_monster(mon, roll_dice( blast.damage )))
                blast.damage.num += 1;
            break;

        case MONS_FLYING_SKULL:
        case MONS_SKELETON_SMALL:
        case MONS_SKELETON_LARGE:       // blast of bone
            explode = true;

#ifdef JP 
            snprintf( info, INFO_SIZE, "骸骨が爆発し、鋭い骨の破片を撒き散らした！" );
#else
            snprintf( info, INFO_SIZE, "The sk%s explodes into sharp fragments of bone!",
                    (menv[mon].type == MONS_FLYING_SKULL) ? "ull" : "eleton");
#endif

#ifdef JP 
            strcpy(blast.beam_name, "骨片の爆風");
#else
            strcpy(blast.beam_name, "blast of bone shards");
#endif

            blast.colour = LIGHTGREY;

            if (random2(50) < (pow / 5))        // potential insta-kill
            {
                monster_die(&menv[mon], KILL_YOU, 0);
                blast.damage.num = 4; 
            }
            else
            {
                blast.damage.num = 2; 
                if (player_hurt_monster(mon, roll_dice( blast.damage )))
                    blast.damage.num = 4;
            }
            goto all_done;      // i.e. no "Foo Explodes!"

        case MONS_WOOD_GOLEM:
            explode = false;
#ifdef JP 
            simple_monster_message(&menv[mon], "は猛烈に振動した！");
#else
            simple_monster_message(&menv[mon], " shudders violently!");
#endif

            // We use blast.damage not only for inflicting damage here, 
            // but so that later on we'll know that the spell didn't 
            // fizzle (since we don't actually explode wood golems). -- bwr
            blast.damage.num = 2; 
            player_hurt_monster( mon, roll_dice( blast.damage ) );
            break;

        case MONS_IRON_GOLEM:
        case MONS_METAL_GARGOYLE:
            explode = true;
#ifdef JP 
            strcpy( blast.beam_name, "金属片の爆風" );
#else
            strcpy( blast.beam_name, "blast of metal fragments" );
#endif
            blast.colour = CYAN;
            blast.damage.num = 4;
            if (player_hurt_monster(mon, roll_dice( blast.damage )))
                blast.damage.num += 2;
            break;

        case MONS_CLAY_GOLEM:   // assume baked clay and not wet loam
        case MONS_STONE_GOLEM:
        case MONS_EARTH_ELEMENTAL:
        case MONS_GARGOYLE:
            explode = true;
            blast.ex_size = 2;
#ifdef JP 
            strcpy(blast.beam_name, "岩石片の爆風");
#else
            strcpy(blast.beam_name, "blast of rock fragments");
#endif
            blast.colour = BROWN;
            blast.damage.num = 3;
            if (player_hurt_monster(mon, roll_dice( blast.damage )))
                blast.damage.num += 1;
            break;

        case MONS_CRYSTAL_GOLEM:
            explode = true;
            blast.ex_size = 2;
#ifdef JP 
            strcpy(blast.beam_name, "水晶片の爆風");
#else
            strcpy(blast.beam_name, "blast of crystal shards");
#endif
            blast.colour = WHITE;
            blast.damage.num = 4;
            if (player_hurt_monster(mon, roll_dice( blast.damage )))
                blast.damage.num += 2;
            break;

        default:
            blast.damage.num = 1;  // to mark that a monster was targetted

            // Yes, this spell does lousy damage if the 
            // monster isn't susceptable. -- bwr 
            player_hurt_monster( mon, roll_dice( 1, 5 + pow / 25 ) );
            goto do_terrain;
        }

        mpr( explode_msg );
        goto all_done;
    }

  do_terrain:
    // FIXME: do nothing in Abyss & Pandemonium?

    switch (grid)
    {
    //
    // Stone and rock terrain
    //
    case DNGN_ROCK_WALL:
    case DNGN_SECRET_DOOR:
        blast.colour = env.rock_colour;
        // fall-through
    case DNGN_STONE_WALL:
#ifdef JP 
        what = "壁";
#else
        what = "wall";
#endif
        if (player_in_branch( BRANCH_HALL_OF_ZOT ))
            blast.colour = env.rock_colour;
        // fall-through
    case DNGN_ORCISH_IDOL:
        if (what == NULL)
#ifdef JP 
            what = "石像";
#else
            what = "stone idol";
#endif
        if (blast.colour == 0)
            blast.colour = DARKGREY;
        // fall-through
    case DNGN_GRANITE_STATUE:   // normal rock -- big explosion
        if (what == NULL)
#ifdef JP 
            what = "像";
#else
            what = "statue";
#endif

        explode = true;

#ifdef JP 
        strcpy(blast.beam_name, "岩石片の爆風");
#else
        strcpy(blast.beam_name, "blast of rock fragments");
#endif
        blast.damage.num = 3;
        if (blast.colour == 0)
            blast.colour = LIGHTGREY;

        if (okay_to_dest
            && (grid == DNGN_ORCISH_IDOL 
                || grid == DNGN_GRANITE_STATUE
                || (pow >= 40 && grid == DNGN_ROCK_WALL && one_chance_in(3))
                || (pow >= 60 && grid == DNGN_STONE_WALL && one_chance_in(10))))
        {
            // terrain blew up real good:
            blast.ex_size = 2;
            grd[beam.tx][beam.ty] = DNGN_FLOOR;
            debris = DEBRIS_ROCK;
        }
        break;

    //
    // Metal -- small but nasty explosion
    //

    case DNGN_METAL_WALL:       
#ifdef JP 
        what = "金属の壁";
#else
        what = "metal wall";
#endif
        blast.colour = CYAN;
        // fallthru
    case DNGN_SILVER_STATUE:
        if (what == NULL)
        {
#ifdef JP 
            what = "銀の像";
#else
            what = "silver statue";
#endif
            blast.colour = WHITE;
        }

        explode = true;
#ifdef JP 
        strcpy( blast.beam_name, "金属片の爆風" );
#else
        strcpy( blast.beam_name, "blast of metal fragments" );
#endif
        blast.damage.num = 4;

        if (okay_to_dest && pow >= 80 && random2(500) < pow / 5)
        {
            blast.damage.num += 2;
            grd[beam.tx][beam.ty] = DNGN_FLOOR;
            debris = DEBRIS_METAL;
        }
        break;

    //
    // Crystal  
    //

    case DNGN_GREEN_CRYSTAL_WALL:       // crystal -- large & nasty explosion
#ifdef JP 
        what = "水晶の壁";
#else
        what = "crystal wall";
#endif
        blast.colour = GREEN;
        // fallthru
    case DNGN_ORANGE_CRYSTAL_STATUE:
        if (what == NULL)
        {
#ifdef JP 
            what = "水晶の像";
#else
            what = "crystal statue";
#endif
            blast.colour = LIGHTRED; //jmf: == orange, right?
        }

        explode = true;
        blast.ex_size = 2;
#ifdef JP 
        strcpy(blast.beam_name, "水晶片の爆風");
#else
        strcpy(blast.beam_name, "blast of crystal shards");
#endif
        blast.damage.num = 5;

        if (okay_to_dest
            && ((grid == DNGN_GREEN_CRYSTAL_WALL && coinflip())
                || (grid == DNGN_ORANGE_CRYSTAL_STATUE 
                    && pow >= 50 && one_chance_in(10))))
        {
            blast.ex_size = coinflip() ? 3 : 2;
            grd[beam.tx][beam.ty] = DNGN_FLOOR;
            debris = DEBRIS_CRYSTAL;
        }
        break;

    // 
    // Traps
    //

    case DNGN_UNDISCOVERED_TRAP:
    case DNGN_TRAP_MECHANICAL:
        trap = trap_at_xy( beam.tx, beam.ty );
        if (trap != -1 
            && trap_category( env.trap[trap].type ) != DNGN_TRAP_MECHANICAL)
        {
            // non-mechanical traps don't explode with this spell -- bwr 
            break;
        }

        // undiscovered traps appear as exploding from the floor -- bwr
#ifdef JP 
        what = ((grid == DNGN_UNDISCOVERED_TRAP) ? "床" : "罠");
#else
        what = ((grid == DNGN_UNDISCOVERED_TRAP) ? "floor" : "trap");
#endif

        explode = true;
        hole = false;           // to hit monsters standing on traps
#ifdef JP 
        strcpy( blast.beam_name, "破片の爆風" );
#else
        strcpy( blast.beam_name, "blast of fragments" );
#endif
        blast.colour = env.floor_colour;  // in order to blend in
        blast.damage.num = 2;

        // Exploded traps are nonfunctional, ammo is also ruined -- bwr
        if (okay_to_dest)
        {
            grd[beam.tx][beam.ty] = DNGN_FLOOR;
            env.trap[trap].type = TRAP_UNASSIGNED;
        }
        break;

    //
    // Stone doors and arches
    //

    case DNGN_OPEN_DOOR:
    case DNGN_CLOSED_DOOR:
        // Doors always blow up, stone arches never do (would cause problems)
        if (okay_to_dest)
            grd[beam.tx][beam.ty] = DNGN_FLOOR;

        // fall-through
    case DNGN_STONE_ARCH:       // floor -- small explosion
        explode = true;
        hole = false;           // to hit monsters standing on doors
#ifdef JP 
        strcpy( blast.beam_name, "岩石片の爆風" );
#else
        strcpy( blast.beam_name, "blast of rock fragments" );
#endif
        blast.colour = LIGHTGREY;
        blast.damage.num = 2;
        break;

    //
    // Permarock and floor are unaffected -- bwr
    //
    case DNGN_PERMAROCK_WALL:
    case DNGN_FLOOR:
        explode = false;
#ifdef JP 
        snprintf( info, INFO_SIZE, "%sは不自然に硬いようだ。",
                  (grid == DNGN_PERMAROCK_WALL) ? "その壁" 
                                                : "そこの床" );
#else
        snprintf( info, INFO_SIZE, "%s seems to be unnaturally hard.",
                  (grid == DNGN_PERMAROCK_WALL) ? "That wall" 
                                                : "The dungeon floor" );
#endif
        explode = false;
        break;

    case DNGN_TRAP_III: // What are these? Should they explode? -- bwr
    default:
        // FIXME: cute message for water?
        break;
    }

  all_done:
    if (explode && blast.damage.num > 0)
    {
        if (what != NULL)
        {
#ifdef JP 
            snprintf( info, INFO_SIZE, "%sは爆発した！", what);
#else
            snprintf( info, INFO_SIZE, "The %s explodes!", what);
#endif
            mpr(info);
        }

        explosion( blast, hole );
    }
    else if (blast.damage.num == 0)
    {
        // if damage dice are zero we assume that nothing happened at all.
        canned_msg(MSG_SPELL_FIZZLES);
    }

    if (debris)
        place_debris(beam.tx, beam.ty, debris);
}                               // end cast_fragmentation()

void cast_twist(int pow)
{
    struct dist targ;
    struct bolt tmp;    // used, but ignored

    // level one power cap -- bwr
    if (pow > 25)
        pow = 25;

    // Get target,  using DIR_TARGET for targetting only,
    // since we don't use fire_beam() for this spell.
    if (spell_direction(targ, tmp, DIR_TARGET) == -1)
        return;

    const int mons = mgrd[ targ.tx ][ targ.ty ];

    // anything there?
    if (mons == NON_MONSTER || targ.isMe)
    {
#ifdef JP 
        mpr("そこにはモンスターがいない！");
#else
        mpr("There is no monster there!");
#endif
        return;
    }

    // Monster can magically save vs attack.
    if (check_mons_resist_magic( &menv[ mons ], pow * 2 ))
    {
#ifdef JP 
        simple_monster_message( &menv[ mons ], "は呪文に抵抗した。" );
#else
        simple_monster_message( &menv[ mons ], " resists." );
#endif
        return;
    }

    // Roll the damage... this spell is pretty low on damage, because
    // it can target any monster in LOS (high utility).  This is
    // similar to the damage done by Magic Dart (although, the
    // distribution is much more uniform). -- bwr
    int damage = 1 + random2( 3 + pow / 5 );

    // Inflict the damage
    player_hurt_monster( mons, damage );
    return;
}                               // end cast_twist()

//
// This version of far strike is a bit too creative for level one, in
// order to make it work we needed to put a lot of restrictions on it
// (like the damage limitation), which wouldn't be necessary if it were
// a higher level spell.  This code might come back as a high level
// translocation spell later (maybe even with special effects if it's
// using some of Josh's ideas about occasionally losing the weapon).
// Would potentially make a good high-level, second book Warper spell
// (since Translocations is a utility school, it should be higher level
// that usual... especially if it turns into a flavoured smiting spell).
// This can all wait until after the next release (as it would be better
// to have a proper way to do a single weapon strike here (you_attack
// does far more than we need or want here)). --bwr
//

void cast_far_strike(int pow)
{
    struct dist targ;
    struct bolt tmp;    // used, but ignored

    // Get target,  using DIR_TARGET for targetting only,
    // since we don't use fire_beam() for this spell.
    if (spell_direction(targ, tmp, DIR_TARGET) == -1)
        return;

    // Get the target monster...
    if (mgrd[targ.tx][targ.ty] == NON_MONSTER
        || targ.isMe)
    {
#ifdef JP 
        mpr("そこにはモンスターがいない！");
#else
        mpr("There is no monster there!");
#endif
        return;
    }

    //  Start with weapon base damage...
    const int weapon = you.equip[ EQ_WEAPON ];

    int damage = 3;     // default unarmed damage
    int speed  = 10;    // default unarmed time

    if (weapon != -1)   // if not unarmed
    {
        // look up the damage base
        if (you.inv[ weapon ].base_type == OBJ_WEAPONS)
        {
            damage = property( you.inv[ weapon ], PWPN_DAMAGE );
            speed = property( you.inv[ weapon ], PWPN_SPEED );

            if (get_weapon_brand( you.inv[ weapon ] ) == SPWPN_SPEED)
            {
                speed *= 5;
                speed /= 10;
            }
        }
        else if (item_is_staff( you.inv[ weapon ] ))
        {
            damage = property( you.inv[ weapon ], PWPN_DAMAGE );
            speed = property( you.inv[ weapon ], PWPN_SPEED );
        }
    }

    // Because we're casting a spell (and don't want to make this level
    // one spell too good), we're not applying skill speed bonuses and at
    // the very least guaranteeing one full turn (speed == 10) like the
    // other spells (if any thing else related to speed is changed, at
    // least leave this right before the application to you.time_taken).
    // Leaving skill out of the speed bonus is an important part of
    // keeping this spell from becoming a "better than actual melee"
    // spell... although, it's fine if that's the case for early Warpers,
    // Fighter types, and such that pick up this trivial first level spell,
    // shouldn't be using it instead of melee (but rather as an accessory
    // long range plinker).  Therefore, we tone things down to try and
    // guarantee that the spell is never begins to approach real combat
    // (although the magic resistance check might end up with a higher
    // hit rate than attacking against EV for high level Warpers). -- bwr
    if (speed < 10)
        speed = 10;

    you.time_taken *= speed;
    you.time_taken /= 10;

    // Apply strength only to damage (since we're only interested in
    // force here, not finesse... the dex/to-hit part of combat is
    // instead handled via magical ability).  This part could probably
    // just be removed, as it's unlikely to make any real difference...
    // if it is, the Warper stats in newgame.cc should be changed back
    // to the standard 6 int-4 dex of spellcasters. -- bwr
    int dammod = 78;
    const int dam_stat_val = you.strength;

    if (dam_stat_val > 11)
        dammod += (random2(dam_stat_val - 11) * 2);
    else if (dam_stat_val < 9)
        dammod -= (random2(9 - dam_stat_val) * 3);

    damage *= dammod;
    damage /= 78;

    struct monsters *monster = &menv[ mgrd[targ.tx][targ.ty] ];

    // apply monster's AC
    if (monster->armour_class > 0)
        damage -= random2( 1 + monster->armour_class );

#if 0
    // Removing damage limiter since it's categorized at level 4 right now.

    // Force transmitted is limited by skill...
    const int limit = (you.skills[SK_TRANSLOCATIONS] + 1) / 2 + 3;
    if (damage > limit)
        damage = limit;
#endif

    // Roll the damage...
    damage = 1 + random2( damage );

    // Monster can magically save vs attack (this could be replaced or
    // augmented with an EV check).
    if (check_mons_resist_magic( monster, pow * 2 ))
    {
#ifdef JP 
        simple_monster_message( monster, "は呪文に抵抗した。" );
#else
        simple_monster_message( monster, " resists." );
#endif
        return;
    }

    // Inflict the damage
    hurt_monster( monster, damage );
    if (monster->hit_points < 1)
        monster_die( monster, KILL_YOU, 0 );
    else
        print_wounds( monster );

    return;
}                               // end cast_far_strike()

void cast_apportation(int pow)
{
    struct dist beam;

#ifdef JP 
    mpr("どこのアイテムを引き寄せますか？");
#else
    mpr("Pull items from where?");
#endif

    direction( beam, DIR_TARGET );

    if (!beam.isValid)
    {
        canned_msg(MSG_SPELL_FIZZLES);
        return;
    }

    // it's already here!
    if (beam.isMe)
    {
#ifdef JP 
        mpr( "それは馬鹿げている。" );
#else
        mpr( "That's just silly." );
#endif
        return;
    }

    // Protect the player from destroying the item
    const int grid = grd[ you.x_pos ][ you.y_pos ];

    if (grid == DNGN_LAVA || grid == DNGN_DEEP_WATER)
    {
#ifdef JP 
        mpr( "この地形を越えて引き寄せようというのは馬鹿げている！" );
#else
        mpr( "That would be silly while over this terrain!" );
#endif
        return;
    }

    // If this is ever changed to allow moving objects that can't
    // be seen, it should at least only allow moving from squares
    // that have been phyisically (and maybe magically) seen and
    // should probably have a range check as well.  In these cases
    // the spell should probably be upped to at least two, or three
    // if magic mapped squares are allowed.  Right now it's okay
    // at one... it has a few uses, but you still have to get line
    // of sight to the object first so it will only help a little
    // with snatching runes or the orb (although it can be quite
    // useful for getting items out of statue rooms or the abyss). -- bwr
    if (!see_grid( beam.tx, beam.ty ))
    {
#ifdef JP 
        mpr( "あなたにはそこは見えない！" );
#else
        mpr( "You cannot see there!" );
#endif
        return;
    }

    // Let's look at the top item in that square...
    const int item = igrd[ beam.tx ][ beam.ty ];
    if (item == NON_ITEM)
    {
        const int  mon = mgrd[ beam.tx ][ beam.ty ];

        if (mon == NON_MONSTER)
#ifdef JP 
            mpr( "そこにはアイテムはない。" );
#else
            mpr( "There are no items there." );
#endif
        else if (mons_is_mimic( menv[ mon ].type ))
        {
#ifdef JP 
            snprintf( info, INFO_SIZE, "%sはびくりと引きつった。",
#else
            snprintf( info, INFO_SIZE, "%s twitches.",
#endif
                      ptr_monam( &(menv[ mon ]), DESC_CAP_THE ) );
            mpr( info );
        }
        else 
#ifdef JP 
            mpr( "この呪文はモンスターには作用しない。" );
#else
            mpr( "This spell does not work on creatures." );
#endif

        return;
    }

    // mass of one unit
    const int unit_mass = mass_item( mitm[ item ] );
    // assume we can pull everything
    int max_units = mitm[ item ].quantity;

    // item has mass: might not move all of them
    if (unit_mass > 0)
    {
        const int max_mass = pow * 30 + random2( pow * 20 );

        // most units our power level will allow
        max_units = max_mass / unit_mass;
    }

    if (max_units <= 0)
    {
#ifdef JP 
        mpr( "アイテムの山はあなたが引き寄せるのに抵抗している。" );
#else
        mpr( "The mass is resisting your pull." );
#endif
        return;
    }

    // Failure should never really happen after all the above checking,
    // but we'll handle it anyways...
    if (move_top_item( beam.tx, beam.ty, you.x_pos, you.y_pos ))
    {
        if (max_units < mitm[ item ].quantity)
        {
            mitm[ item ].quantity = max_units;
#ifdef JP 
            mpr( "あなたは幾つかのアイテムが虚空に失われたことを感じ取った。" );
#else
            mpr( "You feel that some mass got lost in the cosmic void." );
#endif
        }
        else
        {
#ifdef JP 
            mpr( "盗った！" );
            snprintf( info, INFO_SIZE, "あなたはアイテムを自分のところまで引き寄せた。" );
#else
            mpr( "Yoink!" );
            snprintf( info, INFO_SIZE, "You pull the item%s to yourself.",
                                 (mitm[ item ].quantity > 1) ? "s" : "" );
#endif
            mpr( info );
        }
    }
    else
#ifdef JP 
        mpr( "呪文は失敗した。" );
#else
        mpr( "The spell fails." );
#endif
}

void cast_sandblast(int pow)
{
    bool big = true;
    struct dist spd;
    struct bolt beam;

    // this type of power manipulation should be done with the others,
    // currently over in it_use2.cc (ack) -- bwr
    // int hurt = 2 + random2(5) + random2(4) + random2(pow) / 20;

    big = false;

    if (you.equip[EQ_WEAPON] != -1)
    {
        int wep = you.equip[EQ_WEAPON];
        if (you.inv[wep].base_type == OBJ_MISSILES
           && (you.inv[wep].sub_type == MI_STONE || you.inv[wep].sub_type == MI_LARGE_ROCK))
           big = true;
    }

    if (spell_direction(spd, beam) == -1)
        return;

    if (spd.isMe)
    {
        canned_msg(MSG_UNTHINKING_ACT);
        return;
    }

    if (big)
    {
        dec_inv_item_quantity( you.equip[EQ_WEAPON], 1 );
        zapping(ZAP_SANDBLAST, pow, beam);
    }
    else
    {
        zapping(ZAP_SMALL_SANDBLAST, pow, beam);
    }
}                               // end cast_sandblast()

static bool mons_can_host_shuggoth(int type)    //jmf: simplified
{
    if (mons_holiness(type) != MH_NATURAL)
        return false;
    if (mons_flag(type, M_WARM_BLOOD))
        return true;

    return false;
}

void cast_shuggoth_seed(int powc)
{
    struct dist beam;
    int i;

#ifdef JP 
    mpr("誰に種子を植え込みますか？", MSGCH_PROMPT);
#else
    mpr("Sow seed in whom?", MSGCH_PROMPT);
#endif

    direction( beam, DIR_TARGET, TARG_ENEMY );

    if (!beam.isValid)
    {
#ifdef JP 
        mpr("あなたは冷たい失望感を味わった。");
#else
        mpr("You feel a distant frustration.");
#endif
        return;
    }

    if (beam.isMe)
    {
        if (!you.is_undead)
        {
            you.duration[DUR_INFECTED_SHUGGOTH_SEED] = 10;
#ifdef JP 
            mpr("死のごとき恐怖の存在があなたの胸をびくりと痙攣させた。");
#else
            mpr("A deathly dread twitches in your chest.");
#endif
        }
        else
#ifdef JP 
            mpr("あなたは冷たい失望感を味わった。");
#else
            mpr("You feel a distant frustration.");
#endif
    }

    i = mgrd[beam.tx][beam.ty];

    if (i == NON_MONSTER)
    {
#ifdef JP 
        mpr("あなたは冷たい失望感を味わった。");
#else
        mpr("You feel a distant frustration.");
#endif
        return;
    }

    if (mons_can_host_shuggoth(menv[i].type))
    {
        if (random2(powc) > 100)
            mons_add_ench(&menv[i], ENCH_YOUR_SHUGGOTH_III);
        else
            mons_add_ench(&menv[i], ENCH_YOUR_SHUGGOTH_IV);

#ifdef JP 
        simple_monster_message(&menv[i], "はびくりと痙攣した。");
#else
        simple_monster_message(&menv[i], " twitches.");
#endif
    }

    return;
}

void cast_condensation_shield(int pow)
{
    if (you.equip[EQ_SHIELD] != -1 || you.fire_shield)
        canned_msg(MSG_SPELL_FIZZLES);
    else
    {
        if (you.duration[DUR_CONDENSATION_SHIELD] > 0)
            you.duration[DUR_CONDENSATION_SHIELD] += 5 + roll_dice(2, 3);
        else
        {
#ifdef JP 
            mpr("バチバチと音を立てる凝集した気塊が宙に形成された。");
#else
            mpr("A crackling disc of dense vapour forms in the air!");
#endif
            you.redraw_armour_class = 1;

            you.duration[DUR_CONDENSATION_SHIELD] = 10 + roll_dice(2, pow / 5);
        }

        if (you.duration[DUR_CONDENSATION_SHIELD] > 30)
            you.duration[DUR_CONDENSATION_SHIELD] = 30;
    }

    return;
}                               // end cast_condensation_shield()

static int quadrant_blink(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    if (x == you.x_pos && y == you.y_pos)
        return (0);

    if (you.level_type == LEVEL_ABYSS)
    {
        abyss_teleport( false );
        you.pet_target = MHITNOT;
        return (1);
    }

    if (pow > 100)
        pow = 100;

    int tx, ty;         // test x,y
    int rx, ry;         // x,y relative to you.
    int sx, sy;         // test point in the correct direction
    int bx = x;         // blank x
    int by = y;         // blank y
    int arx, ary;       // abs x, y

    bool vx, vy;        // valid?

    sx = (x - you.x_pos);
    sy = (y - you.y_pos);

    // for each point (a,b), distance from the line is | la + mb |


    if ( random_near_space(you.x_pos, you.y_pos, tx, ty) )
    {
        bx = tx;
        by = ty;
    }

    for(int tries = pow * pow / 500 + 1; tries > 0; tries--)
    {
        if (!random_near_space(you.x_pos, you.y_pos, tx, ty))
            return 0;

        rx = tx - you.x_pos;
        ry = ty - you.y_pos;

        arx = abs(rx);
        ary = abs(ry);

        switch (sx)
        {
        case -1:
            vx = (rx <= -1);
            break;
        case  0:
            vx = (arx*2 <= ary);
            break;
        case  1:
            vx = (rx >= 1);
            break;
        default:
            vx = false;
        }

        switch (sy)
        {
        case -1:
            vy = (ry <= -1);
            break;
        case  0:
            vy = (ary*2 <= arx);
            break;
        case  1:
            vy = (ry >= 1);
            break;
        default:
            vy = false;
        }

       if ( (vx)&&(vy) )
       {
           bx = tx;
           by = ty;
       }
    }

    you.x_pos = bx;
    you.y_pos = by;

    return (1);
}

/*
static int quadrant_blink(int x, int y, int pow, int garbage)
{
    UNUSED( garbage );

    if (x == you.x_pos && y == you.y_pos)
        return (0);

    if (you.level_type == LEVEL_ABYSS)
    {
        abyss_teleport( false );
        you.pet_target = MHITNOT;
        return (1);
    }

    if (pow > 100)
        pow = 100;

    // setup: Brent's new algorithm
    // we are interested in two things: distance of a test point from
    // the ideal 'line',  and the distance of a test point from two
    // actual points,  one in the 'correct' direction and one in the
    // 'incorrect' direction.

    // scale distance by 10 for more interesting numbers.
    int l,m;        // for line equation lx + my = 0
    l = (x - you.x_pos);
    m = (you.y_pos - y);

    int tx, ty;         // test x,y
    int rx, ry;         // x,y relative to you.
    int sx, sy;         // test point in the correct direction
    int bx = x;         // best x
    int by = y;         // best y

    int best_dist = 10000;

    sx = l;
    sy = -m;

    // for each point (a,b), distance from the line is | la + mb |

    for(int tries = pow * pow / 500 + 1; tries > 0; tries--)
    {
        if (!random_near_space(you.x_pos, you.y_pos, tx, ty))
            return 0;

        rx = tx - you.x_pos;
        ry = ty - you.y_pos;

        int dist = l * rx + m * ry;
        dist *= 10 * dist;      // square and multiply by 10

        // check distance to test points
        int dist1 = distance(rx, ry, sx, sy) * 10;
        int dist2 = distance(rx, ry, -sx, -sy) * 10;

        // 'good' points will always be closer to test point 1
        if (dist2 < dist1)
            dist += 80;          // make the point less attractive

        if (dist < best_dist)
        {
            best_dist = dist;
            bx = tx;
            by = ty;
        }
    }

    you.x_pos = bx;
    you.y_pos = by;

    return (1);
}
*/

void cast_semi_controlled_blink(int pow)
{
    apply_one_neighbouring_square(quadrant_blink, pow);
    return;
}

void cast_stoneskin(int pow)
{
    if (you.is_undead)
    {
#ifdef JP 
        mpr("この呪文はあなたのアンデッドの肉体には作用しない。");
#else
        mpr("This spell does not affect your undead flesh.");
#endif
        return;
    }

    if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE 
        && you.attribute[ATTR_TRANSFORMATION] != TRAN_STATUE
        && you.attribute[ATTR_TRANSFORMATION] != TRAN_BLADE_HANDS)
    {
#ifdef JP 
        mpr("この呪文はあなたの現在の形態には作用しない。");
#else
        mpr("This spell does not affect your current form.");
#endif
        return;
    }

    if (you.duration[DUR_STONEMAIL] || you.duration[DUR_ICY_ARMOUR])
    {
#ifdef JP 
        mpr("この呪文は現在効果中の他の呪文と相容れない。");
#else
        mpr("This spell conflicts with another spell still in effect.");
#endif
        return;
    }

    if (you.duration[DUR_STONESKIN])
#ifdef JP 
        mpr( "あなたの皮膚は固さを増した。" );
#else
        mpr( "Your skin feels harder." );
#endif
    else
    {
        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_STATUE)
#ifdef JP 
            mpr( "あなたの石の体はより強固になった。" );
#else
            mpr( "Your stone body feels more resilient." );
#endif
        else
#ifdef JP 
            mpr( "あなたの皮膚は固くなった。" );
#else
            mpr( "Your skin hardens." );
#endif

        you.redraw_armour_class = 1;
    }

    you.duration[DUR_STONESKIN] += 10 + random2(pow) + random2(pow);

    if (you.duration[DUR_STONESKIN] > 50)
        you.duration[DUR_STONESKIN] = 50;
}
