/*
 *  File:       spells1.cc
 *  Summary:    Implementations of some additional spells.
 *              Mostly Translocations.
 *  Written by: Linley Henzell
 *
 *  Modified for Crawl Reference by $Author: j-p-e-g $ on $Date: 2008-07-28 19:00:12 -0400 (Mon, 28 Jul 2008) $
 *
 *  Change History (most recent first):
 *
 *      <4>      06-mar-2000    bwr             confusing_touch, sure_blade
 *      <3>      9/11/99        LRH             Can't blink in the Abyss
 *      <3>      6/22/99        BWR             Removed teleport control from
 *                                              random_blink().
 *      <2>      5/20/99        BWR             Increased greatest healing.
 *      <1>      -/--/--        LRH             Created
 */

#include "AppHdr.h"
#include "spells1.h"

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

#include "externs.h"

#include "abyss.h"
#include "beam.h"
#include "cloud.h"
#include "directn.h"
#include "effects.h"
#include "invent.h"
#include "it_use2.h"
#include "itemname.h"
#include "itemprop.h"
#include "message.h"
#include "misc.h"
#include "monplace.h"
#include "monstuff.h"
#include "mon-util.h"
#include "player.h"
#include "randart.h"
#include "religion.h"
#include "skills2.h"
#include "spells2.h"
#include "spells3.h"
#include "spells4.h"
#include "spl-util.h"
#include "state.h"
#include "stuff.h"
#include "terrain.h"
#include "transfor.h"
#include "traps.h"
#include "view.h"

static bool _abyss_blocks_teleport(bool cblink)
{
    // Lugonu worshippers get their perks.
    if (you.religion == GOD_LUGONU)
        return (false);

    // Controlled Blink (the spell) works quite reliably in the Abyss.
    return (cblink ? one_chance_in(3) : !one_chance_in(3));
}

