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



                                                       


                   
 

                     
                  
                  



                   

                    
                  
                     
                 
                     
                  
                  
                     
                    
                    
                
                 
                    


                    
                 
                 
                      
                      
                     
                  
                 


                     
                    





                     
                  
                  
                  
                      
                     
 

                    

      

                      











                                                                                

  
                                                             



                                                        
 







                                                              
 



                                                               

                                                                             
          
                                                               
                                             
          
                                                                 
                                                                 
                    

                                                                    
                  
                                                                
                                                                             
          

                                                                             
              

                                                                             
              

                                                                               

                                                       
                                                                             

                                                

                                                               
           
                                                              
                                                     
              

                                                                             
               

                                                                      
                                    
             

                                                                         
            
                                                             


                                                                
                                                         
             

                                                                          
                  
                                                                     
                                                            

  

                                                                    
  


                                                          
                                         



                                                                    
 

                                                                         



                                                                               

                                                  
                                                                         

                                                        
                                                                        
                                                           
 
                                                               

                                                   









                                                                                 

                                                                             
                                                                              
                                                     



                                                        

                                                          

                                                                     

                                                                   

                                                                  
                                                       

                                                      
                                                                  
                                                                              


                                                                          

                                                      







                                                                               
                                                                    
                                                            
                                                                            
                                                                    

                                                        

                      
                                                                          
                                                                               

                                                              

                    
                                                                               

                  
                                                                           
                                                                                
                                                             
                                 

                                                                          
                                                                          
 

                                                              

                                                       

              







                                                                            

               

                                                     


                                                                            
                                                                               
                                                                

                                                         

                                                          

               

                                                       


                                                               
                                     

                                                               


                                                                 
                                     
                                                                               
                                                                 
                       
 
             


                                                                               
                                                          
                                     
                                                                                 
                                                 
                                                        
 
              

                                                                         
                                                                           

                                                                          
 
            



                                                                    
 


                                                                          
                                                                 
                                                        

                                   

                                                                           
                                                                             
                                                                 
                                                                  
                                                                          

                                                           
 


                                                                                 
                                                                     
                                                                                  
 
                                                                              

                                                               
 


                                                                             
                                                      
 
                            
                                                                 







                                            
                                                        






                                                                    
                                                                              





                                                



                                      

                                                            
                        

                                       
     



                                                         
                                                   
         
     


                
 
                                                             
 
                                                       
                           

                     



                                             



                     
                               
                        
 
                                             


                                             

     

                                                                               
     
                               
                        
 
                                                                    

     
                        
     
                               
                        
 
                                                                    



                                   
                               
                        
 
                        



                                  
                               
                        
 
                       



                                 
                               
                        
 
                      

     







                                  

                                       
                               
                        
 
                            



                                    
                               
                        
 
                                                                    
     

                                  
                               



                        






                                           

                                                                        
                          
                      
 
                       

 
                                                                    
 
                                        
 

                           
 


                                        
 
                       
     
                                                                
                                                                    
         

                                            


         
                                                                  
                                                     
                                                    
                    


                                                        
 
                    
     





                                
 
                                                              

                                                      
                                                                     

                                               
 

                                                               
                                                                        
                                               
 


                                                              
 






                                            
 


                                                              
 

                                            
 

                                                              
              
 

                                                          

              


                                                               

                       
                                                  
              



                                                                 
                                                 
 




                                            
 


                                            
 


                                            

              


                                            
 
                           

                                            
 



                                            
 


                                            
 
                    



                                                                     

              
                            
                                                                               


                                           
 



                                               
              
                                             
 



                                                               

              




                                    
 



                                                     
 

                                                     
 

                                    
                                                                          
                                            
         
                          
         
              
                                                             
 



                                                                           

              
                         
                                            
                            

                                             
                                
                               
                              
                               

                                                                           
              
 

                                              
                                  


                     
 
                                   


                     
 



                                                                         

              
                                                      

                                                        

              
                                                          

                                                        

              
                                   
                                       

                                                                           

              
                               
                                
                            

                                        
                                   

                                                

                                              
                                
                            
                                

                                                                           

              



                                                              
 
                                        
                                    

                                                                           

              
                            

                                                                           

              
                                        
                                  
                              
                                 


                                                                           
 

                                   

                                                 
                             
                          

                                                                           

              
                            
                                        
                                
                                     
                                 
                                      
                                    


                                                                           
 
                                 


                                                                          
 
                                
                     

                                                  






                                                                          
                               



                                                                          
                               




                                                                           




                                
 


                                
              
     
 

                                                             

                                 
 

                      
 

                                 
 
                  
 
 



                                                      
                                                     


                                                                 
 
                                                        


             
                                                              
 
                                                                    
                                          




















                                                              

                       
                      



                                    
 
                                                      
                        
     


                                                                              

                                                                   

                                                                     
         
                                                 
                                                                  

                                                 
         

                                                                            
 
                                       
                       
     
 
                       

                                     
                            

                                        
                                           
                           

         
 
                      
                        
     
                                                                            
                                                
 
                                   
 
                                         

                                                    




                                     
         

                                                                 
         
                                 



                                  
                                      

                                                             
                                               




                                                   
 
                                       
                             

                                          
                                               




                               
                                               

 










                                                                         
                                                                   
                                                              






































                                                                        
                                                                


































































                                                                             







                                                                       






                                                 
                                                                            
 

































                                                                         
                                               
 
                                                                    
                                   
     





                                                                              

                                                 
                                    

                             
                                                                                




                                       
                                                                           
                                     




                                       
                                                








                                        
                           
                                
                  


                  
 

                                                

                                  
                                       

                       
 
                                                         
 
                                                 



                                                                                

                                       
                       
     
 
                                                         

                                       
                       
     
 
                                                    





                                       





                                             
 
                                           
                
                                 

                     
 
 













                                                    
                                    




                
                                                

              


               
 
                                                                  
                                                                        



                                

                                                            







                                                                        
         
                                            


                           
              

     




                                                            
                               



                                                                       
 

                                                                            
 
                                                            
 



                                                               
                                                 
              
     
 
                                                              
     


                                                                    
                                                       


                                                          
         

                           

            
                                                
                                                                 
         
              
     

                                                              
                                                             
                                                                             

                           
 
                                                     
                                       

              







                                   

                                                              
                           
 



                                         
                                                                   
 

                                                                  
 

                                                           
 

                                                                    
                  
 
                                




                                                                             
                  
 
                                 




                                                                              
                  
 
                                    




                                                                               
                  
 
                            




                                                                            
                  
 
                                




                                                                             
                  
 
                                       




                                                                            


                                




                                                                             


                  


                  

                                           

                                                                               
 


                                                     

              


                                           
 
                                             
                                       

              
                                                           

                                                                


                                         

              
                                                               
                  
                                           
 
                                      
                                                         
              
 
                                                     
                     
                                           

              

                                 
                                                      

              
                           
                                                      

              
                       

                                                                        
                           

              
                      
                                            

              
                         

                                                                  

              
                            
                                


                                                                          
         
                           
         

              


                                                

              

                          

                                                                
                                          



                                                                                


                           

              
                               

                                              
                                          
                           
 

                                                                                

              
                                                                         
                                                                           
                                                   
                                   

              

                                          
                                    

              
                                                                
                                                                          
                                   
              
 

                                    
                                         
              
 

                                       
                                                
              
 
                   
 



                                          

                         
                                 
                                                                           
                                                  
 
                                    

              
 
                               

                                                           
              
 


                                                           

              



                                     

                                
                                                       
              
 
                                  
                                                                  
                                                                     

                                                 
 
                                        
                                                                             

                                                  
 
                                   

                                                                   
              
 



                                          
                                   


                                                                 
                                                                    


                                                         

                                                 
 
                                        
                  
                                    
              
 

                                                       
 
                                                                        
                                                         

                                                 
 
                              
                                               

                                                 
 


                                
                                









                                                                            
              
     
 

                                                
 


                                                                       
 
                            
                                                                 

                                                 
 
                            
                                                                 

                                                 
 
                                        
                                        
                           
 
                                          

                                                              
 




                                                                            

                           



                                                                              
                                                                                 
         
 

                                    
 
                                                
                                                                               
                                                                            

                                                 
 
                                        
                                        
                           
 
                                              






                                                                            
 
         


                                               

                                                                           
                                                                           
                                                                           
                                                                           
                                                                           


                                                                           
         
 

                                                 
 
                                                 
                                                                                   
                                                                            

                                                 
 

                                    
                           

              
                           
                                                         
                         
              
 
                            
                                                         
                                        
              
 
                                    
                                                         
                                    

                                                                     
              
 
                                    

                                          
              
 
                                       

                                   
              
 



                                                                              
 
                                                                    
                                                                   
                                                                   
         
                           
         
 
                                    
              
     

                                    
                                                 
              
 



                                                                               
 
                                                                     
                                                                   
                                                                   
         
                           
         
 
                                                 
              
     
                                   

                                         
 
                                                 
              
 

                                     
                           
 
                                                  
              
 
                                

                                                  
              
 
                                
                             

                                                 
 
                            

                                        
                           
 
                                     



                                               
 




                                                                               
 
                                                 





                                                                         
              
 
                                 
     


                                                                               
                                             




                           
                      

                                             
                                 
                                   
 
                      


                                                     
                                              
                                                                      
                                   
                                    
              
     
                               

                                    


                                                
                               

                           
                                                

              
                                  

                                
                                                

              
                                

                           


                                                
                                 

                           
                                                

              
                            

                                                                            
         
                           
         
                                                       

              




                                            
                                  


                                   
                   
         

                                            

         


                                                                   





                                                                               





                                                                          


                               
 
                              
                   


                                                 
                                
                                     

                                            
                           
         



                                                 
                          




                                            



                                                 
                                  




                                            



                                                 
                               
                            
         
                           
         



                                                 
                       




                                           

              

                               
                                                                        

                                            

                                        





                                                 
 
                                 
                                          


                            
     


                                                                           
                                                                        


                                                                     
 

                                                 
     
 

                                        
                                                     
              
 




                                          
                                       

                                   

              

                                                                       
                                                 

              

                                                                   
                                                 
              
 
                                 
                                          
                                                          
                                                 
                              


              
                                
                                                                                 
                             
                                                                      
                                 



                              
                               
              
 
 


                                         
     
 
                  

 
                                                       
 

                                                              
 
                                                                          
                                                  
                                                         
 
                                                 
                                                        
 
                     
     


                                             

     
                     
     
                                 


                                             
 
                  
                                              
 


                                 
 
                                                           
 
                                                                 
 
                                    
                        
                                                             
                                                          


                                                                 
 


                                                         
                               
     

                                                                     
                                                 
     






                                                                          
                                                
                                               
 
                    
                                
                       
 
                                   
 

                                                     
     
                                     


                                     
                                                                       
                                                                          
                                   


                                    
 
                          

                                                                               
                                                         
         
                                         
             
                                                                           
                                                                              
                                       



                                        
 
                
     
                                                            

                                              


                        

                                            
                                                          



                                                         
                                                        

     
 



                                              
 
                                                      

                                            
 

                            
                                                    



                                                              
 

                                                                             
 
                                                          
                                    

                         
 
                                                     

                                
 
                               
                                                              
                                                                     
 

                                                              
 




                                                                       
     
                                                                        
                                                                            
     




                                                               
 
                                                                   
     



























                                                                              
                                        
                                                          
     
 
                                                              
                                          
                                                          
     
                                                            
     
 
                                                           
     


                                                                     
                                                                 
                                                           


                                                         
                                                              
         
     
 
                 
                                                       
                                                                      
 
                                                 
                                                                
 
                                                 
                                                            
 
                                                
                                                           
 
                                              
                                                              
 
                                                  
                                                                 
 
                                                
                                                              
 
                                                
                                                               
 
                                               
                                                               
 
                                         
                                                                    
 
                                        
                                        
     
                                                                      
     
 
                                         
                                                         
 
                                                    
                                                                 
 
                           
                                     
                                                                            
                                      
                                                                        

                                                                       

                                                                            
 
                                                               

                                                                      
                                                        
                                                                          
     
                                                   
         
                                                 
             
                                                                         
                                             
                 
                                                               
                                                                    

                                            
                                                                      

                                                    
                                                                          

                                            
                                                                       


                                                    
             
         




                                      
                                                                              
         
     
 
                                                               
                                                             
                                                                     
 

                                                                              

                                                         
     
                                                                
     
 
                                                
                                               
                                                                    
 
                             
                                   
                                                               
 
                                                                 
                                                                 
 

                                                                 
                                            

                                                                

                                                                 
                                    
                                                                          
            
                                                                            
     
 
                                                       

                                                              
                                           
     
                                                     
                                    








                                                                         
     
 
                                                     
     
                                                                       
     
 
                                                 

                                                     
                                    
                                   
                     
 
                                                            
                                     


                                                
 
                                                                 
                                                             
             
                                              




                                     
 
                         

                                      
                                                               



                                                                  
                                                                  
     
 

                   
 

                                                                     


                                                                       
                                                   
                                                                   

          
                                              
 
                            

                                                
 
                                                       
     
                                          



                                                                
 
                                                                
                        
                                    
 
                                 
                            




                                                   
                           

         
                        
 
 
                            
 
                                       
 
                                                         
 
                                       
                                
     
                                                                     
                                                     
                                                           

     
                                                                      
                
 
                                               
     
                                                               
         





                                                                   
                                                               
                 
                                                                              


                                                         
                                                                    
                 
                                                                               


                                                         


         
 

                                                          
                                                         

                                         
                                                            
                          
 
                                                             
 
                                                        
                                                                  
                                                  

                                                               

                                                           
                          
         

     
                                               
                                                      

                                                               

                                                           
                          
         


                                
                
 
 




                                                                        
                                                           
 




                                                                 
/*
 *  File:       abl-show.cc
 *  Summary:    Functions related to special abilities.
 *  Written by: Linley Henzell
 */

