summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/spells4.cc
blob: 4486a848d33d8adb4b5fddb9f563432898c1dafd (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                          
                                                                    
                                                            
                                                                     




                   
                   
                  
                    



                    
                     

                  
                    

                  
                    

                    
                
                   
                    
                     
                     
                  
                
                     
                    
                 
                     
                      
                  
                      
                     

                   
                   
                     


                    
                    

                     
                  
                     
                    
                      
                  
                 
                  
                     










                                                                             

                                           

                                                         
 

                   
                                             
 
                      
         
                             

                                                   




                                                   

     
                   
 
 
                                
                                                                    
 
                                                                       
                                          
 
                        




                                                                        
                          



                                             
                            









                                              
                            
                     















































                                                                       
                                





                             
                                                        

                   
                                               
        


                    
 
 
                                                                 

                  
 
                        
 
                                              
     
                                                               
         

                                      
         



                    
                                   
                                                      




                 
 
 
                                                                 
 
                   

                                                              
                          
                 
 


                                                                          


                                                 

                          
                                
                                           



                          
                                   
                        
                                
                                    



                         





                             

              
                               

                         

              
                              

                         

              

                                 





              
                                   
     
                         
 
                                

                                     
                                                        
 



                   
 



                          
                                             
 



                                   
                             

                                                 






                                               
                  


















                                                                    

                                                        


                                                   

                                                                        

                                                                  
 
                         
                                      
 
 

                                                                        
 
                                       
                                                                              
        
                                                               
 
                                                                 

                              


                                
                                

                                        
     

                                             






                                                                             
                                                     
                                                                           
 






                                                                          
                                                       
                                                                       
         

                                    
         

              

                        
                                                                      
 
 
                                                                  
 
                                          
                 
                   
 
                                      
                   
 
                                      
                   
 
                                        
                                                       
                   
 

                                                                
 
                         
                                             
               
 


                             
                                             
 
 


                                                                      
                                             





                                                             
                                                   


                                                                      





                                                     

 
                                                                       
 

                                          
                 
 
                                                                      
                                              
     
                 
     
 

                                                                      
                         



                                                                 
                                      

                 


                                                  
                                                       
        
                                                       

             
 


                              

                                                  
 
                                                                         

                  
 
                     
 
                                              
     
                                         
         
                                 







                                                            
                                          




                      
                                                 


                     
     
                                      
                                                                   
     

                      
 
 
                                                                         

                  


                              

                                   
     

                                           

                                
                                    
 
                             
 

                                
         
                                            

                                
                                    


         
                        
 
 
                                                                          
 
              
                                                                      
 
                                                                        
 

                                      

                   
                                                               
                              
                         



                                   
                                     



                                                  

                                                                      

                             
                                 

                   
                                                          
                                                                   
 
                                                                             
 
                                                

                                                   
                                       
                                           









                                
                    
 

                                             
                                         
                                                          
     
                                                                         
         
                                         
                                                            

                                    
 



                                                                  


         



                              
 
                                       
     
                                    
                             

                     
                         
 
                                                                             
         


                                        
         
                                               
         

                                      

                                   
                                              


                                  
                                              


                      
             

                         
                                               



                     
                                          
             

                                    
             
 

                                     




                                  
                   
                                                       
 
                      
     
                                                       


                                              

     

                                     


                      
                    
                                  


                                                                                

     
                   
                           

                                              





                                                                             
                                           

     
                          
                                                        



                                             


                                                        
                        


                            
                                                             
                        





                                                    
                                                        
 
                                            

                                                                    
                                            


         


                                                     
 
                

                                     
                  
 







                                               
                                                                    
 
                     
                               
 
 
                                                                      
 
                                          

                   
              

                                                                      
                           
     

                                            
                                                                
                           
                        
                                                        
     
                             
                   
                                                            


                   

                                                               


                   
                                              

                                                      






                                                                            
                                   
                                    

                                                                        


                        
                                                                 
                                             
                                          


                    
 
 
                            
 
                                              

            

                                                                    
 
                                                
 
                 


                                                                       
            
         






                                                                         

         
 
 

                                                                       
                                     
                                                             
 


                                      
 
                                                 
     

                                                 
     
                                       
     
                                                          

                       

                                                               

            

                                                     


        

                                    

     
               



                            

                                                                         

 
                                                                  
                                                              

                                                                         
 


                                                      
                              
                                                                              
                                                                  

             
 
 
                                                     
 
                                                                    
                                                           
                                       
                 
     

                              


                          
                       
     
 
 




                                                       
 
                   
                                  




                                                             
     

                                           

     

                                                              
                         
                                                         
                                      
                                                                 
                              
                                               
                           



                                                                          
                                                               
     
                  
 
 
                                                                       

                  
 


                                          
                                            
                                     
     
                 
     
 
                                                              
             
 




                                                         



                                                               
                                
     
 
                                                  
 
 
                                                              
 

                    
 

                                          

                       
                       
                                                
                       
 
                                                      

                                
                                                      
                                      
     
                                            
         
                              
                                                                             
                                                                   


                      
 
                                                   
 
                 
                                                                  
                      
                                                                         
        
                                                            
 
                  
 
 
                                                     
 

                                                      
 
                                
                                     
                                                      
                               

                                        
                            
 


                                                                                    

                             
 
                                                 
                                             
 
                            

                           

                                            



                             
                                  

                                                                                

                             
 
                    

                                            


                   

                                            







                             
                                                                   



                      
                                                          

              




                                    
                          
                                  
                                                                           
                                                


                                         


                      








                                                                  




                               
                                  

                           




                                                           
         
                                     


              
                                  








                                                                    

                                                                    
                                     


              
                                                    
                                    
                                                  

                                       

                                
                             


                                            
                
 
                            
     
                                                                    



                           
                   
                                 
 
                   
                                
                           
                
 
                       

                                           
 
                                      
 
                  
 
 
                                                            
                                                             
                                                           
                                                             
                                                           
                                                                      
                                           
 

                    
                                                         
                                                      
     
                                                                        
         

                                                                  
 
                                            
             
                                     










                                      
                                     
 
                                                  
     

                             

              

                                                          
                                               

              

                              

              



                                     

              



                                                                     

              
                                              
            

              
 



                                                        
              
 

                                                      
              
 
            


              


                                                                   
     


                                                             
         
                                         


         

                                                                       
                                                        
                                         




                                      
                                     
                                                      
 

                                                

                                                  
 
                                                              

                                                            


                                                         

 
                                                 
 
                   
                         
                        

                            
                                           

                                          
                       

     
                                                                   


              

                                                      

                                
                         
                                 
                                     
 

                            

                                                                            
                                            
 
                                                      
 
                                               
     
                                                                    
                                                                 
 
                          
         
                             
                                                                
 
                                                                      
                                                                    
                                                                           

                                    

                                                          



                                 



                                                         

                                                              
                                     

                  
                                                                      


                                  
                         




                                                        

                                                              
                                  

                  

                                

                                   
                                                
             


                                                              


                


                                                                   

             
             
                                                           
                                                   
                                                    
 

                                                             
                                         
             

                  
                                




                                                        

                                                              
                                     


                  
                                              





                                               

                                                                   




















                                                                              

                                                                        




                                                            
             
                                                          
                                                                       






                                                                     
                                                                   
                                                         

                                                                      


                                          
             
 




                                                               

                                                                  
                            

         
                                                   


                      
                                                       





















                                                                        








                                                                                  








                                                
                              
                          
                                      
                       
                               

                         

                                                 



                                

                                   






                                                               



                                                    
 



                                                                       
                                    

                                                                         
                                       

                                         
                                    
                                             
                                              






                                         
                         




                                                     
 
                                                     
         
                                 

                                            



              
              


                                                                             





                                                    
 
                                                          
         
                                                 
                                         
                                             


              
      




                                


                                                               
         
                                                                         


                  
                                                                        

                                                                     




                                                                        

                                                                       
                         
              
     






                             
                                   
                                                                              
                                     

                       
                                                               




                                                                      





                                                
                                   

                        
                                                

                                                       

              





                                         
                                       

                         
                                           
 
                                 

                                     
                                                        
     
                                  
     
                                                                        

                                      

                  
 
 
                                    
 




                                                   
                                     

                                           
                       

     
                                                                  
                                                 

                                                 
                       

     

                                                              
 

                                                          
 
                  

 
                                                      
 
                                       
     

                                                 

     








                                                                     
                                                   
                                                       
                                     
                                                  
     
                                                                          
                                            
         
                                                         
             
                                                                    
                                                                          
                                           

                                                                  
         
 

                                         

     

                                    






                                                              
                        

                                                      
 
                                  
                      
                                         


                       

                                                

     




                                                              
 
                                  
     


                                                                    
 





                                                     

                                                  
     
 



                                    

 
                     
 
                    
                     
     
                                           

                                                                              
     

                
 

                                        
                                          

                                                                             

                       
                                                         
 
                     
 
 

                                 
                                                       



                                              

                                      
                                                      



                                                      

                                                                     

                                                          
         


                                                                      

                                                                  
                                           
         
     
 
 

                           
                                                          




                                          





                                                                          
                                         
     

                                            







                                                                     


                                              
                                   
 
                                                            

                                                                
 

                                                                     
 
                                   

 
                                                                  
 
                           




                                      

                                      





                   
                                            


                                                                  


                                                                           
                     
                       
                                                 
     

                                                                           
                                           

                                                      
         
                     
         
 

                                                                           

                     
                                           
                     
 

                     

     


                  

                                                             
 
            

                                
                                                                       


               

 
                                       
 






                                                                     



                            

                                                                         




                                                             
                                                       





















                                                                        
                                       

     
                                                                               
 


                                                             











                                                                         
                                                  






                                                                   
 
/*
 *  File:       spells4.cc
 *  Summary:    new spells, focusing on Transmutations, Divinations,
 *              and other neglected areas of Crawl magic ;^)
 *  Written by: Copyleft Josh Fishman 1999-2000, All Rights Preserved
 */

#include "AppHdr.h"

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

#include "externs.h"

#include "abyss.h"
#include "artefact.h"
#include "beam.h"
#include "cloud.h"
#include "coordit.h"
#include "debug.h"
#include "delay.h"
#include "directn.h"
#include "dungeon.h"
#include "effects.h"
#include "env.h"
#include "invent.h"
#include "it_use2.h"
#include "item_use.h"
#include "itemprop.h"
#include "items.h"
#include "los.h"
#include "makeitem.h"
#include "message.h"
#include "misc.h"
#include "mon-behv.h"
#include "mon-place.h"
#include "coord.h"
#include "mon-stuff.h"
#include "mon-util.h"
#include "ouch.h"
#include "player.h"
#include "quiver.h"
#include "religion.h"
#include "skills.h"
#include "spells1.h"
#include "spells4.h"
#include "spl-mis.h"
#include "spl-util.h"
#include "stuff.h"
#include "areas.h"
#include "teleport.h"
#include "terrain.h"
#include "transform.h"
#include "traps.h"
#include "view.h"
#include "shout.h"
#include "viewchar.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...

// Just to avoid typing this over and over.
// Returns true if monster died. -- bwr
static bool _player_hurt_monster(monsters& m, int damage,
        beam_type flavour = BEAM_MISSILE)
{
    if (damage > 0)
    {
        m.hurt(&you, damage, flavour, false);

        if (m.alive())
        {
            print_wounds(&m);
            behaviour_event(&m, ME_WHACK, MHITYOU);
        }
        else
        {
            monster_die(&m, KILL_YOU, NON_MONSTER);
            return (true);
        }
    }

    return (false);
}

// Here begin the actual spells:
static int _shatter_monsters(coord_def where, int pow, int, actor *)
{
    dice_def   dam_dice( 0, 5 + pow / 3 );  // number of dice set below
    monsters *monster = monster_at(where);

    if (monster == NULL)
        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 (monster->type)
    {
    case MONS_ICE_BEAST:        // 3/2 damage
    case MONS_SIMULACRUM_SMALL:
    case MONS_SIMULACRUM_LARGE:
    case MONS_SILVER_STATUE:
        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_ORANGE_STATUE:
    case MONS_STATUE:
    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(monster))
            dam_dice.num = 1;
        else
            dam_dice.num = 3;
        break;
    }

    int damage = dam_dice.roll() - random2(monster->ac);

    if (damage > 0)
        _player_hurt_monster(*monster, damage);
    else
        damage = 0;

    return (damage);
}