// If wizard_blink is set, all restriction are ignored (except for
// a monster being at the target spot), and the player gains no
// contamination.
int blink(int pow, bool high_level_controlled_blink, bool wizard_blink)
{
    dist beam;

    if (crawl_state.is_repeating_cmd())
    {
        crawl_state.cant_cmd_repeat("You can't repeat controlled blinks.");
        crawl_state.cancel_cmd_again();
        crawl_state.cancel_cmd_repeat();
        return(1);
    }

    // yes, there is a logic to this ordering {dlb}:
    if (scan_randarts(RAP_PREVENT_TELEPORTATION) && !wizard_blink)
        mpr("You feel a weird sense of stasis.");
    else if (you.level_type == LEVEL_ABYSS
             && _abyss_blocks_teleport(high_level_controlled_blink)
             && !wizard_blink)
    {
        mpr("The power of the Abyss keeps you in your place!");
    }
    else if (you.duration[DUR_CONF] && !wizard_blink)
        random_blink(false);
    else if (!allow_control_teleport(true) && !wizard_blink)
    {
        mpr("A powerful magic interferes with your control of the blink.");
        if (high_level_controlled_blink)
            return (cast_semi_controlled_blink(pow));
        random_blink(false);
    }
    else
    {
        // query for location {dlb}:
        while (true)
        {
            direction(beam, DIR_TARGET, TARG_ANY, -1, false, false, false,
                     false, "Blink to where?");

            if (!beam.isValid || coord_def(beam.tx, beam.ty) == you.pos())
            {
                if (!wizard_blink
                    && !yesno("Are you sure you want to cancel this blink?",
                              false, 'n'))
                {
                    mesclr();
                    continue;
                }
                canned_msg(MSG_OK);
                return (-1);         // early return {dlb}
            }

            // Wizard blink can move past translucent walls.
            if (see_grid_no_trans(beam.tx, beam.ty))
                break;
            else if (trans_wall_blocking( beam.tx, beam.ty ))
            {
                // Wizard blink can move past translucent walls.
                if (wizard_blink)
                    break;

                mesclr();
                mpr("You can't blink through translucent walls.");
            }
            else
            {
                mesclr();
                mpr("You can only blink to visible locations.");
            }
        }

        // Allow wizard blink to send player into walls, in case the
        // user wants to alter that grid to something else.
        if (wizard_blink && grid_is_solid(grd[beam.tx][beam.ty]))
            grd[beam.tx][beam.ty] = DNGN_FLOOR;

        if (grid_is_solid(grd[beam.tx][beam.ty])
            || mgrd[beam.tx][beam.ty] != NON_MONSTER)
        {
            mpr("Oops! Maybe something was there already.");
            random_blink(false);
        }
        else if (you.level_type == LEVEL_ABYSS && !wizard_blink)
        {
            abyss_teleport( false );
            if (you.pet_target != MHITYOU)
                you.pet_target = MHITNOT;
        }
        else
        {
            // no longer held in net
            clear_trapping_net();

            move_player_to_grid(beam.tx, beam.ty, false, true, true);

            // controlling teleport contaminates the player -- bwr
            if (!wizard_blink)
                contaminate_player( 1, true );
        }

        if (you.duration[DUR_CONDENSATION_SHIELD] > 0 && !wizard_blink)
        {
            you.duration[DUR_CONDENSATION_SHIELD] = 0;
            you.redraw_armour_class = true;
        }
    }

    crawl_state.cancel_cmd_again();
    crawl_state.cancel_cmd_repeat();

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

void random_blink(bool allow_partial_control, bool override_abyss)
{
    int tx, ty;
    bool succ = false;

    if (scan_randarts(RAP_PREVENT_TELEPORTATION))
        mpr("You feel a weird sense of stasis.");
    else if (you.level_type == LEVEL_ABYSS
             && !override_abyss && !one_chance_in(3))
    {
        mpr("The power of the Abyss keeps you in your place!");
    }
    // First try to find a random square not adjacent to the player,
    // then one adjacent if that fails.
    else if (!random_near_space(you.x_pos, you.y_pos, tx, ty)
             && !random_near_space(you.x_pos, you.y_pos, tx, ty, true))
    {
        mpr("You feel jittery for a moment.");
    }

#ifdef USE_SEMI_CONTROLLED_BLINK
    //jmf: Add back control, but effect is cast_semi_controlled_blink(pow).
    else if (player_control_teleport() && !you.duration[DUR_CONF]
             && allow_partial_control && allow_control_teleport())
    {
        mpr("You may select the general direction of your translocation.");
        cast_semi_controlled_blink(100);
        succ = true;
    }
#endif

    else
    {
        mpr("You blink.");

        // No longer held in net.
        clear_trapping_net();

        succ = true;
        you.moveto(tx, ty);

        if (you.level_type == LEVEL_ABYSS)
        {
            abyss_teleport( false );
            if (you.pet_target != MHITYOU)
                you.pet_target = MHITNOT;
        }
    }

    if (succ && you.duration[DUR_CONDENSATION_SHIELD] > 0)
    {
        you.duration[DUR_CONDENSATION_SHIELD] = 0;
        you.redraw_armour_class = true;
    }

    return;
}

bool fireball(int pow, bolt &beam)
{
    return (zapping(ZAP_FIREBALL, pow, beam, true));
}

void cast_fire_storm(int pow, bolt &beam)
{
    beam.name         = "great blast of fire";
    beam.ex_size      = 2 + (random2(pow) > 75);
    beam.flavour      = BEAM_LAVA;
    beam.type         = dchar_glyph(DCHAR_FIRED_ZAP);
    beam.colour       = RED;
    beam.beam_source  = MHITYOU;
    beam.thrower      = KILL_YOU_MISSILE;
    beam.aux_source.clear();
    beam.obvious_effect = false;
    beam.is_beam      = false;
    beam.is_tracer    = false;
    beam.is_explosion = true;
    beam.ench_power   = pow;      // used for radius
    beam.hit          = 20 + pow / 10;
    beam.damage       = calc_dice(9, 20 + pow);

    if (explosion(beam, false, false, true, true, false) > 0)
        mpr("A raging storm of fire appears!");

    viewwindow(true, false);
}

void cast_chain_lightning(int pow)
{
    bolt beam;

    // initialize beam structure
    beam.name           = "lightning arc";
    beam.aux_source     = "chain lightning";
    beam.beam_source    = MHITYOU;
    beam.thrower        = KILL_YOU_MISSILE;
    beam.range          = 8;
    beam.rangeMax       = 8;
    beam.hit            = AUTOMATIC_HIT;
    beam.type           = dchar_glyph(DCHAR_FIRED_ZAP);
    beam.flavour        = BEAM_ELECTRICITY;
    beam.obvious_effect = true;
    beam.is_beam        = false;       // since we want to stop at our target
    beam.is_explosion   = false;
    beam.is_tracer      = false;

    int sx, sy;
    int tx, ty;
    int i;

    for (sx = you.x_pos, sy = you.y_pos;
         pow > 0;
         pow -= 8 + random2(13), sx = tx, sy = ty)
    {
        // infinity as far as this spell is concerned
        // (Range - 1) is used because the distance is randomized and
        // may be shifted by one.
        int min_dist = MONSTER_LOS_RANGE - 1;

        int dist;
        int count = 0;

        tx = -1;
        ty = -1;

        for (i = 0; i < MAX_MONSTERS; i++)
        {
            struct monsters *monster = &menv[i];

            if (monster->type == -1)
                continue;

            dist = grid_distance( sx, sy, monster->x, monster->y );

            // check for the source of this arc
            if (!dist)
                continue;

            // randomize distance (arcs don't care about a couple of feet)
            dist += (random2(3) - 1);

            // always ignore targets further than current one
            if (dist > min_dist)
                continue;

            if (!check_line_of_sight( sx, sy, monster->x, monster->y ))
                continue;

            count++;

            if (dist < min_dist)
            {
                // switch to looking for closer targets (but not always)
                if (!one_chance_in(10))
                {
                    min_dist = dist;
                    tx = monster->x;
                    ty = monster->y;
                    count = 0;
                }
            }
            else if (tx == -1 || one_chance_in(count))
            {
                // either first target, or new selected target at min_dist
                tx = monster->x;
                ty = monster->y;

                // need to set min_dist for first target case
                dist = std::max(dist, min_dist);
            }
        }

        // now check if the player is a target
        dist = grid_distance(sx, sy, you.x_pos, you.y_pos);

        if (dist)       // i.e., player was not the source
        {
            // distance randomized (as above)
            dist += (random2(3) - 1);

            // select player if only, closest, or randomly selected
            if ((tx == -1
                    || dist < min_dist
                    || (dist == min_dist && one_chance_in(count + 1)))
                && check_line_of_sight(sx, sy, you.x_pos, you.y_pos))
            {
                tx = you.x_pos;
                ty = you.y_pos;
            }
        }

        const bool see_source = see_grid( sx, sy );
        const bool see_targ   = see_grid( tx, ty );

        if (tx == -1)
        {
            if (see_source)
                mpr("The lightning grounds out.");

            break;
        }

        // Trying to limit message spamming here so we'll only mention
        // the thunder when it's out of LoS.
        if (!see_source)
            noisy(25, sx, sy, "You hear a mighty clap of thunder!");

        if (see_source && !see_targ)
            mpr("The lightning arcs out of your line of sight!");
        else if (!see_source && see_targ)
            mpr("The lightning arc suddenly appears!");

        if (!see_grid_no_trans( tx, ty ))
        {
            // It's no longer in the caster's LOS and influence.
            pow = pow / 2 + 1;
        }

        beam.source_x = sx;
        beam.source_y = sy;
        beam.target_x = tx;
        beam.target_y = ty;
        beam.colour = LIGHTBLUE;
        beam.damage = calc_dice(5, 12 + pow * 2 / 3);

        // Be kinder to the player.
        if (tx == you.x_pos && ty == you.y_pos)
        {
            if (!(beam.damage.num /= 2))
                beam.damage.num = 1;
            if ((beam.damage.size /= 2) < 3)
                beam.damage.size = 3;
        }
        fire_beam( beam );
    }

    more();
}

void identify(int power, int item_slot)
{
    int id_used = 1;

    // Scrolls of identify *may* produce "extra" identifications.
    if (power == -1 && one_chance_in(5))
        id_used += (coinflip()? 1 : 2);

    do
    {
        if (item_slot == -1)
        {
            item_slot = prompt_invent_item( "Identify which item?", MT_INVLIST,
                                            OSEL_UNIDENT, true, true, false );
        }
        if (prompt_failed(item_slot))
            return;

        item_def& item(you.inv[item_slot]);

        if (fully_identified(item))
        {
            mpr("Choose an unidentified item, or Esc to abort.");
            if (Options.auto_list)
                more();
            item_slot = -1;
            continue;
        }

        set_ident_type( item, ID_KNOWN_TYPE );
        set_ident_flags( item, ISFLAG_IDENT_MASK );

        // For scrolls, now id the scroll, unless already known.
        if (power == -1
            && get_ident_type(OBJ_SCROLLS, SCR_IDENTIFY) != ID_KNOWN_TYPE)
        {
            set_ident_type(OBJ_SCROLLS, SCR_IDENTIFY, ID_KNOWN_TYPE);

            const int wpn = you.equip[EQ_WEAPON];
            if (wpn != -1
                && you.inv[wpn].base_type == OBJ_SCROLLS
                && you.inv[wpn].sub_type == SCR_IDENTIFY)
            {
                you.wield_change = true;
            }
        }

        // Output identified item.
        mpr(item.name(DESC_INVENTORY_EQUIP).c_str());
        if (item_slot == you.equip[EQ_WEAPON])
            you.wield_change = true;

        id_used--;

        if (Options.auto_list && id_used > 0)
            more();

        // In case we get to try again.
        item_slot = -1;
    }
    while (id_used > 0);
}

// Returns whether the spell was actually cast.
bool conjure_flame(int pow)
{
    struct dist spelld;

    bool done_first_message = false;

    while (true)
    {
        if (done_first_message)
            mpr("Where would you like to place the cloud?", MSGCH_PROMPT);
        else
        {
            mpr("You cast a flaming cloud spell! But where?", MSGCH_PROMPT);
            done_first_message = true;
        }

        direction( spelld, DIR_TARGET, TARG_ENEMY, -1, false, false, false );

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

        if (trans_wall_blocking(spelld.tx, spelld.ty))
        {
            mpr("A translucent wall is in the way.");
            return (false);
        }
        else if (!see_grid(spelld.tx, spelld.ty))
        {
            mpr("You can't see that place!");
            continue;
        }

        if (spelld.tx == you.x_pos && spelld.ty == you.y_pos)
        {
            mpr("You can't place the cloud here!");
            continue;
        }

        const int cloud = env.cgrid[spelld.tx][spelld.ty];

        if (grid_is_solid(grd[ spelld.tx ][ spelld.ty ]) ||
            mgrd[ spelld.tx ][ spelld.ty ] != NON_MONSTER ||
            (cloud != EMPTY_CLOUD && env.cloud[cloud].type != CLOUD_FIRE))
        {
            mpr( "There's already something there!" );
            continue;
        }
        else if (cloud != EMPTY_CLOUD)
        {
            // Reinforce the cloud - but not too much.
            mpr( "The fire roars with new energy!" );
            const int extra_dur = 2 + std::min(random2(pow) / 2, 20);
            env.cloud[cloud].decay += extra_dur * 5;
            env.cloud[cloud].whose = KC_YOU;
            return (true);
        }

        break;
    }

    int durat = 5 + (random2(pow) / 2) + (random2(pow) / 2);

    if (durat > 23)
        durat = 23;

    place_cloud( CLOUD_FIRE, spelld.tx, spelld.ty, durat, KC_YOU );
    return (true);
}

bool stinking_cloud( int pow, bolt &beem )
{
    beem.name        = "ball of vapour";
    beem.colour      = GREEN;
    beem.range       = 6;
    beem.rangeMax    = 6;
    beem.damage      = dice_def( 1, 0 );
    beem.hit         = 20;
    beem.type        = dchar_glyph(DCHAR_FIRED_ZAP);
    beem.ench_power  = pow;
    beem.beam_source = MHITYOU;
    beem.thrower     = KILL_YOU;
    beem.is_beam     = false;
    beem.aux_source.clear();

    // Don't bother tracing if you're targetting yourself.
    if (beem.target_x != you.x_pos || beem.target_y != you.y_pos)
    {
        // Fire tracer.
        beem.source_x      = you.x_pos;
        beem.source_y      = you.y_pos;
        beem.can_see_invis = player_see_invis();
        beem.smart_monster = true;
        beem.attitude      = ATT_FRIENDLY;
        beem.fr_count      = 0;
        beem.flavour       = BEAM_POTION_STINKING_CLOUD;
        beem.is_tracer     = true;
        fire_beam(beem);

        if (beem.beam_cancelled)
        {
            // We don't want to fire through friendlies.
            canned_msg(MSG_OK);
            return (false);
        }
    }

    // Really fire.
    beem.flavour   = BEAM_MMISSILE;
    beem.is_tracer = false;
    fire_beam(beem);

    return (true);
}

int cast_big_c(int pow, cloud_type cty, kill_category whose, bolt &beam)
{
    big_cloud( cty, whose,
               beam.target_x, beam.target_y, pow, 8 + random2(3) );
    return (1);
}

void big_cloud(cloud_type cl_type, kill_category whose,
               int cl_x, int cl_y, int pow, int size, int spread_rate)
{
    apply_area_cloud(make_a_normal_cloud, cl_x, cl_y, pow, size,
                     cl_type, whose, spread_rate);
}

static bool _mons_hostile(const monsters *mon)
{
    // Needs to be done this way because of friendly/neutral enchantments.
    return (!mons_wont_attack(mon) && !mons_neutral(mon));
}

static bool _can_pacify_monster(const monsters *mon, const int healed)
{
    ASSERT(you.religion == GOD_ELYVILON);

    if (healed < 1)
        return (false);

    // I was thinking of jellies when I wrote this, but maybe we shouldn't
    // exclude zombies and such... (jpeg)
    if (mons_intel(mon->type) <= I_PLANT) // no self-awareness
        return (false);

    const mon_holy_type holiness = mons_holiness(mon);

    if (holiness != MH_HOLY
        && holiness != MH_NATURAL
        && holiness != MH_UNDEAD
        && holiness != MH_DEMONIC)
    {
        return (false);
    }

    if (mons_is_stationary(mon)) // not able to leave the level
        return (false);

    if (mons_is_sleeping(mon)) // not aware of what is happening
        return (false);

    const int factor = (mons_intel(mon->type) <= I_ANIMAL) ? 3 : // animals
                       (is_player_same_species(mon->type)) ? 2   // same species
                                                           : 1;  // other

    int divisor = 3;

    if (holiness == MH_HOLY)
        divisor--;
    else if (holiness == MH_UNDEAD)
        divisor++;
    else if (holiness == MH_DEMONIC)
        divisor += 2;

    const int random_factor = random2(you.skills[SK_INVOCATIONS] * healed /
                                      divisor);

#ifdef DEBUG_DIAGNOSTICS
    mprf(MSGCH_DIAGNOSTICS,
         "pacifying %s? max hp: %d, factor: %d, Inv: %d, healed: %d, rnd: %d",
         mon->name(DESC_PLAIN).c_str(), mon->max_hit_points, factor,
         you.skills[SK_INVOCATIONS], healed, random_factor);
#endif

    if (mon->max_hit_points < factor * random_factor)
        return (true);

    return (false);
}

static int _healing_spell(int healed, int target_x = -1, int target_y = -1)
{
    ASSERT(healed >= 1);

    bolt beam;
    dist spd;

    if (target_x == -1 || target_y == -1)
    {
        spd.isValid = spell_direction(spd, beam, DIR_TARGET,
                                      you.religion == GOD_ELYVILON ?
                                          TARG_ANY : TARG_FRIEND,
                                      true, true, true, "Heal whom?");
    }
    else
    {
        spd.tx = target_x;
        spd.ty = target_y;
        spd.isValid = in_bounds(spd.tx, spd.ty);
    }

    if (!spd.isValid)
    {
        canned_msg(MSG_OK);
        return (0);
    }

    if (spd.tx == you.x_pos && spd.ty == you.y_pos)
    {
        mpr("You are healed.");
        inc_hp(healed, false);
        return (1);
    }

    const int mgr = mgrd[spd.tx][spd.ty];

    if (mgr == NON_MONSTER)
    {
        mpr("There isn't anything there!");
        return (-1);
    }

    monsters *monster = &menv[mgr];

    // Don't heal a monster you can't pacify.
    if (you.religion == GOD_ELYVILON && !_can_pacify_monster(monster, healed))
    {
        canned_msg(MSG_NOTHING_HAPPENS);
        return (-1);
    }

    bool nothing_happens = true;
    if (heal_monster(monster, healed, false))
    {
        mprf("You heal %s.", monster->name(DESC_NOCAP_THE).c_str());

        if (monster->hit_points == monster->max_hit_points)
            simple_monster_message(monster, " is completely healed.");
        else
        {
            const monsters *mons = static_cast<const monsters*>(monster);
            print_wounds(mons);
        }

        if (you.religion == GOD_ELYVILON && !_mons_hostile(monster))
        {
            simple_god_message(" appreciates the healing of a fellow creature.");
            if (one_chance_in(8))
                gain_piety(1);
            return (1);
        }

        nothing_happens = false;
    }

    if (you.religion == GOD_ELYVILON && _mons_hostile(monster))
    {
        simple_god_message(" supports your offer of peace.");

        if (mons_is_holy(monster))
            good_god_holy_attitude_change(monster);
        else
        {
            simple_monster_message(monster, " turns neutral.");
            mons_pacify(monster);

            // Give a small piety return.
            gain_piety(1 + random2(healed/15));
        }
    }
    else if (nothing_happens)
        canned_msg(MSG_NOTHING_HAPPENS);

    return (1);
}

#if 0
char cast_lesser_healing( int pow )
{
    return _healing_spell(5 + random2avg(7, 2));
}

char cast_greater_healing( int pow )
{
    return _healing_spell(15 + random2avg(29, 2));
}

char cast_greatest_healing( int pow )
{
    return _healing_spell(50 + random2avg(49, 2));
}
#endif

int cast_healing(int pow, int target_x, int target_y)
{
    pow = std::min(50, pow);

    return (_healing_spell(pow + roll_dice(2, pow) - 2, target_x, target_y));
}

void vitalisation_chain(int amount)
{
    if (amount <= 0)
        return;

    const int old_value = you.duration[DUR_VITALISATION_CHAIN];
    you.duration[DUR_VITALISATION_CHAIN] += amount;

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

    if (old_value == 0)
        mpr("Zin amplifies your power of vitalisation!", MSGCH_DURATION);
}

void reduce_vitalisation_chain(int amount)
{
    if (you.duration[DUR_VITALISATION_CHAIN] == 0 || amount <= 0)
        return;

    you.duration[DUR_VITALISATION_CHAIN] -= amount;

    if (you.duration[DUR_VITALISATION_CHAIN] <= 0)
    {
        you.duration[DUR_VITALISATION_CHAIN] = 0;
        mpr("Your power of vitalisation returns to normal.", MSGCH_DURATION);
    }
}

void remove_divine_robustness()
{
    mpr("Your divine robustness fades.", MSGCH_DURATION);
    you.duration[DUR_DIVINE_ROBUSTNESS] = 0;
    you.attribute[ATTR_DIVINE_ROBUSTNESS] = 0;
    calc_hp();
}

void remove_divine_stamina()
{
    mpr("Your divine stamina fades.", MSGCH_DURATION);
    modify_stat(STAT_STRENGTH, -you.attribute[ATTR_DIVINE_STAMINA],
                true, "Zin's divine stamina running out");
    modify_stat(STAT_INTELLIGENCE, -you.attribute[ATTR_DIVINE_STAMINA],
                true, "Zin's divine stamina running out");
    modify_stat(STAT_DEXTERITY, -you.attribute[ATTR_DIVINE_STAMINA],
                true, "Zin's divine stamina running out");
    you.attribute[ATTR_DIVINE_STAMINA] = 0;
}

int cast_vitalisation(int pow)
{
    const int step_max_chain = 6;
    const int type_max_chain = 4;

    static int step;
    static int step_max;
    static int type;
    static int hp_amt;
    static int mp_amt;
    static bool need_chain;

    // If vitalisation chaining is turned off, start from the beginning.
    if (you.duration[DUR_VITALISATION_CHAIN] == 0)
    {
        step = 0;
        step_max = std::min(pow, step_max_chain);
        type = random2(type_max_chain * 3 / 2);
        hp_amt = 3;
        mp_amt = 1;
        need_chain = false;
    }

    bool success = false;

    switch (type)
    {
    case 0:
        // Restore HP and MP.
        if (you.hp < you.hp_max || you.magic_points < you.max_magic_points)
        {
            success = true;
            inc_hp(hp_amt, false);
            inc_mp(mp_amt, false);
            hp_amt *= 2;
            mp_amt *= 2;
            need_chain =
                (you.hp < you.hp_max
                    || you.magic_points < you.max_magic_points);
            break;
        }

        need_chain = false;
        step = 0;
        type = 1;
        // Deliberate fall through, resetting the vitalisation chaining
        // indicator and the step counter.

    case 1:
        switch (step)
        {
        case 0:
        case 1:
        case 2:
            // Restore stats.
            if (you.strength < you.max_strength
                || you.intel < you.max_intel
                || you.dex < you.max_dex)
            {
                success = true;
                restore_stat(STAT_STRENGTH, step + 1, true);
                restore_stat(STAT_INTELLIGENCE, step + 1, true);
                restore_stat(STAT_DEXTERITY, step + 1, true);
                need_chain =
                    (you.strength < you.max_strength
                        || you.intel < you.max_intel
                        || you.dex < you.max_dex);
                break;
            }

            step = 3;
            // Deliberate fall through.

            if (step >= step_max)
                break;

        default:
            break;
        }

        if (success)
            break;

        need_chain = false;
        step = 0;
        type = 2;
        // Deliberate fall through, resetting the vitalisation chaining
        // indicator and the step counter.

    case 2:
        // Remove negative afflictions.
        switch (step)
        {
        // Remove confusion and poisoning.
        case 0:
            if (you.duration[DUR_CONF] || you.duration[DUR_POISONING])
            {
                success = true;
                you.duration[DUR_CONF] = 0;
                you.duration[DUR_POISONING] = 0;
                need_chain = false;
                break;
            }

            step = 1;
            // Deliberate fall through.

            if (step >= step_max)
                break;

        // Remove sickness and rotting.
        case 1:
            if (you.disease || you.rotting)
            {
                success = true;
                you.disease = 0;
                you.rotting = 0;
                need_chain = false;
                break;
            }

            step = 2;
            // Deliberate fall through.

            if (step >= step_max)
                break;

        // Restore rotted HP.
        case 2:
            if (player_rotted())
            {
                success = true;
                unrot_hp(3 + random2(9));
                need_chain = false;
                break;
            }

            step = 3;
            // Deliberate fall through.

            if (step >= step_max)
                break;

        default:
            break;
        }

        if (success)
            break;

        need_chain = false;
        step = 0;
        type = 3;
        // Deliberate fall through, resetting the vitalisation chaining
        // indicator and the step counter.

    case 3:
    {
stamina_robustness:
        int estep = step / 2;

        // Add divine stamina and divine robustness.
        switch (step)
        {
        // Divine stamina.
        case 0:
        case 2:
        case 4:
            if ((estep == 0 || you.duration[DUR_VITALISATION_CHAIN] > 0)
                && ((you.attribute[ATTR_DIVINE_STAMINA] + 1) / 2) == estep
                && ((player_mutation_level(MUT_STRONG) + 1) / 5) < (3 - estep)
                && ((player_mutation_level(MUT_CLEVER) + 1) / 5) < (3 - estep)
                && ((player_mutation_level(MUT_AGILE) + 1) / 5) < (3 - estep))
            {
                success = true;
                mprf(MSGCH_DURATION, "Zin %s divine stamina.",
                    (estep == 0) ? "grants you" :
                    (estep == 1) ? "strengthens your"
                                 : "maximises your");

                const int stamina_amt = step + 1;
                you.attribute[ATTR_DIVINE_STAMINA] += stamina_amt;
                you.duration[DUR_DIVINE_STAMINA] +=
                    (estep == 0) ? (you.skills[SK_INVOCATIONS] * 2) :
                    (estep == 1) ? (you.skills[SK_INVOCATIONS])
                                 : (you.skills[SK_INVOCATIONS] / 2);

                modify_stat(STAT_STRENGTH, stamina_amt, true, "");
                modify_stat(STAT_INTELLIGENCE, stamina_amt, true, "");
                modify_stat(STAT_DEXTERITY, stamina_amt, true, "");

                // Keep vitalisation chaining on if divine stamina can
                // be increased two vitalisation attempts from now, or
                // if divine robustness can be increased one
                // vitalisation attempt from now.
                need_chain =
                    (((player_mutation_level(MUT_STRONG) + 1) / 5) < (2 - estep)
                        && ((player_mutation_level(MUT_CLEVER) + 1) / 5) < (2 - estep)
                        && ((player_mutation_level(MUT_AGILE) + 1) / 5) < (2 - estep))
                            || (player_mutation_level(MUT_ROBUST) < (3 - estep));
                break;
            }

            step++;
            goto stamina_robustness;
            // Deliberate fall through.

        // Divine robustness.
        case 1:
        case 3:
        case 5:
            if ((estep == 0 || you.duration[DUR_VITALISATION_CHAIN] > 0)
                && you.attribute[ATTR_DIVINE_ROBUSTNESS] == estep
                && player_mutation_level(MUT_ROBUST) < (3 - estep))
            {
                success = true;
                mprf(MSGCH_DURATION, "Zin %s divine robustness.",
                    (estep == 0) ? "grants you" :
                    (estep == 1) ? "strengthens your"
                                 : "maximises your");

                you.attribute[ATTR_DIVINE_ROBUSTNESS]++;
                you.duration[DUR_DIVINE_ROBUSTNESS] +=
                    (estep == 0) ? (you.skills[SK_INVOCATIONS] * 2) :
                    (estep == 1) ? (you.skills[SK_INVOCATIONS])
                                 : (you.skills[SK_INVOCATIONS] / 2);

                const int old_hp_max = you.hp_max;
                calc_hp();
                set_hp(you.hp * you.hp_max / old_hp_max, false);

                // Keep vitalisation chaining on if divine robustness
                // can be increased two vitalisation attempts from now,
                // or if divine stamina can be increased one
                // vitalisation attempt from now.
                need_chain =
                    (player_mutation_level(MUT_ROBUST) < (2 - estep))
                        || (((player_mutation_level(MUT_STRONG) + 1) / 5) < (3 - estep)
                            && ((player_mutation_level(MUT_CLEVER) + 1) / 5) < (3 - estep)
                            && ((player_mutation_level(MUT_AGILE) + 1) / 5) < (3 - estep));
                break;
            }

            step++;
            goto stamina_robustness;
            // Deliberate fall through.

        default:
            break;
        }

        if (success)
            break;

        need_chain = false;
        step = 0;
        type = 4;
        // Deliberate fall through, resetting the vitalisation chaining
        // indicator and the step counter.
    }

    default:
        // Do nothing.
        break;
    }

#ifdef DEBUG_DIAGNOSTICS
    mprf(MSGCH_DIAGNOSTICS,
         "vitalising: step = %d, type = %d, step_max = %d",
         step, type, step_max);
#endif

    // If vitalisation has succeeded, display an appropriate message.
    if (success)
    {
        mprf("You feel %s %s.", (step == 0) ? "only nominally" :
                                (step == 1) ? "very slightly" :
                                (step == 2) ? "slightly" :
                                (step == 3) ? "somewhat" :
                                (step == 4) ? "appropriately"
                                            : "impressively",
                                (type == 0) ? "invigorated" :
                                (type == 1) ? "renewed" :
                                (type == 2) ? "better"
                                            : "powerful");

        // If vitalisation has succeeded, pay the extended piety cost,
        // based on how far the step counter has advanced.
        int loss_amt = step + 1 + (random2(3) - 1);

        if (loss_amt > 0)
            lose_piety(loss_amt);

        // Increment the step counter.
        step++;
    }
    else
        canned_msg(MSG_NOTHING_HAPPENS);

    // Whether vitalisation has succeeded or failed, pay the minimum
    // piety cost.
    lose_piety(2);

    // If there's not enough piety left to vitalise again, turn off
    // vitalisation chaining.
    if (you.piety < piety_breakpoint(1))
        need_chain = false;

    // If vitalisation has succeeded, it hasn't succeeded as far as
    // possible, and vitalisation chaining is needed, turn on
    // vitalisation chaining for several turns.
    if (success && step < step_max && need_chain)
        vitalisation_chain(6);
    // Otherwise, turn off vitalisation chaining.
    else
        reduce_vitalisation_chain(30);

    return (success) ? (step + 1) : 0;
}

bool cast_revivification(int pow)
{
    int loopy = 0;              // general purpose loop variable {dlb}
    bool success = false;
    int loss = 0;

    if (you.hp == you.hp_max)
        canned_msg(MSG_NOTHING_HAPPENS);
    else if (you.hp_max < 21)
        mpr("You lack the resilience to cast this spell.");
    else
    {
        mpr("Your body is healed in an amazingly painful way.");

        loss = 2;
        for (loopy = 0; loopy < 9; loopy++)
            if (x_chance_in_y(8, pow))
                loss++;

        dec_max_hp(loss);
        set_hp(you.hp_max, false);
        success = true;
    }

    return (success);
}

void cast_cure_poison(int mabil)
{
    if (!you.duration[DUR_POISONING])
        canned_msg(MSG_NOTHING_HAPPENS);
    else
        reduce_poison_player( 2 + random2(mabil) + random2(3) );

    return;
}

void purification(void)
{
    mpr("You feel purified!");

    you.disease = 0;
    you.rotting = 0;
    you.duration[DUR_POISONING] = 0;
    you.duration[DUR_CONF] = 0;
    you.duration[DUR_SLOW] = 0;
    you.duration[DUR_PARALYSIS] = 0;          // can't currently happen -- bwr
    you.duration[DUR_PETRIFIED] = 0;
}

int allowed_deaths_door_hp(void)
{
    int hp = you.skills[SK_NECROMANCY] / 2;

    if (you.religion == GOD_KIKUBAAQUDGHA && !player_under_penance())
        hp += you.piety / 15;

    return (hp);
}

void cast_deaths_door(int pow)
{
    if (you.is_undead)
        mpr("You're already dead!");
    else if (you.duration[DUR_DEATHS_DOOR])
        mpr("Your appeal for an extension has been denied.");
    else
    {
        mpr("You feel invincible!");
        mpr("You seem to hear sand running through an hourglass...",
            MSGCH_SOUND);

        set_hp( allowed_deaths_door_hp(), false );
        deflate_hp( you.hp_max, false );

        you.duration[DUR_DEATHS_DOOR] = 10 + random2avg(13, 3)
                                           + (random2(pow) / 10);

        if (you.duration[DUR_DEATHS_DOOR] > 25)
            you.duration[DUR_DEATHS_DOOR] = 23 + random2(5);
    }

    return;
}

void abjuration(int pow)
{
    mpr("Send 'em back where they came from!");

    // Scale power into something comparable to summon lifetime.
    const int abjdur = pow * 12;

    for (int i = 0; i < MAX_MONSTERS; ++i)
    {
        monsters* const monster = &menv[i];

        if (monster->type == -1 || !mons_near(monster))
            continue;

        if (mons_wont_attack(monster))
            continue;

        mon_enchant abj = monster->get_ench(ENCH_ABJ);
        if (abj.ench != ENCH_NONE)
        {
            int sockage = std::max(fuzz_value(abjdur, 60, 30), 40);
#ifdef DEBUG_DIAGNOSTICS
            mprf(MSGCH_DIAGNOSTICS, "%s abj: dur: %d, abj: %d",
                 monster->name(DESC_PLAIN).c_str(), abj.duration, sockage);
#endif

            // TSO and Trog's abjuration protection.
            if (mons_is_god_gift(monster, GOD_SHINING_ONE))
            {
                sockage = sockage * (30 - monster->hit_dice) / 45;
                if (sockage < abj.duration)
                {
                    simple_god_message(" protects a fellow warrior from your evil magic!",
                                       GOD_SHINING_ONE);
                }
            }
            else if (mons_is_god_gift(monster, GOD_TROG))
            {
                sockage = sockage * 8 / 15;
                if (sockage < abj.duration)
                {
                    simple_god_message(" shields an ally from your puny magic!",
                                       GOD_TROG);
                }
            }

            if (!monster->lose_ench_duration(abj, sockage))
                simple_monster_message(monster, " shudders.");
        }
    }
}

// Antimagic is sort of an anti-extension... it sets a lot of magical
// durations to 1 so it's very nasty at times (and potentially lethal,
// that's why we reduce levitation to 2, so that the player has a chance
// to stop insta-death... sure the others could lead to death, but that's
// not as direct as falling into deep water) -- bwr
void antimagic()
{
    duration_type dur_list[] = {
        DUR_INVIS, DUR_CONF, DUR_PARALYSIS, DUR_SLOW, DUR_HASTE,
        DUR_MIGHT, DUR_FIRE_SHIELD, DUR_ICY_ARMOUR, DUR_REPEL_MISSILES,
        DUR_REGENERATION, DUR_SWIFTNESS, DUR_STONEMAIL, DUR_CONTROL_TELEPORT,
        DUR_TRANSFORMATION, DUR_DEATH_CHANNEL, DUR_DEFLECT_MISSILES,
        DUR_FORESCRY, DUR_SEE_INVISIBLE, DUR_WEAPON_BRAND, DUR_SILENCE,
        DUR_CONDENSATION_SHIELD, DUR_STONESKIN, DUR_BARGAIN,
        DUR_INSULATION, DUR_RESIST_POISON, DUR_RESIST_FIRE, DUR_RESIST_COLD,
        DUR_SLAYING, DUR_STEALTH, DUR_MAGIC_SHIELD, DUR_SAGE, DUR_PETRIFIED
    };

    if (!you.permanent_levitation() && !you.permanent_flight()
        && you.duration[DUR_LEVITATION] > 2)
    {
        you.duration[DUR_LEVITATION] = 2;
    }

    if (!you.permanent_flight() && you.duration[DUR_CONTROLLED_FLIGHT] > 1)
        you.duration[DUR_CONTROLLED_FLIGHT] = 1;

    for ( unsigned int i = 0; i < ARRAYSZ(dur_list); ++i )
        if ( you.duration[dur_list[i]] > 1 )
            you.duration[dur_list[i]] = 1;

    contaminate_player( -1 * (1 + random2(5)));
}                               // end antimagic()

void extension(int pow)
{
    int contamination = random2(2);

    if (you.duration[DUR_HASTE])
    {
        potion_effect(POT_SPEED, pow);
        contamination++;
    }

    if (you.duration[DUR_SLOW])
        potion_effect(POT_SLOWING, pow);

    if (you.duration[DUR_MIGHT])
    {
        potion_effect(POT_MIGHT, pow);
        contamination++;
    }

    if (you.duration[DUR_LEVITATION] && !you.duration[DUR_CONTROLLED_FLIGHT])
        potion_effect(POT_LEVITATION, pow);

    if (you.duration[DUR_INVIS])
    {
        potion_effect(POT_INVISIBILITY, pow);
        contamination++;
    }

    if (you.duration[DUR_ICY_ARMOUR])
        ice_armour(pow, true);

    if (you.duration[DUR_REPEL_MISSILES])
        missile_prot(pow);

    if (you.duration[DUR_REGENERATION])
        cast_regen(pow);

    if (you.duration[DUR_DEFLECT_MISSILES])
        deflection(pow);

    if (you.duration[DUR_FIRE_SHIELD])
    {
        you.duration[DUR_FIRE_SHIELD] += random2(pow / 20);

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

        mpr("Your ring of flames roars with new vigour!");
    }

    if ( !(you.duration[DUR_WEAPON_BRAND] < 1
                 || you.duration[DUR_WEAPON_BRAND] > 80) )
    {
        you.duration[DUR_WEAPON_BRAND] += 5 + random2(8);
    }

    if (you.duration[DUR_SWIFTNESS])
        cast_swiftness(pow);

    if (you.duration[DUR_INSULATION])
        cast_insulation(pow);

    if (you.duration[DUR_STONEMAIL])
        stone_scales(pow);

    if (you.duration[DUR_CONTROLLED_FLIGHT])
        cast_fly(pow);

    if (you.duration[DUR_CONTROL_TELEPORT])
        cast_teleport_control(pow);

    if (you.duration[DUR_RESIST_POISON])
        cast_resist_poison(pow);

    if (you.duration[DUR_TRANSFORMATION])
    {
        mpr("Your transformation has been extended.");
        you.duration[DUR_TRANSFORMATION] += random2(pow);
        if (you.duration[DUR_TRANSFORMATION] > 100)
            you.duration[DUR_TRANSFORMATION] = 100;
    }

    //jmf: added following
    if (you.duration[DUR_STONESKIN])
        cast_stoneskin(pow);

    if (you.duration[DUR_FORESCRY])
        cast_forescry(pow);

    if (you.duration[DUR_SEE_INVISIBLE])
        cast_see_invisible(pow);

    if (you.duration[DUR_SILENCE])   //how precisely did you cast extension?
        cast_silence(pow);

    if (you.duration[DUR_CONDENSATION_SHIELD])
        cast_condensation_shield(pow);

    if (contamination)
        contaminate_player( contamination, true );
}                               // end extension()

void ice_armour(int pow, bool extending)
{
    if (!player_light_armour())
    {
        if (!extending)
            mpr("You are wearing too much armour.");

        return;
    }

    if (you.duration[DUR_STONEMAIL] || you.duration[DUR_STONESKIN])
    {
        if (!extending)
            mpr("The spell conflicts with another spell still in effect.");

        return;
    }

    if (you.duration[DUR_ICY_ARMOUR])
        mpr( "Your icy armour thickens." );
    else
    {
        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST)
            mpr( "Your icy body feels more resilient." );
        else
            mpr( "A film of ice covers your body!" );

        you.redraw_armour_class = true;
    }

    you.duration[DUR_ICY_ARMOUR] += 20 + random2(pow) + random2(pow);

    if (you.duration[DUR_ICY_ARMOUR] > 50)
        you.duration[DUR_ICY_ARMOUR] = 50;
}                               // end ice_armour()

