summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/mutation.cc
blob: c1919d0750412d541511f7867061cfd2323bdf84 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13



                                                        








                     

                  
                                       
                    



                    
                     
                
                  

                    
                
                  
                   
                    
                     
                  
                 
                  

                   
                     
                   
                    
                      
                     
                 
                
 
                           
 







                                                  




                          






                                     

                                                        

                                                        

  











                                                                
                        

                                                                    


                                                             





                                                           


                                                 






























                                                          


                                                            











                                                          


                                                                            











                                                           


                                                                    











                                                           


                                                               











                                                          

                                                                   
                                                        
                                   












                                                          
                                                       

































































                                                                     
                                                        



















                                                          
                            
                                                          





















































































































                                                               
                                                          










































                                                              

                                                




                                                             
                                          




                                           

































































































                                                                           


                                                              






























































































                                                             
 
                                  





























































                                                                










































                                                                          
                        
 











































                                                              

                






                                                          
                              


                                                          
                                                                 










                                                         
                     






                                                          
 
                                         

















                                                          


                                                                            
















































                                                                         
 
                                        
                                                          


                                                          











                                                          


                                                                        











                                                               


                                                                            











                                                           


                                                                          











                                                          


                                                              











                                                          


                                                              











                                                          


                                                                       











                                                          


                                                              











                                                          


                                                             











                                                          


                                                               











                                                          


                                                               











                                                          


                                                               











                                                          


                                                               











                                                          


                                                                            











                                                           


                                                                   










                                                          











                                                                           
                                                          


                                                     
 


                                                                         






                                         






                                                                      











                                                                   
                         
      






                                                                                















                                                                        

  
                                                       
 
                                                         




                                             
 
 


                                      
     
                                                             



                                                              
                                            


             
















                                                                  
     
                                                             
         
                                                          
             

                                                                        




                                                              





                                                                           

 
                                                
 
                                                              
                                        
                      
 
                                         
                           
                      
 

                                                                   

                      

                                       
                      
 




                                                          
                                                     
                                                                             
                                                      

 
                                     
 

                          
                                                                      
 
                   



                                            
 


                                 
 


                                                     

                    

                                                                 


                 
                                               




                                                        
                                               
                                              
                                                 





                                                                                   
                        

              

                                                   
                                                        


                        





                                                         
                        

              


                                     



                                          
                            



                  
                                                          
                                      





                                                                

                                                                            
         
                        

              







                                                              


                                     

                                                    





                                     

                                                  
         




                                     
                                                  
                            
         




                                     

                                                       
         

              
                             

                                     


                                                       





                                     

                                                   





                                     

                                                           





                                     

                                                   

              

                   
                                                             


                        
                    
                        
                                            
                                                                      

                                                                                           
                                                


                                                                                           







                                                                        
              
 

                                                     
                                                                             


                        

              
     
 
                       
                                                           
                                                                       



                                                                          
 










                                                                      
                             
 
                           




                                                              





                                                                          

                         
                                                                   
                                           
     
                                                     
         
                                                                   
                                                        
                          
                            


         
                                   



                                                      
                                                                   
                                                        
                          
                            


         
                  
                                                 
 



                                  







                                                                               

     


                                                  

                                         

                                      




                       
                         
                                  



                                                                                                   

                                                                                                              
 
                                                                                                              
 
                                                                                                              
 
                                                                                                              
 
                                    
                                                                                                              
 
                                                                                                              
 
                                                                                                              
 

                                                                                                              

                                                                                                              
                                      
                                                                                                              
 

                                                                                                              
 
                                                                                                              
 
                                                                                                              
 

                                                                                                             

























                             
                                                            










                                                               
 
                  







                                                                           



                                                                             



                                              

 

                        
             
                 

                                                              



                                  
                                          
                                  
                                                  






                                          
 
 
                                                                       
 
                                                                        
 
                           
     



                               
                              
                          
                             


                              

                                 

                        

                                     
 

                         
                          








                             
                   
                   
                  
                    




                           
                   

                    


                            
                       




                                 

     


                       











                                                                        



                                                                       







































                                                         
 
                  

































                                                                        

                                         
              



                            

                                      








                                       

                                                                             
 
                                 
                       

                                                       
 
                   
                                                              
 
                                           

                       


                                                   
                      
                      
 
                                                          
 
                                              
                                       

 
                                                                     
 
                                      
                                     

                                                         

      
                                        
 





                                                                   

                                             
     
                                                       
 


                   


                                                                   


                                         
                                                         



                                     
                                                             
                                                     
                     
 






                                                                  
                                           

                          
 


                    
            






                                                      
      
 











                                                                
                                                                      





                                                      

                                                                      













                                                                  

                                                                        

                                                  

                                                  
      
 

























                                                              

                                   

                                                                       




                                                            
                                                      

             



               








































                                                                             





                                 
                                                       

                                                                      
 

                                                     









                                                                          
                   
                              
 

                                         

                        
                                                                      



                                                                     
         









                                                                      


                            

                                                                          
         
                                                                     
                           


         
                                 
 
                                        
     
                                                                     

                                                                      



                                                                           
                                               






                                                    



                                                                  


             
 
                                                          
                                                                            
                               
     
                                                     
 

                                                         
            
         
                                                    
                                    
         
 
                              
                      

     

                                                 
     

                                                                 
         


                                                                    
                               
                
                                                                 
                                                                           
         




                                          
         

                                                                       
                               
         
                                                           
     
                                                   
     
                                                                           
                           


                                                    
                                                           
                                   
                           
     

                                                   
                                                            
                                   
                           
     
 
                                                       
                                                                               
                       
 
                                                      
                                

                                                                        
                                                                
     
                       

     

                               
                                                                     


                                     
                               
 

                                       
                                                                          
                                        

                                                                      


         
                                             
                       
 


                                                       
                       
 





                                                                             
 
                    
 

                                                                

                          
 



                                                             
 

                                               
 

                             





                                                      

              
                                                       

                    
                                                            
                         

                                             
                                        
         
                                                    

                               


                   
                                                            
                         
 
                                                                    
                                                                        
                
                                                                     
         
                                                     

                               

              
                   
                  
                                                            
                         
 
                                                  
                                      

                                                            
         
                                                     
                               

              
 






                                                            
            
              
     
 

                                     
 
                          
 





                                                                       
     
                                   
         





                                                                  
         

     

                                                              
 




                                                          

                                                   
 
                                                                       
                                                             
 
                                                                   
 
                                                                           

                                                   
                  
 
 
                                                              
 
                                 
                       

                                                    
                       
 

                                                       

                          
 



                                                             
 

                                               
 





                                                      






                                                              
                                        





                                                                       

            
              

     
                                     

                                   
                          
 










                                                              

                                                            





                                                          
                                                                    
 
                  
 
 

                                                                
                                                            


                                                     









                                                                          






















































                                                                             
                                                                      







                                                  
 










                                                                            




















                                                                    
                          
 
                                                             
                    




                                      
                     
 
 

                                                       
 
                     

 


                                                                    
 

                                                            
                                                            


                                                        
     

                                               
                                                         
                                      
     
 


                        

                                                                
     
                      

                                               
     
 



                                                      

                                               

     









                                                     









                                                                               
                                         
                                      
 
                                                                         



                              
 
               
     
                                                            
                                                          















                                                                        

                                    





                                                  

     
                    
 
 
                
 


                          
 




                          
 


                                                      
 
                                         
 

































































































                                                                              
 


                                                           
 
                                                      
 

               
 

                                                              
          







                                         
                           


                                            
                                                               
     


                                                                 
 





                                                                         
 
                                           
 


                                                                       
 




                                                                          
 

                                             
 

                                          
 



                                                                   
                                                                         

                                 
 
                       
         
     
 

                       
 





                          


                           












                                                          
         

                                                            
 
               
 













                                                                      
             
         


                                      

     






                                                                 
 
                              
 
                                         

                                             
     
                                    
 


                                        
 




                                                               
 

                                                 


                                                                          
                                                                            
      
 


                                  
         
                               
     








                                                  
 
 
                                                        
 
                   
 

                                                         
 
                                                           

                 
                                                                            

                 
                                                                            

                 
                                       

                        
 
 


























                                                                          
                                                                         



               
                                                                         
 

                                                                  
                                     


                                                                


                        
 
      
                                         
                                                              
 
                                                               

                                               
 
                    
 
/*
 *  File:       mutation.cc
 *  Summary:    Functions for handling player mutations.
 *  Written by: Linley Henzell
 */

#include "AppHdr.h"
#include "mutation.h"

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

#include <sstream>

#if defined(UNIX) && !defined(USE_TILE)
#include "libunix.h"
#endif

#include "externs.h"

#include "abl-show.h"
#include "cio.h"
#include "delay.h"
#include "defines.h"
#include "effects.h"
#include "env.h"
#include "files.h"
#include "format.h"
#include "godabil.h"
#include "itemprop.h"
#include "macro.h"
#include "menu.h"
#include "notes.h"
#include "ouch.h"
#include "player.h"
#include "religion.h"
#include "random.h"
#include "skills2.h"
#include "transform.h"
#include "tutorial.h"
#include "view.h"
#include "xom.h"

static int _body_covered();

const char *troll_claw_descrip[4] = {
    "You have claws for hands.",
    "You have sharp claws for hands.",
    "You have very sharp claws for hands.",
    "You have claws sharper than steel for hands."
};

const char *troll_claw_gain[3] = {
    "Your claws sharpen.",
    "Your claws sharpen.",
    "Your claws steel!"
};

const char *troll_claw_lose[3] = {
    "Your claws look duller.",
    "Your claws look duller.",
    "Your claws feel softer."
};

const char *naga_speed_descrip[4] = {
    "You cover ground very slowly.",    // 10*14/10 = 14
    "You cover ground rather slowly.",  //  8*14/10 = 11
    "You cover ground rather quickly.", //  7*14/10 =  9
    "You cover ground quickly."         //  6*14/10 =  8
};

const char *centaur_deformed_descrip[3] = {
    "Armour fits poorly on your equine body.",
    "Armour fits poorly on your deformed equine body.",
    "Armour fits poorly on your badly deformed equine body."
};

const char *naga_deformed_descrip[3] = {
    "Armour fits poorly on your serpentine body.",
    "Armour fits poorly on your deformed serpentine body.",
    "Armour fits poorly on your badly deformed serpentine body."
};