#include "AppHdr.h"

#include "abl-show.h"

#include <sstream>
#include <iomanip>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "externs.h"

#include "abyss.h"
#include "artefact.h"
#include "beam.h"
#include "database.h"
#include "decks.h"
#include "delay.h"
#include "describe.h"
#include "directn.h"
#include "effects.h"
#include "env.h"
#include "food.h"
#include "godabil.h"
#include "it_use2.h"
#include "macro.h"
#include "message.h"
#include "menu.h"
#include "misc.h"
#include "mon-place.h"
#include "mgen_data.h"
#include "mutation.h"
#include "notes.h"
#include "ouch.h"
#include "player.h"
#include "religion.h"
#include "skills.h"
#include "species.h"
#include "spl-cast.h"
#include "spl-util.h"
#include "spells1.h"
#include "spells2.h"
#include "spells3.h"
#include "spells4.h"
#include "state.h"
#include "stuff.h"
#include "areas.h"
#include "transform.h"
#include "tutorial.h"

#ifdef UNIX
#include "libunix.h"
#endif

enum ability_flag_type
{
    ABFLAG_NONE           = 0x00000000,
    ABFLAG_BREATH         = 0x00000001, // ability uses DUR_BREATH_WEAPON
    ABFLAG_DELAY          = 0x00000002, // ability has its own delay (ie recite)
    ABFLAG_PAIN           = 0x00000004, // ability must hurt player (ie torment)
    ABFLAG_PIETY          = 0x00000008, // ability has its own piety cost
    ABFLAG_EXHAUSTION     = 0x00000010, // fails if you.exhausted
    ABFLAG_INSTANT        = 0x00000020, // doesn't take time to use
    ABFLAG_PERMANENT_HP   = 0x00000040, // costs permanent HPs
    ABFLAG_PERMANENT_MP   = 0x00000080, // costs permanent MPs
    ABFLAG_CONF_OK        = 0x00000100, // can use even if confused
    ABFLAG_FRUIT          = 0x00000200, // ability requires fruit
    ABFLAG_VARIABLE_FRUIT = 0x00000400  // ability requires fruit or piety
};

static int  _find_ability_slot( ability_type which_ability );
static bool _activate_talent(const talent& tal);
static bool _do_ability(const ability_def& abil);
static void _pay_ability_costs(const ability_def& abil);
static std::string _describe_talent(const talent& tal);

// this all needs to be split into data/util/show files
// and the struct mechanism here needs to be rewritten (again)
// along with the display routine to piece the strings
// together dynamically ... I'm getting to it now {dlb}

// it makes more sense to think of them as an array
// of structs than two arrays that share common index
// values -- well, doesn't it? {dlb}

// declaring this const messes up externs later, so don't do it
ability_type god_abilities[MAX_NUM_GODS][MAX_GOD_ABILITIES] =
{
    // no god
    { ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
      ABIL_NON_ABILITY },
    // Zin
    { ABIL_ZIN_RECITE, ABIL_ZIN_VITALISATION, ABIL_NON_ABILITY,
      ABIL_NON_ABILITY, ABIL_ZIN_SANCTUARY },
    // TSO
    { ABIL_NON_ABILITY, ABIL_TSO_DIVINE_SHIELD, ABIL_NON_ABILITY,
      ABIL_TSO_CLEANSING_FLAME, ABIL_TSO_SUMMON_DIVINE_WARRIOR },
    // Kikubaaqudgha
    { ABIL_KIKU_RECEIVE_CORPSES, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
      ABIL_NON_ABILITY, ABIL_NON_ABILITY },
    // Yredelemnul
    { ABIL_YRED_ANIMATE_REMAINS, ABIL_YRED_RECALL_UNDEAD_SLAVES,
      ABIL_YRED_ANIMATE_DEAD, ABIL_YRED_DRAIN_LIFE, ABIL_YRED_ENSLAVE_SOUL },
    // Xom
    { ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
      ABIL_NON_ABILITY },
    // Vehumet
    { ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
      ABIL_NON_ABILITY },
    // Okawaru
    { ABIL_OKAWARU_MIGHT, ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
      ABIL_OKAWARU_HASTE },
    // Makhleb
    { ABIL_NON_ABILITY, ABIL_MAKHLEB_MINOR_DESTRUCTION,
      ABIL_MAKHLEB_LESSER_SERVANT_OF_MAKHLEB, ABIL_MAKHLEB_MAJOR_DESTRUCTION,
      ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB },
    // Sif Muna
    { ABIL_SIF_MUNA_CHANNEL_ENERGY, ABIL_SIF_MUNA_FORGET_SPELL,
      ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY },
    // Trog
    { ABIL_TROG_BERSERK, ABIL_TROG_REGEN_MR, ABIL_NON_ABILITY,
      ABIL_TROG_BROTHERS_IN_ARMS, ABIL_NON_ABILITY },
    // Nemelex
    { ABIL_NEMELEX_DRAW_ONE, ABIL_NEMELEX_PEEK_TWO, ABIL_NEMELEX_TRIPLE_DRAW,
      ABIL_NEMELEX_MARK_FOUR, ABIL_NEMELEX_STACK_FIVE },
    // Elyvilon
    { ABIL_ELYVILON_LESSER_HEALING_OTHERS, ABIL_ELYVILON_PURIFICATION,
      ABIL_ELYVILON_GREATER_HEALING_OTHERS, ABIL_ELYVILON_RESTORATION,
      ABIL_ELYVILON_DIVINE_VIGOUR },
    // Lugonu
    { ABIL_LUGONU_ABYSS_EXIT, ABIL_LUGONU_BEND_SPACE, ABIL_LUGONU_BANISH,
      ABIL_LUGONU_CORRUPT, ABIL_LUGONU_ABYSS_ENTER },
    // Beogh
    { ABIL_NON_ABILITY, ABIL_BEOGH_SMITING, ABIL_NON_ABILITY,
      ABIL_BEOGH_RECALL_ORCISH_FOLLOWERS, ABIL_NON_ABILITY },
    // Jiyva
    { ABIL_JIYVA_CALL_JELLY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
      ABIL_JIYVA_SLIMIFY, ABIL_JIYVA_CURE_BAD_MUTATION },
    // Fedhas
    { ABIL_FEDHAS_EVOLUTION, ABIL_FEDHAS_SUNLIGHT, ABIL_FEDHAS_PLANT_RING,
      ABIL_FEDHAS_SPAWN_SPORES, ABIL_FEDHAS_RAIN},
    // Cheibriados
    { ABIL_NON_ABILITY, ABIL_CHEIBRIADOS_TIME_BEND, ABIL_NON_ABILITY,
      ABIL_CHEIBRIADOS_SLOUCH, ABIL_CHEIBRIADOS_TIME_STEP },
};