static int _shatter_items(coord_def where, int pow, int, actor *)
{
    UNUSED( pow );

    int broke_stuff = 0;

    for ( stack_iterator si(where); si; ++si )
    {
        if (si->base_type == OBJ_POTIONS && !one_chance_in(10))
        {
            broke_stuff++;
            destroy_item(si->index());
        }
    }

    if (broke_stuff)
    {
        if (player_can_hear(where))
            mpr("You hear glass break.", MSGCH_SOUND);

        return 1;
    }

    return 0;
}

static int _shatter_walls(coord_def where, int pow, int, actor *)
{
    int chance = 0;

    // if not in-bounds then we can't really shatter it -- bwr
    if (!in_bounds(where))
        return 0;

    if (env.markers.property_at(where, MAT_ANY, "veto_shatter") == "veto")
        return 0;

    const dungeon_feature_type grid = grd(where);

    switch (grid)
    {
    case DNGN_SECRET_DOOR:
        if (you.see_cell(where))
            mpr("A secret door shatters!");
        chance = 100;
        break;

    case DNGN_CLOSED_DOOR:
    case DNGN_DETECTED_SECRET_DOOR:
    case DNGN_OPEN_DOOR:
        if (you.see_cell(where))
            mpr("A door shatters!");
        chance = 100;
        break;

    case DNGN_METAL_WALL:
        chance = pow / 10;
        break;

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

    case DNGN_CLEAR_STONE_WALL:
    case DNGN_STONE_WALL:
        chance = pow / 6;
        break;

    case DNGN_CLEAR_ROCK_WALL:
    case DNGN_ROCK_WALL:
        chance = pow / 4;
        break;

    case DNGN_GREEN_CRYSTAL_WALL:
        chance = 50;
        break;

    default:
        break;
    }

    if (x_chance_in_y(chance, 100))
    {
        noisy(30, where);

        grd(where) = DNGN_FLOOR;

        if (grid == DNGN_ORCISH_IDOL)
            did_god_conduct(DID_DESTROY_ORCISH_IDOL, 8);

        return (1);
    }

    return (0);
}