// mutation definitions:
// first  number  = probability (0 means it doesn't appear naturally
//                  unless you have some level of it already)
// second number  = maximum levels
// first  boolean = is mutation mostly bad?
// second boolean = is mutation physical, i.e. external only?
// first  strings = what to show in 'A'
// second strings = message given when gaining the mutation
// third  strings = message given when losing the mutation
// fourth string  = wizard-mode name of mutation
mutation_def mutation_defs[] = {
    { MUT_TOUGH_SKIN,                10,  3, false,  true,
      {"You have tough skin (AC +1).",
       "You have very tough skin (AC +2).",
       "You have extremely tough skin (AC +3)."},

      {"Your skin toughens.",
       "Your skin toughens.",
       "Your skin toughens."},

      {"Your skin feels delicate.",
       "Your skin feels delicate.",
       "Your skin feels delicate."},

      "tough skin"
    },

    { MUT_STRONG,                     8, 14, false,  true,
      {"Your muscles are strong (Str +", "", ""},
      {"", "", ""},
      {"", "", ""},
      "strong"
    },
    { MUT_CLEVER,                     8, 14, false,  true,
      {"Your mind is acute (Int +", "", ""},
      {"", "", ""},
      {"", "", ""},
      "clever"
    },
    { MUT_AGILE,                      8, 14, false,  true,
      {"You are agile (Dex +", "", ""},
      {"", "", ""},
      {"", "", ""},
      "agile"
    },
    { MUT_GREEN_SCALES,               2,  3, false,  true,
      {"You are partially covered in green scales (AC +1).",
       "You are mostly covered in green scales (AC +3).",
       "You are covered in green scales (AC +5)."},

      {"Green scales grow over part of your body.",
       "Green scales spread over more of your body.",
       "Green scales cover you completely."},

      {"Your green scales disappear.",
       "Your green scales recede somewhat.",
       "Your green scales recede somewhat."},

      "green scales"
    },
    { MUT_BLACK_SCALES,               1,  3, false,  true,
      {"You are partially covered in thick black scales (AC +3, Dex -1).",
       "You are mostly covered in thick black scales (AC +6, Dex -2).",
       "You are completely covered in thick black scales (AC +9, Dex -3)."},

      {"Thick black scales grow over part of your body.",
       "Thick black scales spread over more of your body.",
       "Thick black scales cover you completely."},

      {"Your black scales disappear.",
       "Your black scales recede somewhat.",
       "Your black scales recede somewhat."},

      "black scales"
    },
    { MUT_GREY_SCALES,                2,  3, false,  true,
      {"You are partially covered in supple grey scales (AC +1).",
       "You are mostly covered in supple grey scales (AC +2).",
       "You are completely covered in supple grey scales (AC +3)."},

      {"Supple grey scales grow over part of your body.",
       "Supple grey scales spread over more of your body.",
       "Supple grey scales cover you completely."},

      {"Your grey scales disappear.",
       "Your grey scales recede somewhat.",
       "Your grey scales recede somewhat."},

      "grey scales"
    },
    { MUT_BONEY_PLATES,               1,  3, false,  true,
      {"You are protected by plates of bone (AC +2, Dex -1).",
       "You are protected by plates of bone (AC +3, Dex -2).",
       "You are protected by plates of bone (AC +4, Dex -3)."},

      {"You grow protective plates of bone.",
       "You grow more protective plates of bone.",
       "You grow more protective plates of bone."},

      {"Your bony plates shrink away.",
       "Your bony plates shrink.",
       "Your bony plates shrink."},

      "boney plates"
    },
    { MUT_REPULSION_FIELD,            1,  3, false, false,
      {"You are surrounded by a mild repulsion field (EV +1).",
       "You are surrounded by a moderate repulsion field (EV +3).",
       "You are surrounded by a strong repulsion field "
       "(EV +5; repel missiles)."},

      {"You begin to radiate repulsive energy.",
       "Your repulsive radiation grows stronger.",
       "Your repulsive radiation grows stronger."},

      {"You feel attractive.",
       "You feel attractive.",
       "You feel attractive."},

      "repulsion field"
    },

    { MUT_POISON_RESISTANCE,          4,  1, false, false,
      {"Your system is resistant to poisons.", "", ""},
      {"You feel healthy.", "",  ""},
      {"You feel a little less healthy.", "", ""},

      "poison resistance"
    },
    { MUT_CARNIVOROUS,                5,  3, false, false,
      {"Your digestive system is specialised to digest meat.",
       "Your digestive system is highly specialised to digest meat.",
       "You are carnivorous and can eat meat at any time."},

      {"You hunger for flesh.",
       "You hunger for flesh.",
       "You hunger for flesh."},

      {"You feel able to eat a more balanced diet.",
       "You feel able to eat a more balanced diet.",
       "You feel able to eat a more balanced diet."},

      "carnivorous"
    },
    { MUT_HERBIVOROUS,                5,  3,  true, false,
      {"You digest meat inefficiently.",
       "You digest meat very inefficiently.",
       "You are a herbivore."},

      {"You hunger for vegetation.",
       "You hunger for vegetation.",
       "You hunger for vegetation."},

      {"You feel able to eat a more balanced diet.",
       "You feel able to eat a more balanced diet.",
       "You feel able to eat a more balanced diet."},

      "herbivorous"
    },
    { MUT_HEAT_RESISTANCE,            4,  3, false, false,
      {"Your flesh is heat resistant.",
       "Your flesh is very heat resistant.",
       "Your flesh is almost immune to the effects of heat."},

      {"You feel a sudden chill.",
       "You feel a sudden chill.",
       "You feel a sudden chill."},

      {"You feel hot for a moment.",
       "You feel hot for a moment.",
       "You feel hot for a moment."},

      "heat resistance"
    },
    { MUT_COLD_RESISTANCE,            4,  3, false, false,
      {"Your flesh is cold resistant.",
       "Your flesh is very cold resistant.",
       "Your flesh is almost immune to the effects of cold."},

      {"You feel hot for a moment.",
       "You feel hot for a moment.",
       "You feel hot for a moment."},

      {"You feel a sudden chill.",
       "You feel a sudden chill.",
       "You feel a sudden chill."},

      "cold resistance"
    },
    { MUT_SHOCK_RESISTANCE,           2,  1, false, false,
      {"You are resistant to electric shocks.", "", ""},
      {"You feel insulated.", "", ""},
      {"You feel conductive.", "", ""},

      "shock resistance"
    },
    { MUT_REGENERATION,               3,  3, false, false,
      {"Your natural rate of healing is unusually fast.",
       "You heal very quickly.",
       "You regenerate."},

      {"You begin to heal more quickly.",
       "You begin to heal more quickly.",
       "You begin to regenerate."},

      {"Your rate of healing slows.",
       "Your rate of healing slows.",
       "Your rate of healing slows."},

      "regeneration"
    },
    // Deep Dwarf/Ghoul only
    { MUT_SLOW_HEALING,               0,  3,  true, false,
      {"You heal slowly.",
       "You heal very slowly.",
       "You do not heal naturally."},

      {"You begin to heal more slowly.",
       "You begin to heal more slowly.",
       "You stop healing."},

      {"Your rate of healing increases.",
       "Your rate of healing increases.",
       "Your rate of healing increases."},

      "slow healing"
    },
    { MUT_FAST_METABOLISM,           10,  3,  true, false,
      {"You have a fast metabolism.",
       "You have a very fast metabolism.",
       "Your metabolism is lightning-fast."},

      {"You feel a little hungry.",
       "You feel a little hungry.",
       "You feel a little hungry."},

      {"Your metabolism slows.",
       "Your metabolism slows.",
       "Your metabolism slows."},

      "fast metabolism"
    },
    { MUT_SLOW_METABOLISM,            7,  3, false, false,
      {"You have a slow metabolism.",
       "You have a slow metabolism.",
       "You need consume almost no food."},

      {"Your metabolism slows.",
       "Your metabolism slows.",
       "Your metabolism slows."},

      {"You feel a little hungry.",
       "You feel a little hungry.",
       "You feel a little hungry."},

      "slow metabolism"
    },

    { MUT_WEAK,                      10, 14,  true,  true,
      {"You are weak (Str -", "", ""},
      {"", "", ""},
      {"", "", ""},
      "weak"
    },
    { MUT_DOPEY,                     10, 14,  true,  true,
      {"You are dopey (Int -", "", ""},
      {"", "", ""},
      {"", "", ""},
      "dopey",
    },
    { MUT_CLUMSY,                    10, 14,  true,  true,
      {"You are clumsy (Dex -", "", ""},
      {"", "", ""},
      {"", "", ""},
      "clumsy"
    },
    { MUT_TELEPORT_CONTROL,           2,  1, false, false,
      {"You can control translocations.", "", ""},
      {"You feel controlled.", "", ""},
      {"You feel random.", "", ""},

      "teleport control"
    },

    { MUT_TELEPORT,                   3,  3,  true, false,
      {"Space occasionally distorts in your vicinity.",
       "Space sometimes distorts in your vicinity.",
       "Space frequently distorts in your vicinity."},

      {"You feel weirdly uncertain.",
       "You feel even more weirdly uncertain.",
       "You feel even more weirdly uncertain."},

      {"You feel stable.",
       "You feel stable.",
       "You feel stable."},

      "teleport"
    },
    { MUT_MAGIC_RESISTANCE,           5,  3, false, false,
      {"You are resistant to magic.",
       "You are highly resistant to magic.",
       "You are extremely resistant to the effects of magic."},

      {"You feel resistant to magic.",
       "You feel more resistant to magic.",
       "You feel almost impervious to the effects of magic."},

      {"You feel less resistant to magic.",
       "You feel less resistant to magic.",
       "You feel vulnerable to magic again."},

      "magic resistance"
    },

    { MUT_FAST,                       1,  3, false, false,
      {"You cover ground quickly.",
       "You cover ground very quickly.",
       "You cover ground extremely quickly."},

      {"You feel quick.",
       "You feel quick.",
       "You feel quick."},

      {"You feel sluggish.",
       "You feel sluggish.",
       "You feel sluggish."},

      "fast"
    },
    { MUT_ACUTE_VISION,               2,  1, false, false,
      {"You have supernaturally acute eyesight.", "", ""},

      {"Your vision sharpens.",
       "Your vision sharpens.",
       "Your vision sharpens."},

      {"Your vision seems duller.",
       "Your vision seems duller.",
       "Your vision seems duller."},

      "acute vision"
    },
    { MUT_DEFORMED,                   8,  3,  true,  true,
      {"Armour fits poorly on your deformed body.",
       "Armour fits poorly on your badly deformed body.",
       "Armour fits poorly on your hideously deformed body."},

      {"Your body twists and deforms.",
       "Your body twists and deforms.",
       "Your body twists and deforms."},

      {"Your body's shape seems more normal.",
       "Your body's shape seems slightly more normal.",
       "Your body's shape seems slightly more normal."},

      "deformed"
    },
    { MUT_TELEPORT_AT_WILL,           2,  3, false, false,
      {"You can teleport at will.",
       "You are good at teleporting at will.",
       "You can teleport instantly at will."},

      {"You feel jumpy.",
       "You feel more jumpy.",
       "You feel even more jumpy."},

      {"You feel static.",
       "You feel less jumpy.",
       "You feel less jumpy."},

      "teleport at will"
    },
    { MUT_SPIT_POISON,                8,  3, false, false,
      {"You can spit poison.",
       "You can spit moderately strong poison.",
       "You can spit strong poison."},

      {"There is a nasty taste in your mouth for a moment.",
       "There is a nasty taste in your mouth for a moment.",
       "There is a nasty taste in your mouth for a moment."},

      {"You feel an ache in your throat.",
       "You feel an ache in your throat.",
       "You feel an ache in your throat."},

      "spit poison"
    },
    { MUT_BREATHE_FLAMES,             4,  3, false, false,
      {"You can breathe flames.",
       "You can breathe fire.",
       "You can breathe blasts of fire."},

      {"Your throat feels hot.",
       "Your throat feels hot.",
       "Your throat feels hot."},

      {"A chill runs up and down your throat.",
       "A chill runs up and down your throat.",
       "A chill runs up and down your throat."},

      "breathe flames"
    },
    { MUT_BLINK,                      3,  3, false, false,
      {"You can translocate small distances instantaneously.",
       "You can translocate small distances instantaneously.",
       "You can translocate small distances instantaneously."},

      {"You feel a little jumpy.",
       "You feel more jumpy.",
       "You feel even more jumpy."},

      {"You feel a little less jumpy.",
       "You feel less jumpy.",
       "You feel less jumpy."},

      "blink"
    },
    { MUT_HORNS,                      7,  3, false,  true,
      {"You have a pair of small horns on your head.",
       "You have a pair of horns on your head.",
       "You have a pair of large horns on your head."},

      {"A pair of horns grows on your head!",
       "The horns on your head grow some more.",
       "The horns on your head grow some more."},

      {"The horns on your head shrink away.",
       "The horns on your head shrink a bit.",
       "The horns on your head shrink a bit."},

      "horns"
    },
    { MUT_BEAK,                       1,  1, false,  true,
      {"You have a beak for a mouth.", "", ""},
      {"Your mouth lengthens and hardens into a beak!", "", ""},
      {"Your beak shortens and softens into a mouth.", "", ""},

      "beak"
    },
    { MUT_STRONG_STIFF,              10,  3, false,  true,
      {"Your muscles are strong (Str +1), but stiff (Dex -1).",
       "Your muscles are very strong (Str +2), but stiff (Dex -2).",
       "Your muscles are extremely strong (Str +3), but stiff (Dex -3)."},

      {"Your muscles feel sore.",
       "Your muscles feel sore.",
       "Your muscles feel sore."},

      {"Your muscles feel loose.",
       "Your muscles feel loose.",
       "Your muscles feel loose."},

      "strong stiff"
    },
    { MUT_FLEXIBLE_WEAK,             10,  3, false,  true,
      {"Your muscles are flexible (Dex +1), but weak (Str -1).",
       "Your muscles are very flexible (Dex +2), but weak (Str -2).",
       "Your muscles are extremely flexible (Dex +3), but weak (Str -3)."},

      {"Your muscles feel loose.",
       "Your muscles feel loose.",
       "Your muscles feel loose."},

      {"Your muscles feel sore.",
       "Your muscles feel sore.",
       "Your muscles feel sore."},

      "flexible weak"
    },
    { MUT_SCREAM,                     6,  3,  true, false,
      {"You occasionally shout uncontrollably.",
       "You sometimes yell uncontrollably.",
       "You frequently scream uncontrollably."},

      {"You feel the urge to shout.",
       "You feel a strong urge to yell.",
       "You feel a strong urge to scream."},

      {"Your urge to shout disappears.",
       "Your urge to yell lessens.",
       "Your urge to scream lessens."},

      "scream"
    },
    { MUT_CLARITY,                    6,  1, false, false,
      {"You possess an exceptional clarity of mind.", "", ""},
      {"Your thoughts seem clearer.", "", ""},
      {"Your thinking seems confused.", "", ""},

      "clarity"
    },
    { MUT_BERSERK,                    7,  3,  true, false,
      {"You tend to lose your temper in combat.",
       "You often lose your temper in combat.",
       "You have an uncontrollable temper."},

      {"You feel a little pissed off.",
       "You feel angry.",
       "You feel extremely angry at everything!"},

      {"You feel a little more calm.",
       "You feel a little less angry.",
       "You feel a little less angry."},

      "berserk"
    },
    { MUT_DETERIORATION,             10,  3,  true, false,
      {"Your body is slowly deteriorating.",
       "Your body is deteriorating.",
       "Your body is rapidly deteriorating."},

      {"You feel yourself wasting away.",
       "You feel yourself wasting away.",
       "You feel your body start to fall apart."},

      {"You feel healthier.",
       "You feel a little healthier.",
       "You feel a little healthier."},

      "deterioration"
    },
    { MUT_BLURRY_VISION,             10,  3,  true, false,
      {"Your vision is a little blurry.",
       "Your vision is quite blurry.",
       "Your vision is extremely blurry."},

      {"Your vision blurs.",
       "Your vision blurs.",
       "Your vision blurs."},

      {"Your vision sharpens.",
       "Your vision sharpens a little.",
       "Your vision sharpens a little."},

      "blurry vision"
    },
    { MUT_MUTATION_RESISTANCE,        4,  3, false, false,
      {"You are somewhat resistant to further mutation.",
       "You are somewhat resistant to both further mutation "
       "and mutation removal.",
       "Your current mutations are irrevocably fixed, "
       "and you can mutate no more."},

      {"You feel genetically stable.",
       "You feel genetically stable.",
       "You feel genetically immutable."},

      {"You feel genetically unstable.",
       "You feel genetically unstable.",
       "You feel genetically unstable."},

      "mutation resistance"
    },
    { MUT_FRAIL,                     10,  3,  true,  true,
      {"You are frail (-10% HP).",
       "You are very frail (-20% HP).",
       "You are extremely frail (-30% HP)."},

      {"You feel frail.",
       "You feel frail.",
       "You feel frail."},

      {"You feel robust.",
       "You feel robust.",
       "You feel robust."},

      "frail"
    },
    { MUT_ROBUST,                     5,  3, false,  true,
      {"You are robust (+10% HP).",
       "You are very robust (+20% HP).",
       "You are extremely robust (+30% HP)."},

      {"You feel robust.",
       "You feel robust.",
       "You feel robust."},

      {"You feel frail.",
       "You feel frail.",
       "You feel frail."},

      "robust"
    },

// Some demonic powers start here:
    { MUT_TORMENT_RESISTANCE,         0,  1, false, false,
      {"You are immune to unholy pain and torment.", "", ""},
      {"You feel a strange anaesthesia.", "", ""},
      {"", "", ""},

      "torment resistance"
    },
    { MUT_NEGATIVE_ENERGY_RESISTANCE, 0,  3, false, false,
      {"You resist negative energy.",
       "You are quite resistant to negative energy.",
       "You are immune to negative energy."},

      {"You feel negative.",
       "You feel negative.",
       "You feel negative."},

      {"", "", ""},

      "negative energy resistance"
    },
    { MUT_SUMMON_MINOR_DEMONS,        0,  1, false, false,
      {"You can summon minor demons to your aid.", "", ""},
      {"A thousand chattering voices call out to you.", "", ""},
      {"", "", ""},

      "summon minor demons"
    },
    { MUT_SUMMON_DEMONS,              0,  1, false, false,
      {"You can summon demons to your aid.", "", ""},
      {"Help is not far away!", "", ""},
      {"", "", ""},

      "summon demons"
    },
    { MUT_HURL_HELLFIRE,              0,  1, false, false,
      {"You can hurl blasts of hellfire.", "", ""},
      {"You smell fire and brimstone.", "", ""},
      {"", "", ""},

      "hurl hellfire"
    },
    { MUT_CALL_TORMENT,               0,  1, false, false,
      {"You can call on the torments of Hell.", "", ""},
      {"You feel a terrifying power at your call.", "", ""},
      {"", "", ""},

      "call torment"
    },
    { MUT_RAISE_DEAD,                 0,  1, false, false,
      {"You can raise the dead to walk for you.", "", ""},
      {"You feel an affinity for the dead.", "", ""},
      {"", "", ""},

      "raise dead"
    },
    { MUT_CONTROL_DEMONS,             0,  1, false, false,
      {"You can control demons.", "", ""},
      {"You feel an affinity for all demonkind.", "", ""},
      {"", "", ""},

      "control demons"
    },
    { MUT_DEATH_STRENGTH,             0,  1, false, false,
      {"You can draw strength from death and destruction.", "", ""},
      {"You feel hungry for death.", "", ""},
      {"", "", ""},

      "death strength"
    },
    { MUT_CHANNEL_HELL,               0,  1, false, false,
      {"You can channel magical energy from Hell.", "", ""},
      {"You feel a flux of magical energy.", "", ""},
      {"", "", ""},

      "channel hell"
    },
    { MUT_DRAIN_LIFE,                 0,  1, false, false,
      {"You can drain life in unarmed combat.", "", ""},
      {"Your skin tingles in a strangely unpleasant way.", "", ""},
      {"", "", ""},

      "drain life"
    },
    { MUT_THROW_FLAMES,               0,  1, false, false,
      {"You can throw forth the flames of Gehenna.", "", ""},
      {"You smell the fires of Gehenna.", "", ""},
      {"", "", ""},

      "throw flames"
    },
    { MUT_THROW_FROST,                0,  1, false, false,
      {"You can throw forth the frost of Cocytus.", "", ""},
      {"You feel the icy cold of Cocytus chill your soul.", "", ""},
      {"", "", ""},

      "throw frost"
    },
    { MUT_SMITE,                      0,  1, false, false,
      {"You can invoke the powers of Tartarus to smite your living foes.",
       "", ""},
      {"A shadow passes over the world around you.", "", ""},
      {"", "", ""},

      "smite"
    },
// end of demonic powers

    { MUT_CLAWS,                      2,  3, false,  true,
      {"You have sharp fingernails.",
       "You have very sharp fingernails.",
       "You have claws for hands."},

      {"Your fingernails lengthen.",
       "Your fingernails sharpen.",
       "Your hands twist into claws."},

      {"Your fingernails shrink to normal size.",
       "Your fingernails look duller.",
       "Your hands feel fleshier."},

      "claws"
    },
    { MUT_FANGS,                      1,  3, false,  true,
      {"You have very sharp teeth.",
       "You have extremely sharp teeth.",
       "You have razor-sharp teeth."},

      {"Your teeth lengthen and sharpen.",
       "Your teeth lengthen and sharpen some more.",
       "Your teeth are very long and razor-sharp."},

      {"Your teeth shrink to normal size.",
       "Your teeth shrink and become duller.",
       "Your teeth shrink and become duller."},

      "fangs"
    },
    { MUT_HOOVES,                     1,  1, false,  true,
      {"You have hooves in place of feet.", "", ""},
      {"Your feet shrivel into cloven hooves.", "", ""},
      {"Your hooves expand and flesh out into feet!", "", ""},

      "hooves"
    },
    { MUT_TALONS,                     1,  1, false,  true,
      {"You have talons in place of feet.", "", ""},
      {"Your feet stretch and sharpen into talons.", "", ""},
      {"Your talons dull and shrink into feet!", "", ""},

      "talons"
    },

    // Naga only
    { MUT_BREATHE_POISON,             0,  1, false, false,
      {"You can exhale a cloud of poison.", "", ""},
      {"You taste something nasty.", "", ""},
      {"Your breath is less nasty.", "", ""},

      "breathe poison"
    },
    // Naga and Draconian only
    { MUT_STINGER,                    0,  3, false,  true,
      {"Your tail ends in a poisonous barb.",
       "Your tail ends in a sharp poisonous barb.",
       "Your tail ends in a wickedly sharp and poisonous barb."},

      {"A poisonous barb forms on the end of your tail.",
       "The barb on your tail looks sharper.",
       "The barb on your tail looks very sharp."},

      {"The barb on your tail disappears.",
       "The barb on your tail seems less sharp.",
       "The barb on your tail seems less sharp."},

      "stinger"
    },
    // Draconian only
    { MUT_BIG_WINGS,                  0,  1, false,  true,
      {"Your wings are large and strong.", "", ""},
      {"Your wings grow larger and stronger.", "", ""},
      {"Your wings shrivel and weaken.", "", ""},

      "big wings"
    },

    // species-dependent innate mutations
    { MUT_SAPROVOROUS,                0,  3, false, false,
      {"You can tolerate rotten meat.",
       "You can eat rotten meat.",
       "You thrive on rotten meat."},
      {"", "", ""},
      {"", "", ""},

      "saprovorous"
    },
    { MUT_GOURMAND,                   0,  1, false, false,
      {"You like to eat raw meat.", "", ""},
      {"", "", ""},
      {"", "", ""},

      "gourmand"
    },

    { MUT_SHAGGY_FUR,                 2,  3, false,  true,
      {"You are covered in fur (AC +1).",
       "You are covered in thick fur (AC +2).",
       "Your thick and shaggy fur keeps you warm (AC +3, cold resistant)."},

      {"Fur sprouts all over your body.",
       "Your fur grows into a thick mane.",
       "Your thick fur grows shaggy and warm."},

      {"You shed all your fur.",
       "Your thick fur recedes somewhat.",
       "Your shaggy fur recedes somewhat."},

      "shaggy fur"
    },
    { MUT_HIGH_MAGIC,                 1,  3, false, false,
      {"You have an increased reservoir of magic (+10% MP).",
       "You have a considerably increased reservoir of magic (+20% MP).",
       "You have an greatly increased reservoir of magic (+30% MP)."},

      {"You feel more energetic.",
       "You feel more energetic.",
       "You feel more energetic."},

      {"You feel less energetic.",
       "You feel less energetic.",
       "You feel less energetic."},

      "high mp"
    },
    { MUT_LOW_MAGIC,                  9,  3,  true, false,
      {"Your magical capacity is low (-10% MP).",
       "Your magical capacity is very low (-20% MP).",
       "Your magical capacity is extremely low (-30% MP)."},

      {"You feel less energetic.",
       "You feel less energetic.",
       "You feel less energetic."},

      {"You feel more energetic.",
       "You feel more energetic.",
       "You feel more energetic."},

      "low mp"
    },

    { RANDOM_MUTATION,                0,  3, false, false,
      {"", "", ""},
      {"", "", ""},
      {"", "", ""},

      ""
    },

// Scales of various colours and effects
    { MUT_RED_SCALES,                 2,  3, false,  true,
      {"You are partially covered in red scales (AC +1).",
       "You are mostly covered in red scales (AC +2).",
       "You are covered in red scales (AC +4)."},

      {"Red scales grow over part of your body.",
       "Red scales spread over more of your body.",
       "Red scales cover you completely."},

      {"Your red scales disappear.",
       "Your red scales recede somewhat.",
       "Your red scales recede somewhat."},

      "red scales"
    },
    { MUT_NACREOUS_SCALES,            1,  3, false,  true,
      {"You are partially covered in smooth nacreous scales (AC +1).",
       "You are mostly covered in smooth nacreous scales (AC +3).",
       "You are completely covered in smooth nacreous scales (AC +5)."},

      {"Smooth nacreous scales grow over part of your body.",
       "Smooth nacreous scales spread over more of your body.",
       "Smooth nacreous scales cover you completely."},

      {"Your smooth nacreous scales disappear.",
       "Your smooth nacreous scales recede somewhat.",
       "Your smooth nacreous scales recede somewhat."},

      "nacreous scales"
    },
    { MUT_GREY2_SCALES,               2,  3, false,  true,
      {"You are partially covered in ridged grey scales (AC +2, Dex -1).",
       "You are mostly covered in ridged grey scales (AC +4, Dex -1).",
       "You are completely covered in ridged grey scales (AC +6, Dex -2)."},

      {"Ridged grey scales grow over part of your body.",
       "Ridged grey scales spread over more of your body.",
       "Ridged grey scales cover you completely."},

      {"Your ridged grey scales disappear.",
       "Your ridged grey scales recede somewhat.",
       "Your ridged grey scales recede somewhat."},

      "grey2 scales"
    },
    { MUT_METALLIC_SCALES,            1,  3, false,  true,
      {"You are partially covered in metallic scales (AC +3, Dex -2).",
       "You are mostly covered in metallic scales (AC +7, Dex -3).",
       "You are completely covered in metallic scales (AC +10, Dex -4)."},

      {"Metallic scales grow over part of your body.",
       "Metallic scales spread over more of your body.",
       "Metallic scales cover you completely."},

      {"Your metallic scales disappear.",
       "Your metallic scales recede somewhat.",
       "Your metallic scales recede somewhat."},

      "metallic scales"
    },
    { MUT_BLACK2_SCALES,              2,  3, false,  true,
      {"You are partially covered in black scales (AC +1).",
       "You are mostly covered in black scales (AC +3).",
       "You are completely covered in black scales (AC +5)."},

      {"Black scales grow over part of your body.",
       "Black scales spread over more of your body.",
       "Black scales cover you completely."},

      {"Your black scales disappear.",
       "Your black scales recede somewhat.",
       "Your black scales recede somewhat."},

      "black2 scales"
    },
    { MUT_WHITE_SCALES,               2,  3, false,  true,
      {"You are partially covered in white scales (AC +1).",
       "You are mostly covered in white scales (AC +3).",
       "You are completely covered in white scales (AC +5)."},

      {"White scales grow over part of your body.",
       "White scales spread over more of your body.",
       "White scales cover you completely."},

      {"Your white scales disappear.",
       "Your white scales recede somewhat.",
       "Your white scales recede somewhat."},

      "white scales"
    },
    { MUT_YELLOW_SCALES,              2,  3, false,  true,
      {"You are partially covered in yellow scales (AC +2).",
       "You are mostly covered in yellow scales (AC +4, Dex -1).",
       "You are completely covered in yellow scales (AC +6, Dex -2)."},

      {"Yellow scales grow over part of your body.",
       "Yellow scales spread over more of your body.",
       "Yellow scales cover you completely."},

      {"Your yellow scales disappear.",
       "Your yellow scales recede somewhat.",
       "Your yellow scales recede somewhat."},

      "yellow scales"
    },
    { MUT_BROWN_SCALES,               2,  3, false,  true,
      {"You are partially covered in brown scales (AC +2).",
       "You are mostly covered in brown scales (AC +4).",
       "You are completely covered in brown scales (AC +5)."},

      {"Brown scales grow over part of your body.",
       "Brown scales spread over more of your body.",
       "Brown scales cover you completely."},

      {"Your brown scales disappear.",
       "Your brown scales recede somewhat.",
       "Your brown scales recede somewhat."},

      "brown scales"
    },
    { MUT_BLUE_SCALES,                2,  3, false,  true,
      {"You are partially covered in blue scales (AC +1).",
       "You are mostly covered in blue scales (AC +2).",
       "You are completely covered in blue scales (AC +3)."},

      {"Blue scales grow over part of your body.",
       "Blue scales spread over more of your body.",
       "Blue scales cover you completely."},

      {"Your blue scales disappear.",
       "Your blue scales recede somewhat.",
       "Your blue scales recede somewhat."},

      "blue scales"
    },
    { MUT_PURPLE_SCALES,              2,  3, false,  true,
      {"You are partially covered in purple scales (AC +2).",
       "You are mostly covered in purple scales (AC +4).",
       "You are completely covered in purple scales (AC +6)."},

      {"Purple scales grow over part of your body.",
       "Purple scales spread over more of your body.",
       "Purple scales cover you completely."},

      {"Your purple scales disappear.",
       "Your purple scales recede somewhat.",
       "Your purple scales recede somewhat."},

      "purple scales"
    },
    { MUT_SPECKLED_SCALES,            2,  3, false,  true,
      {"You are partially covered in speckled scales (AC +1).",
       "You are mostly covered in speckled scales (AC +2).",
       "You are covered in speckled scales (AC +3)."},

      {"Speckled scales grow over part of your body.",
       "Speckled scales spread over more of your body.",
       "Speckled scales cover you completely."},

      {"Your speckled scales disappear.",
       "Your speckled scales recede somewhat.",
       "Your speckled scales recede somewhat."},

      "speckled scales"
    },
    { MUT_ORANGE_SCALES,              2,  3, false,  true,
      {"You are partially covered in orange scales (AC +1).",
       "You are mostly covered in orange scales (AC +3).",
       "You are completely covered in orange scales (AC +4)."},

      {"Orange scales grow over part of your body.",
       "Orange scales spread over more of your body.",
       "Orange scales cover you completely."},

      {"Your orange scales disappear.",
       "Your orange scales recede somewhat.",
       "Your orange scales recede somewhat."},

      "orange scales"
    },
    { MUT_INDIGO_SCALES,              2,  3, false,  true,
      {"You are partially covered in indigo scales (AC +2).",
       "You are mostly covered in indigo scales (AC +3).",
       "You are completely covered in indigo scales (AC +5)."},

      {"Indigo scales grow over part of your body.",
       "Indigo scales spread over more of your body.",
       "Indigo scales cover you completely."},

      {"Your indigo scales disappear.",
       "Your indigo scales recede somewhat.",
       "Your indigo scales recede somewhat."},

      "indigo scales"
    },
    { MUT_RED2_SCALES,                1,  3, false,  true,
      {"You are partially covered in knobbly red scales (AC +2).",
       "You are mostly covered in knobbly red scales (AC +5, Dex -1).",
       "You are completely covered in knobbly red scales (AC +7, Dex -2)."},

      {"Knobbly red scales grow over part of your body.",
       "Knobbly red scales spread over more of your body.",
       "Knobbly red scales cover you completely."},

      {"Your knobbly red scales disappear.",
       "Your knobbly red scales recede somewhat.",
       "Your knobbly red scales recede somewhat."},

      "red2 scales"
    },
    { MUT_IRIDESCENT_SCALES,          1,  3, false,  true,
      {"You are partially covered in iridescent scales (AC +1).",
       "You are mostly covered in iridescent scales (AC +2).",
       "You are completely covered in iridescent scales (AC +3)."},

      {"Iridescent scales grow over part of your body.",
       "Iridescent scales spread over more of your body.",
       "Iridescent scales cover you completely."},

      {"Your iridescent scales disappear.",
       "Your iridescent scales recede somewhat.",
       "Your iridescent scales recede somewhat."},

      "iridescent scales"
    },
    { MUT_STOCHASTIC_TORMENT_RESISTANCE, 0, 3, false, false,
      {"You are somewhat able to resist unholy torments (1 in 5 success).",
       "You are decently able to resist unholy torments (2 in 5 success).",
       "You are rather able to resist unholy torments (3 in 5 success)."},

      {"You feel a slight anaesthesia.",
       "You feel a slight anaesthesia.",
       "You feel a strange anaesthesia."},

      {"","",""},

      "stochastic torment resistance"},
    { MUT_PASSIVE_MAPPING,            3,  3, false, false,
      {"You passively map a small area around you.",
       "You passively map the area around you.",
       "You passively map a large area around you."},

      {"You feel a strange attunement to the structure of the dungeons.",
       "Your attunement to dungeon structure grows.",
       "Your attunement to dungeon structure grows further."},

      {"You feel slightly disoriented.",
       "You feel slightly disoriented.",
       "You feel slightly disoriented."},

      "passive mapping"
    },

    { MUT_ICEMAIL,                    0,  1, false, false,
      {"A meltable icy envelope protects you from harm (AC+", "", ""},
      {"An icy envelope takes form around you.", "", ""},
      {"", "", ""},
      "icemail"
    },

    { MUT_CONSERVE_SCROLLS,           0,  1, false, false,
      {"You are very good at protecting items from fire.", "", ""},
      {"You feel less concerned about heat.", "", ""},
      {"", "", ""},
      "conserve scrolls",
    },

    { MUT_CONSERVE_POTIONS,           0,  1, false, false,
      {"You are very good at protecting items from cold.", "", ""},
      {"You feel less concerned about cold.", "", ""},
      {"", "", ""},
      "conserve potions",
    },

    { MUT_PASSIVE_FREEZE,             0,  1, false, false,
      {"A frigid envelope surrounds you and freezes all who hurt you.", "", ""},
      {"Your skin feels very cold...", "", ""},
      {"", "", ""},
      "passive freeze",
    },
    // Anything after PATTERNED SCALES will be ignored by the debug code
    { MUT_PATTERNED_SCALES,           1,  3, false,  true,
      {"You are partially covered in patterned scales (AC +1).",
       "You are mostly covered in patterned scales (AC +2).",
       "You are completely covered in patterned scales (AC +3)."},

      {"Patterned scales grow over part of your body.",
       "Patterned scales spread over more of your body.",
       "Patterned scales cover you completely."},

      {"Your patterned scales disappear.",
       "Your patterned scales recede somewhat.",
       "Your patterned scales recede somewhat."},

      "patterned scales"
    },
};