// The description screen was way out of date with the actual costs.
// This table puts all the information in one place... -- bwr
//
// The four numerical fields are: MP, HP, food, and piety.
// Note:  food_cost  = val + random2avg( val, 2 )
//        piety_cost = val + random2( (val + 1) / 2 + 1 );
static const ability_def Ability_List[] =
{
    // NON_ABILITY should always come first
    { ABIL_NON_ABILITY, "No ability", 0, 0, 0, 0, ABFLAG_NONE },
    { ABIL_SPIT_POISON, "Spit Poison", 0, 0, 40, 0, ABFLAG_BREATH },

    { ABIL_TELEPORTATION, "Teleportation", 0, 100, 200, 0, ABFLAG_NONE },
    { ABIL_BLINK, "Blink", 0, 50, 50, 0, ABFLAG_NONE },

    { ABIL_BREATHE_FIRE, "Breathe Fire", 0, 0, 125, 0, ABFLAG_BREATH },
    { ABIL_BREATHE_FROST, "Breathe Frost", 0, 0, 125, 0, ABFLAG_BREATH },
    { ABIL_BREATHE_POISON, "Breathe Poison Gas", 0, 0, 125, 0, ABFLAG_BREATH },
    { ABIL_BREATHE_LIGHTNING, "Breathe Lightning",
      0, 0, 125, 0, ABFLAG_BREATH },
    { ABIL_BREATHE_POWER, "Breathe Power", 0, 0, 125, 0, ABFLAG_BREATH },
    { ABIL_BREATHE_STICKY_FLAME, "Breathe Sticky Flame",
      0, 0, 125, 0, ABFLAG_BREATH },
    { ABIL_BREATHE_STEAM, "Breathe Steam", 0, 0, 75, 0, ABFLAG_BREATH },
    { ABIL_TRAN_BAT, "Bat Form", 2, 0, 0, 0, ABFLAG_NONE },

    { ABIL_SPIT_ACID, "Spit Acid", 0, 0, 125, 0, ABFLAG_NONE },

    { ABIL_FLY, "Fly", 3, 0, 100, 0, ABFLAG_NONE },
    { ABIL_SUMMON_MINOR_DEMON, "Summon Minor Demon", 0, 50, 75, 0, ABFLAG_NONE },
    { ABIL_SUMMON_DEMON, "Summon Demon", 0, 150, 150, 0, ABFLAG_NONE },
    { ABIL_HELLFIRE, "Hellfire", 0, 350, 200, 0, ABFLAG_NONE },
    { ABIL_TORMENT, "Torment", 0, 100, 250, 0, ABFLAG_PAIN },
    { ABIL_RAISE_DEAD, "Raise Dead", 0, 75, 150, 0, ABFLAG_NONE },
    { ABIL_CONTROL_DEMON, "Control Demon", 0, 275, 100, 0, ABFLAG_NONE },
    { ABIL_CHANNELING, "Channeling", 0, 15, 30, 0, ABFLAG_NONE },
    { ABIL_THROW_FLAME, "Throw Flame", 0, 20, 50, 0, ABFLAG_NONE },
    { ABIL_THROW_FROST, "Throw Frost", 0, 20, 50, 0, ABFLAG_NONE },
    { ABIL_BOLT_OF_DRAINING, "Bolt of Draining", 0, 175, 100, 0, ABFLAG_NONE },

    // FLY_II used to have ABFLAG_EXHAUSTION, but that's somewhat meaningless
    // as exhaustion's only (and designed) effect is preventing Berserk. - bwr
    { ABIL_FLY_II, "Fly", 0, 0, 25, 0, ABFLAG_NONE },
    { ABIL_DELAYED_FIREBALL, "Release Delayed Fireball",
      0, 0, 0, 0, ABFLAG_INSTANT },
    { ABIL_MUMMY_RESTORATION, "Self-Restoration",
      1, 0, 0, 0, ABFLAG_PERMANENT_MP },

    // EVOKE abilities use Evocations and come from items:
    // Mapping, Teleportation, and Blink can also come from mutations
    // so we have to distinguish them (see above).  The off items
    // below are labeled EVOKE because they only work now if the
    // player has an item with the evocable power (not just because
    // you used a wand, potion, or miscast effect).  I didn't see
    // any reason to label them as "Evoke" in the text, they don't
    // use or train Evocations (the others do).  -- bwr
    { ABIL_EVOKE_TELEPORTATION, "Evoke Teleportation",
      3, 0, 200, 0, ABFLAG_NONE },
    { ABIL_EVOKE_BLINK, "Evoke Blink", 1, 0, 50, 0, ABFLAG_NONE },
    { ABIL_RECHARGING, "Device Recharging", 1, 0, 0, 0, ABFLAG_PERMANENT_MP },

    { ABIL_EVOKE_BERSERK, "Evoke Berserk Rage", 0, 0, 0, 0, ABFLAG_NONE },

    { ABIL_EVOKE_TURN_INVISIBLE, "Evoke Invisibility",
      2, 0, 250, 0, ABFLAG_NONE },
    { ABIL_EVOKE_TURN_VISIBLE, "Turn Visible", 0, 0, 0, 0, ABFLAG_NONE },
    { ABIL_EVOKE_LEVITATE, "Evoke Levitation", 1, 0, 100, 0, ABFLAG_NONE },
    { ABIL_EVOKE_STOP_LEVITATING, "Stop Levitating", 0, 0, 0, 0, ABFLAG_NONE },

    { ABIL_END_TRANSFORMATION, "End Transformation", 0, 0, 0, 0, ABFLAG_NONE },

    // INVOCATIONS:
    // Zin
    { ABIL_ZIN_SUSTENANCE, "Sustenance", 0, 0, 0, 0, ABFLAG_PIETY },
    { ABIL_ZIN_RECITE, "Recite", 3, 0, 0, 0, ABFLAG_DELAY },
    { ABIL_ZIN_VITALISATION, "Vitalisation", 0, 0, 100, 2, ABFLAG_CONF_OK },
    { ABIL_ZIN_SANCTUARY, "Sanctuary", 7, 0, 150, 15, ABFLAG_NONE },
    { ABIL_ZIN_CURE_ALL_MUTATIONS, "Cure All Mutations",
      0, 0, 0, 0, ABFLAG_NONE },

    // The Shining One
    { ABIL_TSO_DIVINE_SHIELD, "Divine Shield", 3, 0, 50, 2, ABFLAG_NONE },
    { ABIL_TSO_CLEANSING_FLAME, "Cleansing Flame", 5, 0, 100, 2, ABFLAG_NONE },
    { ABIL_TSO_SUMMON_DIVINE_WARRIOR, "Summon Divine Warrior",
      8, 0, 150, 4, ABFLAG_NONE },

    // Kikubaaqudgha
    { ABIL_KIKU_RECEIVE_CORPSES, "Receive Corpses", 3, 0, 50, 2, ABFLAG_NONE },

    // Yredelemnul
    { ABIL_YRED_INJURY_MIRROR, "Injury Mirror", 0, 0, 0, 0, ABFLAG_PIETY },
    { ABIL_YRED_ANIMATE_REMAINS, "Animate Remains", 1, 0, 100, 0, ABFLAG_NONE },
    { ABIL_YRED_RECALL_UNDEAD_SLAVES, "Recall Undead Slaves",
      2, 0, 50, 0, ABFLAG_NONE },
    { ABIL_YRED_ANIMATE_DEAD, "Animate Dead", 3, 0, 100, 1, ABFLAG_NONE },
    { ABIL_YRED_DRAIN_LIFE, "Drain Life", 6, 0, 200, 2, ABFLAG_NONE },
    { ABIL_YRED_ENSLAVE_SOUL, "Enslave Soul", 8, 0, 150, 4, ABFLAG_NONE },

    // Okawaru
    { ABIL_OKAWARU_MIGHT, "Might", 2, 0, 50, 1, ABFLAG_NONE },
    { ABIL_OKAWARU_HASTE, "Haste",
      5, 0, 100, generic_cost::fixed(5), ABFLAG_NONE },

    // Makhleb
    { ABIL_MAKHLEB_MINOR_DESTRUCTION, "Minor Destruction",
      1, 0, 20, 0, ABFLAG_NONE },
    { ABIL_MAKHLEB_LESSER_SERVANT_OF_MAKHLEB, "Lesser Servant of Makhleb",
      2, 0, 50, 1, ABFLAG_NONE },
    { ABIL_MAKHLEB_MAJOR_DESTRUCTION, "Major Destruction",
      4, 0, 100, 2, ABFLAG_NONE },
    { ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB, "Greater Servant of Makhleb",
      6, 0, 100, 3, ABFLAG_NONE },

    // Sif Muna
    { ABIL_SIF_MUNA_CHANNEL_ENERGY, "Channel Energy",
      0, 0, 100, 0, ABFLAG_NONE },
    { ABIL_SIF_MUNA_FORGET_SPELL, "Forget Spell", 5, 0, 0, 8, ABFLAG_NONE },

    // Trog
    { ABIL_TROG_BURN_SPELLBOOKS, "Burn Spellbooks", 0, 0, 10, 0, ABFLAG_NONE },
    { ABIL_TROG_BERSERK, "Berserk", 0, 0, 200, 0, ABFLAG_NONE },
    { ABIL_TROG_REGEN_MR, "Trog's Hand",
      0, 0, 50, generic_cost::range(2, 3), ABFLAG_NONE },
    { ABIL_TROG_BROTHERS_IN_ARMS, "Brothers in Arms",
      0, 0, 100, generic_cost::range(5, 6), ABFLAG_NONE },

    // Elyvilon
    { ABIL_ELYVILON_DESTROY_WEAPONS, "Destroy Weapons",
      0, 0, 0, 0, ABFLAG_NONE },
    { ABIL_ELYVILON_LESSER_HEALING_SELF, "Lesser Self-Healing",
      1, 0, 100, generic_cost::range(0, 1), ABFLAG_CONF_OK },
    { ABIL_ELYVILON_LESSER_HEALING_OTHERS, "Lesser Healing",
      1, 0, 100, 0, ABFLAG_CONF_OK },
    { ABIL_ELYVILON_PURIFICATION, "Purification", 2, 0, 150, 1,
      ABFLAG_CONF_OK },
    { ABIL_ELYVILON_GREATER_HEALING_SELF, "Greater Self-Healing",
      2, 0, 250, 2, ABFLAG_CONF_OK },
    { ABIL_ELYVILON_GREATER_HEALING_OTHERS, "Greater Healing",
      2, 0, 250, 2, ABFLAG_CONF_OK },
    { ABIL_ELYVILON_RESTORATION, "Restoration", 3, 0, 400, 3, ABFLAG_CONF_OK },
    { ABIL_ELYVILON_DIVINE_VIGOUR, "Divine Vigour", 0, 0, 600, 6,
      ABFLAG_CONF_OK },

    // Lugonu
    { ABIL_LUGONU_ABYSS_EXIT, "Depart the Abyss", 1, 0, 150, 10, ABFLAG_NONE },
    { ABIL_LUGONU_BEND_SPACE, "Bend Space", 1, 0, 50, 0, ABFLAG_PAIN },
    { ABIL_LUGONU_BANISH, "Banish",
      4, 0, 200, generic_cost::range(3, 4), ABFLAG_NONE },
    { ABIL_LUGONU_CORRUPT, "Corrupt",
      7, scaling_cost::fixed(5), 500, generic_cost::range(10, 14), ABFLAG_NONE },
    { ABIL_LUGONU_ABYSS_ENTER, "Enter the Abyss",
      9, 0, 500, generic_cost::fixed(35), ABFLAG_PAIN },

    // Nemelex
    { ABIL_NEMELEX_DRAW_ONE, "Draw One", 2, 0, 0, 0, ABFLAG_NONE },
    { ABIL_NEMELEX_PEEK_TWO, "Peek at Two", 3, 0, 0, 1, ABFLAG_INSTANT },
    { ABIL_NEMELEX_TRIPLE_DRAW, "Triple Draw", 2, 0, 100, 2, ABFLAG_NONE },
    { ABIL_NEMELEX_MARK_FOUR, "Mark Four", 4, 0, 125, 5, ABFLAG_NONE },
    { ABIL_NEMELEX_STACK_FIVE, "Stack Five", 5, 0, 250, 10, ABFLAG_NONE },

    // Beogh
    { ABIL_BEOGH_SMITING, "Smiting",
      3, 0, 80, generic_cost::fixed(3), ABFLAG_NONE },
    { ABIL_BEOGH_RECALL_ORCISH_FOLLOWERS, "Recall Orcish Followers",
      2, 0, 50, 0, ABFLAG_NONE },

    // Jiyva
    { ABIL_JIYVA_CALL_JELLY, "Request Jelly", 2, 0, 20, 1, ABFLAG_NONE },
    { ABIL_JIYVA_JELLY_SHIELD, "Jelly Shield", 0, 0, 0, 0, ABFLAG_PIETY },
    { ABIL_JIYVA_SLIMIFY, "Slimify", 4, 0, 100, 8, ABFLAG_NONE },
    { ABIL_JIYVA_CURE_BAD_MUTATION, "Cure Bad Mutation",
      8, 0, 200, 15, ABFLAG_NONE },

    // Fedhas
    { ABIL_FEDHAS_FUNGAL_BLOOM, "Decomposition", 0, 0, 0, 0, ABFLAG_NONE },
    { ABIL_FEDHAS_EVOLUTION, "Evolution", 2, 0, 0, 0, ABFLAG_VARIABLE_FRUIT},
    { ABIL_FEDHAS_SUNLIGHT, "Sunlight", 2, 0, 0, 0, ABFLAG_NONE},
    { ABIL_FEDHAS_PLANT_RING, "Growth", 2, 0, 0, 0, ABFLAG_FRUIT},
    { ABIL_FEDHAS_SPAWN_SPORES, "Reproduction", 4, 0, 50, 2, ABFLAG_NONE},
    { ABIL_FEDHAS_RAIN, "Rain", 4, 0, 100, 4, ABFLAG_NONE},


    // Cheibriados
    { ABIL_CHEIBRIADOS_PONDEROUSIFY, "Make Ponderous", 2, 0, 0, 0, ABFLAG_NONE },
    { ABIL_CHEIBRIADOS_TIME_BEND, "Bend Time", 3, 0, 50, 1, ABFLAG_NONE },
    { ABIL_CHEIBRIADOS_SLOUCH, "Slouch", 5, 0, 100, 5, ABFLAG_NONE },
    { ABIL_CHEIBRIADOS_TIME_STEP, "Step From Time", 10, 0, 200, 10, ABFLAG_NONE },

    { ABIL_HARM_PROTECTION, "Protection From Harm", 0, 0, 0, 0, ABFLAG_NONE },
    { ABIL_HARM_PROTECTION_II, "Reliable Protection From Harm",
      0, 0, 0, 0, ABFLAG_PIETY },

    { ABIL_RENOUNCE_RELIGION, "Renounce Religion", 0, 0, 0, 0, ABFLAG_NONE },
};

const ability_def & get_ability_def(ability_type abil)
{
    for (unsigned int i = 0;
         i < sizeof(Ability_List) / sizeof(Ability_List[0]); i++)
    {
        if (Ability_List[i].ability == abil)
            return (Ability_List[i]);
    }

    return (Ability_List[0]);
}

bool string_matches_ability_name(const std::string& key)
{
    for (int i = ABIL_SPIT_POISON; i <= ABIL_RENOUNCE_RELIGION; ++i)
    {
        const ability_def abil = get_ability_def((ability_type) i);
        if (abil.ability == ABIL_NON_ABILITY)
            continue;

        const std::string name = lowercase_string(ability_name(abil.ability));
        if (name.find(key) != std::string::npos)
            return (true);
    }
    return (false);
}

std::string print_abilities()
{
    std::string text = "\n<w>a:</w> ";

    const std::vector<talent> talents = your_talents(false);

    if (talents.empty())
        text += "no special abilities";
    else
    {
        for (unsigned int i = 0; i < talents.size(); ++i)
        {
            if (i)
                text += ", ";
            text += ability_name(talents[i].which);
        }
    }

    return text;
}

const std::string make_cost_description(ability_type ability)
{
    const ability_def& abil = get_ability_def(ability);
    std::ostringstream ret;
    if (abil.mp_cost)
    {
        ret << abil.mp_cost;
        if (abil.flags & ABFLAG_PERMANENT_MP)
            ret << " Permanent";
        ret << " MP";
    }

    if (abil.hp_cost)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << abil.hp_cost.cost(you.hp_max);
        if (abil.flags & ABFLAG_PERMANENT_HP)
            ret << " Permanent";
        ret << " HP";
    }

    if (abil.food_cost && you.is_undead != US_UNDEAD
        && (you.is_undead != US_SEMI_UNDEAD || you.hunger_state > HS_STARVING))
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Food";   // randomised and amount hidden from player
    }

    if (abil.piety_cost)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Piety";  // randomised and amount hidden from player
    }

    if (abil.flags & ABFLAG_BREATH)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Breath";
    }

    if (abil.flags & ABFLAG_DELAY)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Delay";
    }

    if (abil.flags & ABFLAG_PAIN)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Pain";
    }

    if (abil.flags & ABFLAG_PIETY)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Piety";
    }

    if (abil.flags & ABFLAG_EXHAUSTION)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Exhaustion";
    }

    if (abil.flags & ABFLAG_INSTANT)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Instant"; // not really a cost, more of a bonus -bwr
    }
    if (abil.flags & ABFLAG_FRUIT)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Fruit";
    }
    if (abil.flags & ABFLAG_VARIABLE_FRUIT)
    {
        if (!ret.str().empty())
            ret << ", ";

        ret << "Fruit or Piety";
    }

    // If we haven't output anything so far, then the effect has no cost
    if (ret.str().empty())
        ret << "None";

    return (ret.str());
}