void cast_shatter(int pow)
{
    int damage = 0;
    const bool silence = silenced(you.pos());

    if (silence)
        mpr("The dungeon shakes!");
    else
    {
        noisy(30, you.pos());
        mpr("The dungeon rumbles!", MSGCH_SOUND);
    }

    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_NONE:
    case TRAN_SPIDER:
    case TRAN_LICH:
    case TRAN_DRAGON:
    case TRAN_BAT:
        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
        mpr("Your scythe-like blades vibrate painfully!");
        damage = 2 + random2avg(5, 2);
        break;

    default:
        mpr("cast_shatter(): unknown transformation in spells4.cc");
    }

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

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

    apply_area_within_radius(_shatter_items, you.pos(), pow, rad, 0);
    apply_area_within_radius(_shatter_monsters, you.pos(), pow, rad, 0);
    int dest = apply_area_within_radius(_shatter_walls, you.pos(),
                                        pow, rad, 0);

    if (dest && !silence)
        mpr("Ka-crash!", MSGCH_SOUND);
}

// Cast_phase_shift: raises evasion (by 8 currently) via Translocations.
void cast_phase_shift(int pow)
{
    if (!you.duration[DUR_PHASE_SHIFT])
        mpr("You feel the strange sensation of being on two planes at once.");
    else
        mpr("Your feel the material plane grow further away.");

    you.increase_duration(DUR_PHASE_SHIFT, 5 + random2(pow), 30);
    you.redraw_evasion = true;
}

void cast_see_invisible(int pow)
{
    if (you.can_see_invisible())
        mpr("Nothing seems to happen.");
    else
    {
        mpr("Your vision seems to sharpen.");

        // We might have to turn autopickup back on again.
        // TODO: Once the spell times out we might want to check all monsters
        //       in LOS for invisibility and turn autopickup off again, if
        //       needed.
        autotoggle_autopickup(false);
    }

    // No message if you already are under the spell.
    you.increase_duration(DUR_SEE_INVISIBLE, 10 + random2(2 + pow/2), 100);
}

// 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 (radius_iterator ri(&you.get_los()); ri; ++ri )
        if (grd(*ri) == DNGN_SECRET_DOOR && random2(pow) > random2(15))
        {
            reveal_secret_door(*ri);
            found++;
        }

    if (found)
        redraw_screen();

    mprf("You detect %s", (found > 0) ? "secret doors!" : "nothing.");
}

static int _sleep_monsters(coord_def where, int pow, int, actor *)
{
    monsters *monster = monster_at(where);
    if (!monster)
        return (0);

    if (!monster->can_hibernate(true))
        return (0);

    if (monster->check_res_magic(pow))
        return (0);

    const int res = monster->res_cold();
    if (res > 0 && one_chance_in(std::max(4 - res, 1)))
        return (0);

    if (monster->has_ench(ENCH_SLEEP_WARY) && !one_chance_in(3))
        return (0);

    monster->hibernate();
    monster->expose_to_element(BEAM_COLD, 2);
    return (1);
}

void cast_mass_sleep(int pow)
{
    apply_area_visible(_sleep_monsters, pow);
}

// This is a hack until we set an is_beast flag in the monster data
// (which we might never do, this is sort of minor.)
// It's a list of monster types which can be affected by beast taming.
static bool _is_domesticated_animal(int type)
{
    const monster_type types[] = {
        MONS_GIANT_BAT, MONS_HOUND, MONS_JACKAL, MONS_RAT,
        MONS_YAK, MONS_WYVERN, MONS_HIPPOGRIFF, MONS_GRIFFON,
        MONS_DEATH_YAK, MONS_WAR_DOG, MONS_GREY_RAT,
        MONS_GREEN_RAT, MONS_ORANGE_RAT, MONS_SHEEP,
        MONS_HOG, MONS_GIANT_FROG, MONS_GIANT_TOAD,
        MONS_SPINY_FROG, MONS_BLINK_FROG, MONS_WOLF, MONS_WARG,
        MONS_BEAR, MONS_GRIZZLY_BEAR, MONS_POLAR_BEAR, MONS_BLACK_BEAR
    };

    for (unsigned int i = 0; i < ARRAYSZ(types); ++i)
        if (types[i] == type)
            return (true);

    return (false);
}

static int _tame_beast_monsters(coord_def where, int pow, int, actor *)
{
    monsters *monster = monster_at(where);
    if (monster == NULL)
        return 0;

    if (!_is_domesticated_animal(monster->type) || monster->friendly()
        || player_will_anger_monster(monster))
    {
        return 0;
    }

    // 50% bonus for dogs
    if (monster->type == MONS_HOUND || monster->type == MONS_WAR_DOG )
        pow += (pow / 2);

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

    if (monster->check_res_magic(pow))
        return 0;

    simple_monster_message(monster, " is tamed!");

    if (random2(100) < random2(pow / 10))
        monster->attitude = ATT_FRIENDLY;  // permanent
    else
        monster->add_ench(ENCH_CHARM);     // temporary

    return 1;
}

void cast_tame_beasts(int pow)
{
    apply_area_visible(_tame_beast_monsters, pow);
}

static int _ignite_poison_objects(coord_def where, int pow, int, actor *)
{
    UNUSED( pow );

    int strength = 0;

    for ( stack_iterator si(where); si; ++si )
    {
        if (si->base_type == OBJ_POTIONS)
        {
            switch (si->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(si->index());
            default:
                break;
            }
        }

        // FIXME: implement burning poisoned ammo
    }

    if (strength > 0)
    {
        place_cloud(CLOUD_FIRE, where,
                    strength + roll_dice(3, strength / 4), KC_YOU);
    }

    return (strength);
}

static int _ignite_poison_clouds( coord_def where, int pow, int, actor *)
{
    UNUSED( pow );

    bool did_anything = false;

    const int i = env.cgrid(where);
    if (i != EMPTY_CLOUD)
    {
        cloud_struct& cloud = env.cloud[i];
        if (cloud.type == CLOUD_STINK)
        {
            did_anything = true;
            cloud.type = CLOUD_FIRE;

            cloud.decay /= 2;

            if (cloud.decay < 1)
                cloud.decay = 1;
        }
        else if (cloud.type == CLOUD_POISON)
        {
            did_anything = true;
            cloud.type = CLOUD_FIRE;
        }
    }

    return did_anything;
}

static int _ignite_poison_monsters(coord_def where, int pow, int, actor *)
{
    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.

    monsters *mon = monster_at(where);
    if (mon == NULL)
        return (0);

    // Monsters which have poison corpses or poisonous attacks.
    if (mons_is_poisoner(mon))
        dam_dice.num = 3;

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

    // First check for player poison.
    mon_enchant ench = mon->get_ench(ENCH_POISON);
    if (ench.ench != ENCH_NONE)
        strength += ench.degree;

    // 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 = dam_dice.roll();
    if (damage > 0)
    {
        damage = mons_adjust_flavoured(mon, beam, damage);
        simple_monster_message(mon, " seems to burn from within!");

        dprf("Dice: %dd%d; Damage: %d", dam_dice.num, dam_dice.size, damage);

        if (!_player_hurt_monster(*mon, damage))
        {
            // Monster survived, remove any poison.
            mon->del_ench(ENCH_POISON);
            behaviour_event(mon, ME_ALERT);
        }

        return (1);
    }

    return (0);
}