const mutation_def& get_mutation_def(mutation_type mut)
{
    for (unsigned i = 0; i < ARRAYSZ(mutation_defs); ++i)
        if (mut == mutation_defs[i].mutation)
            return mutation_defs[i];

    ASSERT(0);
    return mutation_defs[0];
}

void fixup_mutations()
{
    if (player_genus(GENPC_DRACONIAN))
    {
        for (unsigned i = 0; i < ARRAYSZ(mutation_defs); ++i)
        {
            if (mutation_defs[i].mutation == MUT_STINGER
                || mutation_defs[i].mutation == MUT_BIG_WINGS)
            {
                mutation_defs[i].rarity = 1;
            }
        }
    }

    if (you.species == SP_TROLL)
    {
        for (unsigned i = 0; i < ARRAYSZ(mutation_defs); ++i)
        {
            if (mutation_defs[i].mutation == MUT_CLAWS)
            {
                for (int j = 0; j < 3; ++j)
                {
                    mutation_defs[i].gain[j] = troll_claw_gain[j];
                    mutation_defs[i].lose[j] = troll_claw_lose[j];
                }
            }
        }
    }

    if (you.species == SP_NAGA)
    {
        for (unsigned i = 0; i < ARRAYSZ(mutation_defs); ++i)
        {
            if (mutation_defs[i].mutation == MUT_DEFORMED)
            {
                for (int j = 0; j < 3; ++j)
                    mutation_defs[i].have[j] = naga_deformed_descrip[j];
            }
            else if (mutation_defs[i].mutation == MUT_STINGER)
                mutation_defs[i].rarity = 1;
        }
    }

    if (you.species == SP_CENTAUR)
        for (unsigned i = 0; i < ARRAYSZ(mutation_defs); ++i)
            if (mutation_defs[i].mutation == MUT_DEFORMED)
                for (int j = 0; j < 3; ++j)
                    mutation_defs[i].have[j] = centaur_deformed_descrip[j];
}