static talent _get_talent(ability_type ability, bool check_confused)
{
    ASSERT(ability != ABIL_NON_ABILITY);

    talent result;
    result.which = ability;

    int failure = 0;
    bool perfect = false;  // is perfect
    bool invoc = false;

    if (check_confused)
    {
        const ability_def &abil = get_ability_def(result.which);
        if (you.confused() && !testbits(abil.flags, ABFLAG_CONF_OK))
        {
            result.which = ABIL_NON_ABILITY;
            return result;
        }
    }

    // Look through the table to see if there's a preference, else
    // find a new empty slot for this ability. -- bwr
    const int index = _find_ability_slot( ability );
    if (index != -1)
        result.hotkey = index_to_letter(index);
    else
        result.hotkey = 0;      // means 'find later on'

    switch (ability)
    {
    // begin spell abilities
    case ABIL_DELAYED_FIREBALL:
    case ABIL_MUMMY_RESTORATION:
        perfect = true;
        failure = 0;
        break;

    // begin species abilities - some are mutagenic, too {dlb}
    case ABIL_SPIT_POISON:
        failure = ((you.species == SP_NAGA) ? 20 : 40)
                        - 10 * player_mutation_level(MUT_SPIT_POISON)
                        - you.experience_level;
        break;

    case ABIL_BREATHE_FIRE:
        failure = ((you.species == SP_RED_DRACONIAN) ? 30 : 50)
                        - 10 * player_mutation_level(MUT_BREATHE_FLAMES)
                        - you.experience_level;

        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON)
            failure -= 20;
        break;

    case ABIL_BREATHE_FROST:
    case ABIL_BREATHE_POISON:
    case ABIL_SPIT_ACID:
    case ABIL_BREATHE_LIGHTNING:
    case ABIL_BREATHE_POWER:
    case ABIL_BREATHE_STICKY_FLAME:
        failure = 30 - you.experience_level;

        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON)
            failure -= 20;
        break;

    case ABIL_BREATHE_STEAM:
        failure = 20 - you.experience_level;

        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON)
            failure -= 20;
        break;

    case ABIL_FLY:              // this is for kenku {dlb}
        failure = 45 - (3 * you.experience_level);
        break;

    case ABIL_FLY_II:           // this is for draconians {dlb}
        failure = 45 - (you.experience_level + you.strength);
        break;

    case ABIL_TRAN_BAT:
        failure = 45 - (2 * you.experience_level);
        break;

    case ABIL_RECHARGING:       // this is for deep dwarves {1KB}
        failure = 45 - (2 * you.experience_level);
        break;
        // end species abilities (some mutagenic)

        // begin demonic powers {dlb}
    case ABIL_THROW_FLAME:
    case ABIL_THROW_FROST:
        failure = 10 - you.experience_level;
        break;

    case ABIL_SUMMON_MINOR_DEMON:
        failure = 27 - you.experience_level;
        break;

    case ABIL_CHANNELING:
    case ABIL_BOLT_OF_DRAINING:
        failure = 30 - you.experience_level;
        break;

    case ABIL_CONTROL_DEMON:
        failure = 35 - you.experience_level;
        break;

    case ABIL_SUMMON_DEMON:
        failure = 40 - you.experience_level;
        break;

    case ABIL_HELLFIRE:
    case ABIL_RAISE_DEAD:
        failure = 50 - you.experience_level;
        break;

    case ABIL_TORMENT:
        failure = 60 - you.experience_level;
        break;

    case ABIL_BLINK:
        // Allowing perfection makes the third level matter much more
        perfect = true;
        failure = 48 - (12 * player_mutation_level(MUT_BLINK))
                  - you.experience_level / 2;
        break;

    case ABIL_TELEPORTATION:
        failure = ((player_mutation_level(MUT_TELEPORT_AT_WILL) > 1) ? 30 : 50)
                    - you.experience_level;
        break;
        // end demonic powers {dlb}

        // begin transformation abilities {dlb}
    case ABIL_END_TRANSFORMATION:
        perfect = true;
        failure = 0;
        break;
        // end transformation abilities {dlb}

        // begin item abilities - some possibly mutagenic {dlb}
    case ABIL_EVOKE_TURN_INVISIBLE:
    case ABIL_EVOKE_TELEPORTATION:
        failure = 60 - 2 * you.skills[SK_EVOCATIONS];
        break;

    case ABIL_EVOKE_TURN_VISIBLE:
    case ABIL_EVOKE_STOP_LEVITATING:
        perfect = true;
        failure = 0;
        break;

    case ABIL_EVOKE_LEVITATE:
    case ABIL_EVOKE_BLINK:
        failure = 40 - 2 * you.skills[SK_EVOCATIONS];
        break;

    case ABIL_EVOKE_BERSERK:
        failure = 50 - 2 * you.skills[SK_EVOCATIONS];

        if (you.species == SP_TROLL)
            failure -= 30;
        else if (player_genus(GENPC_DWARVEN) || you.species == SP_HILL_ORC
                || player_genus(GENPC_OGRE))
        {
            failure -= 10;
        }
        break;
        // end item abilities - some possibly mutagenic {dlb}

        // begin invocations {dlb}
    case ABIL_ELYVILON_PURIFICATION:
        invoc = true;
        failure = 20 - (you.piety / 20) - (5 * you.skills[SK_INVOCATIONS]);
        break;

    case ABIL_ZIN_RECITE:
    case ABIL_BEOGH_RECALL_ORCISH_FOLLOWERS:
    case ABIL_OKAWARU_MIGHT:
    case ABIL_ELYVILON_LESSER_HEALING_SELF:
    case ABIL_ELYVILON_LESSER_HEALING_OTHERS:
    case ABIL_LUGONU_ABYSS_EXIT:
    case ABIL_JIYVA_CALL_JELLY:
    case ABIL_FEDHAS_SUNLIGHT:
    case ABIL_FEDHAS_EVOLUTION:
        invoc = true;
        failure = 30 - (you.piety / 20) - (6 * you.skills[SK_INVOCATIONS]);
        break;

    // destroying stuff doesn't train anything
    case ABIL_ELYVILON_DESTROY_WEAPONS:
    case ABIL_FEDHAS_FUNGAL_BLOOM:
        invoc = true;
        failure = 0;
        break;

    case ABIL_TROG_BURN_SPELLBOOKS:
        invoc = true;
        failure = 0;
        break;

    // These three are Trog abilities... Invocations means nothing -- bwr
    case ABIL_TROG_BERSERK:    // piety >= 30
        invoc = true;
        failure = 30 - you.piety;       // starts at 0%
        break;

    case ABIL_TROG_REGEN_MR:            // piety >= 50
        invoc = true;
        failure = 80 - you.piety;       // starts at 30%
        break;

    case ABIL_TROG_BROTHERS_IN_ARMS:       // piety >= 100
        invoc = true;
        failure = 160 - you.piety;      // starts at 60%
        break;

    case ABIL_YRED_ANIMATE_REMAINS:
    case ABIL_CHEIBRIADOS_PONDEROUSIFY:
        invoc = true;
        failure = 40 - (you.piety / 20) - (3 * you.skills[SK_INVOCATIONS]);
        break;

    case ABIL_ZIN_VITALISATION:
    case ABIL_TSO_DIVINE_SHIELD:
    case ABIL_BEOGH_SMITING:
    case ABIL_MAKHLEB_MINOR_DESTRUCTION:
    case ABIL_SIF_MUNA_FORGET_SPELL:
    case ABIL_KIKU_RECEIVE_CORPSES:
    case ABIL_YRED_ANIMATE_DEAD:
    case ABIL_MAKHLEB_LESSER_SERVANT_OF_MAKHLEB:
    case ABIL_ELYVILON_GREATER_HEALING_SELF:
    case ABIL_ELYVILON_GREATER_HEALING_OTHERS:
    case ABIL_LUGONU_BEND_SPACE:
    case ABIL_JIYVA_SLIMIFY:
    case ABIL_FEDHAS_PLANT_RING:
        invoc = true;
        failure = 40 - (you.piety / 20) - (5 * you.skills[SK_INVOCATIONS]);
        break;

    case ABIL_SIF_MUNA_CHANNEL_ENERGY:
        invoc = true;
        failure = 40 - you.intel - you.skills[SK_INVOCATIONS];
        break;

    case ABIL_YRED_RECALL_UNDEAD_SLAVES:
    case ABIL_CHEIBRIADOS_TIME_BEND:
        invoc = true;
        failure = 50 - (you.piety / 20) - (you.skills[SK_INVOCATIONS] * 4);
        break;

    case ABIL_LUGONU_BANISH:
        invoc = true;
        failure = 60 - (you.piety / 20) - (5 * you.skills[SK_INVOCATIONS]);
        break;

    case ABIL_MAKHLEB_MAJOR_DESTRUCTION:
    case ABIL_FEDHAS_SPAWN_SPORES:
    case ABIL_YRED_DRAIN_LIFE:
    case ABIL_CHEIBRIADOS_SLOUCH:
        invoc = true;
        failure = 60 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 4);
        break;

    case ABIL_TSO_CLEANSING_FLAME:
    case ABIL_ELYVILON_RESTORATION:
    case ABIL_OKAWARU_HASTE:
    case ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB:
    case ABIL_LUGONU_CORRUPT:
    case ABIL_FEDHAS_RAIN:
        invoc = true;
        failure = 70 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 4);
        break;

    case ABIL_ZIN_SANCTUARY:
    case ABIL_TSO_SUMMON_DIVINE_WARRIOR:
    case ABIL_YRED_ENSLAVE_SOUL:
    case ABIL_ELYVILON_DIVINE_VIGOUR:
    case ABIL_LUGONU_ABYSS_ENTER:
    case ABIL_JIYVA_CURE_BAD_MUTATION:
    case ABIL_CHEIBRIADOS_TIME_STEP:
        invoc = true;
        failure = 80 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 4);
        break;

    case ABIL_NEMELEX_STACK_FIVE:
        invoc = true;
        failure = 80 - (you.piety / 25) - (4 * you.skills[SK_EVOCATIONS]);
        break;

    case ABIL_NEMELEX_MARK_FOUR:
        invoc = true;
        failure = 70 - (you.piety * 2 / 45)
            - (9 * you.skills[SK_EVOCATIONS] / 2);
        break;

    case ABIL_NEMELEX_TRIPLE_DRAW:
        invoc = true;
        failure = 60 - (you.piety / 20) - (5 * you.skills[SK_EVOCATIONS]);
        break;

    case ABIL_NEMELEX_PEEK_TWO:
        invoc = true;
        failure = 40 - (you.piety / 20) - (5 * you.skills[SK_EVOCATIONS]);
        break;

    case ABIL_NEMELEX_DRAW_ONE:
        invoc = true;
        perfect = true;         // Tactically important to allow perfection
        failure = 50 - (you.piety / 20) - (5 * you.skills[SK_EVOCATIONS]);
        break;

    case ABIL_RENOUNCE_RELIGION:
        invoc = true;
        perfect = true;
        failure = 0;
        break;

        // end invocations {dlb}
    default:
        failure = -1;
        break;
    }

    // Perfect abilities are things which can go down to a 0%
    // failure rate (e.g., Renounce Religion.)
    if (failure <= 0 && !perfect)
        failure = 1;

    if (failure > 100)
        failure = 100;

    result.fail = failure;
    result.is_invocation = invoc;

    return result;
}

std::vector<const char*> get_ability_names()
{
    std::vector<talent> talents = your_talents(false);
    std::vector<const char*> result;
    for (unsigned int i = 0; i < talents.size(); ++i)
        result.push_back(get_ability_def(talents[i].which).name);
    return result;
}

static void _print_talent_description(const talent& tal)
{
    clrscr();

    const std::string& name = get_ability_def(tal.which).name;

    // XXX: The suffix is necessary to distinguish between similarly
    // named spells.  Yes, this is a hack.
    std::string lookup = getLongDescription(name + "ability");
    if (lookup.empty())
    {
        // Try again without the suffix.
        lookup = getLongDescription(name);
    }

    if (lookup.empty()) // Still nothing found?
        cprintf("No description found.");
    else
    {
        std::ostringstream data;
        data << name << "$$" << lookup;
        print_description(data.str());
    }
    if (getch() == 0)
        getch();

    clrscr();
}

bool activate_ability()
{
    if (you.berserk())
    {
        canned_msg(MSG_TOO_BERSERK);
        return (false);
    }

    std::vector<talent> talents = your_talents(false);
    if (talents.empty())
    {
        // Give messages if the character cannot use innate talents right now.
        // * Vampires can't turn into bats when full of blood.
        // * Permanent flying (Kenku) cannot be turned off.
        if (you.species == SP_VAMPIRE && you.experience_level >= 3)
            mpr("Sorry, you're too full to transform right now.");
        else if (you.species == SP_KENKU && you.experience_level >= 5
                 || player_mutation_level(MUT_BIG_WINGS))
        {
            if (you.flight_mode() == FL_LEVITATE)
                mpr("You can only start flying from the ground.");
            else if (you.flight_mode() == FL_FLY)
                mpr("You're already flying!");
        }
        else
            mpr("Sorry, you're not good enough to have a special ability.");

        crawl_state.zero_turns_taken();
        return (false);
    }

    if (you.confused())
    {
        talents = your_talents(true);
        if (talents.empty())
        {
            mpr("You're too confused!");
            crawl_state.zero_turns_taken();
            return (false);
        }
    }

    int selected = -1;
    while (selected < 0)
    {
        msg::streams(MSGCH_PROMPT) << "Use which ability? (? or * to list) "
                                   << std::endl;

        const int keyin = get_ch();

        if (keyin == '?' || keyin == '*')
        {
            selected = choose_ability_menu(talents);
            if (selected == -1)
            {
                canned_msg( MSG_OK );
                return (false);
            }
        }
        else if (keyin == ESCAPE || keyin == ' ' || keyin == '\r'
                 || keyin == '\n')
        {
            canned_msg( MSG_OK );
            return (false);
        }
        else if ( isalpha(keyin) )
        {
            // Try to find the hotkey.
            for (unsigned int i = 0; i < talents.size(); ++i)
            {
                if (talents[i].hotkey == keyin)
                {
                    selected = static_cast<int>(i);
                    break;
                }
            }

            // If we can't, cancel out.
            if (selected < 0)
            {
                mpr("You can't do that.");
                crawl_state.zero_turns_taken();
                return (false);
            }
        }
    }

    return _activate_talent(talents[selected]);
}