void cast_ignite_poison(int pow)
{
    flash_view(RED);

    // Poison branding becomes fire branding.
    if (you.weapon()
        && you.duration[DUR_WEAPON_BRAND]
        && get_weapon_brand(*you.weapon()) == SPWPN_VENOM)
    {
        if (set_item_ego_type(*you.weapon(), OBJ_WEAPONS, SPWPN_FLAMING))
        {
            mprf("%s bursts into flame!",
                 you.weapon()->name(DESC_CAP_YOUR).c_str());

            you.wield_change = true;

            int increase = 1 + you.duration[DUR_WEAPON_BRAND]
                               /(2 * BASELINE_DELAY);

            you.increase_duration(DUR_WEAPON_BRAND, increase, 80);
        }
    }

    int totalstrength = 0;
    int ammo_burnt = 0;
    int potions_burnt = 0;
    bool was_wielding = false;

    for (int i = 0; i < ENDOFPACK; ++i)
    {
        item_def& item = you.inv[i];
        if (!item.is_valid())
            continue;

        int strength = 0;

        if (item.base_type == OBJ_MISSILES && item.special == SPMSL_POISONED)
        {
            // Burn poison ammo.
            strength = item.quantity;
            ammo_burnt += item.quantity;
        }
        else if (item.base_type == OBJ_POTIONS)
        {
            // Burn poisonous potions.
            switch (item.sub_type)
            {
            case POT_STRONG_POISON:
                strength = 20 * item.quantity;
                break;
            case POT_DEGENERATION:
            case POT_POISON:
                strength = 10 * item.quantity;
                break;
            default:
                break;
            }

            if (strength)
                potions_burnt += item.quantity;
        }

        if (strength)
        {
            if (i == you.equip[EQ_WEAPON])
            {
                unwield_item();
                was_wielding = true;
            }

            item_was_destroyed(item);
            destroy_item(item);
        }

        totalstrength += strength;
    }

    if (ammo_burnt)
        mpr("Some ammunition you are carrying burns!");

    if (potions_burnt)
    {
        mprf("%s potion%s you are carrying explode%s!",
             potions_burnt > 1 ? "Some" : "A",
             potions_burnt > 1 ? "s" : "",
             potions_burnt > 1 ? "" : "s");
    }

    if (was_wielding)
        canned_msg(MSG_EMPTY_HANDED);

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

    int damage = 0;
    // Player is poisonous.
    if (player_mutation_level(MUT_SPIT_POISON)
        || player_mutation_level(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.duration[DUR_POISONING], 6);

    if (damage)
    {
        const int resist = player_res_fire();
        if (resist > 0)
        {
            mpr("You feel like your blood is boiling!");
            damage /= 3;
        }
        else if (resist < 0)
        {
            mpr("The poison in your system burns terribly!");
            damage *= 3;
        }
        else
        {
            mpr("The poison in your system burns!");
        }

        ouch(damage, NON_MONSTER, KILLED_BY_TARGETTING);

        if (you.duration[DUR_POISONING] > 0)
        {
            mpr( "You feel that the poison has left your system." );
            you.duration[DUR_POISONING] = 0;
        }
    }

    apply_area_visible(_ignite_poison_clouds, pow);
    apply_area_visible(_ignite_poison_objects, pow);
    apply_area_visible(_ignite_poison_monsters, pow);

#ifndef USE_TILE
    delay(100); // show a brief flash
#endif
    flash_view(0);
}

void cast_silence(int pow)
{
    if (!you.attribute[ATTR_WAS_SILENCED])
        mpr("A profound silence engulfs you.");

    you.attribute[ATTR_WAS_SILENCED] = 1;

    you.increase_duration(DUR_SILENCE, 10 + random2avg(pow,2), 100);

    if (you.beheld())
        you.update_beholders();
}

static int _discharge_monsters(coord_def where, int pow, int, actor *)
{
    monsters *monster = monster_at(where);
    int damage = 0;

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

    if (where == you.pos())
    {
        mpr("You are struck by lightning.");
        damage = 3 + random2(5 + pow / 10);
        damage = check_your_resists( damage, BEAM_ELECTRICITY );
        if (you.airborne())
            damage /= 2;
        ouch(damage, NON_MONSTER, KILLED_BY_WILD_MAGIC);
    }
    else if (monster == NULL)
        return (0);
    else if (monster->res_elec() > 0 || mons_flies(monster))
        return (0);
    else
    {
        damage = 3 + random2(5 + pow/10);
        damage = mons_adjust_flavoured(monster, beam, damage );

        if (damage)
        {
            mprf("%s is struck by lightning.",
                 monster->name(DESC_CAP_THE).c_str());
            _player_hurt_monster(*monster, 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)))
    {
        mpr("The lightning arcs!");
        pow /= (coinflip() ? 2 : 3);
        damage += apply_random_around_square(_discharge_monsters, where,
                                             true, pow, 1);
    }
    else if (damage > 0)
    {
        // Only printed if we did damage, so that the messages in
        // cast_discharge() are clean. -- bwr
        mpr("The lightning grounds out.");
    }

    return (damage);
}

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

    dam = apply_random_around_square(_discharge_monsters, you.pos(),
                                     true, pow, num_targs);

    dprf("Arcs: %d Damage: %d", num_targs, dam);

    if (dam == 0)
    {
        if (coinflip())
            mpr("The air around you crackles with electrical energy.");
        else
        {
            const bool plural = coinflip();
            mprf("%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"));
        }
    }
}

// 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(coord_def where, int pow, int, actor *)
{
    monsters *mon = monster_at(where);
    if (!mon)
        return (0);

    if (mons_genus(mon->type) == MONS_BLINK_FROG)
    {
        simple_monster_message(mon, " resists.");
        return (1);
    }
    else if (mon->check_res_magic(pow))
    {
        // XXX: Note that this might affect magic-immunes!
        if (coinflip())
        {
            simple_monster_message(mon, " partially resists.");
            monster_blink(mon);
        }
        else
            simple_monster_message(mon, " resists.");
        return (1);
    }
    else
    {
        monster_teleport(mon, true);
        return (1);
    }

    return (0);
}

void cast_dispersal(int pow)
{
    if (apply_area_around_square(disperse_monsters, you.pos(), pow) == 0)
        mpr("The air shimmers briefly around you.");
}

int make_a_normal_cloud(coord_def where, int pow, int spread_rate,
                        cloud_type ctype, kill_category whose,
                        killer_type killer, int colour, std::string name,
                        std::string tile)
{
    if (killer == KILL_NONE)
        killer = cloud_struct::whose_to_killer(whose);

    place_cloud( ctype, where,
                 (3 + random2(pow / 4) + random2(pow / 4) + random2(pow / 4)),
                 whose, killer, spread_rate, colour, name, tile );

    return 1;
}

bool _feat_is_passwallable(dungeon_feature_type feat)
{
    // Irony: you can passwall through a secret door but not a door.
    // Worked stone walls are out, they're not diggable and
    // are used for impassable walls...
    switch (feat)
    {
    case DNGN_ROCK_WALL:
    case DNGN_CLEAR_ROCK_WALL:
    case DNGN_SECRET_DOOR:
        return (true);
    default:
        return (false);
    }
}