bool mutation_is_fully_active(mutation_type mut)
{
    // For all except the semi-undead, mutations always apply.
    if (you.is_undead != US_SEMI_UNDEAD)
        return (true);

    // Innate mutations are always active
    if (you.demon_pow[mut])
        return (true);

    // ... as are all mutations for semi-undead who are fully alive
    if (you.hunger_state == HS_ENGORGED)
        return (true);

    // ... as are physical mutations.
    if (get_mutation_def(mut).physical)
        return (true);

    return (false);
}

static bool _mutation_is_fully_inactive(mutation_type mut)
{
    const mutation_def& mdef = get_mutation_def(mut);
    return (you.is_undead == US_SEMI_UNDEAD && you.hunger_state < HS_SATIATED
            && !you.demon_pow[mut] && !mdef.physical);
}

formatted_string describe_mutations()
{
    std::string result;
    bool have_any = false;
    const char *mut_title = "Innate Abilities, Weirdness & Mutations";

    // center title
    int offset = 39 - strlen(mut_title) / 2;
    if ( offset < 0 ) offset = 0;

    result += std::string(offset, ' ');

    result += "<white>";
    result += mut_title;
    result += "</white>" EOL EOL;

    // Innate abilities which don't fit as mutations.
    result += "<lightblue>";
    switch (you.species)
    {
    case SP_MERFOLK:
        result += "You revert to your normal form in water." EOL;
        have_any = true;
        break;

    case SP_NAGA:
        result += "You cannot wear boots." EOL;
        if (!you.mutation[MUT_FAST])
        {
            result += mutation_name(MUT_FAST, -1, true);
            result += EOL;
        }
        // Breathe poison replaces spit poison.
        if (!you.mutation[MUT_BREATHE_POISON])
            result += "You can spit poison." EOL;
        if (you.experience_level > 2)
        {
            std::ostringstream num;
            num << you.experience_level/3;
            result += "Your serpentine skin is tough (AC +" + num.str() + ")." EOL;
        }
        have_any = true;
        break;

    case SP_GHOUL:
        result += "Your body is rotting away." EOL;
        result += "You have sharp claws for hands." EOL;
        have_any = true;
        break;

    case SP_TROLL:
        if (!you.mutation[MUT_CLAWS])
        {
            result += mutation_name(MUT_CLAWS, -1, true);
            result += EOL;
        }
        have_any = true;
        break;

    case SP_KENKU:
        if (you.experience_level > 4)
        {
            result += "You can fly";
            if (you.experience_level > 14)
                result += " continuously";
            result += "." EOL;
            have_any = true;
        }
        break;

    case SP_MUMMY:
        result += "Your flesh is vulnerable to fire." EOL;
        if (you.experience_level > 12)
        {
            result += "You are";
            if (you.experience_level > 25)
                result += " strongly";

            result += " in touch with the powers of death." EOL;
            result +=
                "You can restore your body by infusing magical energy." EOL;
        }
        have_any = true;
        break;

    case SP_GREY_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "Your tail is studded with spikes." EOL;
            have_any = true;
        }
        break;

    case SP_GREEN_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe poison." EOL;
            have_any = true;
        }
        break;

    case SP_RED_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe fire." EOL;
            have_any = true;
        }
        break;

    case SP_WHITE_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe cold." EOL;
            have_any = true;
        }
        break;

    case SP_BLACK_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe lightning." EOL;
            have_any = true;
        }
        break;

    case SP_YELLOW_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can spit acid." EOL;
            result += "You are resistant to acid." EOL;
            have_any = true;
        }
        break;

    case SP_PURPLE_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe power." EOL;
            have_any = true;
        }
        break;

    case SP_MOTTLED_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe sticky flames." EOL;
            have_any = true;
        }
        break;

    case SP_PALE_DRACONIAN:
        if (you.experience_level > 6)
        {
            result += "You can breathe steam." EOL;
            have_any = true;
        }
        break;

    case SP_KOBOLD:
        result += "You recuperate from illness quickly." EOL;
        have_any = true;
        break;

    case SP_VAMPIRE:
        have_any = true;
        if (you.hunger_state == HS_STARVING)
            result += "<green>You do not heal naturally.</green>" EOL;
        else if (you.hunger_state == HS_ENGORGED)
            result += "<green>Your natural rate of healing is extremely fast.</green>" EOL;
        else if (you.hunger_state < HS_SATIATED)
            result += "<green>You heal slowly.</green>" EOL;
        else if (you.hunger_state >= HS_FULL)
            result += "<green>Your natural rate of healing is unusually fast.</green>" EOL;
        else
            have_any = false;

        if (you.experience_level >= 6)
        {
            result += "You can bottle blood from corpses with 'c'." EOL;
            have_any = true;
        }
        break;

    case SP_DEEP_DWARF:
        result += "You are resistant to damage." EOL;
        result += "You can recharge devices by infusing magical energy." EOL;
        have_any = true;
        break;

    default:
        break;
    }

    // a bit more stuff
    if (player_genus(GENPC_OGRE) || you.species == SP_TROLL
        || player_genus(GENPC_DRACONIAN) || you.species == SP_SPRIGGAN)
    {
        result += "Your body does not fit into most forms of armour." EOL;
        have_any = true;
    }

    if (player_genus(GENPC_DRACONIAN))
    {
        int ac = (you.experience_level < 8) ? 2 :
                 (you.species == SP_GREY_DRACONIAN)
                     ? (you.experience_level - 4) / 2 + 1 :
                       you.experience_level / 4 + 1;
        std::ostringstream num;
        num << ac;
        result += "Your scales are hard (AC +" + num.str() + ")." EOL;
    }

    result += "</lightblue>";

    if (beogh_water_walk())
    {
        result += "<green>You can walk on water.</green>" EOL;
        have_any = true;
    }

    if (you.duration[DUR_FIRE_SHIELD])
    {
        result += "<green>You are immune to clouds of flame.</green>" EOL;
        have_any = true;
    }

    textcolor(LIGHTGREY);

    // First add (non-removable) inborn abilities and demon powers.
    for (int i = 0; i < NUM_MUTATIONS; i++)
    {
        if (you.mutation[i] != 0 && you.demon_pow[i])
        {
            mutation_type mut_type = static_cast<mutation_type>(i);
            result += mutation_name(mut_type, -1, true);
            result += EOL;
            have_any = true;
        }
    }

    // Now add removable mutations.
    for (int i = 0; i < NUM_MUTATIONS; i++)
    {
        if (you.mutation[i] != 0 && !you.demon_pow[i])
        {
            mutation_type mut_type = static_cast<mutation_type>(i);
            result += mutation_name(mut_type, -1, true);
            result += EOL;
            have_any = true;
        }
    }

    if (!have_any)
        result +=  "You are rather mundane." EOL;

    if (you.species == SP_VAMPIRE)
    {
        result += EOL EOL;
        result += EOL EOL;
        result +=
#ifndef USE_TILE
            "Press '<w>!</w>'"
#else
            "<w>Right-click</w>"
#endif
            " to toggle between mutations and properties depending on your" EOL
            "hunger status." EOL;
    }

    return formatted_string::parse_string(result);
}