// Check prerequisites for a number of abilities.
// Abort any attempt if these cannot be met, without losing the turn.
// TODO: Many more cases need to be added!
static bool _check_ability_possible(const ability_def& abil,
                                    bool hungerCheck = true)
{
    // Don't insta-starve the player.
    // (Happens at 100, losing consciousness possible from 500 downward.)
    if (hungerCheck && you.species != SP_VAMPIRE)
    {
        const int expected_hunger = you.hunger - abil.food_cost * 2;
        dprf("hunger: %d, max. food_cost: %d, expected hunger: %d",
             you.hunger, abil.food_cost * 2, expected_hunger);
        // Safety margin for natural hunger, mutations etc.
        if (expected_hunger <= 150)
        {
            mpr("You're too hungry.");
            return (false);
        }
    }

    switch (abil.ability)
    {
    case ABIL_ZIN_RECITE:
    {
        const int result = check_recital_audience();
        if (result < 0)
        {
            mpr("There's no appreciative audience!");
            return (false);
        }
        else if (result < 1)
        {
            mpr("There's no-one here to preach to!");
            return (false);
        }
        return (true);
    }
    case ABIL_ZIN_CURE_ALL_MUTATIONS:
        return (how_mutated());

    case ABIL_ZIN_SANCTUARY:
        if (env.sanctuary_time)
        {
            mpr("There's already a sanctuary in place on this level.");
            return (false);
        }
        return (true);

    case ABIL_ELYVILON_PURIFICATION:
        if (!you.disease && !you.rotting && !you.duration[DUR_POISONING]
            && !you.duration[DUR_CONF] && !you.duration[DUR_SLOW]
            && !you.duration[DUR_PARALYSIS] && !you.petrified())
        {
            mpr("Nothing ails you!");
            return (false);
        }
        return (true);

    case ABIL_ELYVILON_RESTORATION:
    case ABIL_MUMMY_RESTORATION:
        if (you.strength == you.max_strength
            && you.intel == you.max_intel
            && you.dex == you.max_dex
            && (abil.ability == ABIL_MUMMY_RESTORATION || !player_rotted()))
        {
            mprf("You don't need to restore your stats%s!",
                 abil.ability == ABIL_ELYVILON_RESTORATION ? " or hit points"
                                                           : "");
            return (false);
        }
        return (true);

    case ABIL_LUGONU_ABYSS_EXIT:
        if (you.level_type != LEVEL_ABYSS)
        {
            mpr("You aren't in the Abyss!");
            return (false);
        }
        return (true);

    case ABIL_LUGONU_CORRUPT:
        return (!is_level_incorruptible());

    case ABIL_LUGONU_ABYSS_ENTER:
        if (you.level_type == LEVEL_ABYSS)
        {
            mpr("You're already here!");
            return (false);
        }
        else if (you.level_type == LEVEL_PANDEMONIUM)
        {
            mpr("That doesn't work from Pandemonium.");
            return (false);
        }
        return (true);

    case ABIL_SIF_MUNA_FORGET_SPELL:
        if (you.spell_no == 0)
        {
            mpr("You don't know any spells.");
            return (false);
        }
        return (true);

    case ABIL_SPIT_POISON:
    case ABIL_BREATHE_FIRE:
    case ABIL_BREATHE_FROST:
    case ABIL_BREATHE_POISON:
    case ABIL_BREATHE_LIGHTNING:
    case ABIL_BREATHE_POWER:
    case ABIL_BREATHE_STICKY_FLAME:
    case ABIL_BREATHE_STEAM:
        if (you.duration[DUR_BREATH_WEAPON])
        {
            canned_msg(MSG_CANNOT_DO_YET);
            return (false);
        }
        return (true);

    case ABIL_EVOKE_BLINK:
        if (scan_artefacts(ARTP_PREVENT_TELEPORTATION, false))
        {
            return (yesno("You cannot teleport right now. Try anyway?",
                          true, 'n'));
        }
        return (true);

    case ABIL_EVOKE_BERSERK:
    case ABIL_TROG_BERSERK:
        if (you.hunger_state < HS_SATIATED)
        {
            mpr("You're too hungry to berserk.");
            return (false);
        }
        return (you.can_go_berserk(true) && berserk_check_wielded_weapon());

    case ABIL_FLY_II:
        if (you.duration[DUR_EXHAUSTED])
        {
            mpr("You're too exhausted to fly.");
            return (false);
        }
        else if (you.burden_state != BS_UNENCUMBERED)
        {
            mpr("You're carrying too much weight to fly.");
            return (false);
        }
        return (true);

    case ABIL_TORMENT:
        if (you.is_undead)
        {
            mpr("The unliving cannot use this ability.");
            return (false);
        }
        return (true);

    case ABIL_EVOKE_TURN_INVISIBLE:     // ring, randarts, darkness items
        if (you.hunger_state < HS_SATIATED)
        {
            mpr("You're too hungry to turn invisible.");
            return (false);
        }
        return (true);

    default:
        return (true);
    }
}

static bool _activate_talent(const talent& tal)
{
    // Doing these would outright kill the player due to stat drain.
    if (tal.which == ABIL_TRAN_BAT)
    {
        if (you.strength <= 5)
        {
            mpr("You lack the strength for this transformation.", MSGCH_WARN);
            crawl_state.zero_turns_taken();
            return (false);
        }
    }
    else if (tal.which == ABIL_END_TRANSFORMATION
             && player_in_bat_form()
             && you.dex <= 5)
    {
        mpr("Turning back with such low dexterity would be fatal!", MSGCH_WARN);
        more();
        crawl_state.zero_turns_taken();
        return (false);
    }

    if ((tal.which == ABIL_EVOKE_BERSERK || tal.which == ABIL_TROG_BERSERK)
        && !you.can_go_berserk(true))
    {
        crawl_state.zero_turns_taken();
        return (false);
    }

    // Some abilities don't need a hunger check.
    bool hungerCheck = true;
    switch (tal.which)
    {
        case ABIL_RENOUNCE_RELIGION:
        case ABIL_EVOKE_STOP_LEVITATING:
        case ABIL_EVOKE_TURN_VISIBLE:
        case ABIL_END_TRANSFORMATION:
        case ABIL_DELAYED_FIREBALL:
        case ABIL_MUMMY_RESTORATION:
        case ABIL_TRAN_BAT:
            hungerCheck = false;
            break;
        default:
            break;
    }

    if (hungerCheck && you.species != SP_VAMPIRE
        && you.hunger_state == HS_STARVING)
    {
        mpr("You're too hungry.");
        crawl_state.zero_turns_taken();
        return (false);
    }

    const ability_def& abil = get_ability_def(tal.which);

    // Check that we can afford to pay the costs.
    // Note that mutation shenanigans might leave us with negative MP,
    // so don't fail in that case if there's no MP cost.
    if (abil.mp_cost > 0
        && !enough_mp(abil.mp_cost, false, !(abil.flags & ABFLAG_PERMANENT_MP)))
    {
        crawl_state.zero_turns_taken();
        return (false);
    }

    if (!enough_hp(abil.hp_cost.cost(you.hp_max), false))
    {
        crawl_state.zero_turns_taken();
        return (false);
    }

    if (!_check_ability_possible(abil, hungerCheck))
    {
        crawl_state.zero_turns_taken();
        return (false);
    }

    // No turning back now... {dlb}
    if (random2avg(100, 3) < tal.fail)
    {
        mpr("You fail to use your ability.");
        you.turn_is_over = true;
        return (false);
    }

    const bool success = _do_ability(abil);
    if (success)
        _pay_ability_costs(abil);

    return (success);
}

int _calc_breath_ability_range(ability_type ability)
{
    // Following monster draconian abilities.
    switch (ability)
    {
    case ABIL_BREATHE_FIRE:         return 6;
    case ABIL_BREATHE_FROST:        return 6;
    case ABIL_BREATHE_POISON:       return 7;
    case ABIL_BREATHE_LIGHTNING:    return 8;
    case ABIL_SPIT_ACID:            return 8;
    case ABIL_BREATHE_POWER:        return 8;
    case ABIL_BREATHE_STICKY_FLAME: return 5;
    case ABIL_BREATHE_STEAM:        return 7;
    default:
        ASSERT(!"Bad breath type!");
        break;
    }
    return (-2);
}