bool cast_passwall(const coord_def& delta, int pow)
{
    int shallow = 1 + (you.skills[SK_EARTH_MAGIC] / 8);
    int range = shallow + random2(pow) / 25;
    int maxrange = shallow + pow / 25;

    coord_def dest;
    for (dest = you.pos() + delta;
         in_bounds(dest) && _feat_is_passwallable(grd(dest));
         dest += delta) ;

    int walls = (dest - you.pos()).rdist() - 1;
    if (walls == 0)
    {
        mpr("That's not a passable wall.");
        return (false);
    }

    // Below here, failing to cast yields information to the
    // player, so we don't make the spell abort (return true).
    if (!in_bounds(dest))
        mpr("You sense an overwhelming volume of rock.");
    else if (feat_is_solid(grd(dest)))
        mpr("Something is blocking your path through the rock.");
    else if (walls > maxrange)
        mpr("This rock feels extremely deep.");
    else if (walls > range)
        mpr("You fail to penetrate the rock.");
    else
    {
        // Passwall delay is reduced, and the delay cannot be interrupted.
        start_delay(DELAY_PASSWALL, 1 + walls, dest.x, dest.y);
    }
    return (true);
}

static int _intoxicate_monsters(coord_def where, int pow, int, actor *)
{
    UNUSED( pow );

    monsters *monster = monster_at(where);
    if (monster == NULL
        || mons_intel(monster) < I_NORMAL
        || monster->holiness() != MH_NATURAL
        || monster->res_poison() > 0)
    {
        return 0;
    }

    monster->add_ench(mon_enchant(ENCH_CONFUSION, 0, KC_YOU));
    return 1;
}

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), false,
                      "casting intoxication"))
    {
        mpr("Your head spins!");
    }

    apply_area_visible(_intoxicate_monsters, pow);
}

bool backlight_monsters(coord_def where, int pow, int garbage)
{
    UNUSED(pow);
    UNUSED(garbage);

    monsters *monster = monster_at(where);
    if (monster == NULL)
        return (false);

    // Already glowing.
    if (mons_class_flag(monster->type, M_GLOWS))
        return (false);

    mon_enchant bklt = monster->get_ench(ENCH_CORONA);
    const int lvl = bklt.degree;

    // This enchantment overrides invisibility (neat).
    if (monster->has_ench(ENCH_INVIS))
    {
        if (!monster->has_ench(ENCH_CORONA))
        {
            monster->add_ench(
                mon_enchant(ENCH_CORONA, 1, KC_OTHER, random_range(30, 50)));
            simple_monster_message(monster, " is lined in light.");
        }
        return (true);
    }

    monster->add_ench(mon_enchant(ENCH_CORONA, 1));

    if (lvl == 0)
        simple_monster_message(monster, " is outlined in light.");
    else if (lvl == 4)
        simple_monster_message(monster, " glows brighter for a moment.");
    else
        simple_monster_message(monster, " glows brighter.");

    return (true);
}

bool cast_evaporate(int pow, bolt& beem, int pot_idx)
{
    ASSERT(you.inv[pot_idx].base_type == OBJ_POTIONS);
    item_def& potion = you.inv[pot_idx];

    beem.name        = "potion";
    beem.colour      = potion.colour;
    beem.type        = dchar_glyph(DCHAR_FIRED_FLASK);
    beem.beam_source = MHITYOU;
    beem.thrower     = KILL_YOU_MISSILE;
    beem.is_beam     = false;
    beem.aux_source.clear();

    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.is_explosion = true;
    beem.range      = 8;

    beem.flavour    = BEAM_POTION_STINKING_CLOUD;
    beam_type tracer_flavour = BEAM_MMISSILE;

    switch (potion.sub_type)
    {
    case POT_STRONG_POISON:
        beem.flavour   = BEAM_POTION_POISON;
        tracer_flavour = BEAM_POISON;
        beem.ench_power *= 2;
        break;

    case POT_DEGENERATION:
        beem.effect_known = false;
        beem.flavour   = (coinflip() ? BEAM_POTION_POISON : BEAM_POTION_MIASMA);
        tracer_flavour = BEAM_MIASMA;
        beem.ench_power *= 2;
        break;

    case POT_POISON:
        beem.flavour   = BEAM_POTION_POISON;
        tracer_flavour = BEAM_POISON;
        break;

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

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

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

    case POT_BLOOD:
    case POT_BLOOD_COAGULATED:
        if (one_chance_in(3))
            break; // stinking cloud
        // deliberate fall through
    case POT_BERSERK_RAGE:
        beem.effect_known = false;
        beem.flavour = (coinflip() ? BEAM_POTION_FIRE : BEAM_POTION_STEAM);
        if (potion.sub_type == POT_BERSERK_RAGE)
            tracer_flavour = BEAM_FIRE;
        else
            tracer_flavour = BEAM_RANDOM;
        break;

    case POT_MUTATION:
        // Maybe we'll get a mutagenic cloud.
        if (coinflip())
        {
            beem.effect_known = true;
            tracer_flavour = beem.flavour = BEAM_POTION_MUTAGENIC;
            break;
        }
        // if not, deliberate fall through for something random

    case POT_GAIN_STRENGTH:
    case POT_GAIN_DEXTERITY:
    case POT_GAIN_INTELLIGENCE:
    case POT_EXPERIENCE:
    case POT_MAGIC:
        beem.effect_known = false;
        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;
        }
        tracer_flavour = BEAM_RANDOM;
        break;

    default:
        beem.effect_known = false;
        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;
        default:  beem.flavour = BEAM_POTION_STEAM;           break;
        }
        tracer_flavour = BEAM_RANDOM;
        break;
    }

    // Fire tracer. FIXME: use player_tracer() here!
    beem.source         = you.pos();
    beem.can_see_invis  = you.can_see_invisible();
    beem.smart_monster  = true;
    beem.attitude       = ATT_FRIENDLY;
    beem.beam_cancelled = false;
    beem.is_tracer      = true;
    beem.friend_info.reset();

    beam_type real_flavour = beem.flavour;
    beem.flavour           = tracer_flavour;
    beem.fire();

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

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

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

    // Use up a potion.
    if (is_blood_potion(potion))
        remove_oldest_blood_potion(potion);

    dec_inv_item_quantity(pot_idx, 1);

    return (true);
}