static void _display_vampire_attributes()
{
    ASSERT(you.species == SP_VAMPIRE);

    clrscr();
    cgotoxy(1,1);

    std::string result;

    const int lines = 15;
    std::string column[lines][7] =
    {
       {"                     ", "<lightgreen>Alive</lightgreen>      ", "<green>Full</green>    ",
        "Satiated  ", "<yellow>Thirsty</yellow>  ", "<yellow>Near...</yellow>  ",
        "<lightred>Bloodless</lightred>"},
                                //Alive          Full       Satiated      Thirsty   Near...      Bloodless
       {"Metabolism           ", "very fast  ", "fast    ", "fast      ", "normal   ", "slow     ", "none  "},

       {"Regeneration         ", "very fast  ", "fast    ", "normal    ", "slow     ", "slow     ", "none  "},

       {"Stealth boost        ", "none       ", "none    ", "none      ", "minor    ", "major    ", "large "},

       {"Spell hunger         ", "full       ", "full    ", "full      ", "halved   ", "none     ", "none  "},

       {EOL "<w>Resistances</w>" EOL
        "Poison resistance    ", "           ", "        ", "          ", " +       ", " +       ", " +    "},

       {"Cold resistance      ", "           ", "        ", "          ", " +       ", " +       ", " ++   "},

       {"Negative resistance  ", "           ", "        ", " +        ", " ++      ", " +++     ", " +++  "},

       {"Rotting resistance   ", "           ", "        ", "          ", " +       ", " +       ", " +    "},

       {"Torment resistance   ", "           ", "        ", "          ", "         ", "         ", " +    "},

       {EOL "<w>Other effects</w>" EOL
        "Mutation chance      ", "always     ", "often   ", "sometimes ", "never    ", "never    ", "never "},

       {"Non-physical " EOL
        "mutation effects     ", "full       ", "capped  ", "capped    ", "none     ", "none     ", "none  "},

       {"Potion effects       ", "full       ", "full    ", "full      ", "halved   ", "halved   ", "halved"},

       {"Bat Form             ", "no         ", "no      ", "yes       ", "yes      ", "yes      ", "yes   "},

       {"Other transformation " EOL
        "or going berserk     ", "yes        ", "yes     ", "no        ", "no       ", "no       ", "no    "}
    };

    int current = 0;
    switch (you.hunger_state)
    {
    case HS_ENGORGED:
        current = 1;
        break;
    case HS_VERY_FULL:
    case HS_FULL:
        current = 2;
        break;
    case HS_SATIATED:
        current = 3;
        break;
    case HS_HUNGRY:
    case HS_VERY_HUNGRY:
        current = 4;
        break;
    case HS_NEAR_STARVING:
        current = 5;
        break;
    case HS_STARVING:
        current = 6;
    }

    for (int y = 0; y < lines; y++)  // lines   (properties)
    {
        for (int x = 0; x < 7; x++)  // columns (hunger states)
        {
             if (y > 0 && x == current)
                 result += "<w>";
             result += column[y][x];
             if (y > 0 && x == current)
                 result += "</w>";
        }
        result += EOL;
    }

    result += EOL;
    result +=
#ifndef USE_TILE
        "Press '<w>!</w>'"
#else
        "<w>Right-click</w>"
#endif
        " to toggle between mutations and properties depending on your" EOL
        "hunger status." EOL;

    const formatted_string vp_props = formatted_string::parse_string(result);
    vp_props.display();

    mouse_control mc(MOUSE_MODE_MORE);
    const int keyin = getch();
    if (keyin == '!' || keyin == CK_MOUSE_CMD)
        display_mutations();
}

void display_mutations()
{
    clrscr();
    cgotoxy(1,1);

    const formatted_string mutation_fs = describe_mutations();

    if (you.species == SP_VAMPIRE)
    {
        mutation_fs.display();
        mouse_control mc(MOUSE_MODE_MORE);
        const int keyin = getch();
        if (keyin == '!' || keyin == CK_MOUSE_CMD)
            _display_vampire_attributes();
    }
    else
    {
        Menu mutation_menu(mutation_fs);
        mutation_menu.show();
    }
}

static int _calc_mutation_amusement_value(mutation_type which_mutation)
{
    int amusement = 16 * (11 - get_mutation_def(which_mutation).rarity);

    switch (which_mutation)
    {
    case MUT_STRONG:
    case MUT_CLEVER:
    case MUT_AGILE:
    case MUT_POISON_RESISTANCE:
    case MUT_SHOCK_RESISTANCE:
    case MUT_REGENERATION:
    case MUT_SLOW_METABOLISM:
    case MUT_TELEPORT_CONTROL:
    case MUT_MAGIC_RESISTANCE:
    case MUT_TELEPORT_AT_WILL:
    case MUT_CLARITY:
    case MUT_MUTATION_RESISTANCE:
    case MUT_ROBUST:
    case MUT_HIGH_MAGIC:
        amusement /= 2;  // not funny
        break;

    case MUT_CARNIVOROUS:
    case MUT_HERBIVOROUS:
    case MUT_SLOW_HEALING:
    case MUT_FAST_METABOLISM:
    case MUT_WEAK:
    case MUT_DOPEY:
    case MUT_CLUMSY:
    case MUT_TELEPORT:
    case MUT_FAST:
    case MUT_DEFORMED:
    case MUT_SPIT_POISON:
    case MUT_BREATHE_FLAMES:
    case MUT_BLINK:
    case MUT_HORNS:
    case MUT_BEAK:
    case MUT_SCREAM:
    case MUT_BERSERK:
    case MUT_DETERIORATION:
    case MUT_BLURRY_VISION:
    case MUT_FRAIL:
    case MUT_CLAWS:
    case MUT_FANGS:
    case MUT_HOOVES:
    case MUT_TALONS:
    case MUT_BREATHE_POISON:
    case MUT_STINGER:
    case MUT_BIG_WINGS:
    case MUT_LOW_MAGIC:
        amusement *= 2; // funny!
        break;

    default:
        break;
    }

    return (amusement);
}

static bool _is_deadly(mutation_type mutat, bool delete_mut)
{
    if (delete_mut)
    {
        // First handle non-stat related problems.
        if ( mutat == MUT_HEAT_RESISTANCE && grd(you.pos()) == DNGN_LAVA
             && player_res_fire() == 1 && !you.airborne() )
        {
            // Don't let player instantly fry to a crisp in lava.
            return (true);
        }

        // Swap things around to the same effect, but as if we were
        // gaining a mutation, or return early if deleting the mutation
        // is never a problem.
        switch (mutat)
        {
        case MUT_GREY2_SCALES:
        case MUT_METALLIC_SCALES:
        case MUT_YELLOW_SCALES:
        case MUT_RED2_SCALES:
        case MUT_WEAK:
        case MUT_DOPEY:
        case MUT_CLUMSY:
            return (false);

        case MUT_STRONG_STIFF:
            mutat = MUT_FLEXIBLE_WEAK;
            break;

        case MUT_FLEXIBLE_WEAK:
            mutat = MUT_STRONG_STIFF;
            break;

        case MUT_STRONG:
            mutat = MUT_WEAK;
            break;

        case MUT_CLEVER:
            mutat = MUT_DOPEY;
            break;

        case MUT_AGILE:
            mutat = MUT_CLUMSY;
            break;

        default:
            break;
        }
    }

    unsigned char cur_level = you.mutation[mutat];

    char *stat_ptr = &you.dex; // Default for the scales.
    char  amnt     = 1;
    char  mod      = 0;

    switch (mutat)
    {
    case MUT_GREY2_SCALES:
        if (cur_level == 0 || cur_level == 2)
            amnt = 1;
        else
            amnt = 0;
        break;

    case MUT_METALLIC_SCALES:
        if (cur_level == 0)
            amnt = 2;
        else
            amnt = 1;
        break;

    case MUT_YELLOW_SCALES:
    case MUT_RED2_SCALES:
        if (cur_level == 0)
            amnt = 0;
        else
            amnt = 1;
        break;

    case MUT_FLEXIBLE_WEAK:
    case MUT_WEAK:
        stat_ptr = &you.strength;
        // Take might into account so we don't lower base strength below
        // one.
        if (you.duration[DUR_MIGHT])
            mod = -5;
        break;

    case MUT_DOPEY:
        stat_ptr = &you.intel;
        if (you.duration[DUR_BRILLIANCE])
            mod = -5;
        break;

    case MUT_STRONG_STIFF:
    case MUT_CLUMSY:
        stat_ptr = &you.dex;
        if (you.duration[DUR_AGILITY])
            mod = -5;
        break;

    default:
        return (false);
    }

    return (amnt >= (*stat_ptr + mod));
}

static bool _accept_mutation(mutation_type mutat, bool ignore_rarity = false,
                             bool non_fatal = false, bool delete_mut = false)
{
    if (mutat == RANDOM_MUTATION)
        return (false);

    const mutation_def& mdef = get_mutation_def(mutat);

    if (delete_mut)
        return (!non_fatal || !_is_deadly(mutat, delete_mut));

    if (you.mutation[mutat] >= mdef.levels)
        return (false);

    if (non_fatal && _is_deadly(mutat, delete_mut))
        return (false);

    if (ignore_rarity)
        return (true);

    const int rarity = mdef.rarity + you.demon_pow[mutat];

    // Low rarity means unlikely to choose it.
    return (x_chance_in_y(rarity, 10));
}