void stone_scales(int pow)
{
    int dur_change = 0;

    if (you.duration[DUR_ICY_ARMOUR] || you.duration[DUR_STONESKIN])
    {
        mpr("The spell conflicts with another spell still in effect.");
        return;
    }

    if (you.duration[DUR_STONEMAIL])
        mpr("Your scaly armour looks firmer.");
    else
    {
        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_STATUE)
            mpr( "Your stone body feels more resilient." );
        else
            mpr( "A set of stone scales covers your body!" );

        you.redraw_evasion = true;
        you.redraw_armour_class = true;
    }

    dur_change = 20 + random2(pow) + random2(pow);

    if (dur_change + you.duration[DUR_STONEMAIL] >= 100)
        you.duration[DUR_STONEMAIL] = 100;
    else
        you.duration[DUR_STONEMAIL] += dur_change;

    burden_change();
}                               // end stone_scales()

void missile_prot(int pow)
{
    mpr("You feel protected from missiles.");

    you.duration[DUR_REPEL_MISSILES] += 8 + roll_dice( 2, pow );

    if (you.duration[DUR_REPEL_MISSILES] > 100)
        you.duration[DUR_REPEL_MISSILES] = 100;
}

void deflection(int pow)
{
    mpr("You feel very safe from missiles.");

    you.duration[DUR_DEFLECT_MISSILES] += 15 + random2(pow);

    if (you.duration[DUR_DEFLECT_MISSILES] > 100)
        you.duration[DUR_DEFLECT_MISSILES] = 100;
}