static bool _do_ability(const ability_def& abil)
{
    int power;
    dist abild;
    bolt beam;
    dist spd;

    // Note: the costs will not be applied until after this switch
    // statement... it's assumed that only failures have returned! - bwr
    switch (abil.ability)
    {
    case ABIL_MUMMY_RESTORATION:
    {
        mpr("You infuse your body with magical energy.");
        bool did_restore = restore_stat(STAT_ALL, 0, false);

        const int oldhpmax = you.hp_max;
        unrot_hp( 100 );
        if (you.hp_max > oldhpmax)
            did_restore = true;

        // If nothing happened, don't take one max MP, don't use a turn.
        if (!did_restore)
        {
            canned_msg(MSG_NOTHING_HAPPENS);
            return (false);
        }

        break;
    }

    case ABIL_RECHARGING:
        if (!recharge_wand(-1))
            return (false); // fail message is already given
        break;

    case ABIL_DELAYED_FIREBALL:
    {
        // Note: Power level of ball calculated at release. - bwr
        const int pow = calc_spell_power(SPELL_DELAYED_FIREBALL, true);
        const int fake_range = spell_range(SPELL_FIREBALL, pow, false);

        if (!spell_direction(spd, beam, DIR_NONE, TARG_HOSTILE, fake_range))
            return (false);

        beam.range = spell_range(SPELL_FIREBALL, pow, true);

        if (!fireball(pow, beam))
            return (false);

        // Only one allowed, since this is instantaneous. - bwr
        you.attribute[ATTR_DELAYED_FIREBALL] = 0;
        break;
    }

    case ABIL_SPIT_POISON:      // Naga + spit poison mutation
    {
        const int pow = you.experience_level
                        + player_mutation_level(MUT_SPIT_POISON) * 5
                        + (you.species == SP_NAGA) * 10;
        beam.range = 6;         // following Venom Bolt

        if (!spell_direction(abild, beam)
            || !player_tracer(ZAP_SPIT_POISON, pow, beam))
        {
            return (false);
        }
        else
        {
            zapping(ZAP_SPIT_POISON, pow, beam);
            you.set_duration(DUR_BREATH_WEAPON, 3 + random2(5) );
        }
        break;
    }
    case ABIL_EVOKE_TELEPORTATION:    // ring of teleportation
    case ABIL_TELEPORTATION:          // teleport mut
        if (player_mutation_level(MUT_TELEPORT_AT_WILL) == 3)
            you_teleport_now(true, true); // instant and to new area of Abyss
        else
            you_teleport();

        if (abil.ability == ABIL_EVOKE_TELEPORTATION)
            exercise(SK_EVOCATIONS, 1);
        break;

    case ABIL_BREATHE_FIRE:
    case ABIL_BREATHE_FROST:
    case ABIL_BREATHE_POISON:
    case ABIL_BREATHE_LIGHTNING:
    case ABIL_SPIT_ACID:
    case ABIL_BREATHE_POWER:
    case ABIL_BREATHE_STICKY_FLAME:
    case ABIL_BREATHE_STEAM:
        beam.range = _calc_breath_ability_range(abil.ability);
        if (!spell_direction(abild, beam))
            return (false);

        switch (abil.ability)
        {
        case ABIL_BREATHE_FIRE:
            power = you.experience_level;
            power += player_mutation_level(MUT_BREATHE_FLAMES) * 4;

            if (you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON)
                power += 12;

            snprintf(info, INFO_SIZE, "You breathe fire%c",
                     (power < 15) ? '.':'!');

            if (!zapping(ZAP_BREATHE_FIRE, power, beam, true, info))
                return (false);
            break;

        case ABIL_BREATHE_FROST:
            if (!zapping(ZAP_BREATHE_FROST, you.experience_level, beam, true,
                         "You exhale a wave of freezing cold."))
            {
                return (false);
            }
            break;

        case ABIL_BREATHE_POISON:
            if (!zapping(ZAP_BREATHE_POISON, you.experience_level, beam, true,
                         "You exhale a blast of poison gas."))
            {
                return (false);
            }
            break;

        case ABIL_BREATHE_LIGHTNING:
            if (!zapping(ZAP_LIGHTNING, (you.experience_level * 2), beam, true,
                         "You spit a bolt of lightning."))
            {
                return (false);
            }
            break;

        case ABIL_SPIT_ACID:
            if (!zapping(ZAP_BREATHE_ACID, you.experience_level, beam, true,
                         "You spit acid."))
            {
                return (false);
            }
            break;

        case ABIL_BREATHE_POWER:
            if (!zapping(ZAP_BREATHE_POWER, you.experience_level, beam, true,
                         "You spit a bolt of incandescent energy."))
            {
                return (false);
            }
            break;

        case ABIL_BREATHE_STICKY_FLAME:
            if (!zapping(ZAP_STICKY_FLAME, you.experience_level, beam, true,
                         "You spit a glob of burning liquid."))
            {
                return (false);
            }
            break;

        case ABIL_BREATHE_STEAM:
            if (!zapping(ZAP_BREATHE_STEAM, you.experience_level, beam, true,
                         "You exhale a blast of scalding steam."))
            {
                return (false);
            }
            break;

        default:
            break;
        }

        if (abil.ability != ABIL_SPIT_ACID)
        {
            you.increase_duration(DUR_BREATH_WEAPON,
                      3 + random2(4) + random2(30 - you.experience_level) / 2);

            if (abil.ability == ABIL_BREATHE_STEAM)
                you.duration[DUR_BREATH_WEAPON] /= 2;
        }
        break;

    case ABIL_EVOKE_BLINK:      // randarts
    case ABIL_BLINK:            // mutation
        random_blink(true);

        if (abil.ability == ABIL_EVOKE_BLINK)
            exercise(SK_EVOCATIONS, 1);
        break;

    case ABIL_EVOKE_BERSERK:    // amulet of rage, randarts
        // Only exercise if berserk succeeds.
        // Because of the test above, this should always happen,
        // but I'm leaving it in - haranp
        if (go_berserk(true))
            exercise(SK_EVOCATIONS, 1);
        break;

    // Fly (kenku) - eventually becomes permanent (see acr.cc).
    case ABIL_FLY:
        cast_fly(you.experience_level * 4);

        if (you.experience_level > 14)
            mpr("You feel very comfortable in the air.");
        break;

    // Fly (Draconians, or anything else with wings).
    case ABIL_FLY_II:
        cast_fly(you.experience_level * 2);
        break;

    // DEMONIC POWERS:
    case ABIL_SUMMON_MINOR_DEMON:
        summon_lesser_demon(you.experience_level * 4);
        break;

    case ABIL_SUMMON_DEMON:
        summon_common_demon(you.experience_level * 4);
        break;

    case ABIL_HELLFIRE:
        if (your_spells(SPELL_HELLFIRE_BURST,
                        you.experience_level * 5, false) == SPRET_ABORT)
            return (false);
        break;

    case ABIL_TORMENT:
        torment(TORMENT_GENERIC, you.pos());
        break;

    case ABIL_RAISE_DEAD:
        animate_dead(&you, you.experience_level * 5, BEH_FRIENDLY,
                     MHITYOU, &you);
        break;

    case ABIL_CONTROL_DEMON:
        beam.range = LOS_RADIUS;
        if (!spell_direction(abild, beam)
            || !zapping(ZAP_CONTROL_DEMON, you.experience_level * 5, beam,
                        true))
        {
            return (false);
        }
        break;

    case ABIL_CHANNELING:
        mpr("You channel some magical energy.");
        inc_mp(1 + random2(5), false);
        break;

    case ABIL_THROW_FLAME:
    case ABIL_THROW_FROST:
        // Taking ranges from the equivalent spells.
        beam.range = (abil.ability == ABIL_THROW_FLAME ? 7 : 8);
        if (!spell_direction(abild, beam))
            return (false);

        if (!zapping((abil.ability == ABIL_THROW_FLAME ? ZAP_FLAME : ZAP_FROST),
                     you.experience_level * 3, beam, true))
        {
            return (false);
        }
        break;

    case ABIL_BOLT_OF_DRAINING:
        // Taking range from Bolt of Draining.
        beam.range = 6;
        if (!spell_direction(abild, beam))
            return (false);

        if (!zapping(ZAP_NEGATIVE_ENERGY, you.experience_level * 6, beam, true))
            return (false);
        break;

    case ABIL_EVOKE_TURN_INVISIBLE:     // ring, randarts, darkness items
        potion_effect(POT_INVISIBILITY, 2 * you.skills[SK_EVOCATIONS] + 5);
        contaminate_player( 1 + random2(3), true );
        exercise(SK_EVOCATIONS, 1);
        break;

    case ABIL_EVOKE_TURN_VISIBLE:
        mpr("You feel less transparent.");
        you.duration[DUR_INVIS] = 1;
        break;

    case ABIL_EVOKE_LEVITATE:           // ring, boots, randarts
        potion_effect(POT_LEVITATION, 2 * you.skills[SK_EVOCATIONS] + 30);
        exercise(SK_EVOCATIONS, 1);
        break;

    case ABIL_EVOKE_STOP_LEVITATING:
        mpr("You feel heavy.");
        you.duration[DUR_LEVITATION] = 1;
        break;

    case ABIL_END_TRANSFORMATION:
        mpr("You feel almost normal.");
        you.set_duration(DUR_TRANSFORMATION, 2);
        break;

    // INVOCATIONS:

    case ABIL_ZIN_SUSTENANCE:
        // Activated via prayer elsewhere.
        break;

    case ABIL_ZIN_RECITE:
    {
        // up to (60 + 40)/2 = 50
        const int pow = (2*skill_bump(SK_INVOCATIONS) + you.piety / 5) / 2;
        start_delay(DELAY_RECITE, 3, pow, you.hp);

        exercise(SK_INVOCATIONS, 2);
        break;
    }

    case ABIL_ZIN_VITALISATION:
        if (cast_vitalisation())
            exercise(SK_INVOCATIONS, (coinflip() ? 3 : 2));
        break;

    case ABIL_ZIN_SANCTUARY:
        if (cast_sanctuary(you.skills[SK_INVOCATIONS] * 4))
            exercise(SK_INVOCATIONS, 5 + random2(8));
        break;

    case ABIL_ZIN_CURE_ALL_MUTATIONS:
        zin_remove_all_mutations();
        break;

    case ABIL_TSO_DIVINE_SHIELD:
        cast_divine_shield();
        exercise(SK_INVOCATIONS, (coinflip() ? 3 : 2));
        break;

    case ABIL_TSO_CLEANSING_FLAME:
        cleansing_flame(10 + (you.skills[SK_INVOCATIONS] * 7) / 6,
                        CLEANSING_FLAME_INVOCATION, you.pos(), &you);
        exercise(SK_INVOCATIONS, 3 + random2(6));
        break;

    case ABIL_TSO_SUMMON_DIVINE_WARRIOR:
        summon_holy_warrior(you.skills[SK_INVOCATIONS] * 4, GOD_SHINING_ONE);
        exercise(SK_INVOCATIONS, 8 + random2(10));
        break;

    case ABIL_KIKU_RECEIVE_CORPSES:
        receive_corpses(you.skills[SK_INVOCATIONS] * 4, you.pos());
        exercise(SK_INVOCATIONS, (coinflip() ? 3 : 2));
        break;

    case ABIL_YRED_INJURY_MIRROR:
        // Activated via prayer elsewhere.
        break;

    case ABIL_YRED_ANIMATE_REMAINS:
        mpr("You attempt to give life to the dead...");

        if (animate_remains(you.pos(), CORPSE_BODY, BEH_FRIENDLY,
                            MHITYOU, &you, "", GOD_YREDELEMNUL) < 0)
        {
            mpr("There are no remains here to animate!");
        }
        exercise(SK_INVOCATIONS, 2 + random2(4));
        break;

    case ABIL_YRED_RECALL_UNDEAD_SLAVES:
        recall(1);
        exercise(SK_INVOCATIONS, 1);
        break;

    case ABIL_YRED_ANIMATE_DEAD:
        mpr("You call on the dead to walk for you...");

        animate_dead(&you, 1 + you.skills[SK_INVOCATIONS], BEH_FRIENDLY,
                     MHITYOU, &you, "", GOD_YREDELEMNUL);
        exercise(SK_INVOCATIONS, 2 + random2(4));
        break;

    case ABIL_YRED_DRAIN_LIFE:
        drain_life(you.skills[SK_INVOCATIONS]);
        exercise(SK_INVOCATIONS, 2 + random2(4));
        break;

    case ABIL_YRED_ENSLAVE_SOUL:
    {
        god_acting gdact;
        beam.range = LOS_RADIUS;
        if (!spell_direction(spd, beam))
            return (false);

        if (!zapping(ZAP_ENSLAVE_SOUL, you.skills[SK_INVOCATIONS] * 4, beam,
                     true))
        {
            return (false);
        }

        exercise(SK_INVOCATIONS, 8 + random2(10));
        break;
    }

    case ABIL_SIF_MUNA_CHANNEL_ENERGY:
        mpr("You channel some magical energy.");

        inc_mp(1 + random2(you.skills[SK_INVOCATIONS] / 4 + 2), false);
        exercise(SK_INVOCATIONS, 1 + random2(3));
        break;

    case ABIL_OKAWARU_MIGHT:
        potion_effect(POT_MIGHT, you.skills[SK_INVOCATIONS] * 8);
        exercise(SK_INVOCATIONS, 1 + random2(3));
        break;

    case ABIL_OKAWARU_HASTE:
        potion_effect(POT_SPEED, you.skills[SK_INVOCATIONS] * 8);
        exercise(SK_INVOCATIONS, 3 + random2(7));
        break;

    case ABIL_MAKHLEB_MINOR_DESTRUCTION:
        if (!spell_direction(spd, beam))
            return (false);

        power = you.skills[SK_INVOCATIONS]
                    + random2(1 + you.skills[SK_INVOCATIONS])
                    + random2(1 + you.skills[SK_INVOCATIONS]);

        // Since the actual beam is random, check with BEAM_MMISSILE and the
        // highest range possible (electricity).
        if (!player_tracer(ZAP_DEBUGGING_RAY, power, beam, 13))
            return (false);

        switch (random2(5))
        {
        case 0: beam.range =  7; zapping(ZAP_FLAME, power, beam); break;
        case 1: beam.range =  8; zapping(ZAP_PAIN,  power, beam); break;
        case 2: beam.range =  5; zapping(ZAP_STONE_ARROW, power, beam); break;
        case 3: beam.range = 13; zapping(ZAP_ELECTRICITY, power, beam); break;
        case 4: beam.range =  8; zapping(ZAP_BREATHE_ACID, power/2, beam); break;
        }

        exercise(SK_INVOCATIONS, 1);
        break;

    case ABIL_MAKHLEB_LESSER_SERVANT_OF_MAKHLEB:
        summon_demon_type(static_cast<monster_type>(MONS_NEQOXEC + random2(5)),
                          20 + you.skills[SK_INVOCATIONS] * 3, GOD_MAKHLEB);
        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_MAKHLEB_MAJOR_DESTRUCTION:
        if (!spell_direction(spd, beam))
            return (false);

        power = you.skills[SK_INVOCATIONS] * 3
                + random2( 1 + you.skills[SK_INVOCATIONS] )
                + random2( 1 + you.skills[SK_INVOCATIONS] );

        // Since the actual beam is random, check with BEAM_MMISSILE and the
        // highest range possible (orb of electricity).
        if (!player_tracer(ZAP_DEBUGGING_RAY, power, beam, 20))
            return (false);

        {
            zap_type ztype = ZAP_DEBUGGING_RAY;
            switch (random2(7))
            {
            case 0: beam.range =  6; ztype = ZAP_FIRE;               break;
            case 1: beam.range =  6; ztype = ZAP_FIREBALL;           break;
            case 2: beam.range = 10; ztype = ZAP_LIGHTNING;          break;
            case 3: beam.range =  5; ztype = ZAP_STICKY_FLAME;       break;
            case 4: beam.range =  5; ztype = ZAP_IRON_SHOT;          break;
            case 5: beam.range =  6; ztype = ZAP_NEGATIVE_ENERGY;    break;
            case 6: beam.range = 20; ztype = ZAP_ORB_OF_ELECTRICITY; break;
            }
            zapping(ztype, power, beam);
        }

        exercise(SK_INVOCATIONS, 3 + random2(5));
        break;

    case ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB:
        summon_demon_type(static_cast<monster_type>(MONS_EXECUTIONER + random2(5)),
                          20 + you.skills[SK_INVOCATIONS] * 3, GOD_MAKHLEB);
        exercise(SK_INVOCATIONS, 6 + random2(6));
        break;

    case ABIL_TROG_BURN_SPELLBOOKS:
        if (!trog_burn_spellbooks())
            return (false);
        break;

    case ABIL_TROG_BERSERK:
        // Trog abilities don't use or train invocations.
        go_berserk(true);
        break;

    case ABIL_TROG_REGEN_MR:
        // Trog abilities don't use or train invocations.
        cast_regen(you.piety / 2, true);
        break;

    case ABIL_TROG_BROTHERS_IN_ARMS:
        // Trog abilities don't use or train invocations.
        summon_berserker(you.piety +
                         random2(you.piety/4) - random2(you.piety/4),
                         GOD_TROG);
        break;

    case ABIL_SIF_MUNA_FORGET_SPELL:
        if (!cast_selective_amnesia(true))
            return (false);
        break;

    case ABIL_ELYVILON_DESTROY_WEAPONS:
        if (!ely_destroy_weapons())
            return (false);
        break;

    case ABIL_ELYVILON_LESSER_HEALING_SELF:
    case ABIL_ELYVILON_LESSER_HEALING_OTHERS:
    {
        const bool self = (abil.ability == ABIL_ELYVILON_LESSER_HEALING_SELF);

        if (cast_healing(3 + (you.skills[SK_INVOCATIONS] / 6), true,
                         self ? you.pos() : coord_def(0, 0), !self,
                         self ? TARG_NUM_MODES : TARG_HOSTILE) < 0)
        {
            return (false);
        }

        exercise(SK_INVOCATIONS, 1);
        break;
    }
    case ABIL_ELYVILON_PURIFICATION:
        purification();
        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_ELYVILON_GREATER_HEALING_SELF:
    case ABIL_ELYVILON_GREATER_HEALING_OTHERS:
    {
        const bool self = (abil.ability == ABIL_ELYVILON_GREATER_HEALING_SELF);

        if (cast_healing(10 + (you.skills[SK_INVOCATIONS] / 3), true,
                         self ? you.pos() : coord_def(0, 0), !self,
                         self ? TARG_NUM_MODES : TARG_HOSTILE) < 0)
        {
            return (false);
        }

        exercise(SK_INVOCATIONS, 3 + random2(5));
        break;
    }
    case ABIL_ELYVILON_RESTORATION:
        restore_stat(STAT_ALL, 0, false);
        unrot_hp(100);

        exercise(SK_INVOCATIONS, 4 + random2(6));
        break;

    case ABIL_ELYVILON_DIVINE_VIGOUR:
        if (!cast_divine_vigour())
            return (false);

        exercise(SK_INVOCATIONS, 6 + random2(10));
        break;

    case ABIL_LUGONU_ABYSS_EXIT:
        banished(DNGN_EXIT_ABYSS);
        exercise(SK_INVOCATIONS, 8 + random2(10));
        break;

    case ABIL_LUGONU_BEND_SPACE:
        lugonu_bends_space();
        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_LUGONU_BANISH:
        beam.range = LOS_RADIUS;
        if (!spell_direction(spd, beam))
            return (false);

        if (beam.target == you.pos())
        {
            mpr("You cannot banish yourself!");
            return (false);
        }

        if (!zapping(ZAP_BANISHMENT, 16 + you.skills[SK_INVOCATIONS] * 8, beam,
                     true))
        {
            return (false);
        }

        exercise(SK_INVOCATIONS, 3 + random2(5));
        break;

    case ABIL_LUGONU_CORRUPT:
        if (!lugonu_corrupt_level(300 + you.skills[SK_INVOCATIONS] * 15))
            return (false);
        exercise(SK_INVOCATIONS, 5 + random2(5));
        break;

    case ABIL_LUGONU_ABYSS_ENTER:
    {
        // Move permanent hp/mp loss from leaving to entering the Abyss. (jpeg)
        const int maxloss = std::max(2, div_rand_round(you.hp_max, 30));
        // Lose permanent HP
        dec_max_hp(random_range(1, maxloss));

        // Paranoia.
        if (you.hp_max < 1)
            you.hp_max = 1;

        // Deflate HP.
        set_hp( 1 + random2(you.hp), false );

        // Lose 1d2 permanent MP.
        rot_mp(coinflip() ? 2 : 1);

        // Deflate MP.
        if (you.magic_points)
            set_mp(random2(you.magic_points), false);

        bool note_status = notes_are_active();
        activate_notes(false);  // This banishment shouldn't be noted.
        banished(DNGN_ENTER_ABYSS);
        activate_notes(note_status);
        break;
    }
    case ABIL_NEMELEX_DRAW_ONE:
        if (!choose_deck_and_draw())
            return (false);
        exercise(SK_EVOCATIONS, 1 + random2(2));
        break;

    case ABIL_NEMELEX_PEEK_TWO:
        if (!deck_peek())
            return (false);
        exercise(SK_EVOCATIONS, 2 + random2(2));
        break;

    case ABIL_NEMELEX_TRIPLE_DRAW:
        if (!deck_triple_draw())
            return (false);
        exercise(SK_EVOCATIONS, 3 + random2(3));
        break;

    case ABIL_NEMELEX_MARK_FOUR:
        if (!deck_mark())
            return (false);
        exercise(SK_EVOCATIONS, 4 + random2(4));
        break;

    case ABIL_NEMELEX_STACK_FIVE:
        if (!deck_stack())
            return (false);
        exercise(SK_EVOCATIONS, 5 + random2(5));
        break;

    case ABIL_BEOGH_SMITING:
        if (your_spells(SPELL_SMITING, (2 + skill_bump(SK_INVOCATIONS)) * 6,
                        false) == SPRET_ABORT)
        {
            return (false);
        }
        exercise(SK_INVOCATIONS, (coinflip() ? 3 : 2));
        break;

    case ABIL_BEOGH_RECALL_ORCISH_FOLLOWERS:
        recall(2);
        exercise(SK_INVOCATIONS, 1);
        break;

    case ABIL_FEDHAS_FUNGAL_BLOOM:
    {
        int count = fungal_bloom();

        if (!count)
        {
            canned_msg(MSG_NOTHING_HAPPENS);
            return (false);
        }

        simple_god_message(" appreciates your contribution to the "
                           "ecosystem.", GOD_FEDHAS);

        // We are following the blood god sacrifice piety gain model, given as:
        // if (random2(level + 10) > 5)
        //    piety_change = 1;
        //  where level = 10
        // so the chance of gaining 1 piety from a corpse sacrifice to a blood
        // god is (14/20 == 70/100)
        //
        // Doubling the expected value per sacrifice to approximate the
        // extra piety gain blood god worshipers get for the initial kill.
        // -cao

        int piety_gain = binomial_generator(count*2, 70);
        gain_piety(piety_gain);
        break;
    }

    case ABIL_FEDHAS_SUNLIGHT:
        sunlight();
        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_FEDHAS_PLANT_RING:
        if (!plant_ring_from_fruit())
        {
            canned_msg(MSG_NOTHING_HAPPENS);
            return (false);
        }

        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_FEDHAS_RAIN:
        if (!rain(you.pos()))
        {
            canned_msg(MSG_NOTHING_HAPPENS);
            return (false);
        }

        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_FEDHAS_SPAWN_SPORES:
        if (!corpse_spores())
        {
            canned_msg(MSG_NOTHING_HAPPENS);
            return (false);
        }

        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_FEDHAS_EVOLUTION:
        if (!evolve_flora())
        {
            return (false);
        }

        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_TRAN_BAT:
        if (!transform(100, TRAN_BAT))
        {
            crawl_state.zero_turns_taken();
            return (false);
        }
        break;

    case ABIL_JIYVA_CALL_JELLY:
    {
        mgen_data mg(MONS_JELLY, BEH_STRICT_NEUTRAL, 0, 0, 0, you.pos(),
                     MHITNOT, 0, GOD_JIYVA);

        mg.non_actor_summoner = "Jiyva";

        if (create_monster(mg) == -1)
            return (false);

        exercise(SK_INVOCATIONS, 1 + random2(3));
        break;
    }

    case ABIL_JIYVA_JELLY_SHIELD:
        // Activated via prayer elsewhere.
        break;

    case ABIL_JIYVA_SLIMIFY:
    {
        const item_def* const weapon = you.weapon();
        const std::string msg = (weapon) ? weapon->name(DESC_NOCAP_YOUR)
                                         : ("your " + you.hand_name(true));
        mprf(MSGCH_DURATION, "A thick mucus forms on %s.", msg.c_str());
        you.increase_duration(DUR_SLIMIFY,
                              you.skills[SK_INVOCATIONS] * 3 / 2 + 3,
                              100);

        exercise(SK_INVOCATIONS, 3 + random2(5));
        break;
    }

    case ABIL_JIYVA_CURE_BAD_MUTATION:
        if (jiyva_remove_bad_mutation())
            exercise(SK_INVOCATIONS, 5 + random2(5));
        break;

    case ABIL_HARM_PROTECTION:
    case ABIL_HARM_PROTECTION_II:
        // Activated via prayer elsewhere.
        break;

    case ABIL_CHEIBRIADOS_PONDEROUSIFY:
        if (!ponderousify_armour())
            return (false);
        break;

    case ABIL_CHEIBRIADOS_TIME_STEP:
        cheibriados_time_step(you.skills[SK_INVOCATIONS]*you.piety/10);
        exercise(SK_INVOCATIONS, 5 + random2(5));
        break;

    case ABIL_CHEIBRIADOS_TIME_BEND:
        cheibriados_time_bend(16 + you.skills[SK_INVOCATIONS] * 8);
        exercise(SK_INVOCATIONS, 2 + random2(3));
        break;

    case ABIL_CHEIBRIADOS_SLOUCH:
        mpr("You can feel time thicken.");
        dprf("your speed is %d", player_movement_speed());
        exercise(SK_INVOCATIONS, 4 + random2(4));
        cheibriados_slouch(0);
        break;


    case ABIL_RENOUNCE_RELIGION:
        if (yesno("Really renounce your faith, foregoing its fabulous benefits?",
                  false, 'n')
            && yesno("Are you sure you won't change your mind later?",
                     false, 'n'))
        {
            excommunication();
        }
        else
            canned_msg(MSG_OK);
        break;


    case ABIL_NON_ABILITY:
        mpr("Sorry, you can't do that.");
        break;
    }

    return (true);
}