static mutation_type _get_random_xom_mutation(bool non_fatal = false)
{
    const mutation_type bad_muts[] = {
        MUT_WEAK,          MUT_DOPEY,
        MUT_CLUMSY,        MUT_DEFORMED,      MUT_SCREAM,
        MUT_DETERIORATION, MUT_BLURRY_VISION, MUT_FRAIL
    };

    mutation_type mutat = NUM_MUTATIONS;

    do
    {
        mutat = static_cast<mutation_type>(random2(NUM_MUTATIONS));

        if (one_chance_in(1000))
            return (NUM_MUTATIONS);
        else if (one_chance_in(5))
            mutat = RANDOM_ELEMENT(bad_muts);
    }
    while (!_accept_mutation(mutat, false, non_fatal));

    return (mutat);
}

static mutation_type _get_random_mutation(bool prefer_good,
                                          int preferred_multiplier,
                                          bool non_fatal = false)
{
    int cweight = 0;
    mutation_type chosen = NUM_MUTATIONS;
    for (unsigned i = 0; i < ARRAYSZ(mutation_defs); ++i)
    {
        if (!mutation_defs[i].rarity)
            continue;

        const mutation_type curr = mutation_defs[i].mutation;
        if (!_accept_mutation(curr, true, non_fatal))
            continue;

        const bool weighted = mutation_defs[i].bad != prefer_good;
        int weight = mutation_defs[i].rarity;
        if (weighted)
            weight = weight * preferred_multiplier / 100;

        cweight += weight;

        if (x_chance_in_y(weight, cweight))
            chosen = curr;
    }

    return (chosen);
}

#ifdef DEBUG
static bool _is_random(mutation_type which_mutation)
{
    return (which_mutation == RANDOM_MUTATION
            || which_mutation == RANDOM_XOM_MUTATION
            || which_mutation == RANDOM_GOOD_MUTATION
            || which_mutation == RANDOM_BAD_MUTATION);
}
#endif

// Tries to give you the mutation by deleting a conflicting
// one, or clears out conflicting mutations if we should give
// you the mutation anyway.
// Return:
//  1 if we should stop processing (success);
//  0 if we should continue processing;
// -1 if we should stop processing (failure).
static int _handle_conflicting_mutations(mutation_type mutation,
                                         bool override)
{
    if (override)
    {
        // These are mutations which should be cleared away if forced.
        const mutation_type override_conflict[][2] = {
            { MUT_REGENERATION, MUT_SLOW_METABOLISM },
            { MUT_REGENERATION, MUT_SLOW_HEALING    },
            { MUT_ACUTE_VISION, MUT_BLURRY_VISION   }
        };

        // If we have one of the pair, delete all levels of the other,
        // and continue processing.
        for (unsigned i = 0; i < ARRAYSZ(override_conflict); ++i)
        {
            for (int j = 0; j < 2; ++j)
            {
                const mutation_type a = override_conflict[i][j];
                const mutation_type b = override_conflict[i][1-j];

                if (mutation == a)
                    while (delete_mutation(b, true, true))
                        ;
            }
        }
    }

    // These are mutations which can't be traded off against each other,
    // so we just fail.
    const mutation_type fail_conflict[][2] = {
        { MUT_REGENERATION, MUT_SLOW_METABOLISM },
        { MUT_FANGS,        MUT_BEAK            },
        { MUT_HOOVES,       MUT_TALONS          }
    };

    for (unsigned i = 0; i < ARRAYSZ(fail_conflict); ++i)
    {
        for (int j = 0; j < 2; ++j)
        {
            const mutation_type a = fail_conflict[i][j];
            const mutation_type b = fail_conflict[i][1-j];
            if (mutation == a && you.mutation[b] > 0)
                return (-1);    // Fail.
        }
    }

    // These are mutations which trade off against each other.
    const mutation_type simple_conflict[][2] = {
        { MUT_STRONG,          MUT_WEAK            },
        { MUT_CLEVER,          MUT_DOPEY           },
        { MUT_AGILE,           MUT_CLUMSY          },
        { MUT_STRONG_STIFF,    MUT_FLEXIBLE_WEAK   },
        { MUT_ROBUST,          MUT_FRAIL           },
        { MUT_HIGH_MAGIC,      MUT_LOW_MAGIC       },
        { MUT_CARNIVOROUS,     MUT_HERBIVOROUS     },
        { MUT_SLOW_METABOLISM, MUT_FAST_METABOLISM },
        { MUT_REGENERATION,    MUT_SLOW_HEALING    },
        { MUT_ACUTE_VISION,    MUT_BLURRY_VISION   }
    };

    for (unsigned i = 0; i < ARRAYSZ(simple_conflict); ++i)
        for (int j = 0; j < 2; ++j)
        {
            // If we have one of the pair, delete a level of the other,
            // and that's it.
            const mutation_type a = simple_conflict[i][j];
            const mutation_type b = simple_conflict[i][1-j];
            if (mutation == a && you.mutation[b] > 0)
            {
                delete_mutation(b);
                return (1);     // Nothing more to do.
            }
        }

    return (0);
}

static bool _physiology_mutation_conflict(mutation_type mutat)
{
    // Only Nagas and Draconians can get this one.
    if (mutat == MUT_STINGER
        && you.species != SP_NAGA && !player_genus(GENPC_DRACONIAN))
    {
        return (true);
    }

    if ((mutat == MUT_HOOVES || mutat == MUT_TALONS) && !player_has_feet())
        return (true);

    // Already innate.
    if (mutat == MUT_BREATHE_POISON && you.species != SP_NAGA)
        return (true);

    // Red Draconians can already breathe flames.
    if (mutat == MUT_BREATHE_FLAMES && you.species == SP_RED_DRACONIAN)
        return (true);

    // Green Draconians can already breathe poison, so they don't need
    // to spit it.
    if (mutat == MUT_SPIT_POISON && you.species == SP_GREEN_DRACONIAN)
        return (true);

    // Only Draconians can get wings.
    if (mutat == MUT_BIG_WINGS && !player_genus(GENPC_DRACONIAN))
        return (true);

    // Vampires' healing and thirst rates depend on their blood level.
    if (you.species == SP_VAMPIRE
        && (mutat == MUT_CARNIVOROUS || mutat == MUT_HERBIVOROUS
            || mutat == MUT_REGENERATION || mutat == MUT_SLOW_HEALING
            || mutat == MUT_FAST_METABOLISM || mutat == MUT_SLOW_METABOLISM))
    {
        return (true);
    }

    return (false);
}

static bool _reautomap_callback()
{
    reautomap_level();
    return true;
}

bool mutate(mutation_type which_mutation, bool failMsg,
            bool force_mutation, bool god_gift, bool stat_gain_potion,
            bool demonspawn, bool non_fatal)
{
    ASSERT(!non_fatal || _is_random(which_mutation));

    if (!god_gift)
    {
        const god_type god =
            (crawl_state.is_god_acting()) ? crawl_state.which_god_acting()
                                          : GOD_NO_GOD;

        if (god != GOD_NO_GOD)
            god_gift = true;
    }

    if (demonspawn)
        force_mutation = true;

    mutation_type mutat = which_mutation;

    if (!force_mutation)
    {
        // God gifts override all sources of mutation resistance other
        // than divine protection, and stat gain potions override all
        // sources of mutation resistance other than the mutation
        // resistance mutation.
        if (!god_gift)
        {
            if (wearing_amulet(AMU_RESIST_MUTATION)
                    && !one_chance_in(10) && !stat_gain_potion
                || player_mutation_level(MUT_MUTATION_RESISTANCE) == 3
                || player_mutation_level(MUT_MUTATION_RESISTANCE)
                    && !one_chance_in(3))
            {
                if (failMsg)
                    mpr("You feel odd for a moment.", MSGCH_MUTATION);
                return (false);
            }
        }

        // Zin's protection.
        if (you.religion == GOD_ZIN && x_chance_in_y(you.piety, MAX_PIETY)
            && !stat_gain_potion)
        {
            simple_god_message(" protects your body from mutation!");
            return (false);
        }
    }

    bool rotting = you.is_undead;

    if (you.is_undead == US_SEMI_UNDEAD)
    {
        // The stat gain mutations always come through at Satiated or
        // higher (mostly for convenience), and, for consistency, also
        // their negative counterparts.
        if (which_mutation == MUT_STRONG || which_mutation == MUT_CLEVER
            || which_mutation == MUT_AGILE || which_mutation == MUT_WEAK
            || which_mutation == MUT_DOPEY || which_mutation == MUT_CLUMSY)
        {
            if (you.hunger_state > HS_SATIATED)
                rotting = false;
        }
        else
        {
            // Else, chances depend on hunger state.
            switch (you.hunger_state)
            {
            case HS_SATIATED:  rotting = !one_chance_in(3); break;
            case HS_FULL:      rotting = coinflip();        break;
            case HS_VERY_FULL: rotting = one_chance_in(3);  break;
            case HS_ENGORGED:  rotting = false;             break;
            }
        }
    }

    // Undead bodies don't mutate, they fall apart. -- bwr
    // except for demonspawn (or other permamutations) in lichform -- haranp
    if (rotting && !demonspawn)
    {
        mpr("Your body decomposes!", MSGCH_MUTATION);

        if (coinflip())
            lose_stat(STAT_RANDOM, 1, false, "mutating");
        else
        {
            ouch(3, NON_MONSTER, KILLED_BY_ROTTING);
            rot_hp(roll_dice(1, 3));
        }

        xom_is_stimulated(64);
        return (true);
    }

    if (which_mutation == RANDOM_MUTATION
        || which_mutation == RANDOM_XOM_MUTATION)
    {
        // If already heavily mutated, remove a mutation instead.
        if (x_chance_in_y(how_mutated(false, true), 15))
        {
            // God gifts override mutation loss due to being heavily
            // mutated.
            if (!one_chance_in(3) && !god_gift && !force_mutation)
                return (false);
            else
                return (delete_mutation(RANDOM_MUTATION, failMsg,
                                        force_mutation, false, non_fatal));
        }
    }

    if (which_mutation == RANDOM_MUTATION)
    {
        do
        {
            mutat = static_cast<mutation_type>(random2(NUM_MUTATIONS));
            if (one_chance_in(1000))
                return (false);
        }
        while (!_accept_mutation(mutat, false, non_fatal));
    }
    else if (which_mutation == RANDOM_XOM_MUTATION)
    {
        if ((mutat = _get_random_xom_mutation(non_fatal)) == NUM_MUTATIONS)
            return (false);
    }
    else if (which_mutation == RANDOM_GOOD_MUTATION)
    {
        mutat = _get_random_mutation(true, 500, non_fatal);
        if (mutat == NUM_MUTATIONS)
            return (false);
    }
    else if (which_mutation == RANDOM_BAD_MUTATION)
    {
        mutat = _get_random_mutation(false, 500, non_fatal);
        if (mutat == NUM_MUTATIONS)
            return (false);
    }

    // Saprovorous/gourmand can't be randomly acquired.
    if ((mutat == MUT_SAPROVOROUS || mutat == MUT_GOURMAND) && !force_mutation)
        return (false);

    // These can be forced by demonspawn or god gifts.
    if ((mutat == MUT_SHAGGY_FUR
            || mutat >= MUT_GREEN_SCALES && mutat <= MUT_BONEY_PLATES
            || mutat >= MUT_RED_SCALES && mutat <= MUT_PATTERNED_SCALES)
        && _body_covered() >= 3 && !god_gift && !force_mutation)
    {
        return (false);
    }

    if (you.species == SP_NAGA)
    {
        // gdl: Spit poison 'upgrades' to breathe poison.  Why not...
        if (mutat == MUT_SPIT_POISON)
        {
            if (coinflip())
                return (false);

            mutat = MUT_BREATHE_POISON;

            // Breathe poison replaces spit poison (so it takes the slot).
            for (int i = 0; i < 52; ++i)
                if (you.ability_letter_table[i] == ABIL_SPIT_POISON)
                    you.ability_letter_table[i] = ABIL_BREATHE_POISON;
        }
    }

    if (_physiology_mutation_conflict(mutat))
        return (false);

    const mutation_def& mdef = get_mutation_def(mutat);

    if (you.mutation[mutat] >= mdef.levels)
        return (false);

    // God gifts and forced mutations clear away conflicting mutations.
    int rc =_handle_conflicting_mutations(mutat, god_gift || force_mutation);
    if (rc == 1)
        return (true);
    if (rc == -1)
        return (false);

    ASSERT(rc == 0);

    const unsigned int old_talents = your_talents(false).size();

    bool gain_msg = true;
    bool stat_msg = false;

    // Save original stats.
    const stat_type stats[] = {STAT_STRENGTH, STAT_DEXTERITY,
                               STAT_INTELLIGENCE};
    int modifiers[3];

    for (int i = 0; i < 3; ++i)
        modifiers[i] = stat_modifier(stats[i]);

    bool modified_eq = false;

    switch (mutat)
    {
    case MUT_STRONG: case MUT_AGILE:  case MUT_CLEVER:
    case MUT_WEAK:   case MUT_CLUMSY: case MUT_DOPEY:
        stat_msg = true;
        gain_msg = false;
        break;

        // FIXME: these cases should be handled better.
    case MUT_HOOVES:
    case MUT_TALONS:
        mpr(mdef.gain[you.mutation[mutat]], MSGCH_MUTATION);
        gain_msg = false;

        // Hooves and talons force boots off.
        if (you_tran_can_wear(EQ_BOOTS))
        {
            remove_one_equip(EQ_BOOTS, false, true);
            modified_eq = true;
        }
        break;

    case MUT_CLAWS:
        mpr(mdef.gain[you.mutation[mutat]], MSGCH_MUTATION);
        gain_msg = false;

        // Gloves aren't prevented until level 3.  We don't have the
        // mutation yet, so we have to check for level 2 or higher claws
        // here.
        if (you.mutation[mutat] >= 2 && you_tran_can_wear(EQ_GLOVES))
        {
            remove_one_equip(EQ_GLOVES, false, true);
            modified_eq = true;
        }
        break;

    case MUT_HORNS:
    case MUT_BEAK:
        mpr(mdef.gain[you.mutation[mutat]], MSGCH_MUTATION);
        gain_msg = false;

        // Horns and beaks force hard helmets off.
        if (you.equip[EQ_HELMET] != -1
            && is_hard_helmet(you.inv[you.equip[EQ_HELMET]])
            && you_tran_can_wear(EQ_HELMET))
        {
            remove_one_equip(EQ_HELMET, false, true);
            modified_eq = true;
        }
        break;

    case MUT_ACUTE_VISION:
        // We might have to turn autopickup back on again.
        mpr(mdef.gain[you.mutation[mutat]], MSGCH_MUTATION);
        gain_msg = false;
        autotoggle_autopickup(false);
        break;

    default:
        break;
    }

    // For all those scale mutations.
    you.redraw_armour_class = true;

    you.mutation[mutat]++;

    // Don't run this loop if we lost some equipment slots, since
    // removing armour with stat modifiers makes the value of
    // new_modifier below different from the previously recorded value,
    // and the remove_one_equip() call chain already does stat change
    // from removing items correctly. -cao
    if (!modified_eq)
    {
        for (int i = 0; i < 3; ++i)
        {
            const int new_modifier = stat_modifier(stats[i]);
            if (new_modifier != modifiers[i])
            {
                modify_stat(stats[i], new_modifier - modifiers[i],
                            !stat_msg, "losing a mutation");
            }
        }
    }

    if (gain_msg)
        mpr(mdef.gain[you.mutation[mutat]-1], MSGCH_MUTATION);

    // Do post-mutation effects.
    if (mutat == MUT_FRAIL || mutat == MUT_ROBUST)
        calc_hp();
    if (mutat == MUT_LOW_MAGIC || mutat == MUT_HIGH_MAGIC)
        calc_mp();
    if (mutat == MUT_PASSIVE_MAPPING)
        apply_to_all_dungeons(_reautomap_callback);

    // Amusement value will be 16 * (11-rarity) * Xom's-sense-of-humor.
    xom_is_stimulated(_calc_mutation_amusement_value(mutat));

    take_note(Note(NOTE_GET_MUTATION, mutat, you.mutation[mutat]));

    if (Tutorial.tutorial_left && your_talents(false).size() > old_talents)
        learned_something_new(TUT_NEW_ABILITY_MUT);

    return (true);
}