void cast_regen(int pow)
{
    //if (pow > 150) pow = 150;
    mpr("Your skin crawls.");

    you.duration[DUR_REGENERATION] += 5 + roll_dice( 2, pow / 3 + 1 );

    if (you.duration[DUR_REGENERATION] > 100)
        you.duration[DUR_REGENERATION] = 100;
}

void cast_berserk(void)
{
    go_berserk(true);
}

void cast_swiftness(int power)
{
    int dur_incr = 0;

    if (player_in_water())
    {
        mpr("The water foams!");
        return;
    }

    if (!you.duration[DUR_SWIFTNESS] && player_movement_speed() <= 6)
    {
        mpr( "You can't move any more quickly." );
        return;
    }

    // Reduced the duration:  -- bwr
    // dur_incr = random2(power) + random2(power) + 20;
    dur_incr = 20 + random2( power );

    // [dshaligram] Removed the on-your-feet bit.  Sounds odd when
    // you're levitating, for instance.
    mpr("You feel quick.");

    if (dur_incr + you.duration[DUR_SWIFTNESS] > 100)
        you.duration[DUR_SWIFTNESS] = 100;
    else
        you.duration[DUR_SWIFTNESS] += dur_incr;
}

void cast_fly(int power)
{
    const int dur_change = 25 + random2(power) + random2(power);

    const bool was_levitating = player_is_airborne();

    if (you.duration[DUR_LEVITATION] + dur_change > 100)
        you.duration[DUR_LEVITATION] = 100;
    else
        you.duration[DUR_LEVITATION] += dur_change;

    if (you.duration[DUR_CONTROLLED_FLIGHT] + dur_change > 100)
        you.duration[DUR_CONTROLLED_FLIGHT] = 100;
    else
        you.duration[DUR_CONTROLLED_FLIGHT] += dur_change;

    burden_change();

    if (!was_levitating)
    {
        if (you.light_flight())
            mpr("You swoop lightly up into the air.");
        else
            mpr("You fly up into the air.");
    }
    else
        mpr("You feel more buoyant.");
}