// 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 /*pow*/)
{
    int corpse = -1;

    // Search items at the player's location for corpses.
    for (stack_iterator si(you.pos(), true); si; ++si)
    {
        if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY)
        {
            snprintf(info, INFO_SIZE, "Distill a potion from %s?",
                     si->name(DESC_NOCAP_THE).c_str());

            if (yesno(info, true, 0, false))
            {
                corpse = si->index();
                break;
            }
        }
    }

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

    potion_type pot_type = POT_WATER;

    switch (mons_corpse_effect(mitm[corpse].plus))
    {
    case CE_CLEAN:
        pot_type = POT_WATER;
        break;

    case CE_CONTAMINATED:
        pot_type = (mons_weight(mitm[corpse].plus) >= 900)
            ? POT_DEGENERATION : POT_CONFUSION;
        break;

    case CE_POISONOUS:
        pot_type = POT_POISON;
        break;

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

    case CE_MUTAGEN_BAD:    // unused
    case CE_ROTTEN:         // actually this only occurs via mangling
    case CE_HCL:            // necrophage
        pot_type = POT_DECAY;
        break;

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

    switch (mitm[corpse].plus)
    {
    case MONS_RED_WASP:              // paralysis attack
        pot_type = POT_PARALYSIS;
        break;

    case MONS_YELLOW_WASP:           // slowing attack
        pot_type = POT_SLOWING;
        break;

    default:
        break;
    }

    struct monsterentry* smc = get_monster_data(mitm[corpse].plus);

    for (int nattk = 0; nattk < 4; ++nattk)
    {
        if (smc->attack[nattk].flavour == AF_POISON_MEDIUM
            || smc->attack[nattk].flavour == AF_POISON_STRONG
            || smc->attack[nattk].flavour == AF_POISON_STR)
        {
            pot_type = POT_STRONG_POISON;
        }
    }

    const bool was_orc = (mons_species(mitm[corpse].plus) == MONS_ORC);

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

    set_ident_type(mitm[corpse], ID_KNOWN_TYPE);

    mprf("You extract %s from the corpse.",
         mitm[corpse].name(DESC_NOCAP_A).c_str());

    // Try to move the potion to the player (for convenience).
    if (move_item_to_player(corpse, 1) != 1)
        mpr("Unfortunately, you can't carry it right now!");

    if (was_orc)
        did_god_conduct(DID_DESECRATE_ORCISH_REMAINS, 2);
}