static bool _delete_single_mutation_level(mutation_type mutat)
{
    if (you.mutation[mutat] == 0)
        return (false);

    if (you.demon_pow[mutat] >= you.mutation[mutat])
        return (false);

    const mutation_def& mdef = get_mutation_def(mutat);

    bool lose_msg = true;
    bool stat_msg = false;

    // Save original stats.
    const stat_type stats[] = {STAT_STRENGTH, STAT_DEXTERITY,
                               STAT_INTELLIGENCE};
    int modifiers[3];

    for (int i = 0; i < 3; ++i)
        modifiers[i] = stat_modifier(stats[i]);

    switch (mutat)
    {
    case MUT_STRONG: case MUT_AGILE:  case MUT_CLEVER:
    case MUT_WEAK:   case MUT_CLUMSY: case MUT_DOPEY:
        stat_msg = true;
        lose_msg = false;
        break;

    case MUT_BREATHE_POISON:
        // can't be removed yet, but still covered:
        if (you.species == SP_NAGA)
        {
            // natural ability to spit poison retakes the slot
            for (int i = 0; i < 52; ++i)
            {
                if (you.ability_letter_table[i] == ABIL_BREATHE_POISON)
                    you.ability_letter_table[i] = ABIL_SPIT_POISON;
            }
        }
        break;

    default:
        break;
    }

    // For all those scale mutations.
    you.redraw_armour_class = true;

    you.mutation[mutat]--;

    for (int i = 0; i < 3; ++i)
    {
        const int new_modifier = stat_modifier(stats[i]);
        if (new_modifier != modifiers[i])
        {
            modify_stat(stats[i], new_modifier - modifiers[i],
                        !stat_msg, "losing a mutation");
        }
    }

    if (lose_msg)
        mpr(mdef.lose[you.mutation[mutat]], MSGCH_MUTATION);

    // Do post-mutation effects.
    if (mutat == MUT_FRAIL || mutat == MUT_ROBUST)
        calc_hp();
    if (mutat == MUT_LOW_MAGIC || mutat == MUT_HIGH_MAGIC)
        calc_mp();

    take_note(Note(NOTE_LOSE_MUTATION, mutat, you.mutation[mutat]));

    return (true);
}

bool delete_mutation(mutation_type which_mutation, bool failMsg,
                     bool force_mutation, bool god_gift,
                     bool disallow_mismatch, bool non_fatal)
{
    ASSERT(!non_fatal || _is_random(which_mutation));

    if (!god_gift)
    {
        const god_type god =
            (crawl_state.is_god_acting()) ? crawl_state.which_god_acting()
                                          : GOD_NO_GOD;

        if (god != GOD_NO_GOD)
            god_gift = true;
    }

    mutation_type mutat = which_mutation;

    if (!force_mutation)
    {
        if (!god_gift)
        {
            if (player_mutation_level(MUT_MUTATION_RESISTANCE) > 1
                && (player_mutation_level(MUT_MUTATION_RESISTANCE) == 3
                    || coinflip()))
            {
                if (failMsg)
                    mpr("You feel rather odd for a moment.", MSGCH_MUTATION);
                return (false);
            }
        }
    }

    if (which_mutation == RANDOM_MUTATION
        || which_mutation == RANDOM_XOM_MUTATION
        || which_mutation == RANDOM_GOOD_MUTATION
        || which_mutation == RANDOM_BAD_MUTATION)
    {
        while (true)
        {
            if (one_chance_in(1000))
                return (false);

            mutat = static_cast<mutation_type>(random2(NUM_MUTATIONS));

            if (you.mutation[mutat] == 0
                && mutat != MUT_STRONG
                && mutat != MUT_CLEVER
                && mutat != MUT_AGILE
                && mutat != MUT_WEAK
                && mutat != MUT_DOPEY
                && mutat != MUT_CLUMSY)
            {
                continue;
            }

            if (!_accept_mutation(mutat, true, non_fatal, true))
                continue;

            if (you.demon_pow[mutat] >= you.mutation[mutat])
                continue;

            const mutation_def& mdef = get_mutation_def(mutat);

            if (random2(10) >= mdef.rarity)
                continue;

            const bool mismatch =
                (which_mutation == RANDOM_GOOD_MUTATION && mdef.bad)
                    || (which_mutation == RANDOM_BAD_MUTATION && !mdef.bad);

            if (mismatch && (disallow_mismatch || !one_chance_in(10)))
                continue;

            break;
        }
    }

    return (_delete_single_mutation_level(mutat));
}

bool delete_all_mutations()
{
    for (int i = 0; i < NUM_MUTATIONS; ++i)
    {
        while (_delete_single_mutation_level(static_cast<mutation_type>(i)))
            ;
    }

    return (!how_mutated());
}

static const mutation_type _all_scales[] = {
    MUT_SHAGGY_FUR,

    MUT_BONEY_PLATES,      MUT_GREEN_SCALES,    MUT_BLACK_SCALES,
    MUT_GREY_SCALES,       MUT_RED_SCALES,      MUT_NACREOUS_SCALES,
    MUT_GREY2_SCALES,      MUT_METALLIC_SCALES, MUT_BLACK2_SCALES,
    MUT_WHITE_SCALES,      MUT_YELLOW_SCALES,   MUT_BROWN_SCALES,
    MUT_BLUE_SCALES,       MUT_PURPLE_SCALES,   MUT_SPECKLED_SCALES,
    MUT_ORANGE_SCALES,     MUT_INDIGO_SCALES,   MUT_RED2_SCALES,
    MUT_IRIDESCENT_SCALES, MUT_PATTERNED_SCALES
};

static int _is_covering(mutation_type mut)
{
    for (unsigned i = 0; i < ARRAYSZ(_all_scales); ++i)
        if (_all_scales[i] == mut)
            return 1;

    return 0;
}

static int _body_covered()
{
    // Check how much of your body is covered by scales, etc.
    int covered = 0;

    if (you.species == SP_NAGA)
        covered++;

    if (player_genus(GENPC_DRACONIAN))
        covered += 3;


    for (unsigned i = 0; i < ARRAYSZ(_all_scales); ++i)
        covered += you.mutation[_all_scales[i]];

    return (covered);
}

// Return a string describing the mutation.
// If colour is true, also add the colour annotation.
std::string mutation_name(mutation_type mut, int level, bool colour)
{
    const bool fully_active = mutation_is_fully_active(mut);
    const bool fully_inactive =
        (!fully_active && _mutation_is_fully_inactive(mut));

    // level == -1 means default action of current level
    if (level == -1)
    {
        if (!fully_inactive)
            level = player_mutation_level(mut);
        else // give description of fully active mutation
            level = you.mutation[mut];
    }

    std::string result;
    bool innate = false;

    if (mut == MUT_CLAWS
        && (you.species == SP_TROLL || you.species == SP_GHOUL))
    {
        innate = true;
        if (you.species == SP_TROLL)
            result = troll_claw_descrip[level];
    }

    if ((mut == MUT_FAST || mut == MUT_BREATHE_POISON)
        && you.species == SP_NAGA)
    {
        innate = true;
        if (mut == MUT_FAST)
            result = naga_speed_descrip[level];
    }

    const mutation_def& mdef = get_mutation_def(mut);

    if (mut == MUT_STRONG || mut == MUT_CLEVER
        || mut == MUT_AGILE || mut == MUT_WEAK
        || mut == MUT_DOPEY || mut == MUT_CLUMSY)
    {
        std::ostringstream ostr;
        ostr << mdef.have[0] << level << ").";
        result = ostr.str();
    }
    else if (mut == MUT_ICEMAIL)
    {
        std::ostringstream ostr;

        int amt = ICEMAIL_MAX;
        amt -= you.duration[DUR_ICEMAIL_DEPLETED] * ICEMAIL_MAX / ICEMAIL_TIME;

        ostr << mdef.have[0] << amt << ").";
        result = ostr.str();
    }
    else if (result.empty() && level > 0)
        result = mdef.have[level - 1];

    if (fully_inactive || player_mutation_level(mut) < you.mutation[mut])
    {
        result = "(" + result;
        result += ")";
    }

    if (colour)
    {
        const char* colourname = "lightgrey"; // the default
        const bool permanent   = (you.demon_pow[mut] > 0);
        if (innate)
            colourname = (level > 0 ? "cyan" : "lightblue");
        else if (permanent)
        {
            const bool demonspawn = (you.species == SP_DEMONSPAWN);
            const bool extra = (you.mutation[mut] > you.demon_pow[mut]);

            if (fully_inactive)
                colourname = "darkgrey";
            else if (!fully_active)
                colourname = demonspawn ? "yellow"   : "blue";
            else if (extra)
                colourname = demonspawn ? "lightred" : "cyan";
            else
                colourname = demonspawn ? "red"      : "lightblue";
        }
        else if (fully_inactive)
            colourname = "darkgrey";

        // Build the result
        std::ostringstream ostr;
        ostr << '<' << colourname << '>' << result
             << "</" << colourname << ">";
        result = ostr.str();
    }

    return (result);
}

struct facet_def
{
    mutation_type muts[3];
    int tiers[3];
};

struct demon_mutation_info
{
    mutation_type mut;
    int tier;
    int facet;

    demon_mutation_info(mutation_type m, int t, int f)
        : mut(m), tier(t), facet(f) { }
};

static int ct_of_tier[] = { 0, 2, 3, 1 };