static void _pay_ability_costs(const ability_def& abil)
{
    // currently only delayed fireball is instantaneous -- bwr
    you.turn_is_over = !(abil.flags & ABFLAG_INSTANT);

    const int food_cost  = abil.food_cost + random2avg(abil.food_cost, 2);
    const int piety_cost = abil.piety_cost.cost();
    const int hp_cost    = abil.hp_cost.cost(you.hp_max);

    dprf("Cost: mp=%d; hp=%d; food=%d; piety=%d",
         abil.mp_cost, hp_cost, food_cost, piety_cost );

    if (abil.mp_cost)
    {
        dec_mp( abil.mp_cost );
        if (abil.flags & ABFLAG_PERMANENT_MP)
            rot_mp(1);
    }

    if (abil.hp_cost)
    {
        dec_hp( hp_cost, false );
        if (abil.flags & ABFLAG_PERMANENT_HP)
            rot_hp(1);
    }

    if (food_cost)
        make_hungry( food_cost, false, true );

    if (piety_cost)
        lose_piety( piety_cost );
}

int choose_ability_menu(const std::vector<talent>& talents)
{
    Menu abil_menu(MF_SINGLESELECT | MF_ANYPRINTABLE, "ability");

    abil_menu.set_highlighter(NULL);
    abil_menu.set_title(
        new MenuEntry("  Ability - do what?                 "
                      "Cost                    Success"));
    abil_menu.set_title(
        new MenuEntry("  Ability - describe what?           "
                      "Cost                    Success"), false);

    abil_menu.set_flags(MF_SINGLESELECT | MF_ANYPRINTABLE
                            | MF_ALWAYS_SHOW_MORE);

    if (Tutorial.tutorial_left)
    {
        // XXX: This could be buggy if you manage to pick up lots and
        // lots of abilities during the tutorial.
        abil_menu.set_more(tut_abilities_info());
    }
    else
    {
        abil_menu.set_more(formatted_string::parse_string(
                           "Press '<w>!</w>' or '<w>?</w>' to toggle "
                           "between ability selection and description."));
    }

    abil_menu.action_cycle = Menu::CYCLE_TOGGLE;
    abil_menu.menu_action  = Menu::ACT_EXECUTE;

    int numbers[52];
    for (int i = 0; i < 52; ++i)
        numbers[i] = i;

    bool found_invocations = false;

    // First add all non-invocations.
    for (unsigned int i = 0; i < talents.size(); ++i)
    {
        if (talents[i].is_invocation)
            found_invocations = true;
        else
        {
            MenuEntry* me = new MenuEntry(_describe_talent(talents[i]),
                                          MEL_ITEM, 1, talents[i].hotkey);
            me->data = &numbers[i];
            abil_menu.add_entry(me);
        }
    }

    if (found_invocations)
    {
        abil_menu.add_entry(new MenuEntry("    Invocations - ", MEL_SUBTITLE));
        for (unsigned int i = 0; i < talents.size(); ++i)
        {
            if (talents[i].is_invocation)
            {
                MenuEntry* me = new MenuEntry(_describe_talent(talents[i]),
                                              MEL_ITEM, 1, talents[i].hotkey);
                me->data = &numbers[i];
                abil_menu.add_entry(me);
            }
        }
    }

    while (true)
    {
        std::vector<MenuEntry*> sel = abil_menu.show(false);
        if (!crawl_state.doing_prev_cmd_again)
            redraw_screen();
        if (sel.empty())
            return -1;

        ASSERT(sel.size() == 1);
        ASSERT(sel[0]->hotkeys.size() == 1);
        int selected = *(static_cast<int*>(sel[0]->data));

        if (abil_menu.menu_action == Menu::ACT_EXAMINE)
            _print_talent_description(talents[selected]);
        else
            return (*(static_cast<int*>(sel[0]->data)));
    }
}

const char* ability_name(ability_type ability)
{
    return get_ability_def(ability).name;
}

static std::string _describe_talent(const talent& tal)
{
    ASSERT( tal.which != ABIL_NON_ABILITY );

    std::ostringstream desc;
    desc << std::left
         << std::setw(32) << ability_name(tal.which)
         << std::setw(24) << make_cost_description(tal.which)
         << std::setw(10) << failure_rate_to_string(tal.fail);
    return desc.str();
}

static void _add_talent(std::vector<talent>& vec, const ability_type ability,
                        bool check_confused)
{
    const talent t = _get_talent(ability, check_confused);
    if (t.which != ABIL_NON_ABILITY)
        vec.push_back(t);
}