bool cast_fragmentation(int pow, const dist& spd)
{
    int debris = 0;
    bool explode = false;
    bool hole    = true;
    const char *what = NULL;

    if (!exists_ray(you.pos(), spd.target))
    {
        mpr("There's a wall in the way!");
        return (false);
    }

    //FIXME: If (player typed '>' to attack floor) goto do_terrain;

    bolt beam;

    beam.flavour     = BEAM_FRAG;
    beam.type        = dchar_glyph(DCHAR_FIRED_BURST);
    beam.beam_source = MHITYOU;
    beam.thrower     = KILL_YOU;
    beam.ex_size     = 1;
    beam.source      = you.pos();
    beam.hit         = AUTOMATIC_HIT;

    beam.set_target(spd);
    beam.aux_source.clear();

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

    const dungeon_feature_type grid = grd(spd.target);

    if (monsters *mon = monster_at(spd.target))
    {
        // Save the monster's name in case it isn't available later.
        const std::string name_cap_the = mon->name(DESC_CAP_THE);

        switch (mon->type)
        {
        case MONS_WOOD_GOLEM:
            simple_monster_message(mon, " shudders violently!");

            // We use beam.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
            explode         = false;
            beam.damage.num = 2;
            _player_hurt_monster(*mon, beam.damage.roll(),
                    BEAM_DISINTEGRATION);
            break;

        case MONS_IRON_GOLEM:
        case MONS_METAL_GARGOYLE:
            explode         = true;
            beam.name       = "blast of metal fragments";
            beam.colour     = CYAN;
            beam.damage.num = 4;
            if (_player_hurt_monster(*mon, beam.damage.roll(),
                        BEAM_DISINTEGRATION))
                beam.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:
        case MONS_STATUE:
            explode         = true;
            beam.ex_size    = 2;
            beam.name       = "blast of rock fragments";
            beam.colour     = BROWN;
            beam.damage.num = 3;
            if (_player_hurt_monster(*mon, beam.damage.roll(),
                        BEAM_DISINTEGRATION))
                beam.damage.num++;
            break;

        case MONS_SILVER_STATUE:
        case MONS_ORANGE_STATUE:
            explode         = true;
            beam.ex_size    = 2;
            if (mon->type == MONS_SILVER_STATUE)
            {
                beam.name       = "blast of silver fragments";
                beam.colour     = WHITE;
                beam.damage.num = 3;
            }
            else
            {
                beam.name       = "blast of orange crystal shards";
                beam.colour     = LIGHTRED;
                beam.damage.num = 6;
            }

            {
                int statue_damage = beam.damage.roll() * 2;
                if (pow >= 50 && one_chance_in(10))
                    statue_damage = mon->hit_points;

                if (_player_hurt_monster(*mon, statue_damage,
                            BEAM_DISINTEGRATION))
                    beam.damage.num += 2;
            }
            break;

        case MONS_CRYSTAL_GOLEM:
            explode         = true;
            beam.ex_size    = 2;
            beam.name       = "blast of crystal shards";
            beam.colour     = WHITE;
            beam.damage.num = 4;
            if (_player_hurt_monster(*mon, beam.damage.roll(),
                        BEAM_DISINTEGRATION))
                beam.damage.num += 2;
            break;

        default:
            if (mon->is_icy()) // blast of ice
            {
                explode         = true;
                beam.name       = "icy blast";
                beam.colour     = WHITE;
                beam.damage.num = 2;
                    beam.flavour    = BEAM_ICE;
                if (_player_hurt_monster(*mon, beam.damage.roll()),
                                         BEAM_DISINTEGRATION);
                    beam.damage.num++;
                break;
            }
            else if (mons_is_skeletal(mon->type)
                || mon->type == MONS_FLYING_SKULL) // blast of bone
            {
                mprf("The %s explodes into sharp fragments of bone!",
                     (mon->type == MONS_FLYING_SKULL) ? "skull" : "skeleton");

                explode     = true;
                beam.name   = "blast of bone shards";
                beam.colour = LIGHTGREY;

                if (x_chance_in_y(pow / 5, 50)) // potential insta-kill
                {
                    monster_die(mon, KILL_YOU, NON_MONSTER);
                    beam.damage.num = 4;
                }
                else
                {
                      beam.damage.num = 2;
                      if (_player_hurt_monster(*mon, beam.damage.roll(),
                                  BEAM_DISINTEGRATION))
                          beam.damage.num += 2;
                  }
                  goto all_done; // i.e., no "Foo Explodes!"
            }
            else
            {
                const bool petrifying = mon->petrifying();
                const bool petrified = mon->petrified() && !petrifying;

                // Petrifying or petrified monsters can be exploded.
                if (petrifying || petrified)
                {
                    explode         = true;
                    beam.ex_size    = petrifying ? 1 : 2;
                    beam.name       = "blast of petrified fragments";
                    beam.colour     = mons_class_colour(mon->type);
                    beam.damage.num = petrifying ? 2 : 3;
                    if (_player_hurt_monster(*mon, beam.damage.roll(),
                                BEAM_DISINTEGRATION))
                        beam.damage.num++;
                    break;
                }
            }

            // Mark that a monster was targetted.
            beam.damage.num = 1;

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

        mprf("%s shatters!", name_cap_the.c_str());
        goto all_done;
    }

    for (stack_iterator si(spd.target, true); si; ++si)
    {
        if (si->base_type == OBJ_CORPSES)
        {
            std::string nm = si->name(DESC_CAP_THE);
            if (si->sub_type == CORPSE_BODY)
            {
                if (!explode_corpse(*si, spd.target))
                {
                    mprf("%s seems to be exceptionally well connected.",
                         nm.c_str());

                    goto all_done;
                }
            }

            mprf("%s explodes!", nm.c_str());
            destroy_item(si.link());
            // si invalid now!
            goto all_done;
        }
    }

    if (env.markers.property_at(spd.target, MAT_ANY,
                                "veto_fragmentation") == "veto")
    {
        mprf("%s seems to be unnaturally hard.",
             feature_description(spd.target, false, DESC_CAP_THE, false).c_str());
        canned_msg(MSG_SPELL_FIZZLES);
        return (true);
    }

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

    switch (grid)
    {
    //
    // Stone and rock terrain
    //
    case DNGN_ROCK_WALL:
    case DNGN_CLEAR_ROCK_WALL:
    case DNGN_SECRET_DOOR:
        beam.colour = env.rock_colour;
        // fall-through
    case DNGN_CLEAR_STONE_WALL:
    case DNGN_STONE_WALL:
        what = "wall";
        if (player_in_branch(BRANCH_HALL_OF_ZOT))
            beam.colour = env.rock_colour;
        // fall-through
    case DNGN_ORCISH_IDOL:
        if (what == NULL)
            what = "stone idol";
        if (beam.colour == 0)
            beam.colour = DARKGREY;
        // fall-through
    case DNGN_GRANITE_STATUE:   // normal rock -- big explosion
        if (what == NULL)
            what = "statue";

        explode = true;

        beam.name       = "blast of rock fragments";
        beam.damage.num = 3;
        if (beam.colour == 0)
            beam.colour = LIGHTGREY;

        if ((grid == DNGN_ORCISH_IDOL
             || grid == DNGN_GRANITE_STATUE
             || pow >= 40 && grid == DNGN_ROCK_WALL && one_chance_in(3)
             || pow >= 40 && grid == DNGN_CLEAR_ROCK_WALL
                 && one_chance_in(3)
             || pow >= 60 && grid == DNGN_STONE_WALL && one_chance_in(10)
             || pow >= 60 && grid == DNGN_CLEAR_STONE_WALL
                 && one_chance_in(10)))
        {
            // terrain blew up real good:
            beam.ex_size        = 2;
            grd(spd.target)     = DNGN_FLOOR;
            debris              = DEBRIS_ROCK;
        }
        break;

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

    case DNGN_METAL_WALL:
        what            = "metal wall";
        beam.colour     = CYAN;
        explode         = true;
        beam.name       = "blast of metal fragments";
        beam.damage.num = 4;

        if (pow >= 80 && x_chance_in_y(pow / 5, 500))
        {
            beam.damage.num += 2;
            grd(spd.target)  = DNGN_FLOOR;
            debris           = DEBRIS_METAL;
        }
        break;

    //
    // Crystal
    //

    case DNGN_GREEN_CRYSTAL_WALL:       // crystal -- large & nasty explosion
        what            = "crystal wall";
        beam.colour     = GREEN;
        explode         = true;
        beam.ex_size    = 2;
        beam.name       = "blast of crystal shards";
        beam.damage.num = 5;

        if (grid == DNGN_GREEN_CRYSTAL_WALL && coinflip())
        {
            beam.ex_size    = coinflip() ? 3 : 2;
            grd(spd.target) = DNGN_FLOOR;
            debris          = DEBRIS_CRYSTAL;
        }
        break;

    //
    // Traps
    //

    case DNGN_UNDISCOVERED_TRAP:
    case DNGN_TRAP_MECHANICAL:
    {
        trap_def* ptrap = find_trap(spd.target);
        if (ptrap && ptrap->category() != DNGN_TRAP_MECHANICAL)
        {
            // Non-mechanical traps don't explode with this spell. -- bwr
            break;
        }

        // Undiscovered traps appear as exploding from the floor. -- bwr
        what = ((grid == DNGN_UNDISCOVERED_TRAP) ? "floor" : "trap");

        explode         = true;
        hole            = false;    // to hit monsters standing on traps
        beam.name       = "blast of fragments";
        beam.colour     = env.floor_colour;  // in order to blend in
        beam.damage.num = 2;

        // Exploded traps are nonfunctional, ammo is also ruined -- bwr
        ptrap->destroy();
        break;
    }

    //
    // Stone doors and arches
    //

    case DNGN_OPEN_DOOR:
    case DNGN_CLOSED_DOOR:
    case DNGN_DETECTED_SECRET_DOOR:
        // Doors always blow up, stone arches never do (would cause problems).
        grd(spd.target) = DNGN_FLOOR;

        // fall-through
    case DNGN_STONE_ARCH:          // Floor -- small explosion.
        explode         = true;
        hole            = false;  // to hit monsters standing on doors
        beam.name       = "blast of rock fragments";
        beam.colour     = LIGHTGREY;
        beam.damage.num = 2;
        break;

    //
    // Permarock and floor are unaffected -- bwr
    //
    case DNGN_PERMAROCK_WALL:
    case DNGN_CLEAR_PERMAROCK_WALL:
    case DNGN_FLOOR:
        explode = false;
        mprf("%s seems to be unnaturally hard.",
             (grid == DNGN_FLOOR) ? "The dungeon floor"
                                  : "That wall");
        break;

    default:
        // FIXME: cute message for water?
        break;
    }

  all_done:
    if (explode && beam.damage.num > 0)
    {
        if (what != NULL)
            mprf("The %s shatters!", what);

        beam.explode(true, hole);

        if (grid == DNGN_ORCISH_IDOL)
            did_god_conduct(DID_DESTROY_ORCISH_IDOL, 8);
    }
    else if (beam.damage.num == 0)
    {
        // If damage dice are zero, assume that nothing happened at all.
        canned_msg(MSG_SPELL_FIZZLES);
    }

    return (true);
}

bool cast_portal_projectile(int pow)
{
    dist target;
    int item = get_ammo_to_shoot(-1, target, true);
    if (item == -1)
        return (false);

    if (cell_is_solid(target.target))
    {
        mpr("You can't shoot at gazebos.");
        return (false);
    }

    // Can't use portal through walls. (That'd be just too cheap!)
    if (you.trans_wall_blocking( target.target ))
    {
        mpr("A translucent wall is in the way.");
        return (false);
    }

    if (!check_warning_inscriptions(you.inv[item], OPER_FIRE))
        return (false);

    bolt beam;
    throw_it( beam, item, true, random2(pow/4), &target );

    return (true);
}

bool cast_apportation(int pow, const coord_def& where)
{
    if (you.trans_wall_blocking(where))
    {
        mpr("A translucent wall is in the way.");
        return (false);
    }

    // Letting mostly-melee characters spam apport after every Shoals
    // fight seems like it has too much grinding potential.  We could
    // weaken this for high power.
    if (grd(where) == DNGN_DEEP_WATER || grd(where) == DNGN_LAVA)
    {
        mpr("The density of the terrain blocks your spell.");
        return (false);
    }

    // Let's look at the top item in that square...
    // And don't allow apporting from shop inventories.
    const int item_idx = igrd(where);
    if (item_idx == NON_ITEM || !in_bounds(where))
    {
        // Maybe the player *thought* there was something there (a mimic.)
        if (monsters* m = monster_at(where))
        {
            if (mons_is_mimic(m->type) && you.can_see(m))
            {
                mprf("%s twitches.", m->name(DESC_CAP_THE).c_str());
                // Nothing else gives this message, so identify the mimic.
                m->flags |= MF_KNOWN_MIMIC;
                return (true);  // otherwise you get free mimic ID
            }
        }

        mpr("There are no items there.");
        return (false);
    }

    item_def& item = mitm[item_idx];

    // Protect the player from destroying the item.
    if (feat_destroys_item(grd(you.pos()), item))
    {
        mpr( "That would be silly while over this terrain!" );
        return (false);
    }

    // Mass of one unit.
    const int unit_mass = item_mass(item);
    const int max_mass = pow * 30 + random2(pow * 20);

    int max_units = item.quantity;
    if (unit_mass > 0)
        max_units = max_mass / unit_mass;

    if (max_units <= 0)
    {
        mpr("The mass is resisting your pull.");
        return (true);
    }

    // We need to modify the item *before* we move it, because
    // move_top_item() might change the location, or merge
    // with something at our position.
    mprf("Yoink! You pull the item%s to yourself.",
         (item.quantity > 1) ? "s" : "");

    if (max_units < item.quantity)
    {
        item.quantity = max_units;
        mpr("You feel that some mass got lost in the cosmic void.");
    }

    // If we apport a net, free the monster under it.
    if (item.base_type == OBJ_MISSILES
        && item.sub_type == MI_THROWING_NET
        && item_is_stationary(item))
    {
        remove_item_stationary(item);
        if (monsters *monster = monster_at(where))
            monster->del_ench(ENCH_HELD, true);
    }

    // Actually move the item.
    move_top_item(where, you.pos());

    return (true);
}

bool wielding_rocks()
{
    bool rc = false;
    if (you.weapon())
    {
        const item_def& wpn(*you.weapon());
        rc = (wpn.base_type == OBJ_MISSILES
              && (wpn.sub_type == MI_STONE || wpn.sub_type == MI_LARGE_ROCK));
    }
    return (rc);
}

bool cast_sandblast(int pow, bolt &beam)
{
    const bool big     = wielding_rocks();
    const bool success = zapping(big ? ZAP_SANDBLAST
                                     : ZAP_SMALL_SANDBLAST, pow, beam, true);

    if (big && success)
        dec_inv_item_quantity( you.equip[EQ_WEAPON], 1 );

    return (success);
}

void remove_condensation_shield()
{
    mpr("Your icy shield evaporates.", MSGCH_DURATION);
    you.duration[DUR_CONDENSATION_SHIELD] = 0;
    you.redraw_armour_class = true;
}

void cast_condensation_shield(int pow)
{
    if (you.shield() || you.duration[DUR_FIRE_SHIELD])
        canned_msg(MSG_SPELL_FIZZLES);
    else
    {
        if (you.duration[DUR_CONDENSATION_SHIELD] > 0)
        {
            mpr("The disc of vapour around you crackles some more.");
            you.increase_duration(DUR_CONDENSATION_SHIELD,
                                  5 + roll_dice(2,3), 30);
        }
        else
        {
            mpr("A crackling disc of dense vapour forms in the air!");
            you.increase_duration(DUR_CONDENSATION_SHIELD,
                                  10 + roll_dice(2, pow / 5), 30);
            you.redraw_armour_class = true;
        }
    }
}

void remove_divine_shield()
{
    mpr("Your divine shield disappears!", MSGCH_DURATION);
    you.duration[DUR_DIVINE_SHIELD] = 0;
    you.attribute[ATTR_DIVINE_SHIELD] = 0;
    you.redraw_armour_class = true;
}

// shield bonus = attribute for duration turns, then decreasing by 1
//                every two out of three turns
// overall shield duration = duration + attribute
// recasting simply resets those two values (to better values, presumably)
void cast_divine_shield()
{
    if (!you.duration[DUR_DIVINE_SHIELD])
    {
        if (you.shield()
            || you.duration[DUR_FIRE_SHIELD]
            || you.duration[DUR_CONDENSATION_SHIELD])
        {
            mprf("Your shield is strengthened by %s's divine power.",
                 god_name(you.religion).c_str());
        }
        else
            mpr("A divine shield forms around you!");
    }
    else
        mpr("Your divine shield is renewed.");

    you.redraw_armour_class = true;

    // duration of complete shield bonus from 35 to 80 turns
    you.set_duration(DUR_DIVINE_SHIELD,
                     35 + (you.skills[SK_INVOCATIONS] * 4) / 3);

    // shield bonus up to 8
    you.attribute[ATTR_DIVINE_SHIELD] = 3 + you.skills[SK_SHIELDS]/5;

    you.redraw_armour_class = true;
}

static int _quadrant_blink(coord_def where, int pow, int, actor *)
{
    if (where == you.pos())
        return (0);

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

    if (pow > 100)
        pow = 100;

    const int dist = random2(6) + 2;  // 2-7

    // This is where you would *like* to go.
    const coord_def base = you.pos() + (where - you.pos()) * dist;

    // This can take a while if pow is high and there's lots of translucent
    // walls nearby.
    coord_def target;
    bool found = false;
    for (int i = 0; i < (pow*pow) / 500 + 1; ++i)
    {
        // Find a space near our base point...
        // First try to find a random square not adjacent to the basepoint,
        // then one adjacent if that fails.
        if (!random_near_space(base, target)
            && !random_near_space(base, target, true))
        {
            return 0;
        }

        // ... which is close enough, but also far enough from us.
        if (distance(base, target) > 10 || distance(you.pos(), target) < 8)
            continue;

        if (!you.see_cell_no_trans(target))
            continue;

        found = true;
        break;
    }

    if (!found)
        return(0);

    coord_def origin = you.pos();
    int res = move_player_to_grid(target, false, true, true);

    if (res)
    {
        // Leave a purple cloud.
        place_cloud(CLOUD_TLOC_ENERGY, origin, 1 + random2(3), KC_YOU);
    }

    return res;
}

int cast_semi_controlled_blink(int pow)
{
    int result = apply_one_neighbouring_square(_quadrant_blink, pow);

    // Controlled blink causes glowing.
    if (result)
        contaminate_player(1, true);

    return (result);
}

void cast_stoneskin(int pow)
{
    if (you.is_undead
        && (you.species != SP_VAMPIRE || you.hunger_state < HS_SATIATED))
    {
        mpr("This spell does not affect your undead flesh.");
        return;
    }

    if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE
        && you.attribute[ATTR_TRANSFORMATION] != TRAN_STATUE
        && you.attribute[ATTR_TRANSFORMATION] != TRAN_BLADE_HANDS)
    {
        mpr("This spell does not affect your current form.");
        return;
    }

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

    if (you.duration[DUR_STONESKIN])
        mpr( "Your skin feels harder." );
    else
    {
        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_STATUE)
            mpr( "Your stone body feels more resilient." );
        else
            mpr( "Your skin hardens." );

        you.redraw_armour_class = true;
    }

    you.increase_duration(DUR_STONESKIN, 10 + random2(pow) + random2(pow), 50);
}

bool do_slow_monster(monsters* mon, kill_category whose_kill)
{
    // Try to remove haste, if monster is hasted.
    if (mon->del_ench(ENCH_HASTE, true))
    {
        if (simple_monster_message(mon, " is no longer moving quickly."))
            return (true);
    }

    // Not hasted, slow it.
    if (!mon->has_ench(ENCH_SLOW)
        && !mons_is_stationary(mon)
        && mon->add_ench(mon_enchant(ENCH_SLOW, 0, whose_kill)))
    {
        if (!mon->paralysed() && !mon->petrified()
            && simple_monster_message(mon, " seems to slow down."))
        {
            return (true);
        }
    }

    return (false);
}