void cast_insulation(int power)
{
    int dur_incr = 10 + random2(power);

    mpr("You feel insulated.");

    if (dur_incr + you.duration[DUR_INSULATION] > 100)
        you.duration[DUR_INSULATION] = 100;
    else
        you.duration[DUR_INSULATION] += dur_incr;
}

void cast_resist_poison(int power)
{
    int dur_incr = 10 + random2(power);

    mpr("You feel resistant to poison.");

    if (dur_incr + you.duration[DUR_RESIST_POISON] > 100)
        you.duration[DUR_RESIST_POISON] = 100;
    else
        you.duration[DUR_RESIST_POISON] += dur_incr;
}

void cast_teleport_control(int power)
{
    int dur_incr = 10 + random2(power);

    mpr("You feel in control.");

    if (dur_incr + you.duration[DUR_CONTROL_TELEPORT] >= 50)
        you.duration[DUR_CONTROL_TELEPORT] = 50;
    else
        you.duration[DUR_CONTROL_TELEPORT] += dur_incr;
}

void cast_ring_of_flames(int power)
{
    you.duration[DUR_FIRE_SHIELD] += 5 + (power / 10) + (random2(power) / 5);

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

    mpr("The air around you leaps into flame!");

    manage_fire_shield();
}

void cast_confusing_touch(int power)
{
    msg::stream << "Your " << your_hand(true) << " begin to glow "
                << (you.duration[DUR_CONFUSING_TOUCH] ? "brighter" : "red")
                << "." << std::endl;

    you.duration[DUR_CONFUSING_TOUCH] += 5 + (random2(power) / 5);

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

}