static const facet_def _demon_facets[] =
{
    { { MUT_CLAWS, MUT_CLAWS, MUT_CLAWS },
      { 2, 2, 2 } },
    { { MUT_HORNS, MUT_HORNS, MUT_HORNS },
      { 2, 2, 2 } },
    { { MUT_THROW_FLAMES, MUT_HEAT_RESISTANCE, MUT_HURL_HELLFIRE },
      { 3, 3, 3 } },
    { { MUT_THROW_FLAMES, MUT_HEAT_RESISTANCE, MUT_CONSERVE_SCROLLS },
      { 3, 3, 3 } },
    { { MUT_FAST, MUT_FAST, MUT_FAST },
      { 3, 3, 3 } },
    { { MUT_TELEPORT_AT_WILL, MUT_TELEPORT_AT_WILL, MUT_TELEPORT_AT_WILL },
      { 3, 3, 3 } },
    { { MUT_ROBUST, MUT_ROBUST, MUT_ROBUST },
      { 3, 3, 3 } },
    { { MUT_NEGATIVE_ENERGY_RESISTANCE, MUT_NEGATIVE_ENERGY_RESISTANCE,
          MUT_NEGATIVE_ENERGY_RESISTANCE },
      { 3, 3, 3 } },
    { { MUT_BLACK_SCALES, MUT_BLACK_SCALES, MUT_BLACK_SCALES },
      { 3, 3, 3 } },
    { { MUT_METALLIC_SCALES, MUT_METALLIC_SCALES, MUT_METALLIC_SCALES },
      { 3, 3, 3 } },
    { { MUT_RED2_SCALES, MUT_RED2_SCALES, MUT_RED2_SCALES },
      { 3, 3, 3 } },
    { { MUT_STOCHASTIC_TORMENT_RESISTANCE, MUT_STOCHASTIC_TORMENT_RESISTANCE,
          MUT_STOCHASTIC_TORMENT_RESISTANCE },
      { 3, 3, 3 } },
    { { MUT_REGENERATION, MUT_REGENERATION, MUT_REGENERATION },
      { 2, 2, 2 } },
    { { MUT_GREEN_SCALES, MUT_GREEN_SCALES, MUT_GREEN_SCALES },
      { 2, 2, 2 } },
    { { MUT_BLACK_SCALES, MUT_BLACK_SCALES, MUT_BLACK_SCALES },
      { 2, 2, 2 } },
    { { MUT_REPULSION_FIELD, MUT_REPULSION_FIELD, MUT_REPULSION_FIELD },
      { 2, 2, 2 } },
    { { MUT_MAGIC_RESISTANCE, MUT_MAGIC_RESISTANCE, MUT_MAGIC_RESISTANCE },
      { 2, 2, 2 } },
    { { MUT_BREATHE_FLAMES, MUT_BREATHE_FLAMES, MUT_BREATHE_FLAMES },
      { 2, 2, 2 } },
    { { MUT_NACREOUS_SCALES, MUT_NACREOUS_SCALES, MUT_NACREOUS_SCALES },
      { 2, 2, 2 } },
    { { MUT_GREY2_SCALES, MUT_GREY2_SCALES, MUT_GREY2_SCALES },
      { 2, 2, 2 } },
    { { MUT_BLACK2_SCALES, MUT_BLACK2_SCALES, MUT_BLACK2_SCALES },
      { 2, 2, 2 } },
    { { MUT_WHITE_SCALES, MUT_WHITE_SCALES, MUT_WHITE_SCALES },
      { 2, 2, 2 } },
    { { MUT_YELLOW_SCALES, MUT_YELLOW_SCALES, MUT_YELLOW_SCALES },
      { 2, 2, 2 } },
    { { MUT_BROWN_SCALES, MUT_BROWN_SCALES, MUT_BROWN_SCALES },
      { 2, 2, 2 } },
    { { MUT_PURPLE_SCALES, MUT_PURPLE_SCALES, MUT_PURPLE_SCALES },
      { 2, 2, 2 } },
    { { MUT_INDIGO_SCALES, MUT_INDIGO_SCALES, MUT_INDIGO_SCALES },
      { 2, 2, 2 } },
    { { MUT_PASSIVE_MAPPING, MUT_PASSIVE_MAPPING, MUT_PASSIVE_MAPPING },
      { 2, 2, 2 } },
    { { MUT_COLD_RESISTANCE, MUT_CONSERVE_POTIONS, MUT_ICEMAIL },
      { 2, 2, 2 } },
    { { MUT_COLD_RESISTANCE, MUT_CONSERVE_POTIONS, MUT_PASSIVE_FREEZE },
      { 2, 2, 2 } },
    { { MUT_TOUGH_SKIN, MUT_TOUGH_SKIN, MUT_TOUGH_SKIN },
      { 1, 1, 1 } },
    { { MUT_GREY_SCALES, MUT_GREY_SCALES, MUT_GREY_SCALES },
      { 1, 1, 1 } },
    { { MUT_BONEY_PLATES, MUT_BONEY_PLATES, MUT_BONEY_PLATES },
      { 1, 1, 1 } },
    { { MUT_SLOW_METABOLISM, MUT_SLOW_METABOLISM, MUT_SLOW_METABOLISM },
      { 1, 1, 1 } },
    { { MUT_SPIT_POISON, MUT_SPIT_POISON, MUT_SPIT_POISON },
      { 1, 1, 1 } },
    { { MUT_BLINK, MUT_BLINK, MUT_BLINK },
      { 1, 1, 1 } },
    { { MUT_SHAGGY_FUR, MUT_SHAGGY_FUR, MUT_SHAGGY_FUR },
      { 1, 1, 1 } },
    { { MUT_HIGH_MAGIC, MUT_HIGH_MAGIC, MUT_HIGH_MAGIC },
      { 1, 1, 1 } },
    { { MUT_RED_SCALES, MUT_RED_SCALES, MUT_RED_SCALES },
      { 1, 1, 1 } },
    { { MUT_BLUE_SCALES, MUT_BLUE_SCALES, MUT_BLUE_SCALES },
      { 1, 1, 1 } },
    { { MUT_SPECKLED_SCALES, MUT_SPECKLED_SCALES, MUT_SPECKLED_SCALES },
      { 1, 1, 1 } },
    { { MUT_ORANGE_SCALES, MUT_ORANGE_SCALES, MUT_ORANGE_SCALES },
      { 1, 1, 1 } },
    { { MUT_IRIDESCENT_SCALES, MUT_IRIDESCENT_SCALES, MUT_IRIDESCENT_SCALES },
      { 1, 1, 1 } },
    { { MUT_PATTERNED_SCALES, MUT_PATTERNED_SCALES, MUT_PATTERNED_SCALES },
      { 1, 1, 1 } },
};

static bool _works_at_tier(const facet_def& facet, int tier)
{
    return facet.tiers[0] == tier
        || facet.tiers[1] == tier
        || facet.tiers[2] == tier;
}

static int _rank_for_tier(const facet_def& facet, int tier)
{
    int k;

    for (k = 0; k < 3 && facet.tiers[k] <= tier; ++k);

    return (k);
}

static std::vector<demon_mutation_info> _select_ds_mutations()
{
try_again:
    std::vector<demon_mutation_info> ret;

    ret.clear();
    int absfacet = 0;
    int scales = 0;
    int slow_dig = 0;
    int regen = 0;
    int slots_lost = 0;
    int breath_weapons = 0;

    std::set<const facet_def *> facets_used;

    for (int tier = ARRAYSZ(ct_of_tier) - 1; tier >= 0; --tier)
    {
        for (int nfacet = 0; nfacet < ct_of_tier[tier]; ++nfacet)
        {
            const facet_def* next_facet;

            do
            {
                next_facet = &RANDOM_ELEMENT(_demon_facets);
            }
            while (!_works_at_tier(*next_facet, tier)
                   || facets_used.find(next_facet) != facets_used.end());

            facets_used.insert(next_facet);

            for (int i = 0; i < _rank_for_tier(*next_facet, tier); ++i)
            {
                mutation_type m = next_facet->muts[i];

                ret.push_back(demon_mutation_info(m, next_facet->tiers[i],
                                                  absfacet));

                if (_is_covering(m))
                    ++scales;

                if (m == MUT_SLOW_METABOLISM)
                    slow_dig = 1;

                if (m == MUT_REGENERATION)
                    regen = 1;

                if (m == MUT_SPIT_POISON || m == MUT_BREATHE_POISON
                        || m == MUT_BREATHE_FLAMES)
                    breath_weapons++;

                if (m == MUT_CLAWS && i == 2 || m == MUT_HORNS && i == 0)
                    ++slots_lost;
            }

            ++absfacet;
        }
    }

    if (scales > 3)
        goto try_again;

    if (slots_lost != 1)
        goto try_again;

    if (slow_dig && regen)
        goto try_again;

    if (breath_weapons > 1)
        goto try_again;

    return ret;
}

static std::vector<mutation_type>
_order_ds_mutations(std::vector<demon_mutation_info> muts)
{
    std::vector<mutation_type> out;

    while (! muts.empty())
    {
        int first_tier = 99;

        for (unsigned i = 0; i < muts.size(); ++i)
        {
            first_tier = std::min(first_tier, muts[i].tier);
        }

        int ix;

        do
        {
            ix = random2(muts.size());
        }
        // Don't consider mutations from more than two tiers at a time
        while (muts[ix].tier >= first_tier + 2);

        // Don't reorder mutations within a facet
        for (int j = 0; j < ix; ++j)
        {
            if (muts[j].facet == muts[ix].facet)
            {
                ix = j;
                break;
            }
        }

        out.push_back(muts[ix].mut);
        muts.erase(muts.begin() + ix);
    }

    return out;
}

static std::vector<player::demon_trait>
_schedule_ds_mutations(std::vector<mutation_type> muts)
{
    std::list<mutation_type> muts_left(muts.begin(), muts.end());

    std::list<int> slots_left;

    std::vector<player::demon_trait> out;

    for (int level = 2; level <= 27; ++level)
    {
        int ct = coinflip() ? 2 : 1;

        for (int i = 0; i < ct; ++i)
            slots_left.push_back(level);
    }

    while (!muts_left.empty())
    {
        if (x_chance_in_y(muts_left.size(), slots_left.size()))
        {
            player::demon_trait dt;

            dt.level_gained = slots_left.front();
            dt.mutation     = muts_left.front();

#ifdef DEBUG_DIAGNOSTICS
            mprf(MSGCH_DIAGNOSTICS, "Demonspawn will gain %s at level %d",
                    get_mutation_def(dt.mutation).wizname, dt.level_gained);
#endif

            out.push_back(dt);

            muts_left.pop_front();
        }
        slots_left.pop_front();
    }

    return out;
}

void roll_demonspawn_mutations()
{
    you.demonic_traits = _schedule_ds_mutations(
                         _order_ds_mutations(
                         _select_ds_mutations()));
}

bool perma_mutate(mutation_type which_mut, int how_much)
{
    int levels = 0;

    how_much = std::min(static_cast<short>(how_much),
                        mutation_defs[which_mut].levels);

    if (mutate(which_mut, false, true, false, false, true))
        levels++;

    if (how_much >= 2 && mutate(which_mut, false, true, false, false, true))
        levels++;

    if (how_much >= 3 && mutate(which_mut, false, true, false, false, true))
        levels++;

    you.demon_pow[which_mut] += levels;

    return (levels > 0);
}

int how_mutated(bool all, bool levels)
{
    int j = 0;

    for (int i = 0; i < NUM_MUTATIONS; ++i)
    {
        if (you.mutation[i])
        {
            if (!all && you.demon_pow[i] >= you.mutation[i])
                continue;

            if (levels)
            {
                // These allow for 14 levels.
                if (i == MUT_STRONG || i == MUT_CLEVER || i == MUT_AGILE
                    || i == MUT_WEAK || i == MUT_DOPEY || i == MUT_CLUMSY)
                {
                    j += (you.mutation[i] / 5 + 1);
                }
                else
                    j += you.mutation[i];
            }
            else
                j++;
        }
    }

    dprf("how_mutated(): all = %u, levels = %u, j = %d", all, levels, j);

    return (j);
}

bool give_bad_mutation(bool failMsg, bool force_mutation, bool non_fatal)
{
    const mutation_type bad_muts[] = {
        MUT_CARNIVOROUS,   MUT_HERBIVOROUS,   MUT_FAST_METABOLISM,
        MUT_WEAK,          MUT_DOPEY,
        MUT_CLUMSY,        MUT_TELEPORT,      MUT_DEFORMED,
        MUT_SCREAM,        MUT_DETERIORATION, MUT_BLURRY_VISION,
        MUT_FRAIL,         MUT_LOW_MAGIC
    };

    mutation_type mutat;

    do
        mutat = RANDOM_ELEMENT(bad_muts);
    while (non_fatal && !_accept_mutation(mutat, true, true));

    const bool result = mutate(mutat, failMsg, force_mutation);
    if (result)
        learned_something_new(TUT_YOU_MUTATED);

    return (result);
}