std::vector<talent> your_talents(bool check_confused)
{
    std::vector<talent> talents;

    // Species-based abilities.
    if (you.species == SP_MUMMY && you.experience_level >= 13)
        _add_talent(talents, ABIL_MUMMY_RESTORATION, check_confused);

    if (you.species == SP_DEEP_DWARF)
        _add_talent(talents, ABIL_RECHARGING, check_confused);

    // Spit Poison. Nontransformed nagas can upgrade to Breathe Poison.
    // Transformed nagas, or non-nagas, can only get Spit Poison.
    if (you.species == SP_NAGA
        && (!transform_changed_physiology()
            || you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER))
    {
        _add_talent(talents, player_mutation_level(MUT_BREATHE_POISON) ?
                    ABIL_BREATHE_POISON : ABIL_SPIT_POISON, check_confused);
    }
    else if (player_mutation_level(MUT_SPIT_POISON)
             || player_mutation_level(MUT_BREATHE_POISON))
    {
        _add_talent(talents, ABIL_SPIT_POISON, check_confused);
    }

    if (player_genus(GENPC_DRACONIAN) && you.experience_level >= 7)
    {
        ability_type ability = ABIL_NON_ABILITY;
        switch (you.species)
        {
        case SP_GREEN_DRACONIAN:   ability = ABIL_BREATHE_POISON;       break;
        case SP_RED_DRACONIAN:     ability = ABIL_BREATHE_FIRE;         break;
        case SP_WHITE_DRACONIAN:   ability = ABIL_BREATHE_FROST;        break;
        case SP_YELLOW_DRACONIAN:  ability = ABIL_SPIT_ACID;            break;
        case SP_BLACK_DRACONIAN:   ability = ABIL_BREATHE_LIGHTNING;    break;
        case SP_PURPLE_DRACONIAN:  ability = ABIL_BREATHE_POWER;        break;
        case SP_PALE_DRACONIAN:    ability = ABIL_BREATHE_STEAM;        break;
        case SP_MOTTLED_DRACONIAN: ability = ABIL_BREATHE_STICKY_FLAME; break;
        default: break;
        }

        // Draconians don't maintain their original breath weapons
        // if shapechanged, but green draconians do get spit poison
        // in spider form.
        if (transform_changed_physiology())
        {
            if (you.species == SP_GREEN_DRACONIAN
                && you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER)
            {
                ability = ABIL_SPIT_POISON; // spit, not breath
            }
            else
                ability = ABIL_NON_ABILITY;
        }

        if (ability != ABIL_NON_ABILITY)
            _add_talent(talents, ability, check_confused);
    }

    if (you.species == SP_VAMPIRE && you.experience_level >= 3
        && you.hunger_state <= HS_SATIATED
        && you.attribute[ATTR_TRANSFORMATION] != TRAN_BAT)
    {
        _add_talent(talents, ABIL_TRAN_BAT, check_confused);
    }

    if (!you.airborne() && !transform_changed_physiology())
    {
        // Kenku can fly, but only from the ground
        // (until level 15, when it becomes permanent until revoked).
        // jmf: "upgrade" for draconians -- expensive flight
        if (you.species == SP_KENKU && you.experience_level >= 5)
            _add_talent(talents, ABIL_FLY, check_confused);
        else if (player_genus(GENPC_DRACONIAN)
                 && player_mutation_level(MUT_BIG_WINGS))
        {
            _add_talent(talents, ABIL_FLY_II, check_confused);
        }
    }

    // Mutations.
    if (player_mutation_level(MUT_SUMMON_MINOR_DEMONS))
        _add_talent(talents, ABIL_SUMMON_MINOR_DEMON, check_confused);

    if (player_mutation_level(MUT_SUMMON_DEMONS))
        _add_talent(talents, ABIL_SUMMON_DEMON, check_confused);

    if (player_mutation_level(MUT_HURL_HELLFIRE))
        _add_talent(talents, ABIL_HELLFIRE, check_confused);

    if (player_mutation_level(MUT_CALL_TORMENT))
        _add_talent(talents, ABIL_TORMENT, check_confused);

    if (player_mutation_level(MUT_RAISE_DEAD))
        _add_talent(talents, ABIL_RAISE_DEAD, check_confused);

    if (player_mutation_level(MUT_CONTROL_DEMONS))
        _add_talent(talents, ABIL_CONTROL_DEMON, check_confused);

    if (player_mutation_level(MUT_CHANNEL_HELL))
        _add_talent(talents, ABIL_CHANNELING, check_confused);

    if (player_mutation_level(MUT_THROW_FLAMES))
        _add_talent(talents, ABIL_THROW_FLAME, check_confused);

    if (player_mutation_level(MUT_THROW_FROST))
        _add_talent(talents, ABIL_THROW_FROST, check_confused);

    if (player_mutation_level(MUT_SMITE))
        _add_talent(talents, ABIL_BOLT_OF_DRAINING, check_confused);

    if (you.duration[DUR_TRANSFORMATION]
        && !you.transform_uncancellable)
    {
        _add_talent(talents, ABIL_END_TRANSFORMATION, check_confused);
    }

    if (player_mutation_level(MUT_BLINK))
        _add_talent(talents, ABIL_BLINK, check_confused);

    if (player_mutation_level(MUT_TELEPORT_AT_WILL))
        _add_talent(talents, ABIL_TELEPORTATION, check_confused);

    // Religious abilities.
    if (you.religion == GOD_ELYVILON)
        _add_talent(talents, ABIL_ELYVILON_DESTROY_WEAPONS, check_confused);
    else if (you.religion == GOD_TROG)
        _add_talent(talents, ABIL_TROG_BURN_SPELLBOOKS, check_confused);
    else if (you.religion == GOD_FEDHAS)
        _add_talent(talents, ABIL_FEDHAS_FUNGAL_BLOOM, check_confused);
    else if (you.religion == GOD_CHEIBRIADOS)
        _add_talent(talents, ABIL_CHEIBRIADOS_PONDEROUSIFY, check_confused);

    // Gods take abilities away until penance completed. -- bwr
    // God abilities generally don't work while silenced (they require
    // invoking the god), but Nemelex is an exception.
    if (!player_under_penance() && (!silenced(you.pos())
                                    || you.religion == GOD_NEMELEX_XOBEH))
    {
        for (int i = 0; i < MAX_GOD_ABILITIES; ++i)
        {
            if (you.piety >= piety_breakpoint(i))
            {
                const ability_type abil = god_abilities[you.religion][i];
                if (abil != ABIL_NON_ABILITY)
                {
                    _add_talent(talents, abil, check_confused);
                    if (abil == ABIL_ELYVILON_LESSER_HEALING_OTHERS)
                    {
                        _add_talent(talents,
                                    ABIL_ELYVILON_LESSER_HEALING_SELF,
                                    check_confused);
                    }
                    else if (abil == ABIL_ELYVILON_GREATER_HEALING_OTHERS)
                    {
                        _add_talent(talents,
                                    ABIL_ELYVILON_GREATER_HEALING_SELF,
                                    check_confused);
                    }
                }
            }
        }

        if (you.religion == GOD_ZIN
            && !you.num_gifts[GOD_ZIN]
            && you.piety > 160)
        {
            _add_talent(talents, ABIL_ZIN_CURE_ALL_MUTATIONS, check_confused);
        }
    }

    // And finally, the ability to opt-out of your faith {dlb}:
    if (you.religion != GOD_NO_GOD && !silenced( you.pos() ))
        _add_talent(talents, ABIL_RENOUNCE_RELIGION, check_confused);

    //jmf: Check for breath weapons - they're exclusive of each other, I hope!
    //     Make better ones come first.
    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
        || player_mutation_level(MUT_BREATHE_FLAMES))
    {
        _add_talent(talents, ABIL_BREATHE_FIRE, check_confused);
    }

    // Checking for unreleased Delayed Fireball.
    if (you.attribute[ ATTR_DELAYED_FIREBALL ])
        _add_talent(talents, ABIL_DELAYED_FIREBALL, check_confused);

    // Evocations from items.
    if (scan_artefacts(ARTP_BLINK))
        _add_talent(talents, ABIL_EVOKE_BLINK, check_confused);

    if (wearing_amulet(AMU_RAGE) || scan_artefacts(ARTP_BERSERK))
        _add_talent(talents, ABIL_EVOKE_BERSERK, check_confused);

    if (player_equip( EQ_RINGS, RING_INVISIBILITY )
        || player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_DARKNESS )
        || scan_artefacts( ARTP_INVISIBLE ))
    {
        // Now you can only turn invisibility off if you have an
        // activatable item.  Wands and potions will have to time
        // out. -- bwr
        if (you.duration[DUR_INVIS])
            _add_talent(talents, ABIL_EVOKE_TURN_VISIBLE, check_confused);
        else
            _add_talent(talents, ABIL_EVOKE_TURN_INVISIBLE, check_confused);
    }

    // Note: This ability only applies to this counter.
    if (player_equip( EQ_RINGS, RING_LEVITATION )
        || player_equip_ego_type( EQ_BOOTS, SPARM_LEVITATION )
        || scan_artefacts( ARTP_LEVITATE ))
    {
        // Has no effect on permanently flying Kenku.
        if (!you.permanent_flight())
        {
            // Now you can only turn levitation off if you have an
            // activatable item.  Potions and miscast effects will
            // have to time out (this makes the miscast effect actually
            // a bit annoying). -- bwr
            _add_talent(talents, you.duration[DUR_LEVITATION] ?
                        ABIL_EVOKE_STOP_LEVITATING : ABIL_EVOKE_LEVITATE,
                        check_confused);
        }
    }

    if (player_equip( EQ_RINGS, RING_TELEPORTATION ))
    {
        _add_talent(talents, ABIL_EVOKE_TELEPORTATION, check_confused);
    }

    // Find hotkeys for the non-hotkeyed talents.
    for (unsigned int i = 0; i < talents.size(); ++i)
    {
        // Skip preassigned hotkeys.
        if (talents[i].hotkey != 0)
            continue;

        // Try to find a free hotkey for i, starting from Z.
        for (int k = 51; k >= 0; ++k)
        {
            const int kkey = index_to_letter(k);
            bool good_key = true;

            // Check that it doesn't conflict with other hotkeys.
            for (unsigned int j = 0; j < talents.size(); ++j)
            {
                if (talents[j].hotkey == kkey)
                {
                    good_key = false;
                    break;
                }
            }

            if (good_key)
            {
                talents[i].hotkey = k;
                you.ability_letter_table[k] = talents[i].which;
                break;
            }
        }
        // In theory, we could be left with an unreachable ability
        // here (if you have 53 or more abilities simultaneously).
    }

    return talents;
}

// Note: we're trying for a behaviour where the player gets
// to keep their assigned invocation slots if they get excommunicated
// and then rejoin (but if they spend time with another god we consider
// the old invocation slots void and erase them).  We also try to
// protect any bindings the character might have made into the
// traditional invocation slots (A-E and X). -- bwr
static void _set_god_ability_helper(ability_type abil, char letter)
{
    int i;
    const int index = letter_to_index(letter);

    for (i = 0; i < 52; i++)
        if (you.ability_letter_table[i] == abil)
            break;

    if (i == 52)    // Ability is not already assigned.
    {
        // If slot is unoccupied, move in.
        if (you.ability_letter_table[index] == ABIL_NON_ABILITY)
            you.ability_letter_table[index] = abil;
    }
}

// Return GOD_NO_GOD if it isn't a god ability, otherwise return
// the index of the god.
static int _is_god_ability(int abil)
{
    if (abil == ABIL_NON_ABILITY)
        return (GOD_NO_GOD);

    for (int i = 0; i < MAX_NUM_GODS; ++i)
        for (int j = 0; j < MAX_GOD_ABILITIES; ++j)
        {
            if (god_abilities[i][j] == abil)
                return (i);
        }

    return (GOD_NO_GOD);
}

void set_god_ability_slots()
{
    ASSERT(you.religion != GOD_NO_GOD);

    _set_god_ability_helper(ABIL_RENOUNCE_RELIGION, 'X');

    // Clear out other god invocations.
    for (int i = 0; i < 52; i++)
    {
        const int god = _is_god_ability(you.ability_letter_table[i]);
        if (god != GOD_NO_GOD && god != you.religion)
            you.ability_letter_table[i] = ABIL_NON_ABILITY;
    }

    // Finally, add in current god's invocations in traditional slots.
    int num = 0;

    for (int i = 0; i < MAX_GOD_ABILITIES; ++i)
    {
        if (god_abilities[you.religion][i] != ABIL_NON_ABILITY)
        {
            _set_god_ability_helper(god_abilities[you.religion][i],
                                    'a' + num++);

            if (you.religion == GOD_ELYVILON)
            {
                if (god_abilities[you.religion][i]
                        == ABIL_ELYVILON_LESSER_HEALING_OTHERS)
                {
                    _set_god_ability_helper(ABIL_ELYVILON_LESSER_HEALING_SELF,
                                            'a' + num++);
                }
                else if (god_abilities[you.religion][i]
                            == ABIL_ELYVILON_GREATER_HEALING_OTHERS)
                {
                    _set_god_ability_helper(ABIL_ELYVILON_GREATER_HEALING_SELF,
                                            'a' + num++);
                }
            }
        }
    }
}

// Returns an index (0-51) if successful, -1 if you should
// just use the next one.
static int _find_ability_slot(ability_type which_ability)
{
    for (int slot = 0; slot < 52; slot++)
        if (you.ability_letter_table[slot] == which_ability)
            return (slot);

    // No requested slot, find new one and make it preferred.

    // Skip over a-e (invocations), or a-g for Elyvilon.
    const int first_slot = (you.religion == GOD_ELYVILON ? 7 : 5);
    for (int slot = first_slot; slot < 52; ++slot)
    {
        if (you.ability_letter_table[slot] == ABIL_NON_ABILITY)
        {
            you.ability_letter_table[slot] = which_ability;
            return (slot);
        }
    }

    // If we can't find anything else, try a-e.
    for (int slot = first_slot - 1; slot >= 0; --slot)
    {
        if (you.ability_letter_table[slot] == ABIL_NON_ABILITY)
        {
            you.ability_letter_table[slot] = which_ability;
            return (slot);
        }
    }

    // All letters are assigned.
    return (-1);
}

////////////////////////////////////////////////////////////////////////
// generic_cost

int generic_cost::cost() const
{
    return (base + (add > 0 ? random2avg(add, rolls) : 0));
}

int scaling_cost::cost(int max) const
{
    return (value < 0) ? (-value) : ((value * max + 500) / 1000);
}