bool cast_sure_blade(int power)
{
    bool success = false;

    if (you.equip[EQ_WEAPON] == -1)
        mpr("You aren't wielding a weapon!");
    else if (weapon_skill( you.inv[you.equip[EQ_WEAPON]].base_type,
                     you.inv[you.equip[EQ_WEAPON]].sub_type) != SK_SHORT_BLADES)
    {
        mpr("You cannot bond with this weapon.");
    }
    else
    {
        if (!you.duration[DUR_SURE_BLADE])
            mpr("You become one with your weapon.");
        else if (you.duration[DUR_SURE_BLADE] < 25)
            mpr("Your bond becomes stronger.");

        you.duration[DUR_SURE_BLADE] += 8 + (random2(power) / 10);

        if (you.duration[DUR_SURE_BLADE] > 25)
            you.duration[DUR_SURE_BLADE] = 25;

        success = true;
    }

    return (success);
}

void manage_fire_shield(void)
{
    you.duration[DUR_FIRE_SHIELD]--;

    if (!you.duration[DUR_FIRE_SHIELD])
        return;

    char stx = 0, sty = 0;

    for (stx = -1; stx < 2; stx++)
        for (sty = -1; sty < 2; sty++)
        {
            if (sty == 0 && stx == 0)
                continue;

            //if ( one_chance_in(3) ) beam.range ++;

            if (!grid_is_solid(grd[you.x_pos + stx][you.y_pos + sty])
                && env.cgrid[you.x_pos + stx][you.y_pos + sty] == EMPTY_CLOUD)
            {
                place_cloud( CLOUD_FIRE, you.x_pos + stx, you.y_pos + sty,
                             1 + random2(6), KC_YOU );
            }
        }
}
