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



                                         


                   
 

                   


                   

                  
                  
                    
 
                     
                   
                  
                 

                    
                  
                     
                    
                    
                
                          
                  
                 
                    
                    
                     
                     
                  
                     
                    
                  
                  
                    


                     
                  
                    
                 
                   
                   
                   
                     
                     
                   
                    
                    

                    
                    
                     
                  
                  
                  
                    
                      
                  
                   
                     
                 
                  
                     
                
 





                           

                                                   

 
                                               
 

                                                             

 





                                                                

                         
                   
                 


      

                                                    

                 

                   







                                           
                        

                      

 

                                                                       
 
                                  
 
                                                                             
          
  
                                     
                                                                       
                                                                            
                                                           
                                                                             
                                                     
 
                               
                             
 
                                                                                
                                             

                                                   
                                                 
 
                            
                                                    
 
                                                   


                                                         
                                                                   
 

                                     
     

                                                    
         




                                                                        
             

                                                                             
                                                                   






                                                       

             
 

                                                                
         
                                                   
             







                                                                                   
 
                                      
 

                                                                                
                                                
 

                                                     
 

                                                
 
                                             
 

                                   
             
                                                  
                    

                                                        
      
                                                      
             

                                                        
                 






                                                                                   
                 




                                                                                    
                    

                                                                              

                                                                        
                                                                       
                                                                    
      
                 












                                                                                  
                 
             


         


                                  
                                                         
                        
     

                                      
         
                                        
                                     
 
                                   
                                                                    
             







                                                                 
 



                                                                               
 
                                         


                                        
             

                                                                        


                                           


                                        
             
         

                           
         

                                                                         
                                            
             

                                                                  
 



                                                         
                 



                                                                  
                                  



                                                                     
             
                                                                             
             
                             
                                                   










                                                                     
                                        





                                                                            
                                       
                  



                           
 
                      

                     
                                                                               


                                            
                                      


                                              
                    

                                                                         
 
                                                            

                                          
                                      







                                                 




                                                                               






                                                                        
                                                                          


                                                                            


                                                                      



             


                  
                                                 
 
                           
                                 
                                                                        

 
                                  
 

                                                                  

 
                                 



                                                                            
                         
 


                                                                     
                                                           
 
                                           

                                                     


                                                              
                                         
 
                                                                    

 




                                                            

                            


                                                               

 







                                           
                         
                                                               



                              
                                                   
 
 



                                                                    
 



                                                   
                           



                                           
                                          

                                                    
                                        


                                                         
                                                                            

                                                 
                
                                                        

         
 


                        
                                              


                              
                                                


                              
                                             


                              
                                              


                              
                                              

                              
                                                                          


                                                
                        
                                                


                              
                                                 


                              
                                             


                              
                                            


                              

                                                   




                              


                           
                                            





                                
                                 




                                  
                                                  



                              
                                               







                                         

                                                  
                                                    
                                                     


                                                   
                                                    
                                              
 





                                                                    
 





                                                                              
 

                                                                                
 





                                                                     
 


                                                                       

                                                                



                                

                         

 


                                                               
                      
 
                                                                              
                      
 
                 

 







                                                       










                                                                           




                                                                              



                                                                         
         
                          
         
 
                                                        

                           






                                                              
                                                   
 







                                                    
                                                       


                            




                                                               
 
                          
                                                           
                                                    




                                                      
 

                                                             


                                                         
 

                                                                            
                       
 
                                                  
                                                                     
                      
 
                                               
                                                             
                       
 





                                                                  
 





                                    
 

                                 


                                                                 
     
 
                  

 






                                         
                                             




                                                


                  


                                                                              
                      

                      
                                          
                                                           


















                                                                          
                                                                       
                                                                     
                                                                     
 


                                 





                                                 


                                                     





                  




                                                             





                  
                                         
                                                                    

                                                                        



                  
                                          
                                                                     

                                                                         





                       
                                         




                                                                     
                                          






                                                                      
                                         




                                                                     
                                          






                                                                      
                                                              


              
                                 
                                                            
                                                                        









                  
                                                                           
                                                                     

                                                                               

                                                  


                                 





                   























                                                           



                                                         
                                  



















                                                                           












































                                                                               
 
























                                                   



                                                                        
 
                          

                      

                                              
 

                                                            

 
                                   
 

                               

               
            
                                                                      
 
                
                                                  
 
                           

                                                 
                                      
     
                                                                  


              
 
 
                  





                                  
             
                                                         
 
             


                                                    
                  
     
 
                                         
                                                              

                                   
                 
     
 
                          
                                                       
 

                                                                      
                         
 
                                      
                                                   

                                  

                                                     
                   


                                                             
                                                
                                                       
                    

                                                    
                     

     

                                                                      
                                                    
     
                                                          

                
 
                                                              
                                                
                  
 






                            
                             
                 
 
                                
                                                                            
 
                                                              

                    
                                                                           
                                       
                                            

                 
                                                                  

                         
                                        

                        
                                                                       


                                                                            
                                                          
                         

     



























                                  
                                                             
 
                                                           
                        
                                                                

                     

            

                                                                  



                                                            

                                  
                                                                  
                          


        
                                                                  
                        
     
 
                           
                                                       

                                                                     

               
                                              










































                                                                            
                                          
 
                                                   
                                                         
                                                                   

 
                                                                             
                                                          


               

              
                                        

                                                                             
 
                       
                                                            
 
                 
                                                              
 



                                                                     
 


                                                                            
 
                           
                                                   
     

               
                                
             

                 
                                                     
 

              
     




                                              
 








                                                   

         
 







                    












                                                                       
                                                          


               
             
     

                                              
                 
 














                                                   

         







                                                     
 

              
                                       

                                                                             
 
                        
                                                             
 

                                                              
 



                                                                        
 




                                                                            
                                                   
     
 
                 
                                                     
 
                                                   
             
 







                    
                                               



                                        
                                                                           

                     
                                                                

     







                                                                
 


                                                                         
























                                                                             
                                                                 


               



                                         
 


                                                              
 




                                                            
 








                                                                      
                                                          
     





                                                                       
                                                    

             
                
 
 
                                                                   
 



                                                                            

 
                                       
 

                                                              

                                                                                                        

 
                                                        
                                                                             
                                                            


               



                                                                          
 

                                                                
 

                                                                              
 


                                                                      
 
                           
                                                     
     
 
                 
                                                       
 



                                                                    
             
     










                                                   


                  

     

               

                
 
 















                                                                         
                       


               
             





















                                                                
                      





                                                                          
                      








                                               
                                      

             



                                  


              
                      











                                               
                       


               
             

                                                


              
                     


               
             

                                              


              
                      


               
             

                                                      
                             





                                                                
                      


               
             








                                                                
                      


               
             








                                                                
                        


               
             

                                                 
                                           
             



              
                   
 
               






                                                 

                                                                    
                                                           


               


                                                                       






                                  
                  


                            
                  

                         
                  




                  

                                                                      
                                                               







                                                       
                 
                  






                       
 



                                                   
 

                                                                      
 

                                                                          
 
                       
                                                              
     
 
                           
                                                                
 
                         



                
                                                                 










                                                                       
                       








                                                                  
                          
                                                              
                   
                                      
                                                 

                                                                
 
                 


                                                             
                          
                                                                            
 
                                                                 




                                                                   
                                                               
                                                        
 
                                                                 




                                                                        































                                                                         

                               
 
                                








                                               






              
                                                            
 




                                                           

                                                                          
                                   




                                                                               















                                                                       
                                                                 


















                                                     

                                            
                                             






                            

                                  



                                   






                       
                                         
 
                                             
                      
 


                                                                  




                                                                               
 
                                           
 



















                                                                             
                                                           
                                       
 


                                                                




                                                                     
                                                          

                             

                                                                       


                                   
                                                                    
                    
                                                            


                                           
                                                                           


                                
                                                                       

                                                   
                                    



                                                                    
                                                                    






                                                  
                                       


































                                                                           
                                  

                
                                                                        





                                                      



                                                          
                                         
 
                       
                                                                        
 

                                                                   






                                                                  
 



              





                                                 









                                                                               




                           
                                             
                                        









                                                                  





                            














                                                                         
                                    
                                                                    













                                               
                                                              









                                                                     
                                      















                                                                        

                                                                   







                                               






                     


                                               
 



              
                                       
 

                
      
 
                              


                

                                                           
                                                   
 






                                     


                                                     
                                                    
 
             
                                                     
 
                                                       
                                                                    

                                                          
 


                                                            




                            
 
                                       
     
                                                             
                                                  
 
                                                             
 
                                                            


                                                           
 







                                                    
     


                                           



                                                           



                                                     

                                                            

         
 
                                        






                                                                            
 
                                      
 
 
                                    
 
               
 
                                                                    
 

                
 
                                           
 




                                                                           
                                            

                                                                          




                                 
        
                     



                       

                                                               


                   


                                    
                                          



                                     
                                                                      



                                       
                              

                                                                     
                                                        






                                                                        
                                                           


                                         

                                                
                                                                          
                                                  
         





                                                
         
                                                                     

                                                  

     




                                                                          



                                                    

                                                              

     

                        
 
                                                     
                                                           
 

                               

                                                               
 




                                                                             
                                            
     
                              

                                                                    
         
                                           
               
                                                    
                                               

                                                                             
      

         



                                  
 
 

                                                                  
 


                          

                                                                
 

                                                      
 
                                      
 
                                                                  
                                       


                                     









                                                                        
                                                                    


                                         

                   
 




                                                           
 
 
                                               
 


                                                              

                                                                
                                 

                                    
                                                                    
     

                               
                                                 
         
                                         









                                                                            


                                         


                          





                                                                      
 


                                                     
         
                                      
                                                       

                           
 


                                                           
                                                                       

                            
         
                                                 
         
                              
             

                                                                 
             
                
             
                                                                       
                             
             
 

                                                           

                                


                                                           

                         
                             
                                       
                                  
                                                                        
                
                                                                        
 
                                           

                               
 



                                                
                                                                     

                      
                             







                                                


                                                                        


                      


                                              
 





                                                
                                                                           









                                                


                                                                        


                      
                                   






                                                
                                                                       

                      








                                                                    
 





                                                         







                                                                         

                                                
                                                                        
 
                                             

                                




                                                


                                                                        




















                                                   
                                                                       




                                                                             

                                                                           



                                                                           

                                                                             


                      


                                              
                                                       
                     

                                                                             


                        
                                                                        
                                                  
                     
                 


                                                                              




                                                                             

                                                           
                 
                      
 
                         


                                                
                                                                     



                                                                          
                                                   


                      


                            






                                                
                                                                       

                      
                         
                            
 

                                                                       




                                    
                                     
                                     



                                      
                                   

                                              


                                       












                                                                               
                                                                
                                                               
                              
                                             

                                                                                  

                                            

                                                               

                                             

                                                                                

                                              

                                                                                   

                                           

                                                                              
                              
                                           

                                


                              




                                    
                                               



                                          
                                                             

                                            
                                                             
                              
                            


                              




                                                          






                                                                            
                                                   
                                                                     





                                              


                                       

                                                          






                                                
 






                                                                            
                                                   




                                                                              
                                                                     





                                                

                                                                        
                                              













                                                     
                                                
                                                                     






















                                                     


                                                                        















                                                     

                                                                        
                                              



                               
                 
















                                                                              

                 
                                                
                                                                     













                                                     
                                                                       









                                                
                                                                     

                                              



                                                              








                                                                               
                                                                     
                      
 
                    
                      



                                          

                              
 
                                      
 
                                                 
                                                         
 









                                                                            
 
                               





                                                 

                              
                                             

     
                                                
 

                                                                      
                                                            
                            
 
 
                                                                          



                                                                          
                               





                                                                       
                                                     









                                                     
                                                               


                          





                                                         
                            
                                                                     
                                                    
                                                             
                                             
                                                                 
                 
                                                             
                 
                                                        
                                                             
                    
                                                             
                      



                                                                     








                                                         

                                           
 
                       






                                                
                                                    
 
                                                                      






                                                                   
                                                           


                          


                                    
                                  

                      
                                              
 
                       
                      
                            



                                                             
                                                            

                                                  
                                               
                                        
 
                                                                       







                                                                         
                                   

                     
 
 
                                 
 


                                                                   
                                           




                                                                               
                
     
                                  
 

                      
                                                                            









                                                                    

                 
                                                               
                                            



                   
                                                                   
                                                



                   
                                                                
                                             

                   
     
 
 































                                                                              




                                                                   




                        









                                                       
 

                                      
 
                

 








                                                                            

                                   
                                    










                                                       
                         

                              
                                    



                                             
                     
                                     



                                                 
                                     

                             
                                     

            
                   





















                                                                        
                          
 
                                                                           
                                      
                           
                               
                                           



                                                                         
        
                              
 

                                            
     
                          
                                                               
                               
                                                         
            
                                                               
     
 



                                                              
 

                                 

                                                  


                                                                    
                                                 




                                                                    
                                                                      
                                                 





                                                                         



                                                                    
                                                 















                                                                      
         
 

                            
                                                                      


                             
 
                                             
     
                         
 
                                                                






























                                                   


                                              

                          

     



                                               






                                                                    
 
                               







                                                                         
 
                                        
                                        
 
                                        
                                                 
 

                                      



                                                                            

     
                                            
                                                                    
 

                                                                 
 
                                                                            

                                 
                                
 

                                              
     






                                                                       
     
        
     

                                                                          
                                                              


                                                                       
     
 
                                                                     
 
                                                                   
 

                                                                    

                                            
                                            

                                   
                                          
 

                                                               
 
                                                                                
 
                                             
                                                                              
 
                                                                  
 
                                    
                                            

                                        
                                      
 
                                                              
 
                       
                                 
 
                                                     
                     
                                   
 
                                                  
                        
                                  
                        
                                  
                     
                               
 
                                    
                                  
 
                                                          
                                                               
                                    
                                        
                                     
                                                                        
 
                                
                               



                                     
 
                                        
                                          
 
                                         
                                           
 
                      
                                                      
 
                       







                                                         
 
                                 
                                      
 
                                    
     
                                    


                                                               
                                                             



                    

                                        
                                   

                                              
                                              


                                               
                                                             
 
                                      
                                       
 
                                          
     

                                      
                                              

                                                                               
                                                                  

     
                                     
     
                                                  

                                                                               
                                                            
     
 
                                                                          
 
                                       
                                       
 
                                       
                                                      
                                                          





                                          

                                  










                                                                 
                                    
                                            

                     
                                                     


                               
                                                                 





                                                                








                                                                               
                                                                           
                                                                            
  
 
                       
                                   
                                                                            
 
                                                         
                                                                
                                         
 
 





                                                                    
                                                     
 

                                                                        
                                                          



                                 


                                                                  
                                                                   

 

                                                                 
                                                   
 
                   
     


                                                   
                                                                         




                                                                    


                                                                         

                                                                         

                                                         
            
                       
     
 
 

                                                          
                                        
                      
 
                                        
                       
 

                                                                           
 
 
                                                 
 
                                               
                  
                                                  
                  
                    
     




                         
                     
                      







                           
                         





                           
                        
                      




                        
     
 






                                 




                                                                          
                                                                       
                                                                           

                                                                            
                                                                      
                                                                      





















































                                        
                                                
         
            






                                                           
                                                              
 

                                              
                                                   





                                                           
                                             

                                                            



                                                            
                                           

                                                            

     
                                                                          
 
                 
 
 

                                                                          
                                                                


                                               
                                    

                     

                                    






                                                                     
 

                                                      
                                                                                 
                              
                                                                                    



                                 
                                                                         



                                       
                                          

                     
                                                       




                                                        
 
 


                                                                      
                                                                     
 

                   
                                               
     
                                    

                     

                                    
                                                             


                                                                     
                                          

                     
                                                                   


                                                              
                     
         
 
                                                                         


                    
 
 

                                                                      
 

                               

                              
                            
 
                                            
 



                                   
                                   


                                             
                                  

                                  
                                                                



                       
                                     
                                         

                                            
                                                        

              
                           
                                  
                                      

                                                
                                                      
              
 







                                                     

              

     
                                     
                                                                             
 
                            
                            




                                    
                                                        
            
                                                                           
     


                                  


                                  
 
                                                                      
                                                           












                                                                             
                                          
















                                                                            
                            



























                                                 
                                                     
 

                               





                                    


                    



                                                                               
                                                             

                          
 

                                 


                        

                               




                                
                                                     
 
                                   
                                                   

                                                                    
                                                             
     
 
                                                                            
                                   
 


                                              

                               
                                            




                                                              

                                        
                       

     

                  
 
                                                                  
 

                               




                                             
                                                      

                                        


                                                           


                  
 

            

                                        

     
                
 




                                                                      

                               


                    

                                                               


                                
                            



                                                


                                                              
                                                                            
                                   
 


                                                                      
                                                             

                                      

                               


                    

                                           







                              


                                          
                                 
 
 
                        



                           


                                        
                                 

 
                               







                                           
                                 

 
                   



                                
                        



                                     
                                   






                              
                                                   
                                 






                              
                                                   
                                 






                                      
                                                             
                                   






                                      
                                                             
                                   

 
                                                     

                                          

                               




                                          


                                                               




                                                                      

                               











                                            
                
                                                       
 


                                                               




                                                                      

                               




                                                      
                                                           





                                                   
 
                                                                            

                                                                 
 


                                                               
 










                                                                           
                               
                  























                                                   



                                                     




                  










                                                                               
                                                         











                                                                           


                                                     









                                    
                             
 




                                             
                   
                  
                   
                  
                   
                 
                   
 
               
 
 




                                                                      

                               


                                                
                                      


                                               


                                                              
                                                            
                                                        


      











                                                   
                                          
 

                                                            
     
                          
         



                                                                      




                                                                          







                                                           
         
     
                                    



                                                               


                                              





                                                                           
     

                                                                              
 


                    


                                           










                                                                             

 
                                                
 

                               





                                                  












                                                                    



                                                 
                                                








                                                 
 

                                                              




                  
                                                     
 

                               
                          
 



                                                    
                              
     
                               
 






                                                                

                                                        
         
 
                                                            
     

                        


                                          
 

                               
                                                         
                       
 

                                                      
 

                                         
 
                                                
     
                                               
                                           
 
                                              
     
 
                  

 

                        
                                                               

                                                             
                                       
                                           


                      





















                                                                   
                               
                                                        

                                                       






                                                                      
                                     
 
                    

               
                                                      
                                          
 
                                        
                                        




                                                                 
     

 



                               
                          

                       






                                                                       
















                                      

                              

                               
                                                 


                                                          
                                                          






                                                             
                                 
 
                                                         
     
                                           




                                                  








                                                             

                                                                               




                                       
                                                                   
                                                 
 

                             

                                                                          
             




                                                      


                                             


                                            
                           
 

                               
                   
                       
 


                                                                     




                                                                        
                                                             
                                                        
        
     
                                        
                                                
            
                                                               
 
                                                          
                                                 
     
 
                  

 
                               
 



                                                
     
                                                            
                                        
     
                                                 

                                                           
                                   


     
                            
 

                               
                   






                                                                      
 


                                                                           
                             
 
                                     
                                           
                                                                  
                                                                 

        
                                                                        
                                                         

     
                                                       
                                                    

                  

 
                                
 



                                                 
     

                                              
                                         
 


                                                                        
         
                                                                            
                           
                                                          

         
                                                       
     
                                                            
                                    


     
                                  

                        
     
                                                                   
                                                        
                                           
                                               




                          


                             
 
                                                      
                                            
                                        
                                                 
                                                                 
         
                                              
         
 
                             



                                                                 
                                 

                   

                                                             


                                                                          
                     
         
     
 
                    

 
                               

                   

                                

                    


                                                     



                                                 
                                                         

                           
                                            

                



                                                         
 
                      
                                               



                                                   
                                                   

                                                   

                    

 



                          


                                     


                                                     



                                                     
                                                             

                               
                                                




                                                 
                    

 
                          


                   


                                  


                                                     



                                                  
                                                          

                            
                                             

                






                                                        



                                                      
 



                                                                            
 
                      
                                               


                                          
                                          

                                          

                    

 
                                 
 
                 
     
                                                        
                                                   
                                                   
            
                                                



                 

                                                                      












                                                         

 
                
                 
 
           
                                                                 
 
 
                                   
                 

           


                                           
                  
                            

                                           
                                  

 
                                      




                                           

                                           


                  
                         
                            

                                  


 


                               


                                    
                          
 

                                                          
     
                   
      
 
                   
 
                     
                        

                           

                        
                                 
 
                        


                        
                        
 
                            
 
                    
                    

                              
                              
                           

                                  






                         
                         


                                   
 
                        

                               

                                    

                                    


                                                  
                     



                                   







                                           
 
                         

                         


                          
 


                    
                          
 

                                  
                         
 
                         
 


                       
 
                   
 
                                
 

                                                
 

                      
 
                           
 
                         
 
                                       
 



                                      
 
                           
                           
 
                      
                           
                            
 
                                       
                       
 
                     

                       
















                                                        
 

                                    
                           

      

                        
                                 


                                                               

                            



                          

 
                                                               
 

                                      
                                            



                                      
                                           
               


                             
                   

 
                                                                   
 

                                                                
 
 
                                                
 



                                                           
 


                                               

                                             

               
                         

      
                      

 
                 
 
                 
                    
 
 

                                  
                                      



                             
                                              
                                                



                             


                                                                   

 










                                                                    




                                    




                              
                                              









                                

                                   
                                                                   

 

                                
                                                          

 
                                                                   
 
                                

 
                                                                  
 


                                            
     


                                                                        
     

 
                                        
 



                                          
 
                                           


















                                    

                                 
                              

                      
                                                                
                      
 












                                                               

                        

                        
                                          
                                                      
                       
                           
                            
                          
                          
                            


     

                            
                                     

                  
                                           
                                                                
                               
                                
                               






                             
                                     
 
                  
     
                                       
                                             
     
                                           


















                                               
                      
                                                                
                                        
                  
 







                  

                                                                           




                                               
                                            




                                                
                                  

 





                                                               
                                           






                                                                          
                                                                       







                                                                 





                                                                      
     
                       
     


                      
                                                      
                           

     


                  
                                                             

                          


                                      
                                  

 
                                                                 



                               
                                                         



                




                                                      
                                                 


     




                                                            















                                                                  
                                                            
                         





                                                   
                                          



                                       
                                                                 














                                          









                                                                 
                                                                  
                           
                                       
                              
                                       
                                    






                             
                               
 
                         

 




                          




                                        






                                                                     









                                                                

                                         
                                 

                      
                                                      













                                                                          
                                                                   

                                                               
                                                               
                                     





                                                                   
 
                                 

                      
                                                                           
                           





                    

                                    

                               

                                               
                                                             
                                                
                                       

     


                                                                              




                          
                                                      
 
                                      

 

                                   
                                   

 
                                                                
 
                                               
 
                  






                                                       
                                


                                                       


                                                       
 
                 


                                                   



                                                       

                                                                        


                                                                        
 



                                                      














                                                                                
                                                               

















                                                                                    


                  




                                     
                                                            



                                         
                                           
 

                               

                       

 

                                                          





                                                  
                                                                               
                                                                  



                                                                          



                                                 



                       




                                    




                              
                                     

 

                                
                                        

 

                             
                                

 

                           
                                  

 




                                     






                                               
                                                   
                          
                      
 
                                                   



                                                  
                            

 
                                               
 

                                       
                    

                                


















                                                        





















































































































































































                                                                            

 
                                                                      
 


                                                         
                                
                                              
                                                                 

 
                                           

                              
                                                                

 
                                      



                           


                                 


                        






                                                     







                              

                              
                                      

 

                            
                                





                              

 

                               


                                 
                   

 

                                                              

                                            




                                         

                                                            
 











                                                                             

 




                               




                                






                               
                                          

 





                                                                            

                              












                                                                 

 




                                 

                               

                                                                       




                   

 




                                       
                                                        
 


                            
                  

                    
                  




                   




                                       

                               
                                  

 



                             
                          


                           
                                        

              
                                        






                       
                                        

                 
                                        


                             
                                        

                     
                                        












                                                                            
                                            




                                                           
                                                          


                  
                                                  


                         
                                       




                






                                          
                

 
                                       
 

                                                      
     
                        

                             
     
                                                     


                                                                        

                         

 

                                         



                                                                 
                                                                           
                                                   



                                     
                                                               
                                                                          

 

                                 
                                             

                                                              



                                     
                                                                          



                                

                                      
 



                          
                                                           
                          
 




                            
                                             








                                                         
                                              
 
                                        

 
                                                                   
 

                               
            
                                                         



                       

                                                                   
 
                                   
                                      
     
                                                                   
                                   
                                                           
     



                               
                                                       
     





                                                                                
                    

 
                                                              
 







                                                                          

 
                                                                   
 

                               
                                      
                       
 
                      




                                  






                                                               
                      
                          
 
                           



                                                                     
                                                             
 
                                


                                               

                         
                                  

                  

 
                                                       
 
                           

 
                                         



                        
                                          
 

                               



                                                                          
                                            
 

                                                        
 
                          

                                                                       
 

                                        

 
                                         
 

                               


                                                                          
                          

                                         

                                                     



                                                              
                                                   

 
                                           
 
                       
 
 
                                            
 
                   
     
                                                      
                                                          
                       
 




                                                               
                       
         

     



                                                                 
 
                                              

 
                                                    
 
                                                             
 




                              




                                            
 
                               
 

                               
                                     
                       
 






                                                                
                         
 
                                       
                                       
                                       
 
                                        
                  
 
 












                                                                  
                                              













                                                             

                                      
                                     



                              
                                               
 
 




                                  
                                                  
 


                          
                       
                                                     
 
                                                                



                                       

 
                                             
 
                                                                 

                                                

 


                                  
                                   
     
                                       



                                              
                                                                     




                                               
                                                                   


     

                               
                  



                                      
                      
                       
 


                                                     

 

                              

                                                                


                       
 
                                                          
                                                     
                                                     
                                                    





                       
                     
 

                               
                      
                       
 
                         

                                      
         
                                                   
                          
         
     
 
                                 
 




                                                              
 




                             
                                             
 

                               
                                        
                           

                  
     
                                
                                                                


                                                                    
                                                        


     

                                       
                         

                             
                                
                    

 










                                            






                                                            


                                        
                    

 

                              
                      
 
 

                           
                                 

 
                               
 
                                       

 
                                          
 
                                                             

 




                                 
                           
 

                               
                         

                                            
               
     

                            
 
                 
                         

                                                                      
                                                  

 
                                            



                               
               



                            
                         

                                      
                                                         

 

                    

                               

                            
                      



                                          
                                                       


                


























                                                                             
                                                 




                                                                             

                                                                       
                  

                                                 



                                                                   



                                                             


                                                                       

                                                                          
 


                                                                   

















                                               
 
                                
                                    












































































                                                                

















                                                                    


                                                                                

























                                                                          


                                                                          
                     
         




                                                        


                                                                              
                     
         




                                           
 








                                                                  
                                                 
     
                                 













                                    
                                                  
 
                                              
                          







                                         










                                               







                                
                           







                                 
                                  
                      
                                                   

     


















                                                                     
/*
 *  File:       player.cc
 *  Summary:    Player related functions.
 *  Written by: Linley Henzell
 */

#include "AppHdr.h"

#include "player.h"

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

#include <sstream>
#include <algorithm>

#include "artefact.h"
#include "branch.h"
#include "cloud.h"
#include "clua.h"
#include "coord.h"
#include "coordit.h"
#include "delay.h"
#include "dgnevent.h"
#include "directn.h"
#include "effects.h"
#include "env.h"
#include "map_knowledge.h"
#include "fight.h"
#include "food.h"
#include "godabil.h"
#include "goditem.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
#include "item_use.h"
#include "it_use2.h"
#include "kills.h"
#include "macro.h"
#include "message.h"
#include "misc.h"
#include "mon-util.h"
#include "mutation.h"
#include "notes.h"
#include "options.h"
#include "ouch.h"
#include "output.h"
#include "quiver.h"
#include "random.h"
#include "religion.h"
#include "shopping.h"
#include "skills.h"
#include "skills2.h"
#include "species.h"
#include "spells1.h"
#include "spells3.h"
#include "spells4.h"
#include "spl-util.h"
#include "state.h"
#include "stuff.h"
#include "areas.h"
#include "terrain.h"
#include "transform.h"
#include "traps.h"
#include "travel.h"
#include "tutorial.h"
#include "view.h"
#include "shout.h"
#include "viewgeom.h"
#include "xom.h"

#ifdef USE_TILE
#include "tiles.h"
#include "tiledef-player.h"

dolls_data::dolls_data()
{
    parts = new int[TILEP_PART_MAX];
    memset(parts, 0, TILEP_PART_MAX * sizeof(int));
}

dolls_data::dolls_data(const dolls_data& _orig)
{
    parts = new int[TILEP_PART_MAX];
    memcpy(parts, _orig.parts, TILEP_PART_MAX * sizeof(int));
}

const dolls_data& dolls_data::operator=(const dolls_data& other)
{
    memcpy(parts, other.parts, TILEP_PART_MAX * sizeof(int));
    return (*this);
}

dolls_data::~dolls_data()
{
    delete[] parts;
    parts = NULL;
}
#endif

std::string pronoun_you(description_level_type desc)
{
    switch (desc)
    {
    case DESC_NONE:
        return "";
    case DESC_CAP_A: case DESC_CAP_THE:
        return "You";
    case DESC_NOCAP_A: case DESC_NOCAP_THE:
    default:
        return "you";
    case DESC_CAP_YOUR:
        return "Your";
    case DESC_NOCAP_YOUR:
    case DESC_NOCAP_ITS:
        return "your";
    }
}

// Contains functions which return various player state vars, and other
// stuff related to the player.

static void _attribute_increase();

// Use this function whenever the player enters (or lands and thus re-enters)
// a grid.
//
// stepped     - normal walking moves
// allow_shift - allowed to scramble in any direction out of lava/water
// force       - ignore safety checks, move must happen (traps, lava/water).
// swapping    - player is swapping with a monster at (x,y)
bool move_player_to_grid( const coord_def& p, bool stepped, bool allow_shift,
                          bool force, bool swapping )
{
    ASSERT(!crawl_state.arena);
    ASSERT( in_bounds( p ) );

    // assuming that entering the same square means coming from above (levitate)
    const bool from_above = (you.pos() == p);
    const dungeon_feature_type old_grid =
        (from_above) ? DNGN_FLOOR : grd(you.pos());
    const dungeon_feature_type new_grid = grd(p);

    // Really must be clear.
    ASSERT( you.can_pass_through_feat( new_grid ) );

    // Better not be an unsubmerged monster either.
    ASSERT(swapping && monster_at(p)
           || !swapping && (!monster_at(p)
                            || monster_at(p)->submerged()
                            || fedhas_passthrough(monster_at(p))));

    // Don't prompt if force is true.
    if (!force)
    {
        const int cloud = env.cgrid(p);
        if (cloud != EMPTY_CLOUD && !you.confused())
        {
            const cloud_type ctype = env.cloud[ cloud ].type;
            // Don't prompt if already in a cloud of the same type.
            if (is_damaging_cloud(ctype, true)
                && (env.cgrid(you.pos()) == EMPTY_CLOUD
                    || ctype != env.cloud[ env.cgrid(you.pos()) ].type))
            {
                std::string prompt = make_stringf(
                                        "Really step into that cloud of %s?",
                                        cloud_name(cloud).c_str());

                if (!yesno(prompt.c_str(), false, 'n'))
                {
                    canned_msg(MSG_OK);
                    you.turn_is_over = false;
                    return (false);
                }
            }
        }

        // If we're walking along, give a chance to avoid traps.
        if (stepped && !you.confused())
        {
            if (new_grid == DNGN_UNDISCOVERED_TRAP)
            {
                const int skill = 4 + you.skills[SK_TRAPS_DOORS]
                                    + player_mutation_level(MUT_ACUTE_VISION)
                                    - 2 * player_mutation_level(MUT_BLURRY_VISION);

                if (random2(skill) > 6)
                {
                    if (trap_def* ptrap = find_trap(p))
                        ptrap->reveal();

                    viewwindow(false);

                    mprf(MSGCH_WARN,
                         "Wait a moment, %s! Do you really want to step there?",
                         you.your_name.c_str());

                    if (!you.running.is_any_travel())
                        more();

                    exercise(SK_TRAPS_DOORS, 3);
                    print_stats();

                    you.turn_is_over = false;

                    return (false);
                }
            }
            else if (new_grid == DNGN_TRAP_MAGICAL
#ifdef CLUA_BINDINGS
                     || new_grid == DNGN_TRAP_MECHANICAL
                        && Options.trap_prompt
#endif
                     || new_grid == DNGN_TRAP_NATURAL)
            {
                const trap_type type = get_trap_type(p);
                if (type == TRAP_ZOT)
                {
                    if (!yes_or_no("Do you really want to step into the Zot trap"))
                    {
                        canned_msg(MSG_OK);
                        stop_running();
                        you.turn_is_over = false;
                        return (false);
                    }
                }
                else if (new_grid != DNGN_TRAP_MAGICAL && you.airborne())
                {
                    // No prompt (shaft and mechanical traps ineffective, if flying)
                }
                else
#ifdef CLUA_BINDINGS
                     // Prompt for any trap where you might not have enough hp
                     // as defined in init.txt (see trapwalk.lua)
                     if (type != TRAP_SHAFT // Known shafts aren't traps
                         && (new_grid != DNGN_TRAP_MECHANICAL
                         || !clua.callbooleanfn(false, "ch_cross_trap",
                                                "s", trap_name(p))))
#endif
                {
                    std::string prompt = make_stringf(
                        "Really step %s that %s?",
                        (type == TRAP_ALARM) ? "onto" : "into",
                        feature_description(new_grid, type,
                                            false, DESC_BASENAME, false).c_str());

                    if (!yesno(prompt.c_str(), true, 'n'))
                    {
                        canned_msg(MSG_OK);
                        stop_running();
                        you.turn_is_over = false;
                        return (false);
                    }
                }
            }
        }
    }

#ifdef USE_TILE
    bool need_doll_update = false;
#endif
    // Only consider terrain if player is not levitating.
    if (!you.airborne())
    {
        bool merfolk_check = false;
        if (you.species == SP_MERFOLK)
        {
            if (feat_is_water(new_grid))
                merfolk_check = true;

            // Safer water effects.
            if (feat_is_water(new_grid) && !feat_is_water(old_grid))
            {
                // Check for fatal stat loss due to transforming.
                // Also handles the warning message.
                if (!merfolk_change_is_safe())
                {
                    stop_running();
                    you.turn_is_over = false;
                    return (false);
                }

                if (stepped)
                    mpr("Your legs become a tail as you enter the water.");
                else
                    mpr("Your legs become a tail as you dive into the water.");

                merfolk_start_swimming();
#ifdef USE_TILE
                need_doll_update = true;
#endif
            }
            else if (!feat_is_water(new_grid) && feat_is_water(old_grid)
                     && !is_feat_dangerous(new_grid))
            {
                unmeld_one_equip(EQ_BOOTS);
                you.redraw_evasion = true;
#ifdef USE_TILE
                need_doll_update = true;
#endif
            }
        }

        if (!merfolk_check)
        {
            // XXX: at some point we're going to need to fix the swimming
            // code to handle burden states.
            if (is_feat_dangerous(new_grid))
            {
                // Lava and dangerous deep water (ie not merfolk).
                const coord_def entry = (stepped) ? you.pos() : p;

                if (stepped && !force && !you.confused())
                {
                    canned_msg(MSG_UNTHINKING_ACT);
                    return (false);
                }

                // Have to move now so fall_into_a_pool will work.
                you.moveto(p);

                viewwindow(false);

                // If true, we were shifted and so we're done.
                if (fall_into_a_pool( entry, allow_shift, new_grid ))
                    return (true);
            }
            else if (new_grid == DNGN_SHALLOW_WATER && !player_likes_water())
            {
                if (!stepped)
                    noisy(8, you.pos(), "Splash!");

                you.time_taken *= 13 + random2(8);
                you.time_taken /= 10;

                if (old_grid != DNGN_SHALLOW_WATER)
                {
                    mprf( "You %s the shallow water.",
                          (stepped ? "enter" : "fall into") );

                    mpr("Moving in this stuff is going to be slow.");

                    if (you.invisible())
                        mpr( "... and don't expect to remain undetected." );
                }
            }
        }
    }

    // Move the player to new location.
    you.moveto(p);
#ifdef USE_TILE
    if (need_doll_update)
        init_player_doll();
#endif

    viewwindow(false);

    // Other Effects:
    // Clouds -- do we need this? (always seems to double up with main.cc call)
    // if (is_cloud( you.x_pos, you.y_pos ))
    //     in_a_cloud();

    // Icy shield goes down over lava.
    if (new_grid == DNGN_LAVA)
        expose_player_to_element( BEAM_LAVA );

    // Traps go off.
    if (trap_def* ptrap = find_trap(you.pos()))
        ptrap->trigger(you, !stepped); // blinking makes it hard to evade

    command_type stair_dir = feat_stair_direction(new_grid);

    if (stepped && stair_dir != CMD_NO_CMD
        && new_grid != DNGN_ENTER_SHOP
        && you.duration[DUR_REPEL_STAIRS_MOVE])
    {
        int pct;
        if (you.duration[DUR_REPEL_STAIRS_CLIMB])
            pct = 29;
        else
            pct = 50;

        // When the effect is still strong, the chance to actually catch
        // a stair is smaller. (Assuming the duration starts out at 1000.)
        const int dur = std::max(0, you.duration[DUR_REPEL_STAIRS_MOVE] - 700);
        pct += dur/10;

        if (x_chance_in_y(pct, 100))
        {
            if (slide_feature_over(you.pos(), coord_def(-1, -1), false))
            {
                std::string stair_str =
                    feature_description(new_grid, NUM_TRAPS, false,
                                        DESC_CAP_THE, false);
                std::string prep = feat_preposition(new_grid, true, &you);

                mprf("%s slides away as you move %s it!", stair_str.c_str(),
                     prep.c_str());

                if (player_in_a_dangerous_place() && one_chance_in(5))
                    xom_is_stimulated(32);
            }
        }
    }

    return (true);
}

bool is_feat_dangerous(dungeon_feature_type grid)
{
    return (!you.airborne()
            && (grid == DNGN_LAVA
                || (grid == DNGN_DEEP_WATER && !player_likes_water())));
}

bool player_in_mappable_area(void)
{
    return (!testbits(env.level_flags, LFLAG_NOT_MAPPABLE)
            && !testbits(get_branch_flags(), BFLAG_NOT_MAPPABLE));
}

bool player_in_branch(int branch)
{
    return (you.level_type == LEVEL_DUNGEON && you.where_are_you == branch);
}

bool player_in_hell(void)
{
    // No real reason except to draw someone's attention here if they
    // mess with the branch enum.
    COMPILE_CHECK(BRANCH_FIRST_HELL == BRANCH_DIS, a);
    COMPILE_CHECK(BRANCH_LAST_HELL  == BRANCH_TARTARUS, b);

    return (you.level_type == LEVEL_DUNGEON
            && you.where_are_you >= BRANCH_FIRST_HELL
            && you.where_are_you <= BRANCH_LAST_HELL
            && you.where_are_you != BRANCH_VESTIBULE_OF_HELL);
}

bool player_likes_water(bool permanently)
{
    return (you.can_swim() || (!permanently && beogh_water_walk()));
}

bool player_in_bat_form()
{
    return (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT);
}

bool player_can_open_doors()
{
    // Bats and pigs can't open/close doors.
    return (you.attribute[ATTR_TRANSFORMATION] != TRAN_BAT
            && you.attribute[ATTR_TRANSFORMATION] != TRAN_PIG);
}

bool player_under_penance(void)
{
    if (you.religion != GOD_NO_GOD)
        return (you.penance[you.religion]);
    else
        return (false);
}

// TODO: get rid of this.
bool player_genus(genus_type which_genus, species_type species)
{
    if (species == SP_UNKNOWN)
        species = you.species;

    return (species_genus(species) == which_genus);
}

// If transform is true, compare with current transformation instead
// of (or in addition to) underlying species.
// (See mon-data.h for species/genus use.)
bool is_player_same_species(const int mon, bool transform)
{
    if (transform)
    {
        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        // Unique monsters.
        case TRAN_BAT:
            return (mon == MONS_GIANT_BAT);
        case TRAN_ICE_BEAST:
            return (mon == MONS_ICE_BEAST);
        // Compare with monster *species*.
        case TRAN_LICH:
            return (mons_species(mon) == MONS_LICH);
        // Compare with monster *genus*.
        case TRAN_SPIDER:
            return (mons_genus(mon) == MONS_WOLF_SPIDER);
        case TRAN_DRAGON:
            return (mons_genus(mon) == MONS_DRAGON); // Includes all drakes.
        case TRAN_PIG:
            return (mons_genus(mon) == MONS_HOG);
        default:
            break; // Check real (non-transformed) form.
        }
    }

    switch (you.species)
    {
        case SP_HUMAN:
            if (mons_genus(mon) == MONS_HUMAN)
                return (true);
            return (false);
        case SP_CENTAUR:
            if (mons_genus(mon) == MONS_CENTAUR)
                return (true);
            return (false);
        case SP_OGRE:
            if (mons_genus(mon) == MONS_OGRE)
                return (true);
            return (false);
        case SP_TROLL:
            if (mons_genus(mon) == MONS_TROLL)
                return (true);
            return (false);
        case SP_MUMMY:
            if (mons_genus(mon) == MONS_MUMMY)
                return (true);
            return (false);
        case SP_GHOUL: // Genus would include necrophage and rotting hulk.
            if (mons_species(mon) == MONS_GHOUL)
                return (true);
            return (false);
        case SP_VAMPIRE:
            if (mons_genus(mon) == MONS_VAMPIRE)
                return (true);
            return (false);
        case SP_MINOTAUR:
            if (mons_genus(mon) == MONS_MINOTAUR)
                return (true);
            return (false);
        case SP_NAGA:
            if (mons_genus(mon) == MONS_NAGA)
                return (true);
            return (false);
        case SP_HILL_ORC:
            if (mons_genus(mon) == MONS_ORC)
                return (true);
            return (false);
        case SP_MERFOLK:
            if (mons_genus(mon) == MONS_MERFOLK
                || mons_genus(mon) == MONS_MERMAID)
            {
                return (true);
            }
            return (false);

        case SP_HIGH_ELF:
        case SP_DEEP_ELF:
        case SP_SLUDGE_ELF:
            if (mons_genus(mon) == MONS_ELF)
                return (true);
            return (false);

        case SP_RED_DRACONIAN:
        case SP_WHITE_DRACONIAN:
        case SP_GREEN_DRACONIAN:
        case SP_YELLOW_DRACONIAN:
        case SP_GREY_DRACONIAN:
        case SP_BLACK_DRACONIAN:
        case SP_PURPLE_DRACONIAN:
        case SP_MOTTLED_DRACONIAN:
        case SP_PALE_DRACONIAN:
            if (mons_genus(mon) == MONS_DRACONIAN)
                return (true);
            return (false);

        case SP_KOBOLD:
            if (mons_genus(mon) == MONS_KOBOLD)
                return (true);
            return (false);
        default: // no monster equivalent
            return (false);

    }
}

// Checks whether the player's current species can
// use (usually wear) a given piece of equipment.
// Note that EQ_BODY_ARMOUR and EQ_HELMET only check
// the ill-fitting variant (i.e. not caps and robes).
// If special_armour is set to true, special cases
// such as bardings, light armour and caps are
// considered. Otherwise these simply return false.
// -------------------------------------------------
bool you_can_wear(int eq, bool special_armour)
{
    // These can be used by all.
    if (eq == EQ_LEFT_RING || eq == EQ_RIGHT_RING || eq == EQ_AMULET
        || eq == EQ_WEAPON || eq == EQ_CLOAK)
    {
        return (true);
    }

    // Anyone can wear caps/hats and robes and at least one of buckler/shield.
    if (special_armour
        && (eq == EQ_HELMET || eq == EQ_BODY_ARMOUR || eq == EQ_SHIELD))
    {
        return (true);
    }

    if (eq == EQ_BOOTS && (you.species == SP_NAGA || you.species == SP_CENTAUR))
        return (special_armour);

    // Of the remaining items, these races can't wear anything.
    if (you.species == SP_TROLL || you.species == SP_SPRIGGAN
        || player_genus(GENPC_OGRE) || player_genus(GENPC_DRACONIAN))
    {
        return (false);
    }

    if (you.species == SP_KENKU && (eq == EQ_HELMET || eq == EQ_BOOTS))
        return (false);

    if (eq == EQ_HELMET && (player_mutation_level(MUT_HORNS)
                            || player_mutation_level(MUT_BEAK)))
    {
        return (special_armour);
    }

    // Else, no problems.
    return (true);
}

bool player_has_feet()
{
   if (you.species == SP_NAGA || player_genus(GENPC_DRACONIAN))
       return (false);

   if (player_mutation_level(MUT_HOOVES) || player_mutation_level(MUT_TALONS))
       return (false);

   return (true);
}

bool player_wearing_slot(int eq)
{
    if (you.equip[eq] == -1)
        return (false);

    return (you_tran_can_wear(you.inv[you.equip[eq]]));
}

bool you_tran_can_wear(const item_def &item)
{
    switch (item.base_type)
    {
    case OBJ_WEAPONS:
        return you_tran_can_wear(EQ_WEAPON);

    case OBJ_JEWELLERY:
        return you_tran_can_wear(jewellery_is_amulet(item) ? EQ_AMULET
                                                           : EQ_LEFT_RING);
    case OBJ_ARMOUR:
        if (item.sub_type == ARM_NAGA_BARDING)
            return (you.species == SP_NAGA && you_tran_can_wear(EQ_BOOTS));
        else if (item.sub_type == ARM_CENTAUR_BARDING)
            return (you.species == SP_CENTAUR && you_tran_can_wear(EQ_BOOTS));

        if (get_armour_slot(item) == EQ_HELMET
            && !is_hard_helmet(item)
            && (you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER
                || you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST))
        {
            return (true);
        }

        if (fit_armour_size(item, you.body_size()) != 0)
            return (false);

        return you_tran_can_wear(get_armour_slot(item), true);

    default:
        return (true);
    }
}

bool you_tran_can_wear(int eq, bool check_mutation)
{
    if (eq == EQ_NONE)
        return (true);

    if (eq == EQ_STAFF)
        eq = EQ_WEAPON;
    else if (eq >= EQ_RINGS && eq <= EQ_RINGS_PLUS2)
        eq = EQ_LEFT_RING;

    // Everybody can wear at least some type of armour.
    if (eq == EQ_ALL_ARMOUR)
        return (true);

    // Not a transformation, but also temporary -> check first.
    if (check_mutation)
    {
        if (eq == EQ_GLOVES && you.has_claws(false) >= 3)
            return (false);

        if (eq == EQ_BOOTS
            && (you.swimming() && you.species == SP_MERFOLK
                || player_mutation_level(MUT_HOOVES)
                || player_mutation_level(MUT_TALONS)))
        {
            return (false);
        }
    }

    const int transform = you.attribute[ATTR_TRANSFORMATION];

    // No further restrictions.
    if (transform == TRAN_NONE || transform == TRAN_LICH)
        return (true);

    // Bats and pigs cannot wear anything except amulets.
    if ((transform == TRAN_BAT || transform == TRAN_PIG) && eq != EQ_AMULET)
        return (false);

    // Everyone else can wear jewellery, at least.
    if (eq == EQ_LEFT_RING || eq == EQ_RIGHT_RING || eq == EQ_AMULET)
        return (true);

    // These cannot use anything but jewellery.
    if (transform == TRAN_SPIDER || transform == TRAN_DRAGON)
        return (false);

    if (transform == TRAN_BLADE_HANDS)
    {
        if (eq == EQ_WEAPON || eq == EQ_GLOVES || eq == EQ_SHIELD)
            return (false);
        return (true);
    }

    if (transform == TRAN_ICE_BEAST)
    {
        if (eq != EQ_CLOAK)
            return (false);
        return (true);
    }

    if (transform == TRAN_STATUE)
    {
        if (eq == EQ_WEAPON || eq == EQ_CLOAK || eq == EQ_HELMET)
            return (true);
        return (false);
    }

    return (true);
}

bool player_weapon_wielded()
{
    const int wpn = you.equip[EQ_WEAPON];

    if (wpn == -1)
        return (false);

    if (you.inv[wpn].base_type != OBJ_WEAPONS
        && you.inv[wpn].base_type != OBJ_STAVES)
    {
        return (false);
    }

    return (true);
}

// Returns false if the player is wielding a weapon inappropriate for Berserk.
bool berserk_check_wielded_weapon()
{
    if (!you.weapon())
        return (true);

    const item_def weapon = *you.weapon();
    if (weapon.is_valid() && weapon.base_type != OBJ_STAVES
           && (weapon.base_type != OBJ_WEAPONS || is_range_weapon(weapon))
        || you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED])
    {
        std::string prompt = "Do you really want to go berserk while "
                             "wielding " + weapon.name(DESC_NOCAP_YOUR)
                             + "? ";

        if (!yesno(prompt.c_str(), true, 'n'))
        {
            canned_msg(MSG_OK);
            return (false);
        }

        you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED] = 0;
    }

    return (true);
}

// Looks in equipment "slot" to see if there is an equipped "sub_type".
// Returns number of matches (in the case of rings, both are checked)
int player_equip( equipment_type slot, int sub_type, bool calc_unid )
{
    if (!you_tran_can_wear(slot))
        return (0);

    int ret = 0;

    switch (slot)
    {
    case EQ_WEAPON:
        // Hands can have more than just weapons.
        if (you.weapon()
            && you.weapon()->base_type == OBJ_WEAPONS
            && you.weapon()->sub_type == sub_type)
        {
            ret++;
        }
        break;

    case EQ_STAFF:
        // Like above, but must be magical staff.
        if (you.weapon()
            && you.weapon()->base_type == OBJ_STAVES
            && you.weapon()->sub_type == sub_type
            && (calc_unid || item_type_known(*you.weapon())))
        {
            ret++;
        }
        break;

    case EQ_RINGS:
        if (you.equip[EQ_LEFT_RING] != -1
            && you.inv[you.equip[EQ_LEFT_RING]].sub_type == sub_type
            && (calc_unid
                || item_type_known( you.inv[you.equip[EQ_LEFT_RING]] )))
        {
            ret++;
        }

        if (you.equip[EQ_RIGHT_RING] != -1
            && you.inv[you.equip[EQ_RIGHT_RING]].sub_type == sub_type
            && (calc_unid
                || item_type_known( you.inv[you.equip[EQ_RIGHT_RING]] )))
        {
            ret++;
        }
        break;

    case EQ_RINGS_PLUS:
        if (you.equip[EQ_LEFT_RING] != -1
            && you.inv[you.equip[EQ_LEFT_RING]].sub_type == sub_type)
        {
            ret += you.inv[you.equip[EQ_LEFT_RING]].plus;
        }

        if (you.equip[EQ_RIGHT_RING] != -1
            && you.inv[you.equip[EQ_RIGHT_RING]].sub_type == sub_type)
        {
            ret += you.inv[you.equip[EQ_RIGHT_RING]].plus;
        }
        break;

    case EQ_RINGS_PLUS2:
        if (you.equip[EQ_LEFT_RING] != -1
            && you.inv[you.equip[EQ_LEFT_RING]].sub_type == sub_type)
        {
            ret += you.inv[you.equip[EQ_LEFT_RING]].plus2;
        }

        if (you.equip[EQ_RIGHT_RING] != -1
            && you.inv[you.equip[EQ_RIGHT_RING]].sub_type == sub_type)
        {
            ret += you.inv[you.equip[EQ_RIGHT_RING]].plus2;
        }
        break;

    case EQ_ALL_ARMOUR:
        // Doesn't make much sense here... be specific. -- bwr
        break;

    default:
        if (you.equip[slot] != -1
            && you.inv[you.equip[slot]].sub_type == sub_type
            && (calc_unid || item_type_known(you.inv[you.equip[slot]])))
        {
            ret++;
        }
        break;
    }

    return (ret);
}


// Looks in equipment "slot" to see if equipped item has "special" ego-type
// Returns number of matches (jewellery returns zero -- no ego type).
// [ds] There's no equivalent of calc_unid or req_id because as of now, weapons
// and armour type-id on wield/wear.
int player_equip_ego_type( int slot, int special )
{
    if (!you_tran_can_wear(slot))
        return (0);

    int ret = 0;
    int wpn;

    switch (slot)
    {
    case EQ_WEAPON:
        // Hands can have more than just weapons.
        wpn = you.equip[EQ_WEAPON];
        if (wpn != -1
            && you.inv[wpn].base_type == OBJ_WEAPONS
            && get_weapon_brand( you.inv[wpn] ) == special)
        {
            ret++;
        }
        break;

    case EQ_LEFT_RING:
    case EQ_RIGHT_RING:
    case EQ_AMULET:
    case EQ_STAFF:
    case EQ_RINGS:
    case EQ_RINGS_PLUS:
    case EQ_RINGS_PLUS2:
        // no ego types for these slots
        break;

    case EQ_ALL_ARMOUR:
        // Check all armour slots:
        for (int i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++)
        {
            // ... but skip ones you can't currently use!
            if (!you_tran_can_wear(i))
                continue;

            if (you.equip[i] != -1
                && get_armour_ego_type( you.inv[you.equip[i]] ) == special)
            {
                ret++;
            }
        }
        break;

    default:
        // Check a specific armour slot for an ego type:
        if (you.equip[slot] != -1
            && get_armour_ego_type( you.inv[you.equip[slot]] ) == special)
        {
            ret++;
        }
        break;
    }

    return (ret);
}

// Return's true if the indicated unrandart is equipped
// [ds] There's no equivalent of calc_unid or req_id because as of now, weapons
// and armour type-id on wield/wear.
bool player_equip_unrand(int unrand_index)
{
    unrandart_entry* entry = get_unrand_entry(unrand_index);
    equipment_type   slot  = get_item_slot(entry->base_type,
                                           entry->sub_type);

    if (!you_tran_can_wear(slot))
        return (false);

    int it;

    switch (slot)
    {
    case EQ_WEAPON:
        // Hands can have more than just weapons.
        it = you.equip[EQ_WEAPON];
        if (it != -1
            && you.inv[it].base_type == OBJ_WEAPONS
            && is_unrandom_artefact(you.inv[it])
            && you.inv[it].special == unrand_index)
        {
            return (true);
        }
        break;

    case EQ_RINGS:
        it = you.equip[EQ_LEFT_RING];
        if (it != -1
            && is_unrandom_artefact(you.inv[it])
            && you.inv[it].special == unrand_index)
        {
            return (true);
        }

        it = you.equip[EQ_RIGHT_RING];
        if (it != -1
            && is_unrandom_artefact(you.inv[it])
            && you.inv[it].special == unrand_index)
        {
            return (true);
        }
        break;

    case EQ_NONE:
    case EQ_STAFF:
    case EQ_LEFT_RING:
    case EQ_RIGHT_RING:
    case EQ_RINGS_PLUS:
    case EQ_RINGS_PLUS2:
    case EQ_ALL_ARMOUR:
        // no unrandarts for these slots.
        break;

    default:
        // Check a specific slot.
        it = you.equip[slot];
        if (it != -1
            && is_unrandom_artefact(you.inv[it])
            && you.inv[it].special == unrand_index)
        {
            return (true);
        }
        break;
    }

    return (false);
}

// Given an adjacent monster, returns true if the player can hit it (the
// monster should not be submerged, or be submerged in shallow water if
// the player has a polearm).
bool player_can_hit_monster(const monsters *mon)
{
    if (!mon->submerged())
        return (true);

    if (grd(mon->pos()) != DNGN_SHALLOW_WATER)
        return (false);

    const item_def *weapon = you.weapon();
    return (weapon && weapon_skill(*weapon) == SK_POLEARMS);
}

int player_teleport(bool calc_unid)
{
    ASSERT(!crawl_state.arena);

    int tp = 0;

    // rings
    tp += 8 * player_equip( EQ_RINGS, RING_TELEPORTATION, calc_unid );

    // mutations
    tp += player_mutation_level(MUT_TELEPORT) * 3;

    // randart weapons only
    if (you.weapon()
        && you.weapon()->base_type == OBJ_WEAPONS
        && is_artefact(*you.weapon()))
    {
        tp += scan_artefacts(ARTP_CAUSE_TELEPORTATION, calc_unid);
    }

    return tp;
}

int player_regen()
{
    int rr = you.hp_max / 3;

    if (rr > 20)
        rr = 20 + ((rr - 20) / 2);

    // Rings.
    rr += 40 * player_equip(EQ_RINGS, RING_REGENERATION);

    // Spell.
    if (you.duration[DUR_REGENERATION]
        && !you.attribute[ATTR_DIVINE_REGENERATION])
    {
        rr += 100;
    }

    // Troll leather (except for trolls).
    if (player_equip(EQ_BODY_ARMOUR, ARM_TROLL_LEATHER_ARMOUR)
        && you.species != SP_TROLL)
    {
        rr += 30;
    }

    // Fast heal mutation.
    rr += player_mutation_level(MUT_REGENERATION) * 20;

    // Before applying other effects, make sure that there's something
    // to heal.
    rr = std::max(1, rr);

    // Healing depending on satiation.
    // The better-fed you are, the faster you heal.
    if (you.species == SP_VAMPIRE)
    {
        if (you.hunger_state == HS_STARVING)
            // No regeneration for starving vampires.
            rr = 0;
        else if (you.hunger_state == HS_ENGORGED)
            // More bonus regeneration for engorged vampires.
            rr += 20;
        else if (you.hunger_state < HS_SATIATED)
            // Halved regeneration for hungry vampires.
            rr /= 2;
        else if (you.hunger_state >= HS_FULL)
            // Bonus regeneration for full vampires.
            rr += 10;
    }

    // Slow heal mutation.  Each level reduces your natural healing by
    // one third.
    if (player_mutation_level(MUT_SLOW_HEALING) > 0)
    {
        rr *= 3 - player_mutation_level(MUT_SLOW_HEALING);
        rr /= 3;
    }

    // Trog's Hand.  This circumvents the slow healing effect.
    if (you.attribute[ATTR_DIVINE_REGENERATION])
        rr += 100;

    return (rr);
}

int player_hunger_rate(void)
{
    int hunger = 3;

    if (player_in_bat_form())
        return 1;

    if (you.species == SP_TROLL)
        hunger += 3;            // in addition to the +3 for fast metabolism

    if (you.duration[DUR_REGENERATION] && you.hp < you.hp_max)
        hunger += 4;

    // If Cheibriados has slowed your life processes, you will hunger less.
    if (GOD_CHEIBRIADOS == you.religion
        && you.piety >= piety_breakpoint(0))
        hunger--;

    // Moved here from main.cc... maintaining the >= 40 behaviour.
    if (you.hunger >= 40)
    {
        if (you.duration[DUR_INVIS] > 0)
            hunger += 5;

        // Berserk has its own food penalty -- excluding berserk haste.
        // Doubling the hunger cost for haste so that the per turn hunger
        // is consistent now that a hasted turn causes 50% the normal hunger
        // -cao
        if (you.duration[DUR_HASTE] > 0 && !you.berserk())
            hunger += 10;
    }

    if (you.species == SP_VAMPIRE)
    {
        switch (you.hunger_state)
        {
        case HS_STARVING:
        case HS_NEAR_STARVING:
            hunger -= 3;
            break;
        case HS_VERY_HUNGRY:
            hunger -= 2;
            break;
        case HS_HUNGRY:
            hunger--;
            break;
        case HS_SATIATED:
            break;
        case HS_FULL:
            hunger++;
            break;
        case HS_VERY_FULL:
            hunger += 2;
            break;
        case HS_ENGORGED:
            hunger += 3;
        }
    }
    else
    {
        hunger += player_mutation_level(MUT_FAST_METABOLISM);

        if (player_mutation_level(MUT_SLOW_METABOLISM) > 2)
            hunger -= 2;
        else if (player_mutation_level(MUT_SLOW_METABOLISM) > 0)
            hunger--;
    }

    // rings
    if (you.hp < you.hp_max)
        hunger += 3 * player_equip( EQ_RINGS, RING_REGENERATION );
    hunger += 4 * player_equip( EQ_RINGS, RING_HUNGER );
    hunger -= 2 * player_equip( EQ_RINGS, RING_SUSTENANCE );

    // weapon ego types
    if (you.species != SP_VAMPIRE)
    {
        if (player_equip_ego_type( EQ_WEAPON, SPWPN_VAMPIRICISM ))
              hunger += 6;
    }
    else
    {
        if (player_equip_ego_type( EQ_WEAPON, SPWPN_VAMPIRICISM ))
            hunger += 1;
    }

    // troll leather armour
    if (you.species != SP_TROLL && you.hp < you.hp_max)
        if (player_equip( EQ_BODY_ARMOUR, ARM_TROLL_LEATHER_ARMOUR ))
            hunger += coinflip() ? 2 : 1;

    // randarts
    hunger += scan_artefacts(ARTP_METABOLISM);

    // burden
    hunger += you.burden_state;

    if (hunger < 1)
        hunger = 1;

    return (hunger);
}

int player_spell_levels(void)
{
    int sl = (you.experience_level - 1) + (you.skills[SK_SPELLCASTING] * 2);

    bool fireball = false;
    bool delayed_fireball = false;

    if (sl > 99)
        sl = 99;

    for (int i = 0; i < 25; i++)
    {
        if (you.spells[i] == SPELL_FIREBALL)
            fireball = true;
        else if (you.spells[i] == SPELL_DELAYED_FIREBALL)
            delayed_fireball = true;

        if (you.spells[i] != SPELL_NO_SPELL)
            sl -= spell_difficulty(you.spells[i]);
    }

    // Fireball is free for characters with delayed fireball
    if (fireball && delayed_fireball)
        sl += spell_difficulty( SPELL_FIREBALL );

    // Note: This can happen because of level drain.  Maybe we should
    // force random spells out when that happens. -- bwr
    if (sl < 0)
        sl = 0;

    return (sl);
}

bool player_likes_chunks(bool permanently)
{
    return (player_mutation_level(MUT_GOURMAND) > 0
            || player_mutation_level(MUT_CARNIVOROUS) > 0
            || (!permanently && wearing_amulet(AMU_THE_GOURMAND)));
}

// If temp is set to false, temporary sources or resistance won't be counted.
int player_res_fire(bool calc_unid, bool temp, bool items)
{
    int rf = 0;

    if (items)
    {
        // rings of fire resistance/fire
        rf += player_equip( EQ_RINGS, RING_PROTECTION_FROM_FIRE, calc_unid );
        rf += player_equip( EQ_RINGS, RING_FIRE, calc_unid );

        // rings of ice
        rf -= player_equip( EQ_RINGS, RING_ICE, calc_unid );

        // Staves
        rf += player_equip( EQ_STAFF, STAFF_FIRE, calc_unid );

        // body armour:
        rf += 2 * player_equip( EQ_BODY_ARMOUR, ARM_DRAGON_ARMOUR );
        rf += player_equip( EQ_BODY_ARMOUR, ARM_GOLD_DRAGON_ARMOUR );
        rf -= player_equip( EQ_BODY_ARMOUR, ARM_ICE_DRAGON_ARMOUR );

        // ego armours
        rf += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_FIRE_RESISTANCE );
        rf += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_RESISTANCE );

        // randart weapons:
        rf += scan_artefacts(ARTP_FIRE, calc_unid);
    }

    // species:
    if (you.species == SP_MUMMY)
        rf--;

    // mutations:
    rf += player_mutation_level(MUT_HEAT_RESISTANCE);

    // spells:
    if (temp)
    {
        if (you.duration[DUR_RESIST_FIRE] > 0)
            rf++;

        if (you.duration[DUR_FIRE_SHIELD])
            rf += 2;

        // transformations:
        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_ICE_BEAST:
            rf--;
            break;
        case TRAN_DRAGON:
            rf += 2;
            break;
        }
    }

    if (rf < -3)
        rf = -3;
    else if (rf > 3)
        rf = 3;

    return (rf);
}

int player_res_steam(bool calc_unid, bool temp, bool items)
{
    int res = 0;

    if (you.species == SP_PALE_DRACONIAN && you.experience_level > 5)
        res += 2;

    if (items && player_equip(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR))
        res += 2;

    return (res + player_res_fire(calc_unid, temp, items) / 2);
}

int player_res_cold(bool calc_unid, bool temp, bool items)
{
    int rc = 0;

    if (temp)
    {
        // spells:
        if (you.duration[DUR_RESIST_COLD] > 0)
            rc++;

        if (you.duration[DUR_FIRE_SHIELD])
            rc -= 2;

        // transformations:
        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_ICE_BEAST:
            rc += 3;
            break;
        case TRAN_DRAGON:
            rc--;
            break;
        case TRAN_LICH:
            rc++;
            break;
        }

        if (you.species == SP_VAMPIRE)
        {
            if (you.hunger_state <= HS_NEAR_STARVING)
                rc += 2;
            else if (you.hunger_state < HS_SATIATED)
                rc++;
        }
    }

    if (items)
    {
        // rings of cold resistance/ice
        rc += player_equip( EQ_RINGS, RING_PROTECTION_FROM_COLD, calc_unid );
        rc += player_equip( EQ_RINGS, RING_ICE, calc_unid );

        // rings of fire
        rc -= player_equip( EQ_RINGS, RING_FIRE, calc_unid );

        // Staves
        rc += player_equip( EQ_STAFF, STAFF_COLD, calc_unid );

        // body armour:
        rc += 2 * player_equip( EQ_BODY_ARMOUR, ARM_ICE_DRAGON_ARMOUR );
        rc += player_equip( EQ_BODY_ARMOUR, ARM_GOLD_DRAGON_ARMOUR );
        rc -= player_equip( EQ_BODY_ARMOUR, ARM_DRAGON_ARMOUR );

        // ego armours
        rc += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_COLD_RESISTANCE );
        rc += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_RESISTANCE );

        // randart weapons:
        rc += scan_artefacts(ARTP_COLD, calc_unid);
    }

    // mutations:
    rc += player_mutation_level(MUT_COLD_RESISTANCE);

    if (player_mutation_level(MUT_SHAGGY_FUR) == 3)
        rc++;

    if (rc < -3)
        rc = -3;
    else if (rc > 3)
        rc = 3;

    return (rc);
}

int player_res_acid(bool calc_unid, bool items)
{
    int res = 0;
    if (!transform_changed_physiology())
    {
        if (you.species == SP_YELLOW_DRACONIAN && you.experience_level > 6)
            res += 2;

        res += player_mutation_level(MUT_YELLOW_SCALES) * 2 / 3;
    }

    if (items)
    {
        if (wearing_amulet(AMU_RESIST_CORROSION, calc_unid))
            res++;

        if (player_equip_ego_type(EQ_CLOAK, SPARM_PRESERVATION))
            res++;
    }

    if (you.religion == GOD_JIYVA && x_chance_in_y(you.piety, MAX_PIETY))
        res++;

    return (res);
}

// Returns a factor X such that post-resistance acid damage can be calculated
// as pre_resist_damage * X / 100.
int player_acid_resist_factor()
{
    int res = player_res_acid();

    int factor = 100;

    if (res == 1)
        factor = 50;
    else if (res == 2)
        factor = 34;
    else if (res > 2)
    {
        factor = 30;
        while (res-- > 2 && factor >= 20)
            factor = factor * 90 / 100;
    }

    return (factor);
}

int player_res_electricity(bool calc_unid, bool temp, bool items)
{
    int re = 0;

    if (temp)
    {
        if (you.duration[DUR_INSULATION])
            re++;

        // transformations:
        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_STATUE)
            re += 1;

        if (you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION])
            re = 3;
        else if (re > 1)
            re = 1;
    }

    if (items)
    {
        // staff
        re += player_equip( EQ_STAFF, STAFF_AIR, calc_unid );

        // body armour:
        re += player_equip( EQ_BODY_ARMOUR, ARM_STORM_DRAGON_ARMOUR );

        // randart weapons:
        re += scan_artefacts(ARTP_ELECTRICITY, calc_unid);
    }

    // species:
    if (you.species == SP_BLACK_DRACONIAN && you.experience_level > 17)
        re++;

    // mutations:
    if (player_mutation_level(MUT_SHOCK_RESISTANCE))
        re++;

    return (re);
}

bool player_control_teleport(bool calc_unid, bool temp, bool items)
{
    return ((temp && you.duration[DUR_CONTROL_TELEPORT])
            || (items
                && player_equip(EQ_RINGS, RING_TELEPORT_CONTROL, calc_unid))
            || player_mutation_level(MUT_TELEPORT_CONTROL));
}

int player_res_torment(bool, bool temp)
{
    return (player_mutation_level(MUT_TORMENT_RESISTANCE)
            || you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH
            || you.species == SP_VAMPIRE && you.hunger_state == HS_STARVING
            || temp && (20 * player_mutation_level(MUT_STOCHASTIC_TORMENT_RESISTANCE) >= random2(100)));
}

// Funny that no races are susceptible to poisons. {dlb}
// If temp is set to false, temporary sources or resistance won't be counted.
int player_res_poison(bool calc_unid, bool temp, bool items)
{
    int rp = 0;

    if (items)
    {
        // rings of poison resistance
        rp += player_equip( EQ_RINGS, RING_POISON_RESISTANCE, calc_unid );

        // Staves
        rp += player_equip( EQ_STAFF, STAFF_POISON, calc_unid );

        // ego armour:
        rp += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_POISON_RESISTANCE );

        // body armour:
        rp += player_equip( EQ_BODY_ARMOUR, ARM_GOLD_DRAGON_ARMOUR );
        rp += player_equip( EQ_BODY_ARMOUR, ARM_SWAMP_DRAGON_ARMOUR );

        // randart weapons:
        rp += scan_artefacts(ARTP_POISON, calc_unid);
    }

    // mutations:
    rp += player_mutation_level(MUT_POISON_RESISTANCE);

    // Only thirsty vampires are naturally poison resistant.
    if (you.species == SP_VAMPIRE && you.hunger_state < HS_SATIATED)
        rp++;

    if (temp)
    {
        // spells:
        if (you.duration[DUR_RESIST_POISON] > 0)
            rp++;

        // transformations:
        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_LICH:
        case TRAN_ICE_BEAST:
        case TRAN_STATUE:
        case TRAN_DRAGON:
            rp++;
            break;
        }
    }

    if (rp > 1)
        rp = 1;

    return (rp);
}

int player_res_sticky_flame(bool calc_unid, bool temp, bool items)
{
    int rsf = 0;

    if (you.species == SP_MOTTLED_DRACONIAN && you.experience_level > 5)
        rsf++;

    if (items && player_equip(EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR))
        rsf++;

    if (rsf > 1)
        rsf = 1;

    return (rsf);
}

int player_spec_death()
{
    int sd = 0;

    // Staves
    sd += player_equip( EQ_STAFF, STAFF_DEATH );

    // body armour:
    if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
        sd++;

    // species:
    if (you.species == SP_MUMMY)
    {
        if (you.experience_level >= 13)
            sd++;
        if (you.experience_level >= 26)
            sd++;
    }

    // transformations:
    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
        sd++;

    return sd;
}

int player_spec_holy()
{
    //if ( you.char_class == JOB_PRIEST || you.char_class == JOB_PALADIN )
    //  return 1;
    return 0;
}

int player_spec_fire()
{
    int sf = 0;

    // staves:
    sf += player_equip( EQ_STAFF, STAFF_FIRE );

    // rings of fire:
    sf += player_equip( EQ_RINGS, RING_FIRE );

    if (you.duration[DUR_FIRE_SHIELD])
        sf++;

    // if it's raining... {due}
    if (in_what_cloud(CLOUD_RAIN))
        sf--;

    return sf;
}

int player_spec_cold()
{
    int sc = 0;

    // staves:
    sc += player_equip( EQ_STAFF, STAFF_COLD );

    // rings of ice:
    sc += player_equip( EQ_RINGS, RING_ICE );

    return sc;
}

int player_spec_earth()
{
    int se = 0;

    // Staves
    se += player_equip( EQ_STAFF, STAFF_EARTH );

    return se;
}

int player_spec_air()
{
    int sa = 0;

    // Staves
    sa += player_equip( EQ_STAFF, STAFF_AIR );

    return sa;
}

int player_spec_conj()
{
    int sc = 0;

    // Staves
    sc += player_equip( EQ_STAFF, STAFF_CONJURATION );

    // armour of the Archmagi
    if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
        sc++;

    return sc;
}

int player_spec_ench()
{
    int se = 0;

    // Staves
    se += player_equip( EQ_STAFF, STAFF_ENCHANTMENT );

    // armour of the Archmagi
    if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
        se++;

    return se;
}

int player_spec_summ()
{
    int ss = 0;

    // Staves
    ss += player_equip( EQ_STAFF, STAFF_SUMMONING );

    // armour of the Archmagi
    if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
        ss++;

    return ss;
}

int player_spec_poison()
{
    int sp = 0;

    // Staves
    sp += player_equip( EQ_STAFF, STAFF_POISON );

    if (player_equip_unrand(UNRAND_OLGREB))
        sp++;

    return sp;
}

int player_energy()
{
    int pe = 0;

    // Staves
    pe += player_equip( EQ_STAFF, STAFF_ENERGY );

    return pe;
}

// If temp is set to false, temporary sources of resistance won't be
// counted.
int player_prot_life(bool calc_unid, bool temp, bool items)
{
    int pl = 0;

    // Hunger is temporary, true, but that's something you can control,
    // especially as life protection only increases the hungrier you
    // get.
    if (you.species == SP_VAMPIRE)
    {
        switch (you.hunger_state)
        {
        case HS_STARVING:
        case HS_NEAR_STARVING:
            pl = 3;
            break;
        case HS_VERY_HUNGRY:
        case HS_HUNGRY:
            pl = 2;
            break;
        case HS_SATIATED:
            pl = 1;
            break;
        default:
            break;
        }
    }

    // Same here.  Your piety status, and, hence, TSO's protection, is
    // something you can more or less control.
    if (you.religion == GOD_SHINING_ONE && you.piety > pl * 50)
        pl = you.piety / 50;

    if (temp)
    {
        // Now, transformations could stop at any time.
        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_STATUE:
            pl++;
            break;
        case TRAN_LICH:
            pl += 3;
            break;
        default:
           break;
        }
    }

    if (items)
    {
        if (wearing_amulet(AMU_WARDING, calc_unid))
            pl++;

        // rings
        pl += player_equip(EQ_RINGS, RING_LIFE_PROTECTION, calc_unid);

        // armour (checks body armour only)
        pl += player_equip_ego_type(EQ_ALL_ARMOUR, SPARM_POSITIVE_ENERGY);

        // randart wpns
        pl += scan_artefacts(ARTP_NEGATIVE_ENERGY, calc_unid);
    }

    // undead/demonic power
    pl += player_mutation_level(MUT_NEGATIVE_ENERGY_RESISTANCE);

    pl = std::min(3, pl);

    return (pl);
}

// New player movement speed system... allows for a bit more than
// "player runs fast" and "player walks slow" in that the speed is
// actually calculated (allowing for centaurs to get a bonus from
// swiftness and other such things).  Levels of the mutation now
// also have meaning (before they all just meant fast).  Most of
// this isn't as fast as it used to be (6 for having anything), but
// even a slight speed advantage is very good... and we certainly don't
// want to go past 6 (see below). -- bwr
int player_movement_speed(void)
{
    int mv = 10;

    if (you.swimming())
    {
        // This is swimming... so it doesn't make sense to really
        // apply the other things (the mutation is "cover ground",
        // swiftness is an air spell, can't wear boots, can't be
        // transformed).
        mv = 6;
    }
    else
    {
        // transformations
        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER)
            mv = 8;
        else if (player_in_bat_form())
            mv = 5; // but allowed minimum is six
        else if (you.attribute[ATTR_TRANSFORMATION] == TRAN_PIG)
            mv = 7;

        // armour
        if (player_equip_ego_type( EQ_BOOTS, SPARM_RUNNING ))
            mv -= 2;

        // ponderous brand
        mv += 2 * player_equip_ego_type(EQ_ALL_ARMOUR, SPARM_PONDEROUSNESS);

        // In the air, can fly fast (should be lightly burdened).
        if (you.light_flight())
            mv--;

        // Swiftness is an Air spell, it doesn't work in water, but
        // flying players will move faster.
        if (you.duration[DUR_SWIFTNESS] > 0 && !you.in_water())
            mv -= (you.flight_mode() == FL_FLY ? 4 : 2);

        // Mutations: -2, -3, -4, unless innate and shapechanged.
        if (player_mutation_level(MUT_FAST) > 0
            && (!you.demon_pow[MUT_FAST] || !player_is_shapechanged()) )
        {
            mv -= (player_mutation_level(MUT_FAST) + 1);
        }

        // Burden
        if (you.burden_state == BS_ENCUMBERED)
            mv += 1;
        else if (you.burden_state == BS_OVERLOADED)
            mv += 3;
    }

    // We'll use the old value of six as a minimum, with haste this could
    // end up as a speed of three, which is about as fast as we want
    // the player to be able to go (since 3 is 3.33x as fast and 2 is 5x,
    // which is a bit of a jump, and a bit too fast) -- bwr
    if (mv < 6)
        mv = 6;

    // Nagas move slowly:
    if (you.species == SP_NAGA && !player_is_shapechanged())
    {
        mv *= 14;
        mv /= 10;
    }

    return (mv);
}

// This function differs from the above in that it's used to set the
// initial time_taken value for the turn.  Everything else (movement,
// spellcasting, combat) applies a ratio to this value.
int player_speed(void)
{
    int ps = 10;

    if (you.duration[DUR_SLOW])
        ps *= 2;

    if (you.duration[DUR_HASTE])
        ps /= 2;

    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_STATUE:
        ps *= 15;
        ps /= 10;
        break;

    default:
        break;
    }

    return ps;
}

static int _player_armour_racial_bonus(const item_def& item)
{
    if (item.base_type != OBJ_ARMOUR)
        return 0;

    int racial_bonus = 0;
    const unsigned long armour_race = get_equip_race(item);

    // get the armour race value that corresponds to the character's race:
    const unsigned long racial_type
                            = ((player_genus(GENPC_DWARVEN)) ? ISFLAG_DWARVEN :
                               (player_genus(GENPC_ELVEN))   ? ISFLAG_ELVEN :
                               (you.species == SP_HILL_ORC)  ? ISFLAG_ORCISH
                                                             : 0);

    // Dwarven armour is universally good -- bwr
    if (armour_race == ISFLAG_DWARVEN)
        racial_bonus += 4;

    if (racial_type && armour_race == racial_type)
    {
        // Elven armour is light, but still gives one level to elves.
        // Orcish and Dwarven armour are worth +2 to the correct
        // species, plus the plus that anyone gets with dwarven armour.
        // -- bwr
        if (racial_type == ISFLAG_ELVEN)
            racial_bonus += 2;
        else
            racial_bonus += 4;

        // an additional bonus for Beogh worshippers
        if (you.religion == GOD_BEOGH && !player_under_penance())
        {
            if (you.piety >= 185)
                racial_bonus += racial_bonus * 9 / 4;
            else if (you.piety >= 160)
                racial_bonus += racial_bonus * 2;
            else if (you.piety >= 120)
                racial_bonus += racial_bonus * 7 / 4;
            else if (you.piety >= 80)
                racial_bonus += racial_bonus * 5 / 4;
            else if (you.piety >= 40)
                racial_bonus += racial_bonus * 3 / 4;
            else
                racial_bonus += racial_bonus / 4;
        }
    }

    return racial_bonus;
}

bool is_light_armour( const item_def &item )
{
    if (get_equip_race(item) == ISFLAG_ELVEN)
        return (true);

    switch (item.sub_type)
    {
    case ARM_ROBE:
    case ARM_ANIMAL_SKIN:
    case ARM_LEATHER_ARMOUR:
    case ARM_TROLL_HIDE:
    case ARM_TROLL_LEATHER_ARMOUR:
    case ARM_STEAM_DRAGON_HIDE:
    case ARM_STEAM_DRAGON_ARMOUR:
    case ARM_MOTTLED_DRAGON_HIDE:
    case ARM_MOTTLED_DRAGON_ARMOUR:
        return (true);

    default:
        return (false);
    }
}

bool player_light_armour(bool with_skill)
{
    if (!player_wearing_slot(EQ_BODY_ARMOUR))
        return (true);

    // We're wearing some kind of body armour and it's not melded.
    const int arm = you.equip[EQ_BODY_ARMOUR];

    if (with_skill
        && property(you.inv[arm], PARM_EVASION) + you.skills[SK_ARMOUR]/3 >= 0)
    {
        return (true);
    }

    return (is_light_armour(you.inv[arm]));
}

//
// This function returns true if the player has a radically different
// shape... minor changes like blade hands don't count, also note
// that lich transformation doesn't change the character's shape
// (so we end up with Naga-lichs, Spiggan-lichs, Minotaur-lichs)
// it just makes the character undead (with the benefits that implies). --bwr
//
bool player_is_shapechanged(void)
{
    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_NONE
        || you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS
        || you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
    {
        return (false);
    }

    return (true);
}

// New and improved 4.1 evasion model, courtesy Brent Ross.
int player_evasion(ev_ignore_type evit)
{
    // XXX: you.body_size() implementations are incomplete, fix.
    const size_type size  = you.body_size(PSIZE_BODY);
    const size_type torso = you.body_size(PSIZE_TORSO);

    const int size_factor = SIZE_MEDIUM - size;
    int ev = 10 + 2 * size_factor;

    // Repulsion fields and size are all that matters when paralysed.
    if (you.cannot_move() && !(evit & EV_IGNORE_HELPLESS))
    {
        ev = 2 + size_factor;
        if (player_mutation_level(MUT_REPULSION_FIELD) > 0)
            ev += (player_mutation_level(MUT_REPULSION_FIELD) * 2) - 1;
        return ((ev < 1) ? 1 : ev);
    }

    // Calculate the base bonus here, but it may be reduced by heavy
    // armour below.
    int dodge_bonus = (you.skills[SK_DODGING] * you.dex + 7)
                      / (20 - size_factor);

    // Limit on bonus from dodging:
    const int max_bonus = (you.skills[SK_DODGING] * (7 + size_factor)) / 9;
    if (dodge_bonus > max_bonus)
        dodge_bonus = max_bonus;

    // Some lesser armours have small penalties now (shields, barding).
    for (int i = EQ_CLOAK; i < EQ_BODY_ARMOUR; i++)
    {
        if (!player_wearing_slot(i))
            continue;

        int pen = property( you.inv[ you.equip[i] ], PARM_EVASION );

        // Reducing penalty of larger shields for larger characters.
        if (i == EQ_SHIELD && torso > SIZE_MEDIUM)
            pen += (torso - SIZE_MEDIUM);

        if (pen < 0)
            ev += pen;
    }

    // Handle main body armour penalty.
    if (you.equip[EQ_BODY_ARMOUR] != -1)
    {
        // XXX: magnify arm_penalty for weak characters?
        // Remember: arm_penalty and ev_change are negative.
        int arm_penalty = property( you.inv[ you.equip[EQ_BODY_ARMOUR] ],
                                          PARM_EVASION );

        // The very large races take a penalty to AC for not being able
        // to fully cover, and in compensation we give back some freedom
        // of movement here.  Likewise, the very small are extra encumbered
        // by armour (which partially counteracts their size bonus above).
        if (size < SIZE_SMALL || size > SIZE_LARGE)
        {
            arm_penalty -= ((size - SIZE_MEDIUM) * arm_penalty) / 4;

            if (arm_penalty > 0)
                arm_penalty = 0;
        }

        int ev_change = arm_penalty;
        ev_change += (you.skills[SK_ARMOUR] * you.strength) / 60;

        if (ev_change > arm_penalty / 2)
            ev_change = arm_penalty / 2;

        ev += ev_change;

        // This reduces dodging ability in heavy armour.
        if (!player_light_armour())
            dodge_bonus += (arm_penalty * 30 + 15) / you.strength;
    }

    if (dodge_bonus > 0)                // always a bonus
        ev += dodge_bonus;

    if (you.duration[DUR_AGILITY])
        ev += 5;

    if (you.duration[DUR_PHASE_SHIFT] && !(evit & EV_IGNORE_PHASESHIFT))
        ev += 8;

    if (you.duration[DUR_STONEMAIL])
        ev -= 2;

    ev += player_equip( EQ_RINGS_PLUS, RING_EVASION );

    if (player_equip_ego_type( EQ_WEAPON, SPWPN_EVASION ))
        ev += 5;

    ev += scan_artefacts( ARTP_EVASION );

    // ponderous ev mod
    ev -= 2 * player_equip_ego_type(EQ_ALL_ARMOUR, SPARM_PONDEROUSNESS);

    if (player_mutation_level(MUT_REPULSION_FIELD) > 0)
        ev += (player_mutation_level(MUT_REPULSION_FIELD) * 2) - 1;

    // transformation penalties/bonuses not covered by size alone:
    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_STATUE:
        ev -= 5;                // stiff
        break;

    default:
        break;
    }

    switch (you.species)
    {
    case SP_MERFOLK:
        // Merfolk get an evasion bonus in water.
        if (you.swimming())
        {
            // ... though a bit less so if swimming in heavy armour.
            int factor = 4;
            int min_bonus = 2;
            if (grd(you.pos()) == DNGN_DEEP_WATER && !player_light_armour())
            {
                factor = 6;
                min_bonus = 1;
            }

            const int ev_bonus = std::min(9, std::max(min_bonus, ev / factor));
            ev += ev_bonus;
        }
        break;

    case SP_KENKU:
        // Flying Kenku get an evasion bonus.
        if (you.flight_mode() == FL_FLY)
        {
            const int ev_bonus = std::min(9, std::max(1, ev / 5));
            ev += ev_bonus;
        }
        break;

    default:
        break;
    }

    return (ev);
}

// Obsolete evasion calc.
#if 0
int old_player_evasion(void)
{
    int ev = 10;

    int armour_ev_penalty;

    if (you.equip[EQ_BODY_ARMOUR] == -1)
        armour_ev_penalty = 0;
    else
    {
        armour_ev_penalty = property( you.inv[you.equip[EQ_BODY_ARMOUR]],
                                      PARM_EVASION );
    }

    // We return 2 here to give the player some chance of not being hit,
    // repulsion fields still work while paralysed
    if (you.duration[DUR_PARALYSIS])
        return (2 + player_mutation_level(MUT_REPULSION_FIELD) * 2);

    if (you.species == SP_CENTAUR)
        ev -= 3;

    if (you.equip[EQ_BODY_ARMOUR] != -1)
    {
        int ev_change = 0;

        ev_change = armour_ev_penalty;
        ev_change += you.skills[SK_ARMOUR] / 3;

        if (ev_change > armour_ev_penalty / 3)
            ev_change = armour_ev_penalty / 3;

        ev += ev_change;        // remember that it's negative
    }

    ev += player_equip( EQ_RINGS_PLUS, RING_EVASION );

    if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_PONDEROUSNESS ))
        ev -= 2;

    if (you.duration[DUR_STONEMAIL])
        ev -= 2;

    if (you.duration[DUR_PHASE_SHIFT])
        ev += 8;                //jmf: is this a reasonable value?

    int emod = 0;

    if (!player_light_armour())
    {
        // meaning that the armour evasion modifier is often effectively
        // applied twice, but not if you're wearing elven armour
        emod += (armour_ev_penalty * 14) / 10;
    }

    emod += you.skills[SK_DODGING] / 2;

    if (emod > 0)
        ev += emod;

    if (player_mutation_level(MUT_REPULSION_FIELD) > 0)
        ev += (player_mutation_level(MUT_REPULSION_FIELD) * 2) - 1;

    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_DRAGON:
        ev -= 3;
        break;

    case TRAN_STATUE:
        ev -= 5;
        break;

    case TRAN_SPIDER:
        ev += 3;
        break;

    case TRAN_BAT:
        ev += 20 + (you.experience_level - 10);
        break;

    default:
        break;
    }

    ev += scan_artefacts(ARTP_EVASION);

    return (ev);
}
#endif

int player_magical_power(void)
{
    int ret = 0;

    ret += 13 * player_equip(EQ_STAFF, STAFF_POWER);
    ret +=  9 * player_equip(EQ_RINGS, RING_MAGICAL_POWER);
    ret +=      scan_artefacts(ARTP_MAGICAL_POWER);

    return (ret);
}

int player_mag_abil(bool is_weighted)
{
    int ma = 0;

    // Brilliance Potion
    ma += 6 * (you.duration[DUR_BRILLIANCE] ? 1 : 0);

    ma += 3 * player_equip(EQ_RINGS, RING_WIZARDRY);

    // Staves
    ma += 4 * player_equip(EQ_STAFF, STAFF_WIZARDRY);

    // armour of the Archmagi (checks body armour only)
    ma += 2 * player_equip_ego_type(EQ_BODY_ARMOUR, SPARM_ARCHMAGI);

    return ((is_weighted) ? ((ma * you.intel) / 10) : ma);
}

int player_shield_class(void)   //jmf: changes for new spell
{
    int shield = 0;
    int stat = 0;

    if (you.incapacitated())
        return (0);

    if (player_wearing_slot(EQ_SHIELD))
    {
        const item_def& item = you.inv[you.equip[EQ_SHIELD]];
        int base_shield = property(item, PARM_AC);

        int racial_bonus = _player_armour_racial_bonus(item);

        // bonus applied only to base, see above for effect:
        shield += base_shield * 100;
        shield += base_shield * 5 * you.skills[SK_SHIELDS];
        shield += base_shield * racial_bonus * 10 / 3;

        shield += item.plus * 100;

        if (item.sub_type == ARM_BUCKLER)
            stat = you.dex * 38;
        else if (item.sub_type == ARM_LARGE_SHIELD)
            stat = you.dex * 12 + you.strength * 26;
        else
            stat = you.dex * 19 + you.strength * 19;
    }
    else
    {
        if (you.duration[DUR_MAGIC_SHIELD])
        {
            stat   =  600 + you.skills[SK_EVOCATIONS] * 50;
            shield += 300 + you.skills[SK_EVOCATIONS] * 25;
        }

        if (!you.duration[DUR_FIRE_SHIELD]
            && you.duration[DUR_CONDENSATION_SHIELD])
        {
            shield += 300 + (you.skills[SK_ICE_MAGIC] * 25);
            stat    = std::max(stat, you.intel * 38);
        }
    }

    if (you.duration[DUR_DIVINE_SHIELD])
    {
        shield += you.attribute[ATTR_DIVINE_SHIELD] * 150;
        stat = std::max(stat, int(you.attribute[ATTR_DIVINE_SHIELD] * 300));
    }

    if (shield + stat > 0)
        shield += skill_bump(SK_SHIELDS) * 38;

    return (shield + stat + 50) / 100;
}

int player_sust_abil(bool calc_unid)
{
    int sa = 0;

    sa += player_equip(EQ_RINGS, RING_SUSTAIN_ABILITIES, calc_unid);

    return (sa);
}

int carrying_capacity(burden_state_type bs)
{
    // Yuck.  We need this for gameplay - it nerfs small forms too much
    // otherwise - but there's no good way to rationalize here...  --sorear
    int used_weight = std::max(you.body_weight(), you.body_weight(true));

    int cap = (2 * used_weight) + (you.strength * 300)
              + (you.airborne() ? 1000 : 0);
    // We are nice to the lighter species in that strength adds absolutely
    // instead of relatively to body weight. --dpeg

    if (bs == BS_UNENCUMBERED)
        return ((cap * 5) / 6);
    else if (bs == BS_ENCUMBERED)
        return ((cap * 11) / 12);
    else
        return (cap);
}

int burden_change(void)
{
    const burden_state_type old_burdenstate = you.burden_state;
    const bool was_flying_light = you.light_flight();

    you.burden = 0;

    if (you.duration[DUR_STONEMAIL])
        you.burden += 800;

    for (int bu = 0; bu < ENDOFPACK; bu++)
    {
        if (you.inv[bu].quantity < 1)
            continue;

        you.burden += item_mass( you.inv[bu] ) * you.inv[bu].quantity;
    }

    you.burden_state = BS_UNENCUMBERED;
    set_redraw_status( REDRAW_BURDEN );
    you.redraw_evasion = true;

    // changed the burdened levels to match the change to max_carried
    if (you.burden < carrying_capacity(BS_UNENCUMBERED))
    {
        you.burden_state = BS_UNENCUMBERED;

        // this message may have to change, just testing {dlb}
        if (old_burdenstate != you.burden_state)
            mpr("Your possessions no longer seem quite so burdensome.");
    }
    else if (you.burden < carrying_capacity(BS_ENCUMBERED))
    {
        you.burden_state = BS_ENCUMBERED;

        if (old_burdenstate != you.burden_state)
        {
            mpr("You are being weighed down by all of your possessions.");
            learned_something_new(TUT_HEAVY_LOAD);
        }
    }
    else
    {
        you.burden_state = BS_OVERLOADED;

        if (old_burdenstate != you.burden_state)
        {
            mpr("You are being crushed by all of your possessions.");
            learned_something_new(TUT_HEAVY_LOAD);
        }
    }

    // Stop travel if we get burdened (as from potions of might/levitation
    // wearing off).
    if (you.burden_state > old_burdenstate)
        interrupt_activity( AI_BURDEN_CHANGE );

    const bool is_flying_light = you.light_flight();

    if (is_flying_light != was_flying_light)
    {
        mpr(is_flying_light ? "You feel quicker in the air."
                            : "You feel heavier in the air.");
    }

    return (you.burden);
}

// force is true for forget_map command on level map.
void forget_map(unsigned char chance_forgotten, bool force)
{
    ASSERT(!crawl_state.arena);

    if (force && !yesno("Really forget level map?", true, 'n'))
        return;

    // The radius is only used in labyrinths.
    const bool use_lab_check = (!force && you.level_type == LEVEL_LABYRINTH
                                && chance_forgotten < 100);
    const int radius = (use_lab_check && you.species == SP_MINOTAUR) ? 40*40
                                                                     : 25*25;
    for (rectangle_iterator ri(0); ri; ++ri)
    {
        if (!you.see_cell(*ri)
            && (force || x_chance_in_y(chance_forgotten, 100)
                || use_lab_check && (you.pos()-*ri).abs() > radius))
        {
            env.map_knowledge(*ri).clear();
#ifdef USE_TILE
            set_map_knowledge_obj(*ri, DNGN_UNSEEN);
            tiles.update_minimap(ri->x, ri->y);
            env.tile_bk_fg(*ri) = 0;
            env.tile_bk_bg(*ri) = tileidx_feature(DNGN_UNSEEN, ri->x, ri->y);
#endif
        }
    }

#ifdef USE_TILE
    tiles.update_minimap_bounds();
#endif
}

void gain_exp( unsigned int exp_gained, unsigned int* actual_gain,
               unsigned int* actual_avail_gain)
{
    if (crawl_state.arena)
        return;

    if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
        exp_gained = div_rand_round( exp_gained, 4 );

    const unsigned long old_exp   = you.experience;
    const int           old_avail = you.exp_available;

    dprf("gain_exp: %d", exp_gained );

    if (you.experience + exp_gained > (unsigned int)MAX_EXP_TOTAL)
        you.experience = MAX_EXP_TOTAL;
    else
        you.experience += exp_gained;

    if (you.duration[DUR_SAGE])
    {
        // Bonus skill training from Sage.
        you.exp_available =
            (exp_gained * you.sage_bonus_degree) / 100 + exp_gained / 2;
        exercise(you.sage_bonus_skill, 20);
        you.exp_available = old_avail;
        exp_gained /= 2;
    }

    if (you.exp_available + exp_gained > (unsigned int)MAX_EXP_POOL)
        you.exp_available = MAX_EXP_POOL;
    else
        you.exp_available += exp_gained;

    level_change();

    if (actual_gain != NULL)
        *actual_gain = you.experience - old_exp;

    if (actual_avail_gain != NULL)
        *actual_avail_gain = you.exp_available - old_avail;
}

void level_change(bool skip_attribute_increase)
{
    const bool wiz_cmd = crawl_state.prev_cmd == CMD_WIZARD
                      || crawl_state.repeat_cmd == CMD_WIZARD;

    // necessary for the time being, as level_change() is called
    // directly sometimes {dlb}
    you.redraw_experience = true;

    while (you.experience_level < 27
           && you.experience > exp_needed(you.experience_level + 2))
    {
        bool skip_more = false;

        if (!skip_attribute_increase && !wiz_cmd)
        {
            crawl_state.cancel_cmd_all();

            if (is_processing_macro())
                flush_input_buffer(FLUSH_ABORT_MACRO);
        }
        else if (crawl_state.is_replaying_keys()
                 || crawl_state.is_repeating_cmd() || is_processing_macro())
        {
            skip_more = true;
        }

        const int old_hp = you.hp;
        const int old_maxhp = you.hp_max;

        int hp_adjust = 0;
        int mp_adjust = 0;

        // [ds] Make sure we increment you.experience_level and apply
        // any stat/hp increases only after we've cleared all prompts
        // for this experience level. If we do part of the work before
        // the prompt, and a player on telnet gets disconnected, the
        // SIGHUP will save Crawl in the in-between state and rob the
        // player of their level-up perks.

        const int new_exp = you.experience_level + 1;

        if (new_exp <= you.max_level)
        {
            mprf(MSGCH_INTRINSIC_GAIN,
                 "Welcome back to level %d!", new_exp);
            if (!skip_more)
                more();

            // No more prompts for this XL past this point.

            you.experience_level = new_exp;
            // Gain back the hp and mp we lose in lose_level().  -- bwr
            inc_hp(4, true);
            inc_mp(1, true);
        }
        else  // Character has gained a new level
        {
            if (new_exp == 27)
            {
               mprf(MSGCH_INTRINSIC_GAIN,
                    "You have reached level 27, the final one!");
            }
            else
            {
               mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d!",
                    new_exp);
            }

            if (!(new_exp % 3) && !skip_attribute_increase)
                _attribute_increase();
            else if (!skip_more)
                more();

            // No more prompts for this XL past this point.

            int brek = 0;

            if (new_exp > 21)
                brek = 2 + new_exp % 2;
            else if (new_exp > 12)
                brek = 4;                  // up from 2 + rand(3) -- bwr
            else
                brek = 5 + new_exp % 2;    // up from 3 + rand(4) -- bwr

            you.experience_level = new_exp;
            inc_hp(brek, true);
            inc_mp(1, true);

            switch (you.species)
            {
            case SP_HUMAN:
                if (!(you.experience_level % 5))
                    modify_stat(STAT_RANDOM, 1, false, "level gain");
                break;

            case SP_HIGH_ELF:
                if (you.experience_level % 3)
                    hp_adjust--;

                if (!(you.experience_level % 2))
                    mp_adjust++;

                if (!(you.experience_level % 3))
                {
                    modify_stat((coinflip() ? STAT_INTELLIGENCE
                                            : STAT_DEXTERITY), 1, false,
                                "level gain");
                }
                break;

            case SP_DEEP_ELF:
                if (you.experience_level < 17)
                    hp_adjust--;

                if (!(you.experience_level % 3))
                    hp_adjust--;

                mp_adjust++;

                if (!(you.experience_level % 4))
                    modify_stat(STAT_INTELLIGENCE, 1, false, "level gain");
                break;

            case SP_SLUDGE_ELF:
                if (you.experience_level % 3)
                    hp_adjust--;
                else
                    mp_adjust++;

                if (!(you.experience_level % 4))
                {
                    modify_stat((coinflip() ? STAT_INTELLIGENCE
                                            : STAT_DEXTERITY), 1, false,
                                "level gain");
                }
                break;

            case SP_MOUNTAIN_DWARF:
                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (!(you.experience_level % 3))
                    mp_adjust--;

                if (!(you.experience_level % 4))
                    modify_stat(STAT_STRENGTH, 1, false, "level gain");
                break;

            case SP_DEEP_DWARF:
                hp_adjust++;

                if (you.experience_level == 14)
                {
                    mpr("You feel somewhat more resistant.",
                        MSGCH_INTRINSIC_GAIN);
                    perma_mutate(MUT_NEGATIVE_ENERGY_RESISTANCE, 1);
                }

                if ((you.experience_level == 9)
                    || (you.experience_level == 18))
                {
                    perma_mutate(MUT_PASSIVE_MAPPING, 1);
                }

                if (!(you.experience_level % 4))
                {
                    modify_stat(coinflip() ? STAT_STRENGTH
                                           : STAT_INTELLIGENCE, 1, false,
                                "level gain");
                }
                break;

            case SP_HALFLING:
                if (!(you.experience_level % 5))
                    modify_stat(STAT_DEXTERITY, 1, false, "level gain");

                if (you.experience_level % 3)
                    hp_adjust--;

                break;

            case SP_KOBOLD:
                if (!(you.experience_level % 5))
                {
                    modify_stat((coinflip() ? STAT_STRENGTH
                                            : STAT_DEXTERITY), 1, false,
                                "level gain");
                }

                if (you.experience_level < 17)
                    hp_adjust--;

                if (!(you.experience_level % 2))
                    hp_adjust--;
                break;

            case SP_HILL_ORC:
                // lower because of HD raise -- bwr
                // if (you.experience_level < 17)
                //     hp_adjust++;

                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (!(you.experience_level % 3))
                    mp_adjust--;

                if (!(you.experience_level % 5))
                    modify_stat(STAT_STRENGTH, 1, false, "level gain");
                break;

            case SP_MUMMY:
                if (you.experience_level == 13 || you.experience_level == 26)
                {
                    mpr("You feel more in touch with the powers of death.",
                        MSGCH_INTRINSIC_GAIN);
                }

                if (you.experience_level == 13)  // level 13 for now -- bwr
                {
                    mpr("You can now infuse your body with magic to restore "
                        "decomposition.", MSGCH_INTRINSIC_GAIN);
                }
                break;

            case SP_VAMPIRE:
                if (you.experience_level == 3)
                {
                    if (you.hunger_state > HS_SATIATED)
                    {
                        mpr("If you weren't so full you could now transform "
                            "into a vampire bat.", MSGCH_INTRINSIC_GAIN);
                    }
                    else
                    {
                        mpr("You can now transform into a vampire bat.",
                            MSGCH_INTRINSIC_GAIN);
                    }
                }
                else if (you.experience_level == 6)
                {
                    mpr("You can now bottle potions of blood from chopped up "
                        "corpses.", MSGCH_INTRINSIC_GAIN);
                }
                else if (you.experience_level == 10)
                {
                    mpr("Cursed equipment will now meld into your body when "
                        "transforming into a vampire bat.",
                        MSGCH_INTRINSIC_GAIN);
                }
                break;

            case SP_NAGA:
                hp_adjust++;

                if (!(you.experience_level % 4))
                    modify_stat(STAT_RANDOM, 1, false, "level gain");

                if (!(you.experience_level % 3))
                {
                    mpr("Your skin feels tougher.", MSGCH_INTRINSIC_GAIN);
                    you.redraw_armour_class = true;
                }
                break;

            case SP_TROLL:
                hp_adjust++;

                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (you.experience_level % 3)
                    mp_adjust--;

                if (!(you.experience_level % 3))
                    modify_stat(STAT_STRENGTH, 1, false, "level gain");
                break;

            case SP_OGRE:
                hp_adjust++;

                if (!(you.experience_level % 3))
                    modify_stat(STAT_STRENGTH, 1, false, "level gain");
                break;

            case SP_RED_DRACONIAN:
            case SP_WHITE_DRACONIAN:
            case SP_GREEN_DRACONIAN:
            case SP_YELLOW_DRACONIAN:
            // Grey is handled later.
            case SP_BLACK_DRACONIAN:
            case SP_PURPLE_DRACONIAN:
            case SP_MOTTLED_DRACONIAN:
            case SP_PALE_DRACONIAN:
            case SP_BASE_DRACONIAN:
                if (you.experience_level == 7)
                {
#ifdef USE_TILE
                    init_player_doll();
#endif
                    switch (you.species)
                    {
                    case SP_RED_DRACONIAN:
                        mpr("Your scales start taking on a fiery red colour.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_WHITE_DRACONIAN:
                        mpr("Your scales start taking on an icy white colour.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_GREEN_DRACONIAN:
                        mpr("Your scales start taking on a green colour.",
                            MSGCH_INTRINSIC_GAIN);
                        // Green draconians get this at level 7.
                        perma_mutate(MUT_POISON_RESISTANCE, 1);
                        break;
                    case SP_YELLOW_DRACONIAN:
                        mpr("Your scales start taking on a golden yellow colour.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_BLACK_DRACONIAN:
                        mpr("Your scales start turning black.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_PURPLE_DRACONIAN:
                        mpr("Your scales start taking on a rich purple colour.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_MOTTLED_DRACONIAN:
                        mpr("Your scales start taking on a weird mottled pattern.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_PALE_DRACONIAN:
                        mpr("Your scales start fading to a pale grey colour.",
                            MSGCH_INTRINSIC_GAIN);
                        break;
                    case SP_BASE_DRACONIAN:
                        mpr("");
                        break;

                    default:
                        break;
                    }
                    more();
                    redraw_screen();
                }

                if (you.experience_level == 14)
                {
                    switch (you.species)
                    {
                    case SP_RED_DRACONIAN:
                        perma_mutate(MUT_HEAT_RESISTANCE, 1);
                        break;
                    case SP_WHITE_DRACONIAN:
                        perma_mutate(MUT_COLD_RESISTANCE, 1);
                        break;
                    default:
                        break;
                    }
                }
                else if (you.species == SP_BLACK_DRACONIAN
                         && you.experience_level == 18)
                {
                    perma_mutate(MUT_SHOCK_RESISTANCE, 1);
                }

                if (!(you.experience_level % 3))
                    hp_adjust++;

                if (you.experience_level > 7 && !(you.experience_level % 4))
                {
                    mpr("Your scales feel tougher.", MSGCH_INTRINSIC_GAIN);
                    you.redraw_armour_class = true;
                    modify_stat(STAT_RANDOM, 1, false, "level gain");
                }
                break;

            case SP_GREY_DRACONIAN:
                if (you.experience_level == 7)
                {
#ifdef USE_TILE
                    init_player_doll();
#endif
                    mpr("Your scales start turning grey.",
                        MSGCH_INTRINSIC_GAIN);
                    more();
                    redraw_screen();
                }

                if (!(you.experience_level % 3))
                {
                    hp_adjust++;

                    if (you.experience_level > 7)
                        hp_adjust++;
                }

                if (you.experience_level > 7 && !(you.experience_level % 2))
                {
                    mpr("Your scales feel tougher.", MSGCH_INTRINSIC_GAIN);
                    you.redraw_armour_class = true;
                }

                if ((you.experience_level > 7 && !(you.experience_level % 3))
                    || you.experience_level == 4 || you.experience_level == 7)
                {
                    modify_stat(STAT_RANDOM, 1, false, "level gain");
                }
                break;

            case SP_CENTAUR:
                if (!(you.experience_level % 4))
                {
                    modify_stat((coinflip() ? STAT_STRENGTH
                                            : STAT_DEXTERITY), 1, false,
                                "level gain");
                }

                // lowered because of HD raise -- bwr
                // if (you.experience_level < 17)
                //     hp_adjust++;

                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (!(you.experience_level % 3))
                    mp_adjust--;
                break;

            case SP_DEMIGOD:
                if (!(you.experience_level % 2))
                    modify_stat(STAT_RANDOM, 1, false, "level gain");

                // lowered because of HD raise -- bwr
                // if (you.experience_level < 17)
                //    hp_adjust++;

                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (you.experience_level % 3)
                    mp_adjust++;
                break;

            case SP_SPRIGGAN:
                if (you.experience_level < 17)
                    hp_adjust--;

                if (you.experience_level % 3)
                    hp_adjust--;

                mp_adjust++;

                if (!(you.experience_level % 5))
                {
                    modify_stat((coinflip() ? STAT_INTELLIGENCE
                                            : STAT_DEXTERITY), 1, false,
                                "level gain");
                }
                break;

            case SP_MINOTAUR:
                // lowered because of HD raise -- bwr
                // if (you.experience_level < 17)
                //     hp_adjust++;

                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (!(you.experience_level % 2))
                    mp_adjust--;

                if (!(you.experience_level % 4))
                {
                    modify_stat((coinflip() ? STAT_STRENGTH
                                            : STAT_DEXTERITY), 1, false,
                                "level gain");
                }
                break;

            case SP_DEMONSPAWN:
                {
                    bool gave_message = false;

                    for (unsigned i = 0; i < you.demonic_traits.size(); ++i)
                    {
                        if (you.demonic_traits[i].level_gained
                            == you.experience_level)
                        {
                            if (!gave_message)
                            {
                                mpr("Your demonic ancestry asserts itself...",
                                    MSGCH_INTRINSIC_GAIN);

                                gave_message = true;
                            }
                            perma_mutate(you.demonic_traits[i].mutation, 1);
                        }
                    }
                }

                if (!(you.experience_level % 4))
                    modify_stat(STAT_RANDOM, 1, false, "level gain");
                break;

            case SP_GHOUL:
                // lowered because of HD raise -- bwr
                // if (you.experience_level < 17)
                //     hp_adjust++;

                if (!(you.experience_level % 2))
                    hp_adjust++;

                if (!(you.experience_level % 3))
                    mp_adjust--;

                if (!(you.experience_level % 5))
                    modify_stat(STAT_STRENGTH, 1, false, "level gain");
                break;

            case SP_KENKU:
                if (you.experience_level < 17)
                    hp_adjust--;

                if (!(you.experience_level % 3))
                    hp_adjust--;

                if (!(you.experience_level % 4))
                    modify_stat(STAT_RANDOM, 1, false, "level gain");

                if (you.experience_level == 5)
                {
                    mpr("You have gained the ability to fly.",
                        MSGCH_INTRINSIC_GAIN);
                }
                else if (you.experience_level == 15)
                    mpr("You can now fly continuously.", MSGCH_INTRINSIC_GAIN);
                break;

            case SP_MERFOLK:
                if (you.experience_level % 3)
                    hp_adjust++;

                if (!(you.experience_level % 5))
                    modify_stat(STAT_RANDOM, 1, false, "level gain");
                break;

            default:
                break;
            }
        }

        // add hp and mp adjustments - GDL
        inc_max_hp(hp_adjust);
        inc_max_mp(mp_adjust);

        deflate_hp(you.hp_max, false);

        you.hp = old_hp * you.hp_max / old_maxhp;
        you.magic_points = std::max(0, you.magic_points);

        // Get "real" values for note-taking, i.e. ignore Berserk,
        // transformations or equipped items.
        const int note_maxhp = get_real_hp(false, false);
        const int note_maxmp = get_real_mp(false);

        char buf[200];
        sprintf(buf, "HP: %d/%d MP: %d/%d",
                std::min(you.hp, note_maxhp), note_maxhp,
                std::min(you.magic_points, note_maxmp), note_maxmp);
        take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, buf));

        // recalculate for game
        calc_hp();
        calc_mp();

        if (you.experience_level > you.max_level)
            you.max_level = you.experience_level;

        xom_is_stimulated(16);

        learned_something_new(TUT_NEW_LEVEL);
    }

    redraw_skill(you.your_name, player_title());

    // Increase tutorial time-out now that it's actually become useful
    // for a longer time.
    if (Tutorial.tutorial_left && you.experience_level >= 7)
        tutorial_finished();
}

// Here's a question for you: does the ordering of mods make a difference?
// (yes) -- are these things in the right order of application to stealth?
// - 12mar2000 {dlb}
int check_stealth(void)
{
    ASSERT(!crawl_state.arena);
#ifdef WIZARD
    // Extreme stealthiness can be enforced by wizmode stealth setting.
    if (you.skills[SK_STEALTH] > 27)
        return (1000);
#endif

    if (you.attribute[ATTR_SHADOWS] || you.berserk())
        return (0);

    int stealth = you.dex * 3;

    if (you.skills[SK_STEALTH])
    {
        if (player_genus(GENPC_DRACONIAN))
            stealth += (you.skills[SK_STEALTH] * 12);
        else
        {
            switch (you.species) // why not use body_size here?
            {
            case SP_TROLL:
            case SP_OGRE:
            case SP_CENTAUR:
                stealth += (you.skills[SK_STEALTH] * 9);
                break;
            case SP_MINOTAUR:
                stealth += (you.skills[SK_STEALTH] * 12);
                break;
            case SP_VAMPIRE:
                // Thirsty/bat-form vampires are (much) more stealthy
                if (you.hunger_state == HS_STARVING)
                    stealth += (you.skills[SK_STEALTH] * 21);
                else if (player_in_bat_form()
                         || you.hunger_state <= HS_NEAR_STARVING)
                {
                    stealth += (you.skills[SK_STEALTH] * 20);
                }
                else if (you.hunger_state < HS_SATIATED)
                    stealth += (you.skills[SK_STEALTH] * 19);
                else
                    stealth += (you.skills[SK_STEALTH] * 18);
                break;
            case SP_HALFLING:
            case SP_KOBOLD:
            case SP_SPRIGGAN:
            case SP_NAGA:       // not small but very good at stealth
                stealth += (you.skills[SK_STEALTH] * 18);
                break;
            default:
                stealth += (you.skills[SK_STEALTH] * 15);
                break;
            }
        }
    }

    if (you.burden_state > BS_UNENCUMBERED)
        stealth /= you.burden_state;

    if (you.confused())
        stealth /= 3;

    const int arm   = you.equip[EQ_BODY_ARMOUR];
    const int cloak = you.equip[EQ_CLOAK];
    const int boots = you.equip[EQ_BOOTS];

    if (arm != -1 && !player_light_armour())
        stealth -= (item_mass( you.inv[arm] ) / 10);

    if (cloak != -1 && get_equip_race(you.inv[cloak]) == ISFLAG_ELVEN)
        stealth += 20;

    if (boots != -1)
    {
        if (get_armour_ego_type( you.inv[boots] ) == SPARM_STEALTH)
            stealth += 50;

        if (get_equip_race(you.inv[boots]) == ISFLAG_ELVEN)
            stealth += 20;
    }

    if ( you.duration[DUR_STEALTH] )
        stealth += 80;

    if (you.duration[DUR_AGILITY])
        stealth += 50;

    stealth += scan_artefacts( ARTP_STEALTH );

    if (you.airborne())
        stealth += 10;
    else if (you.in_water())
    {
        // Merfolk can sneak up on monsters underwater -- bwr
        if (you.species == SP_MERFOLK)
            stealth += 50;
        else if ( !you.can_swim() && !you.extra_balanced() )
            stealth /= 2;       // splashy-splashy
    }
    else if (player_mutation_level(MUT_HOOVES))
        stealth -= 10;  // clippety-clop

    // Radiating silence is the negative complement of shouting all the
    // time... a sudden change from background noise to no noise is going
    // to clue anything in to the fact that something is very wrong...
    // a personal silence spell would naturally be different, but this
    // silence radiates for a distance and prevents monster spellcasting,
    // which pretty much gives away the stealth game.
    if (you.duration[DUR_SILENCE])
        stealth -= 50;

    stealth = std::max(0, stealth);

    return (stealth);
}

static void _attribute_increase()
{
    mpr("Your experience leads to an increase in your attributes!",
        MSGCH_INTRINSIC_GAIN);

    learned_something_new(TUT_CHOOSE_STAT);
    more();
    mesclr();

    mpr("Increase (S)trength, (I)ntelligence, or (D)exterity? ", MSGCH_PROMPT);

    while (true)
    {
        const int keyin = getch();

        switch (keyin)
        {
#if defined(USE_UNIX_SIGNALS) && defined(SIGHUP_SAVE) && defined(USE_CURSES)
        case ESCAPE:
        case -1:
            // [ds] It's safe to save the game here; when the player
            // reloads, the game will re-prompt for their level-up
            // stat gain.
            if (crawl_state.seen_hups)
                sighup_save_and_exit();
            break;
#endif

        case 's':
        case 'S':
            modify_stat(STAT_STRENGTH, 1, false, "level gain");
            you.last_chosen = STAT_STRENGTH;
            return;

        case 'i':
        case 'I':
            modify_stat(STAT_INTELLIGENCE, 1, false, "level gain");
            you.last_chosen = STAT_INTELLIGENCE;
            return;

        case 'd':
        case 'D':
            modify_stat(STAT_DEXTERITY, 1, false, "level gain");
            you.last_chosen = STAT_DEXTERITY;
            return;
        }
    }
}

// Rearrange stats, biased towards the stat chosen last at level up.
void jiyva_stat_action()
{
    char* max_statp[]  = { &you.max_strength, &you.max_intel, &you.max_dex };
    char* base_statp[] = { &you.strength, &you.intel, &you.dex };
    int incremented_weight[] = {1, 1, 1};
    int decremented_weight[3];
    int stat_up_choice;
    int stat_down_choice;

    incremented_weight[you.last_chosen] = 2;

    for (int x = 0; x < 3; ++x)
         decremented_weight[x] = std::min(10, std::max(0, *max_statp[x] - 7));

    stat_up_choice = choose_random_weighted(incremented_weight,
                                            incremented_weight + 3);
    stat_down_choice = choose_random_weighted(decremented_weight,
                                              decremented_weight + 3);

    if (stat_up_choice != stat_down_choice)
    {
        // We have a stat change noticeable to the player at this point.
        // This could be lethal if the player currently has 1 in a stat
        // but has a max stat of something higher -- perhaps we should
        // check for that?

        (*max_statp[stat_up_choice])++;
        (*max_statp[stat_down_choice])--;
        (*base_statp[stat_up_choice])++;
        (*base_statp[stat_down_choice])--;

        simple_god_message("'s power touches on your attributes.");

        you.redraw_strength     = true;
        you.redraw_intelligence = true;
        you.redraw_dexterity    = true;

        burden_change();
    }
}

static const char * _get_rotting_how()
{
    ASSERT(you.rotting > 0 || you.species == SP_GHOUL);

    if (you.rotting > 15)
        return (" before your eyes");
    if (you.rotting > 8)
        return (" away quickly");
    if (you.rotting > 4)
        return (" badly");

    if (you.species == SP_GHOUL)
        return (" faster than usual");

    return ("");
}

// Returns the medium duration value which is usually announced by a special
// message ("XY is about to time out") or a change of colour in the
// status display.
// Note that these values cannot be relied on when playing since there are
// random decrements precisely to avoid this.
int get_expiration_threshold(duration_type dur)
{
    switch (dur)
    {
    case DUR_FIRE_SHIELD:
    case DUR_SILENCE: // no message
        return (5 * BASELINE_DELAY);

    case DUR_DEFLECT_MISSILES:
    case DUR_REPEL_MISSILES:
    case DUR_REGENERATION:
    case DUR_INSULATION:
    case DUR_STONEMAIL:
    case DUR_SWIFTNESS:
    case DUR_INVIS:
    case DUR_HASTE:
    // The following are not shown in the status lines.
    case DUR_ICY_ARMOUR:
    case DUR_PHASE_SHIFT:
    case DUR_CONTROL_TELEPORT:
    case DUR_DEATH_CHANNEL:
        return (6 * BASELINE_DELAY);

    case DUR_LEVITATION:
    case DUR_TRANSFORMATION: // not on status
    case DUR_DEATHS_DOOR:    // not on status
    case DUR_SLIMIFY:
        return (10 * BASELINE_DELAY);

    // These get no messages when they "flicker".
    case DUR_SAGE:
    case DUR_BARGAIN:
        return (15 * BASELINE_DELAY);

    case DUR_CONFUSING_TOUCH:
        return (20 * BASELINE_DELAY);

    default:
        return (0);
    }
}

// Is a given duration about to expire?
bool dur_expiring(duration_type dur)
{
    const int value = you.duration[dur];
    if (value <= 0)
        return (false);

    return (value <= get_expiration_threshold(dur));
}

static void _output_expiring_message(duration_type dur, const char* msg)
{
    if (you.duration[dur])
    {
        const bool expires = dur_expiring(dur);
        mprf("%s%s", expires ? "Expiring: " : "", msg);
    }
}

void display_char_status()
{
    if (you.is_undead == US_SEMI_UNDEAD && you.hunger_state == HS_ENGORGED)
        mpr("You feel almost alive.");
    else if (you.is_undead)
        mpr("You are undead.");
    else if (you.duration[DUR_DEATHS_DOOR])
    {
        _output_expiring_message(DUR_DEATHS_DOOR,
                                 "You are standing in death's doorway.");
    }
    else
        mpr("You are alive.");

    const int halo_size = you.halo_radius();
    if (halo_size > 0)
    {
        if (halo_size > 6)
            mpr("You are illuminated by a large divine halo.");
        else if (halo_size > 3)
            mpr("You are illuminated by a divine halo.");
        else
            mpr("You are illuminated by a small divine halo.");
    }

    if (you.species == SP_VAMPIRE)
    {
        std::string msg = "At your current hunger state you ";
        std::vector<std::string> attrib;

        switch (you.hunger_state)
        {
            case HS_STARVING:
                attrib.push_back("resist poison");
                attrib.push_back("significantly resist cold");
                attrib.push_back("strongly resist negative energy");
                attrib.push_back("resist torment");
                attrib.push_back("do not heal.");
                break;
            case HS_NEAR_STARVING:
                attrib.push_back("resist poison");
                attrib.push_back("significantly resist cold");
                attrib.push_back("strongly resist negative energy");
                attrib.push_back("have an extremely slow metabolism");
                attrib.push_back("heal slowly.");
                break;
            case HS_HUNGRY:
            case HS_VERY_HUNGRY:
                attrib.push_back("resist poison");
                attrib.push_back("resist cold");
                attrib.push_back("significantly resist negative energy");
                if (you.hunger_state == HS_HUNGRY)
                    attrib.push_back("have a slow metabolism");
                else
                    attrib.push_back("have a very slow metabolism");
                attrib.push_back("heal slowly.");
                break;
            case HS_SATIATED:
                attrib.push_back("resist negative energy.");
                break;
            case HS_FULL:
                attrib.push_back("have a fast metabolism");
                attrib.push_back("heal quickly.");
                break;
            case HS_VERY_FULL:
                attrib.push_back("have a very fast metabolism");
                attrib.push_back("heal quickly.");
                break;
            case HS_ENGORGED:
                attrib.push_back("have an extremely fast metabolism");
                attrib.push_back("heal extremely quickly.");
                break;
        }

        if (!attrib.empty())
        {
            msg += comma_separated_line(attrib.begin(), attrib.end());
            mpr(msg.c_str());
        }
    }

    if (you.duration[DUR_TRANSFORMATION] > 0)
    {
        std::string text;

        if ((you.species != SP_VAMPIRE || !player_in_bat_form())
            && dur_expiring(DUR_TRANSFORMATION))
        {
            text = "Expiring: ";
        }

        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_SPIDER:
            text += "You are in spider-form.";
            break;
        case TRAN_BAT:
            text += "You are in ";
            if (you.species == SP_VAMPIRE)
                text += "vampire ";
            text += "bat-form.";
            break;
        case TRAN_BLADE_HANDS:
            text += "You have blades for hands.";
            break;
        case TRAN_STATUE:
            text += "You are a statue.";
            break;
        case TRAN_ICE_BEAST:
            text += "You are an ice creature.";
            break;
        case TRAN_DRAGON:
            text += "You are in dragon-form.";
            break;
        case TRAN_LICH:
            text += "You are in lich-form.";
            break;
        case TRAN_PIG:
            text += "You are a filthy swine.";
            break;
        }
        mpr(text.c_str());
    }

    if (you.burden_state == BS_ENCUMBERED)
        mpr("You are burdened.");
    else if (you.burden_state == BS_OVERLOADED)
        mpr("You are overloaded with stuff.");
    else if (you.species == SP_KENKU && you.flight_mode() == FL_FLY)
    {
        if (you.travelling_light())
            mpr("Your small burden allows quick flight.");
        else
            mpr("Your heavy burden is slowing your flight.");
    }

    if (you.duration[DUR_SAGE])
    {
        std::string msg  = "You are studying ";
                    msg += skill_name(you.sage_bonus_skill);
                    msg += ".";
        _output_expiring_message(DUR_SAGE, msg.c_str());
    }

    _output_expiring_message(DUR_BARGAIN, "You get a bargain in shops.");

    if (you.duration[DUR_BREATH_WEAPON])
        mpr("You are short of breath.");

    if (you.duration[DUR_LIQUID_FLAMES])
        mpr("You are covered in liquid flames.");

    if (you.duration[DUR_FIRE_SHIELD])
    {
        _output_expiring_message(DUR_FIRE_SHIELD,
                                 "You are surrounded by a ring of flames.");
        _output_expiring_message(DUR_FIRE_SHIELD,
                                 "You are immune to clouds of flame.");
    }

    _output_expiring_message(DUR_ICY_ARMOUR,
                             "You are protected by an icy armour.");

    _output_expiring_message(DUR_REPEL_MISSILES,
                             "You are protected from missiles.");

    _output_expiring_message(DUR_DEFLECT_MISSILES, "You deflect missiles.");

    if (you.duration[DUR_PRAYER])
        mpr("You are praying.");

    // Disease and regen influence each other.
    if (you.disease)
    {
        if (!you.duration[DUR_REGENERATION])
            mpr("You are sick.");
        else
        {
            _output_expiring_message(DUR_REGENERATION,
                                     "recuperating from your illness");
        }
    }
    else
    {
        bool no_heal =
            (you.species == SP_VAMPIRE && you.hunger_state == HS_STARVING)
            || (player_mutation_level(MUT_SLOW_HEALING) == 3);

        if (!no_heal)
            _output_expiring_message(DUR_REGENERATION, "regenerating");
    }

    _output_expiring_message(DUR_SWIFTNESS, "You can move swiftly.");

    _output_expiring_message(DUR_INSULATION, "You are insulated.");

    _output_expiring_message(DUR_STONEMAIL,
                             "You are covered in scales of stone.");

    if (you.duration[DUR_CONTROLLED_FLIGHT])
        mpr("You can control your flight.");

    if (you.duration[DUR_TELEPORT])
        mpr("You are about to teleport.");

    _output_expiring_message(DUR_CONTROL_TELEPORT,
                             "You can control teleportation.");

    _output_expiring_message(DUR_DEATH_CHANNEL, "You are channeling the dead.");

    _output_expiring_message(DUR_PHASE_SHIFT,
                             "You are out of phase with the material plane.");

    _output_expiring_message(DUR_SILENCE, "You radiate silence.");

    if (you.duration[DUR_STONESKIN])
        mpr("Your skin is tough as stone.");

    if (you.duration[DUR_SEE_INVISIBLE])
        mpr("You can see invisible.");

    _output_expiring_message(DUR_INVIS, "You are invisible.");

    if (you.confused())
        mpr("You are confused.");

    // TODO: Distinguish between mermaids and sirens!
    if (you.beheld())
        mpr("You are mesmerised.");

    // How exactly did you get to show the status?
    if (you.paralysed())
        mpr("You are paralysed.");
    if (you.petrified())
        mpr("You are petrified.");
    if (you.asleep())
        mpr("You are asleep.");

    if (you.duration[DUR_EXHAUSTED])
        mpr("You are exhausted.");

    if (you.duration[DUR_SLOW] && you.duration[DUR_HASTE])
        mpr("You are under both slowing and hasting effects.");
    else if (you.duration[DUR_SLOW])
        mpr("Your actions are slowed.");
    else if (you.duration[DUR_HASTE])
        _output_expiring_message(DUR_HASTE, "Your actions are hasted.");

    if (you.duration[DUR_MIGHT])
        mpr("You are mighty.");
    if (you.duration[DUR_BRILLIANCE])
        mpr("You are brilliant.");
    if (you.duration[DUR_AGILITY])
        mpr("You are agile.");

    if (you.duration[DUR_DIVINE_VIGOUR])
        mpr("You are divinely vigorous.");

    if (you.duration[DUR_DIVINE_STAMINA])
        mpr("You are divinely fortified.");

    if (you.berserk())
        mpr("You are possessed by a berserker rage.");

    if (you.airborne())
    {
        const bool expires = dur_expiring(DUR_LEVITATION)
                             && !you.permanent_flight();

        mprf(expires ? MSGCH_WARN : MSGCH_PLAIN,
             "%sYou are hovering above the floor.",
             expires ? "Expiring: " : "");
    }

    if (you.attribute[ATTR_HELD])
        mpr("You are held in a net.");

    if (you.duration[DUR_POISONING])
    {
        mprf("You are %s poisoned.",
             (you.duration[DUR_POISONING] > 10) ? "extremely" :
             (you.duration[DUR_POISONING] > 5)  ? "very" :
             (you.duration[DUR_POISONING] > 3)  ? "quite"
                                                : "mildly" );
    }

    if (you.disease)
    {
        int high = 120 * BASELINE_DELAY;
        int low  =  40 * BASELINE_DELAY;
        mprf("You are %sdiseased.",
             (you.disease > high) ? "badly " :
             (you.disease >  low) ? ""
                                 : "mildly ");
    }

    if (you.rotting || you.species == SP_GHOUL)
        mprf("Your flesh is rotting%s.", _get_rotting_how());

    // Prints a contamination message.
    contaminate_player(0, false, true);

    if (you.duration[DUR_CONFUSING_TOUCH])
    {
        int hi = 40 * BASELINE_DELAY;
        int low = 20 * BASELINE_DELAY;
        mprf("Your hands are glowing %s red.",
             (you.duration[DUR_CONFUSING_TOUCH] > hi) ? "an extremely bright" :
             (you.duration[DUR_CONFUSING_TOUCH] > low) ? "bright"
                                                      : "a soft");
    }

    if (you.duration[DUR_SURE_BLADE])
    {
        mprf("You have a %sbond with your blade.",
             (you.duration[DUR_SURE_BLADE] > 15 * BASELINE_DELAY) ? "strong " :
             (you.duration[DUR_SURE_BLADE] >  5 * BASELINE_DELAY) ? ""
                                                 : "weak ");
    }

    const int move_cost = (player_speed() * player_movement_speed()) / 10;

    const bool water  = you.in_water();
    const bool swim   = you.swimming();

    const bool lev    = you.airborne();
    const bool fly    = (you.flight_mode() == FL_FLY);
    const bool swift  = (you.duration[DUR_SWIFTNESS] > 0);

    mprf( "Your %s speed is %s%s%s.",
          // order is important for these:
          (swim)    ? "swimming" :
          (water)   ? "wading" :
          (fly)     ? "flying" :
          (lev)     ? "levitating"
                    : "movement",

          (water && !swim)  ? "uncertain and " :
          (!water && swift) ? "aided by the wind" : "",

          (!water && swift) ? ((move_cost >= 10) ? ", but still "
                                                 : " and ")
                            : "",

          (move_cost <   8) ? "very quick" :
          (move_cost <  10) ? "quick" :
          (move_cost == 10) ? "average" :
          (move_cost <  13) ? "slow"
                            : "very slow" );

#if DEBUG_DIAGNOSTICS
    const int to_hit = calc_your_to_hit( false ) * 2;
    mprf("To-hit: %d", to_hit);
#endif
/*
    // Messages based largely on percentage chance of missing the
    // average EV 10 humanoid, and very agile EV 30 (pretty much
    // max EV for monsters currently).
    //
    // "awkward"    - need lucky hit (less than EV)
    // "difficult"  - worse than 2 in 3
    // "hard"       - worse than fair chance
    mprf("%s given your current equipment.",
         (to_hit <   1) ? "You are completely incapable of fighting" :
         (to_hit <   5) ? "Hitting even clumsy monsters is extremely awkward" :
         (to_hit <  10) ? "Hitting average monsters is awkward" :
         (to_hit <  15) ? "Hitting average monsters is difficult" :
         (to_hit <  20) ? "Hitting average monsters is hard" :
         (to_hit <  30) ? "Very agile monsters are a bit awkward to hit" :
         (to_hit <  45) ? "Very agile monsters are a bit difficult to hit" :
         (to_hit <  60) ? "Very agile monsters are a bit hard to hit" :
         (to_hit < 100) ? "You feel comfortable with your ability to fight"
                        : "You feel confident with your ability to fight" );
*/

    // magic resistance
    const int mr = you.res_magic();
    mprf("You are %s resistant to magic.", magic_res_adjective(mr).c_str());

    // character evaluates their ability to sneak around:
    mprf("You feel %s.", stealth_desc(check_stealth()).c_str());
    dprf("stealth: %d", check_stealth());
}

bool player_item_conserve(bool calc_unid)
{
    return (player_equip(EQ_AMULET, AMU_CONSERVATION, calc_unid)
            || player_equip_ego_type(EQ_CLOAK, SPARM_PRESERVATION));
}

int player_mental_clarity(bool calc_unid, bool items)
{
    const int ret = (3 * player_equip(EQ_AMULET, AMU_CLARITY, calc_unid)
                       * items)
                     + player_mutation_level(MUT_CLARITY);

    return ((ret > 3) ? 3 : ret);
}

int player_spirit_shield(bool calc_unid)
{
    return player_equip(EQ_AMULET, AMU_GUARDIAN_SPIRIT, calc_unid)
           + player_equip_ego_type(EQ_HELMET, SPARM_SPIRIT_SHIELD);
}

// Returns whether the player has the effect of the amulet from a
// non-amulet source.
bool extrinsic_amulet_effect(jewellery_type amulet)
{
    switch (amulet)
    {
    case AMU_CONTROLLED_FLIGHT:
        return (you.duration[DUR_CONTROLLED_FLIGHT]
                || player_genus(GENPC_DRACONIAN)
                || (you.species == SP_KENKU && you.experience_level >= 5)
                || you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
                || you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT);
    case AMU_CLARITY:
        return (player_mutation_level(MUT_CLARITY) > 0);
    case AMU_RESIST_CORROSION:
        if (you.religion == GOD_JIYVA && you.piety > piety_breakpoint(2))
            return (true);
        // else fall-through
    case AMU_CONSERVATION:
        return (player_equip_ego_type(EQ_CLOAK, SPARM_PRESERVATION) > 0);
    case AMU_THE_GOURMAND:
        return (player_mutation_level(MUT_GOURMAND) > 0);
    default:
        return (false);
    }
}

bool wearing_amulet(jewellery_type amulet, bool calc_unid)
{
    if (extrinsic_amulet_effect(amulet))
        return (true);

    if (!player_wearing_slot(EQ_AMULET))
        return (false);

    const item_def& amu(you.inv[you.equip[EQ_AMULET]]);
    return (amu.sub_type == amulet && (calc_unid || item_type_known(amu)));
}

static int _species_exp_mod(species_type species)
{
    if (player_genus(GENPC_DRACONIAN, species))
        return 14;
    else if (player_genus(GENPC_DWARVEN, species))
        return 13;
    switch (species)
    {
        case SP_HUMAN:
        case SP_HALFLING:
        case SP_HILL_ORC:
        case SP_KOBOLD:
            return 10;
        case SP_OGRE:
            return 11;
        case SP_SLUDGE_ELF:
        case SP_NAGA:
        case SP_GHOUL:
        case SP_MERFOLK:
            return 12;
        case SP_SPRIGGAN:
        case SP_KENKU:
            return 13;
        case SP_DEEP_ELF:
        case SP_CENTAUR:
        case SP_MINOTAUR:
        case SP_DEMONSPAWN:
            return 14;
        case SP_HIGH_ELF:
        case SP_MUMMY:
        case SP_VAMPIRE:
        case SP_TROLL:
            return 15;
        case SP_DEMIGOD:
            return 16;
        default:
            return 0;
    }
}

unsigned long exp_needed(int lev)
{
    lev--;

    unsigned long level = 0;

    // Basic plan:
    // Section 1: levels  1- 5, second derivative goes 10-10-20-30.
    // Section 2: levels  6-13, second derivative is exponential/doubling.
    // Section 3: levels 14-27, second derivative is constant at 6000.
    //
    // Section three is constant so we end up with high levels at about
    // their old values (level 27 at 850k), without delta2 ever decreasing.
    // The values that are considerably different (ie level 13 is now 29000,
    // down from 41040 are because the second derivative goes from 9040 to
    // 1430 at that point in the original, and then slowly builds back
    // up again).  This function smoothes out the old level 10-15 area
    // considerably.

    // Here's a table:
    //
    // level      xp      delta   delta2
    // =====   =======    =====   ======
    //   1           0        0       0
    //   2          10       10      10
    //   3          30       20      10
    //   4          70       40      20
    //   5         140       70      30
    //   6         270      130      60
    //   7         520      250     120
    //   8        1010      490     240
    //   9        1980      970     480
    //  10        3910     1930     960
    //  11        7760     3850    1920
    //  12       15450     7690    3840
    //  13       29000    13550    5860
    //  14       48500    19500    5950
    //  15       74000    25500    6000
    //  16      105500    31500    6000
    //  17      143000    37500    6000
    //  18      186500    43500    6000
    //  19      236000    49500    6000
    //  20      291500    55500    6000
    //  21      353000    61500    6000
    //  22      420500    67500    6000
    //  23      494000    73500    6000
    //  24      573500    79500    6000
    //  25      659000    85500    6000
    //  26      750500    91500    6000
    //  27      848000    97500    6000


    switch (lev)
    {
    case 1:
        level = 1;
        break;
    case 2:
        level = 10;
        break;
    case 3:
        level = 30;
        break;
    case 4:
        level = 70;
        break;

    default:
        if (lev < 13)
        {
            lev -= 4;
            level = 10 + 10 * lev + (60 << lev);
        }
        else
        {
            lev -= 12;
            level = 15500 + 10500 * lev + 3000 * lev * lev;
        }
        break;
    }

    return ((level - 1) * _species_exp_mod(you.species) / 10);
}

// returns bonuses from rings of slaying, etc.
int slaying_bonus(char which_affected, bool ranged)
{
    int ret = 0;

    if (which_affected == PWPN_HIT)
    {
        ret += player_equip( EQ_RINGS_PLUS, RING_SLAYING );
        ret += scan_artefacts(ARTP_ACCURACY);
        if (player_equip_ego_type(EQ_GLOVES, SPARM_ARCHERY))
            ret += ranged ? 5 : -1;
    }
    else if (which_affected == PWPN_DAMAGE)
    {
        ret += player_equip( EQ_RINGS_PLUS2, RING_SLAYING );
        ret += scan_artefacts(ARTP_DAMAGE);
        if (player_equip_ego_type(EQ_GLOVES, SPARM_ARCHERY))
            ret += ranged ? 3 : -1;
    }

    ret += std::min(you.duration[DUR_SLAYING] / (13 * BASELINE_DELAY), 6);

    return (ret);
}

// Checks each equip slot for an evokable item (jewellery or randart).
// Returns true if any of these has the same ability as the one handed in.
bool items_give_ability(const int slot, artefact_prop_type abil)
{
    for (int i = EQ_WEAPON; i < NUM_EQUIP; i++)
    {
        if (!player_wearing_slot(i))
            continue;

        const int eq = you.equip[i];

        // skip item to compare with
        if (eq == slot)
            continue;

        // only weapons give their effects when in our hands
        if (i == EQ_WEAPON && you.inv[ eq ].base_type != OBJ_WEAPONS)
            continue;

        if (eq == EQ_LEFT_RING || eq == EQ_RIGHT_RING)
        {
            if (abil == ARTP_LEVITATE && you.inv[eq].sub_type == RING_LEVITATION)
                return (true);
            if (abil == ARTP_INVISIBLE && you.inv[eq].sub_type == RING_INVISIBILITY)
                return (true);
        }
        else if (eq == EQ_AMULET)
        {
            if (abil == ARTP_BERSERK && you.inv[eq].sub_type == AMU_RAGE)
                return (true);
        }

        // other items are not evokable
        if (!is_artefact( you.inv[ eq ] ))
            continue;

        if (artefact_wpn_property(you.inv[ eq ], abil))
            return (true);
    }

    // none of the equipped items possesses this ability
    return (false);
}

// Checks each equip slot for a randart, and adds up all of those with
// a given property. Slow if any randarts are worn, so avoid where
// possible.
int scan_artefacts(artefact_prop_type which_property, bool calc_unid)
{
    int retval = 0;

    for (int i = EQ_WEAPON; i < NUM_EQUIP; ++i)
    {
        if (!player_wearing_slot(i))
            continue;

        const int eq = you.equip[i];

        // Only weapons give their effects when in our hands.
        if (i == EQ_WEAPON && you.inv[ eq ].base_type != OBJ_WEAPONS)
            continue;

        if (!is_artefact( you.inv[ eq ] ))
            continue;

        // Ignore unidentified items [TileCrawl dump enhancements].
        if (!item_ident(you.inv[ eq ], ISFLAG_KNOW_PROPERTIES)
            && !calc_unid)
        {
            continue;
        }

        retval += artefact_wpn_property( you.inv[ eq ], which_property );
    }

    return (retval);
}

void modify_stat(stat_type which_stat, char amount, bool suppress_msg,
                 const char *cause, bool see_source)
{
    ASSERT(!crawl_state.arena);

    char *ptr_stat = NULL;
    char *ptr_stat_max = NULL;
    bool *ptr_redraw = NULL;

    kill_method_type kill_type = NUM_KILLBY;

    // sanity - is non-zero amount?
    if (amount == 0)
        return;

    // Stop delays if a stat drops.
    if (amount < 0)
        interrupt_activity( AI_STAT_CHANGE );

    std::string msg = "You feel ";

    if (which_stat == STAT_RANDOM)
        which_stat = static_cast<stat_type>(random2(NUM_STATS));

    switch (which_stat)
    {
    case STAT_STRENGTH:
        ptr_stat     = &you.strength;
        ptr_stat_max = &you.max_strength;
        ptr_redraw   = &you.redraw_strength;
        kill_type    = KILLED_BY_WEAKNESS;
        msg += ((amount > 0) ? "stronger." : "weaker.");
        break;

    case STAT_INTELLIGENCE:
        ptr_stat     = &you.intel;
        ptr_stat_max = &you.max_intel;
        ptr_redraw   = &you.redraw_intelligence;
        kill_type    = KILLED_BY_STUPIDITY;
        msg += ((amount > 0) ? "clever." : "stupid.");
        break;

    case STAT_DEXTERITY:
        ptr_stat     = &you.dex;
        ptr_stat_max = &you.max_dex;
        ptr_redraw   = &you.redraw_dexterity;
        kill_type    = KILLED_BY_CLUMSINESS;
        msg += ((amount > 0) ? "agile." : "clumsy.");
        break;

    default:
        break;
    }

    if (!suppress_msg && amount != 0)
        mpr( msg.c_str(), (amount > 0) ? MSGCH_INTRINSIC_GAIN : MSGCH_WARN );

    *ptr_stat     += amount;
    *ptr_stat_max += amount;
    *ptr_redraw    = 1;

    if (amount < 0 && *ptr_stat < 1)
    {
        if (cause == NULL)
            ouch(INSTANT_DEATH, NON_MONSTER, kill_type);
        else
            ouch(INSTANT_DEATH, NON_MONSTER, kill_type, cause, see_source);
    }

    if (ptr_stat == &you.strength)
        burden_change();
    if (ptr_stat == &you.dex)
        you.redraw_evasion = true;
}

void modify_stat(stat_type which_stat, char amount, bool suppress_msg,
                 const std::string& cause, bool see_source)
{
    modify_stat(which_stat, amount, suppress_msg, cause.c_str(), see_source);
}

void modify_stat(stat_type which_stat, char amount, bool suppress_msg,
                 const monsters* cause)
{
    if (cause == NULL || invalid_monster(cause))
    {
        modify_stat(which_stat, amount, suppress_msg, NULL, true);
        return;
    }

    bool        vis  = you.can_see(cause);
    std::string name = cause->name(DESC_NOCAP_A, true);

    if (cause->has_ench(ENCH_SHAPESHIFTER))
        name += " (shapeshifter)";
    else if (cause->has_ench(ENCH_GLOWING_SHAPESHIFTER))
        name += " (glowing shapeshifter)";

    modify_stat(which_stat, amount, suppress_msg, name, vis);
}

void modify_stat(stat_type which_stat, char amount, bool suppress_msg,
                 const item_def &cause, bool removed)
{
    std::string name = cause.name(DESC_NOCAP_THE, false, true, false, false,
                                  ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES);
    std::string verb;

    switch (cause.base_type)
    {
    case OBJ_ARMOUR:
    case OBJ_JEWELLERY:
        if (removed)
            verb = "removing";
        else
            verb = "wearing";
        break;

    case OBJ_WEAPONS:
    case OBJ_STAVES:
        if (removed)
            verb = "unwielding";
        else
            verb = "wielding";
        break;

    case OBJ_WANDS:   verb = "zapping";  break;
    case OBJ_FOOD:    verb = "eating";   break;
    case OBJ_SCROLLS: verb = "reading";  break;
    case OBJ_POTIONS: verb = "drinking"; break;
    default:          verb = "using";
    }

    modify_stat(which_stat, amount, suppress_msg,
                verb + " " + name, true);
}

void dec_hp(int hp_loss, bool fatal, const char *aux)
{
    ASSERT(!crawl_state.arena);

    if (!fatal && you.hp < 1)
        you.hp = 1;

    if (!fatal && hp_loss >= you.hp)
        hp_loss = you.hp - 1;

    if (hp_loss < 1)
        return;

    // If it's not fatal, use ouch() so that notes can be taken. If it IS
    // fatal, somebody else is doing the bookkeeping, and we don't want to mess
    // with that.
    if (!fatal && aux)
        ouch(hp_loss, NON_MONSTER, KILLED_BY_SOMETHING, aux);
    else
        you.hp -= hp_loss;

    you.redraw_hit_points = true;
}

void dec_mp(int mp_loss)
{
    ASSERT(!crawl_state.arena);

    if (mp_loss < 1)
        return;

    you.magic_points -= mp_loss;

    you.magic_points = std::max(0, you.magic_points);

    if (Options.magic_point_warning
        && you.magic_points < (you.max_magic_points
                               * Options.magic_point_warning) / 100)
    {
        mpr( "* * * LOW MAGIC WARNING * * *", MSGCH_DANGER );
    }

    take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points));
    you.redraw_magic_points = true;
}

bool enough_hp(int minimum, bool suppress_msg)
{
    ASSERT(!crawl_state.arena);

    // We want to at least keep 1 HP. -- bwr
    if (you.hp < minimum + 1)
    {
        if (!suppress_msg)
            mpr("You haven't enough vitality at the moment.");

        crawl_state.cancel_cmd_again();
        crawl_state.cancel_cmd_repeat();
        return (false);
    }

    return (true);
}

bool enough_mp(int minimum, bool suppress_msg, bool include_items)
{
    ASSERT(!crawl_state.arena);

    bool rc = false;

    if (get_real_mp(include_items) < minimum)
    {
        if (!suppress_msg)
            mpr("You haven't enough magic capacity.");
    }
    else if (you.magic_points < minimum)
    {
        if (!suppress_msg)
            mpr("You haven't enough magic at the moment.");
    }
    else
        rc = true;

    if (!rc)
    {
        crawl_state.cancel_cmd_again();
        crawl_state.cancel_cmd_repeat();
    }

    return (rc);
}

// Note that "max_too" refers to the base potential, the actual
// resulting max value is subject to penalties, bonuses, and scalings.
void inc_mp(int mp_gain, bool max_too)
{
    ASSERT(!crawl_state.arena);

    if (mp_gain < 1)
        return;

    bool wasnt_max = (you.magic_points < you.max_magic_points);

    you.magic_points += mp_gain;

    if (max_too)
        inc_max_mp(mp_gain);

    if (you.magic_points > you.max_magic_points)
        you.magic_points = you.max_magic_points;

    if (wasnt_max && you.magic_points == you.max_magic_points)
        interrupt_activity(AI_FULL_MP);

    take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points));
    you.redraw_magic_points = true;
}

// Note that "max_too" refers to the base potential, the actual
// resulting max value is subject to penalties, bonuses, and scalings.
// To avoid message spam, don't take notes when HP increases.
void inc_hp(int hp_gain, bool max_too)
{
    ASSERT(!crawl_state.arena);

    if (hp_gain < 1)
        return;

    bool wasnt_max = (you.hp < you.hp_max);

    you.hp += hp_gain;

    if (max_too)
        inc_max_hp( hp_gain );

    if (you.hp > you.hp_max)
        you.hp = you.hp_max;

    if (wasnt_max && you.hp == you.hp_max)
        interrupt_activity(AI_FULL_HP);

    you.redraw_hit_points = true;
}

void rot_hp(int hp_loss)
{
    you.base_hp -= hp_loss;
    calc_hp();

    if (you.species != SP_GHOUL)
        xom_is_stimulated(hp_loss * 32);

    you.redraw_hit_points = true;
}

void unrot_hp(int hp_recovered)
{
    if (hp_recovered >= 5000 - you.base_hp)
        you.base_hp = 5000;
    else
        you.base_hp += hp_recovered;

    calc_hp();

    you.redraw_hit_points = true;
}

int player_rotted()
{
    return (5000 - you.base_hp);
}

void rot_mp(int mp_loss)
{
    you.base_magic_points -= mp_loss;
    calc_mp();

    you.redraw_magic_points = true;
}

void inc_max_hp( int hp_gain )
{
    you.base_hp2 += hp_gain;
    calc_hp();

    take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max));
    you.redraw_hit_points = true;
}

void dec_max_hp( int hp_loss )
{
    you.base_hp2 -= hp_loss;
    calc_hp();

    take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max));
    you.redraw_hit_points = true;
}

void inc_max_mp( int mp_gain )
{
    you.base_magic_points2 += mp_gain;
    calc_mp();

    take_note(Note(NOTE_MAXMP_CHANGE, you.max_magic_points));
    you.redraw_magic_points = true;
}

void dec_max_mp( int mp_loss )
{
    you.base_magic_points2 -= mp_loss;
    calc_mp();

    take_note(Note(NOTE_MAXMP_CHANGE, you.max_magic_points));
    you.redraw_magic_points = true;
}

// Use of floor: false = hp max, true = hp min. {dlb}
void deflate_hp(int new_level, bool floor)
{
    ASSERT(!crawl_state.arena);

    if (floor && you.hp < new_level)
        you.hp = new_level;
    else if (!floor && you.hp > new_level)
        you.hp = new_level;

    // Must remain outside conditional, given code usage. {dlb}
    you.redraw_hit_points = true;
}

// Note that "max_too" refers to the base potential, the actual
// resulting max value is subject to penalties, bonuses, and scalings.
void set_hp(int new_amount, bool max_too)
{
    ASSERT(!crawl_state.arena);

    if (you.hp != new_amount)
        you.hp = new_amount;

    if (max_too && you.hp_max != new_amount)
    {
        you.base_hp2 = 5000 + new_amount;
        calc_hp();
    }

    if (you.hp > you.hp_max)
        you.hp = you.hp_max;

    if (max_too)
        take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max));

    // Must remain outside conditional, given code usage. {dlb}
    you.redraw_hit_points = true;
}

// Note that "max_too" refers to the base potential, the actual
// resulting max value is subject to penalties, bonuses, and scalings.
void set_mp(int new_amount, bool max_too)
{
    ASSERT(!crawl_state.arena);

    if (you.magic_points != new_amount)
        you.magic_points = new_amount;

    if (max_too && you.max_magic_points != new_amount)
    {
        // Note that this gets scaled down for values > 18.
        you.base_magic_points2 = 5000 + new_amount;
        calc_mp();
    }

    if (you.magic_points > you.max_magic_points)
        you.magic_points = you.max_magic_points;

    take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points));
    if (max_too)
        take_note(Note(NOTE_MAXMP_CHANGE, you.max_magic_points));

    // Must remain outside conditional, given code usage. {dlb}
    you.redraw_magic_points = true;
}

// If trans is true, being berserk and/or transformed is taken into account
// here. Else, the base hp is calculated. If rotted is true, calculate the
// real max hp you'd have if the rotting was cured.
int get_real_hp(bool trans, bool rotted)
{
    int hitp;

    hitp  = (you.base_hp - 5000) + (you.base_hp2 - 5000);
    hitp += (you.experience_level * you.skills[SK_FIGHTING]) / 5;

    // Being berserk makes you resistant to damage. I don't know why.
    if (trans && you.berserk())
        hitp *= 2;

    if (trans)
    {
        // Some transformations give you extra hp.
        switch (you.attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_STATUE:
            hitp *= 15;
            hitp /= 10;
            break;
        case TRAN_ICE_BEAST:
            hitp *= 12;
            hitp /= 10;
            break;
        case TRAN_DRAGON:
            hitp *= 16;
            hitp /= 10;
            break;
        }
    }

    if (rotted)
        hitp += player_rotted();

    // Frail and robust mutations, and divine vigour.
    hitp *= 10 + player_mutation_level(MUT_ROBUST)
               + you.attribute[ATTR_DIVINE_VIGOUR]
               - player_mutation_level(MUT_FRAIL);
    hitp /= 10;

    return (hitp);
}

int get_real_mp(bool include_items)
{
    // base_magic_points2 accounts for species
    int enp = (you.base_magic_points2 - 5000);

    int spell_extra = (you.experience_level * you.skills[SK_SPELLCASTING]) / 4;
    int invoc_extra = (you.experience_level * you.skills[SK_INVOCATIONS]) / 6;

    enp += std::max(spell_extra, invoc_extra);
    enp = stepdown_value(enp, 9, 18, 45, 100);

    // This is our "rotted" base (applied after scaling):
    enp += (you.base_magic_points - 5000);

    // Yes, we really do want this duplication... this is so the stepdown
    // doesn't truncate before we apply the rotted base.  We're doing this
    // the nice way. -- bwr
    enp = std::min(enp, 50);

    // Now applied after scaling so that power items are more useful -- bwr
    if (include_items)
        enp += player_magical_power();

    // Analogous to ROBUST/FRAIL
    enp *= 10 + player_mutation_level(MUT_HIGH_MAGIC)
              + you.attribute[ATTR_DIVINE_VIGOUR]
              - player_mutation_level(MUT_LOW_MAGIC);
    enp /= 10;

    if (enp > 50)
        enp = 50 + ((enp - 50) / 2);

    enp = std::max(enp, 0);

    return enp;
}

int get_contamination_level()
{
    const int glow = you.magic_contamination;

    if (glow > 60)
        return (glow / 20 + 2);
    if (glow > 40)
        return (4);
    if (glow > 25)
        return (3);
    if (glow > 15)
        return (2);
    if (glow > 5)
        return (1);

    return (0);
}

// controlled is true if the player actively did something to cause
// contamination (such as drink a known potion of resistance),
// status_only is true only for the status output
void contaminate_player(int change, bool controlled, bool status_only)
{
    ASSERT(!crawl_state.arena);

    if (status_only && !you.magic_contamination)
        return;

    // get current contamination level
    int old_amount = you.magic_contamination;
    int old_level  = get_contamination_level();
    int new_level  = 0;
#if DEBUG_DIAGNOSTICS
    if (change > 0 || (change < 0 && you.magic_contamination))
    {
        mprf(MSGCH_DIAGNOSTICS, "change: %d  radiation: %d",
             change, change + you.magic_contamination );
    }
#endif

    // make the change
    if (change + you.magic_contamination < 0)
        you.magic_contamination = 0;
    else
    {
        if (change + you.magic_contamination > 250)
            you.magic_contamination = 250;
        else
            you.magic_contamination += change;
    }

    // figure out new level
    new_level = get_contamination_level();

    if (status_only || (new_level >= 1 && old_level == 0)
        || (old_amount == 0 && you.magic_contamination > 0))
    {
        if (new_level > 3)
        {
            mpr( (new_level == 4) ?
                 "Your entire body has taken on an eerie glow!" :
                 "You are engulfed in a nimbus of crackling magics!");
        }
        else if (new_level == 0 && old_amount == 0
                 && you.magic_contamination > 0)
        {
            mpr("You are very lightly contaminated with residual magic.");
        }
        else
        {
            mprf("You are %s with residual magics%s",
                 (new_level == 3) ? "practically glowing" :
                 (new_level == 2) ? "heavily infused" :
                 (new_level == 1) ? "contaminated"
                                  : "lightly contaminated",
                 (new_level == 3) ? "!" : ".");
        }
    }
    else if (new_level != old_level)
    {
        mprf((change > 0) ? MSGCH_WARN : MSGCH_RECOVERY,
             "You feel %s contaminated with magical energies.",
             (change > 0) ? "more" : "less" );

        if (change > 0)
            xom_is_stimulated(new_level * 32);

        if (new_level == 0 && you.duration[DUR_INVIS] && !you.backlit())
        {
            mpr("You fade completely from view now that you are no longer "
                "glowing from magical contamination.");
        }
    }
    else if (old_level == 0 && old_amount > 0 && you.magic_contamination == 0)
        mpr("Your magical contamination has completely faded away.");

    if (status_only)
        return;

    if (you.magic_contamination > 0)
        learned_something_new(TUT_GLOWING);

    // Zin doesn't like mutations or mutagenic radiation.
    if (you.religion == GOD_ZIN)
    {
        // Whenever the glow status is first reached, give a warning message.
        if (old_level < 1 && new_level >= 1)
            did_god_conduct(DID_CAUSE_GLOWING, 0, false);
        // If the player actively did something to increase glowing,
        // Zin is displeased.
        else if (controlled && change > 0 && old_level > 0)
            did_god_conduct(DID_CAUSE_GLOWING, 1 + new_level, true);
    }
}

bool confuse_player(int amount, bool resistable)
{
    ASSERT(!crawl_state.arena);

    if (amount <= 0)
        return (false);

    if (resistable && wearing_amulet(AMU_CLARITY))
    {
        mpr("You feel momentarily confused.");
        // Identify the amulet if necessary.
        if (!extrinsic_amulet_effect(AMU_CLARITY))
        {
            // Since it's not extrinsic, it must be from the amulet.
            ASSERT(player_wearing_slot(EQ_AMULET));
            item_def* const amu = you.slot_item(EQ_AMULET);
            if (!item_ident(*amu, ISFLAG_KNOW_TYPE))
            {
                set_ident_flags(*amu, ISFLAG_KNOW_TYPE);
                mprf("You are wearing: %s",
                     amu->name(DESC_INVENTORY_EQUIP).c_str());
            }
        }
        return (false);
    }

    const int old_value = you.duration[DUR_CONF];
    you.increase_duration(DUR_CONF, amount, 40);

    if (you.duration[DUR_CONF] > old_value)
    {
        you.check_awaken(500);

        mprf(MSGCH_WARN, "You are %sconfused.",
             old_value > 0 ? "more " : "");

        learned_something_new(TUT_YOU_ENCHANTED);

        xom_is_stimulated((you.duration[DUR_CONF] - old_value)
                           / BASELINE_DELAY);
    }

    return (true);
}

bool curare_hits_player(int death_source, int amount)
{
    ASSERT(!crawl_state.arena);

    poison_player(amount);

    const bool res_poison = player_res_poison() > 0;

    int hurted = 0;

    if (you.res_asphyx() <= 0)
    {
      hurted = roll_dice(2, 6);

        // Note that the hurtage is halved by poison resistance.
        if (res_poison)
            hurted /= 2;

        if (hurted)
        {
            mpr("You have difficulty breathing.");
            ouch(hurted, death_source, KILLED_BY_CURARE,
                 "curare-induced apnoea");
        }

        potion_effect(POT_SLOWING, 2 + random2(4 + amount));
    }

    return (hurted > 0);
}

bool poison_player(int amount, bool force)
{
    ASSERT(!crawl_state.arena);

    if (!force && player_res_poison() > 0 || amount <= 0)
        return (false);

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

    if (you.duration[DUR_POISONING] > 40)
        you.duration[DUR_POISONING] = 40;

    if (you.duration[DUR_POISONING] > old_value)
    {
        mprf(MSGCH_WARN, "You are %spoisoned.",
             old_value > 0 ? "more " : "");

        learned_something_new(TUT_YOU_POISON);
    }

    return (true);
}

void dec_poison_player()
{
    // If Cheibriados has slowed your life processes, there's a
    // chance that your poison level is simply unaffected and
    // you aren't hurt by poison.
    if (GOD_CHEIBRIADOS == you.religion
        && you.piety >= piety_breakpoint(0)
        && coinflip())
        return;

    if (you.duration[DUR_POISONING] > 0)
    {
        if (x_chance_in_y(you.duration[DUR_POISONING], 5))
        {
            int hurted = 1;
            msg_channel_type channel = MSGCH_PLAIN;
            const char *adj = "";

            if (you.duration[DUR_POISONING] > 10
                && random2(you.duration[DUR_POISONING]) >= 8)
            {
                hurted = random2(10) + 5;
                channel = MSGCH_DANGER;
                adj = "extremely ";
            }
            else if (you.duration[DUR_POISONING] > 5 && coinflip())
            {
                hurted = coinflip() ? 3 : 2;
                channel = MSGCH_WARN;
                adj = "very ";
            }

            int oldhp = you.hp;
            ouch(hurted, NON_MONSTER, KILLED_BY_POISON);
            if (you.hp < oldhp)
                mprf(channel, "You feel %ssick.", adj);

            if ((you.hp == 1 && one_chance_in(3)) || one_chance_in(8))
                reduce_poison_player(1);
        }
    }
}

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

    const int old_value = you.duration[DUR_POISONING];
    you.duration[DUR_POISONING] -= amount;

    if (you.duration[DUR_POISONING] < 0)
        you.duration[DUR_POISONING] = 0;

    if (you.duration[DUR_POISONING] < old_value)
    {
        mprf(MSGCH_RECOVERY, "You feel %sbetter.",
             you.duration[DUR_POISONING] > 0 ? "a little " : "");
    }
}

bool miasma_player()
{
    ASSERT(!crawl_state.arena);

    if (you.res_rotting())
        return (false);

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

    bool success = poison_player(1);

    if (you.hp_max > 4 && coinflip())
    {
        rot_hp(1);
        success = true;
    }

    if (one_chance_in(3))
    {
        potion_effect(POT_SLOWING, 5);
        success = true;
    }

    return (success);
}

bool napalm_player(int amount)
{
    ASSERT(!crawl_state.arena);

    if (player_res_sticky_flame() || amount <= 0)
        return (false);

    const int old_value = you.duration[DUR_LIQUID_FLAMES];
    you.increase_duration(DUR_LIQUID_FLAMES, amount, 100);

    if (you.duration[DUR_LIQUID_FLAMES] > old_value)
        mpr("You are covered in liquid flames!", MSGCH_WARN);

    return (true);
}

void dec_napalm_player(int delay)
{
    if (you.duration[DUR_LIQUID_FLAMES] > BASELINE_DELAY)
    {
        if (feat_is_watery(grd(you.pos())))
        {
            mpr("The flames go out!", MSGCH_WARN);
            you.duration[DUR_LIQUID_FLAMES] = 0;
            return;
        }

        mpr("You are covered in liquid flames!", MSGCH_WARN);

        expose_player_to_element(BEAM_NAPALM, 12);

        const int res_fire = player_res_fire();

        if (res_fire > 0)
        {
            ouch((((random2avg(9, 2) + 1) * delay) /
                    (1 + (res_fire * res_fire))) / BASELINE_DELAY, NON_MONSTER,
                    KILLED_BY_BURNING);
        }

        if (res_fire <= 0)
        {
            ouch(((random2avg(9, 2) + 1) * delay) / BASELINE_DELAY,
                 NON_MONSTER, KILLED_BY_BURNING);

            if (res_fire < 0)
            {
                ouch(((random2avg(9, 2) + 1) * delay)
                        / BASELINE_DELAY, NON_MONSTER, KILLED_BY_BURNING);
            }
        }

        if (you.duration[DUR_CONDENSATION_SHIELD] > 0)
            remove_condensation_shield();
    }

    you.duration[DUR_LIQUID_FLAMES] -= delay;
    if (you.duration[DUR_LIQUID_FLAMES] < 0)
        you.duration[DUR_LIQUID_FLAMES] = 0;
}

bool slow_player(int turns)
{
    ASSERT(!crawl_state.arena);

    if (turns <= 0)
        return (false);

    if (stasis_blocks_effect(true, "%s rumbles.", 20, "%s rumbles."))
        return (false);

    // Doubling these values because moving while slowed takes twice the
    // usual delay.
    turns *= 2;
    int threshold = 100 * 2;

    if (you.duration[DUR_SLOW] >= threshold * BASELINE_DELAY)
        mpr("You already are as slow as you could be.");
    else
    {
        if (you.duration[DUR_SLOW] == 0)
            mpr("You feel yourself slow down.");
        else
            mpr("You feel as though you will be slow longer.");

        you.increase_duration(DUR_SLOW, turns, threshold);
        learned_something_new(TUT_YOU_ENCHANTED);
    }

    return (true);
}

void dec_slow_player(int delay)
{
    if (!you.duration[DUR_SLOW])
        return;

    if (you.duration[DUR_SLOW] > BASELINE_DELAY)
    {
        // BCR - Amulet of resist slow affects slow counter.
        you.duration[DUR_SLOW] -= delay;
    }
    if (you.duration[DUR_SLOW] <= BASELINE_DELAY)
    {
        mpr("You feel yourself speed up.", MSGCH_DURATION);
        you.duration[DUR_SLOW] = 0;
    }
}

bool haste_player(int turns)
{
    ASSERT(!crawl_state.arena);

    if (turns <= 0)
        return (false);

    if (stasis_blocks_effect(true, "%s emits a piercing whistle.", 20,
                             "%s makes your neck tingle."))
    {
        return (false);
    }

    // Cutting the nominal turns in half since hasted actions take half the
    // usual delay.
    turns /= 2;
    const int threshold = 40;

    if (you.duration[DUR_HASTE] == 0)
        mpr("You feel yourself speed up.");
    else if (you.duration[DUR_HASTE] > threshold * BASELINE_DELAY)
        mpr("You already have as much speed as you can handle.");
    else
    {
        mpr("You feel as though your hastened speed will last longer.");
        contaminate_player(1, true); // always deliberate
    }

    you.increase_duration(DUR_HASTE, turns, threshold);
    did_god_conduct(DID_STIMULANTS, 4 + random2(4));

    return (true);
}

void dec_haste_player(int delay)
{
    if (!you.duration[DUR_HASTE])
        return;

    if (you.duration[DUR_HASTE] > BASELINE_DELAY)
    {
        int old_dur = you.duration[DUR_HASTE];

        you.duration[DUR_HASTE] -= delay;

        int threshold = 6 * BASELINE_DELAY;
        // message if we cross the threshold
        if (old_dur > threshold && you.duration[DUR_HASTE] <= threshold)
        {
            mpr("Your extra speed is starting to run out.", MSGCH_DURATION);
            if (coinflip())
                you.duration[DUR_HASTE] -= BASELINE_DELAY;
        }
    }
    else if (you.duration[DUR_HASTE] <= BASELINE_DELAY)
    {
        mpr("You feel yourself slow down.", MSGCH_DURATION);
        you.duration[DUR_HASTE] = 0;
    }
}

void dec_disease_player(int delay)
{
    if (you.disease > 0)
    {
        // If Cheibriados has slowed your life processes, there's a
        // chance that your disease level is unaffected.
        if (GOD_CHEIBRIADOS == you.religion
            && you.piety >= piety_breakpoint(0)
            && coinflip())
        {
          return;
        }

        you.disease -= delay;
        if(you.disease < 0)
            you.disease = 0;

        // kobolds and regenerators recuperate quickly
        if (you.disease > 5 * BASELINE_DELAY
            && (you.species == SP_KOBOLD
                || you.duration[DUR_REGENERATION]
                || player_mutation_level(MUT_REGENERATION) == 3))
        {
            you.disease -= 2 * BASELINE_DELAY;
        }

        if (you.disease == 0)
            mpr("You feel your health improve.", MSGCH_RECOVERY);
    }
}

int count_worn_ego(int which_ego)
{
    int result = 0;
    for (int slot = EQ_CLOAK; slot <= EQ_BODY_ARMOUR; ++slot)
    {
        if (you.equip[slot] != -1
            && get_armour_ego_type(you.inv[you.equip[slot]]) == which_ego)
        {
            result++;
        }
    }

    return (result);
}

static int _strength_modifier()
{
    int result = 0;

    if (you.duration[DUR_MIGHT])
        result += 5;

    if (you.duration[DUR_DIVINE_STAMINA])
        result += you.attribute[ATTR_DIVINE_STAMINA];

    // ego items of strength
    result += 3 * count_worn_ego(SPARM_STRENGTH);

    // rings of strength
    result += player_equip(EQ_RINGS_PLUS, RING_STRENGTH);

    // randarts of strength
    result += scan_artefacts(ARTP_STRENGTH);

    // mutations
    result += player_mutation_level(MUT_STRONG)
              - player_mutation_level(MUT_WEAK);
    result += player_mutation_level(MUT_STRONG_STIFF)
              - player_mutation_level(MUT_FLEXIBLE_WEAK);

    // transformations
    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_STATUE:          result +=  2; break;
    case TRAN_DRAGON:          result += 10; break;
    case TRAN_LICH:            result +=  3; break;
    case TRAN_BAT:             result -=  5; break;
    default:                                 break;
    }

    return (result);
}

static int _int_modifier()
{
    int result = 0;

    if (you.duration[DUR_BRILLIANCE])
        result += 5;

    if (you.duration[DUR_DIVINE_STAMINA])
        result += you.attribute[ATTR_DIVINE_STAMINA];

    // ego items of intelligence
    result += 3 * count_worn_ego(SPARM_INTELLIGENCE);

    // rings of intelligence
    result += player_equip(EQ_RINGS_PLUS, RING_INTELLIGENCE);

    // randarts of intelligence
    result += scan_artefacts(ARTP_INTELLIGENCE);

    // mutations
    result += player_mutation_level(MUT_CLEVER)
              - player_mutation_level(MUT_DOPEY);

    return (result);
}

static int _dex_modifier()
{
    int result = 0;

    if (you.duration[DUR_AGILITY])
        result += 5;

    if (you.duration[DUR_DIVINE_STAMINA])
        result += you.attribute[ATTR_DIVINE_STAMINA];

    // ego items of dexterity
    result += 3 * count_worn_ego(SPARM_DEXTERITY);

    // rings of dexterity
    result += player_equip(EQ_RINGS_PLUS, RING_DEXTERITY);

    // randarts of dexterity
    result += scan_artefacts(ARTP_DEXTERITY);

    // mutations
    result += player_mutation_level(MUT_AGILE)
              - player_mutation_level(MUT_CLUMSY);
    result += player_mutation_level(MUT_FLEXIBLE_WEAK)
              - player_mutation_level(MUT_STRONG_STIFF);
    result -= player_mutation_level(MUT_BLACK_SCALES);
    result -= player_mutation_level(MUT_BONEY_PLATES);

    const int grey2_modifier[]    = { 0, -1, -1, -2 };
    const int metallic_modifier[] = { 0, -2, -3, -4 };
    const int yellow_modifier[]   = { 0,  0, -1, -2 };
    const int red2_modifier[]     = { 0,  0, -1, -2 };

    result += grey2_modifier[player_mutation_level(MUT_GREY2_SCALES)];
    result += metallic_modifier[player_mutation_level(MUT_METALLIC_SCALES)];
    result += yellow_modifier[player_mutation_level(MUT_YELLOW_SCALES)];
    result += red2_modifier[player_mutation_level(MUT_RED2_SCALES)];

    // transformations
    switch (you.attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_SPIDER: result +=  5; break;
    case TRAN_STATUE: result -=  2; break;
    case TRAN_BAT:    result +=  5; break;
    default:                        break;
    }

    return (result);
}

int stat_modifier(stat_type stat)
{
    switch (stat)
    {
    case STAT_STRENGTH:     return _strength_modifier();
    case STAT_INTELLIGENCE: return _int_modifier();
    case STAT_DEXTERITY:    return _dex_modifier();
    default:
        mprf(MSGCH_ERROR, "Bad stat: %d", stat);
        return 0;
    }
}

bool player::is_habitable_feat(dungeon_feature_type actual_grid) const
{
    if (!can_pass_through_feat(actual_grid))
        return (false);

    if (airborne())
        return (true);

    if (actual_grid == DNGN_LAVA
        || actual_grid == DNGN_DEEP_WATER && !can_swim())
    {
        return (false);
    }

    return (true);
}

player::player()
    : m_quiver(0)
{
    init();
    kills = new KillMaster();   // why isn't this done in init()?
}

player::player(const player &other)
    : m_quiver(0)
{
    init();

    // why doesn't this do a copy_from?
    player_quiver* saved_quiver = m_quiver;
    *this = other;
    m_quiver = saved_quiver;

    kills = new KillMaster(*(other.kills));
    *m_quiver = *(other.m_quiver);
}

// why is this not called "operator="?
void player::copy_from(const player &other)
{
    if (this == &other)
        return;

    KillMaster *saved_kills = kills;
    player_quiver* saved_quiver = m_quiver;

    *this = other;

    kills  = saved_kills;
    *kills = *(other.kills);
    m_quiver = saved_quiver;
    *m_quiver = *(other.m_quiver);
}


// player struct initialization
void player::init()
{
    birth_time       = time( NULL );
    real_time        = 0;
    num_turns        = 0L;
    last_view_update = 0L;

#ifdef WIZARD
    wizard = (Options.wiz_mode == WIZ_YES) ? true : false;
#else
    wizard = false;
#endif

    your_name = "";

    banished = false;
    banished_by.clear();

    entering_level = false;
    lava_in_sight  = -1;
    water_in_sight = -1;
    transit_stair  = DNGN_UNSEEN;

    berserk_penalty = 0;
    disease         = 0;
    elapsed_time    = 0;
    rotting         = 0;
    unrand_reacts   = 0;

    magic_contamination = 0;

    base_hp  = 5000;
    base_hp2 = 5000;
    hp_max   = 0;
    base_magic_points  = 5000;
    base_magic_points2 = 5000;
    max_magic_points   = 0;

    magic_points_regeneration = 0;
    strength         = 0;
    max_strength     = 0;
    intel            = 0;
    max_intel        = 0;
    dex              = 0;
    max_dex          = 0;
    experience       = 0;
    experience_level = 1;
    max_level        = 1;
    char_class       = JOB_UNKNOWN;
    species          = SP_UNKNOWN;

    hunger       = 6000;
    hunger_state = HS_SATIATED;

    wield_change            = false;
    redraw_quiver           = false;
    received_weapon_warning = false;

    gold = 0;
    // speed = 10;             // 0.75;  // unused

    burden       = 0;
    burden_state = BS_UNENCUMBERED;

    spell_no = 0;

    your_level       = 0;
    level_type       = LEVEL_DUNGEON;
    entry_cause      = EC_SELF_EXPLICIT;
    entry_cause_god  = GOD_NO_GOD;
    where_are_you    = BRANCH_MAIN_DUNGEON;
    char_direction   = GDT_DESCENDING;
    opened_zot       = false;
    royal_jelly_dead = false;

    prev_targ  = MHITNOT;
    pet_target = MHITNOT;

    prev_grd_targ.reset();
    position.reset();
    prev_move.reset();

    running.clear();
    travel_x = 0;
    travel_y = 0;
    travel_z = level_id();

    religion         = GOD_NO_GOD;
    piety            = 0;
    piety_hysteresis = 0;

    gift_timeout     = 0;

    penance.init(0);
    worshipped.init(0);
    num_gifts.init(0);

    equip.init(-1);

    spells.init(SPELL_NO_SPELL);

    spell_letter_table.init(-1);
    ability_letter_table.init(ABIL_NON_ABILITY);

    mutation.init(0);
    demon_pow.init(0);

    demonic_traits.clear();

    had_book.init(false);

    unique_items.init(UNIQ_NOT_EXISTS);

    skills.init(0);
    skill_points.init(0);
    skill_order.init(MAX_SKILL_ORDER);
    practise_skill.init(true);

    skill_cost_level   = 1;
    total_skill_points = 0;

    attribute.init(0);
    quiver.init(ENDOFPACK);
    sacrifice_value.init(0);

    for (int i = 0; i < ENDOFPACK; i++)
        inv[i].clear();

    duration.init(0);

    exp_available = 25;

    global_info.make_global();
    global_info.assert_validity();

    for (int i = 0; i < NUM_BRANCHES; i++)
    {
        branch_info[i].level_type = LEVEL_DUNGEON;
        branch_info[i].branch     = i;
        branch_info[i].assert_validity();
    }

    for (int i = 0; i < (NUM_LEVEL_AREA_TYPES - 1); i++)
    {
        non_branch_info[i].level_type = i + 1;
        non_branch_info[i].branch     = -1;
        non_branch_info[i].assert_validity();
    }

#ifdef USE_TILE
    last_clicked_grid = coord_def();
    last_clicked_item = -1;
#endif

    if (m_quiver)
        delete m_quiver;
    m_quiver = new player_quiver;

    // Currently only set if Xom accidentally kills the player.
    reset_escaped_death();

    on_current_level = true;

#if WIZARD || DEBUG
    you.never_die = false;
#endif
}

player_save_info player_save_info::operator=(const player& rhs)
{
    name             = rhs.your_name;
    experience       = rhs.experience;
    experience_level = rhs.experience_level;
    wizard           = rhs.wizard;
    species          = rhs.species;
    class_name       = rhs.class_name;
    religion         = rhs.religion;
    second_god_name  = rhs.second_god_name;
#ifdef USE_TILE
    held_in_net      = false;
#endif

    return (*this);
}

bool player_save_info::operator<(const player_save_info& rhs) const
{
    return experience < rhs.experience
           || (experience == rhs.experience && name < rhs.name);
}

std::string player_save_info::short_desc() const
{
    std::ostringstream desc;
    desc << name << ", a level " << experience_level << ' '
         << species_name(species, experience_level) << ' '
         << class_name;

    if (religion == GOD_JIYVA)
        desc << " of " << god_name_jiyva(true);
    else if (religion != GOD_NO_GOD)
        desc << " of " << god_name(religion);

#ifdef WIZARD
    if (wizard)
        desc << " (WIZ)";
#endif

    return desc.str();
}

player::~player()
{
    delete kills;
    delete m_quiver;
}

bool player::is_levitating() const
{
    return (duration[DUR_LEVITATION]);
}

bool player::in_water() const
{
    return (!airborne() && !beogh_water_walk()
            && feat_is_water(grd(this->pos())));
}

bool player::can_swim() const
{
    // Transforming could be fatal if it would cause unequipment of
    // stat-boosting boots or heavy armour.
    return (species == SP_MERFOLK && merfolk_change_is_safe(true));
}

int player::visible_igrd(const coord_def &where) const
{
    if (grd(where) == DNGN_LAVA
        || (grd(where) == DNGN_DEEP_WATER && species != SP_MERFOLK))
    {
        return (NON_ITEM);
    }

    return igrd(where);
}

bool player::swimming() const
{
    return in_water() && can_swim();
}

bool player::submerged() const
{
    return (false);
}

bool player::has_spell(spell_type spell) const
{
    for (int i = 0; i < 25; i++)
    {
        if (spells[i] == spell)
            return (true);
    }

    return (false);
}

bool player::extra_balanced() const
{
    return (species == SP_NAGA && !transform_changed_physiology());
}

bool player::floundering() const
{
    return in_water() && !can_swim() && !extra_balanced();
}

bool player::can_pass_through_feat(dungeon_feature_type grid) const
{
    return !feat_is_solid(grid);
}

size_type player::body_size(size_part_type psize, bool base) const
{
    if (base)
        return species_size(species, psize);
    else
    {
        size_type tf_size = transform_size(psize);
        return (tf_size == SIZE_CHARACTER ? species_size(species, psize)
                                          : tf_size);
    }
}

int player::body_weight(bool base) const
{
    int weight = actor::body_weight(base);

    if (base)
        return (weight);

    switch (attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_STATUE:
        weight *= 2;
        break;
    case TRAN_LICH:
        weight /= 2;
        break;
    default:
        break;
    }

    return (weight);
}

int player::total_weight() const
{
    return (body_weight() + burden);
}

bool player::cannot_speak() const
{
    if (silenced(this->pos()))
        return (true);

    if (this->cannot_move()) // we allow talking during sleep ;)
        return (true);

    // No transform that prevents the player from speaking yet.
    return (false);
}

std::string player::shout_verb() const
{
    const int transform = attribute[ATTR_TRANSFORMATION];
    switch (transform)
    {
    case TRAN_DRAGON:
        return "roar";
    case TRAN_SPIDER:
        return "hiss";
    case TRAN_BAT:
        return "squeak";
    case TRAN_PIG:
        return "squeal";
    default: // depends on SCREAM mutation
        int level = player_mutation_level(MUT_SCREAM);
        if (level <= 1)
            return "shout";
        else if (level == 2)
            return "yell";
        else // level == 3
            return "scream";
    }
}

int player::damage_type(int)
{
    const int wpn = equip[EQ_WEAPON];

    if (wpn != -1)
        return (get_vorpal_type(inv[wpn]));
    else if (attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS)
        return (DVORP_SLICING);
    else if (has_usable_claws())
        return (DVORP_CLAWING);

    return (DVORP_CRUSHING);
}

int player::damage_brand(int)
{
    int ret = SPWPN_NORMAL;
    const int wpn = equip[EQ_WEAPON];

    if (wpn != -1)
    {
        if (!is_range_weapon(inv[wpn]))
            ret = get_weapon_brand(inv[wpn]);
    }
    else if (duration[DUR_CONFUSING_TOUCH])
        ret = SPWPN_CONFUSE;
    else if (mutation[MUT_DRAIN_LIFE])
        ret = SPWPN_DRAINING;
    else
    {
        switch (attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_SPIDER:
            ret = SPWPN_VENOM;
            break;

        case TRAN_ICE_BEAST:
            ret = SPWPN_FREEZING;
            break;

        case TRAN_LICH:
            ret = SPWPN_DRAINING;
            break;

        case TRAN_BAT:
            if (this->species == SP_VAMPIRE && one_chance_in(8))
                ret = SPWPN_VAMPIRICISM;
            break;

        default:
            break;
        }
    }

    return (ret);
}

// Returns the item in the given equipment slot, NULL if the slot is empty.
// eq must be in [EQ_WEAPON, EQ_AMULET], or bad things will happen.
item_def *player::slot_item(equipment_type eq)
{
    ASSERT(eq >= EQ_WEAPON && eq <= EQ_AMULET);

    const int item = equip[eq];
    return (item == -1 ? NULL : &inv[item]);
}

// Returns the item in the player's weapon slot.
item_def *player::weapon(int /* which_attack */)
{
    return (slot_item(EQ_WEAPON));
}

bool player::can_wield(const item_def& item, bool ignore_curse,
                       bool ignore_brand, bool ignore_shield,
                       bool ignore_transform) const
{
    if (equip[EQ_WEAPON] != -1 && !ignore_curse)
    {
        if (inv[equip[EQ_WEAPON]].cursed())
            return (false);
    }

    // Unassigned means unarmed combat.
    const bool two_handed = item.base_type == OBJ_UNASSIGNED
                            || hands_reqd(item, body_size()) == HANDS_TWO;

    if (two_handed && !ignore_shield && player_wearing_slot(EQ_SHIELD))
        return (false);

    return could_wield(item, ignore_brand, ignore_transform);
}

bool player::could_wield(const item_def &item, bool ignore_brand,
                         bool /* ignore_transform */) const
{
    if (body_size(PSIZE_TORSO) < SIZE_LARGE && item_mass(item) >= 300)
        return (false);

    // Small species wielding large weapons...
    if (body_size(PSIZE_BODY) < SIZE_MEDIUM
        && !check_weapon_wieldable_size(item, body_size(PSIZE_BODY)))
    {
        return (false);
    }

    if (!ignore_brand)
    {
        if (undead_or_demonic() && is_holy_item(item))
            return (false);
    }

    return (true);
}

// Returns the shield the player is wearing, or NULL if none.
item_def *player::shield()
{
    if (!you_tran_can_wear(EQ_SHIELD))
        return (NULL);

    return (slot_item(EQ_SHIELD));
}

std::string player::name(description_level_type type, bool) const
{
    return (pronoun_you(type));
}

std::string player::pronoun(pronoun_type pro, bool) const
{
    switch (pro)
    {
    default:
    case PRONOUN_CAP:               return "You";
    case PRONOUN_NOCAP:             return "you";
    case PRONOUN_CAP_POSSESSIVE:    return "Your";
    case PRONOUN_NOCAP_POSSESSIVE:  return "your";
    case PRONOUN_REFLEXIVE:         return "yourself";
    case PRONOUN_OBJECTIVE:         return "you";
    }
}

std::string player::conj_verb(const std::string &verb) const
{
    return (verb);
}

std::string player::hand_name(bool plural, bool *can_plural) const
{
    if (can_plural != NULL)
        *can_plural = true;
    return your_hand(plural);
}

std::string player::foot_name(bool plural, bool *can_plural) const
{
    bool _can_plural;
    if (can_plural == NULL)
        can_plural = &_can_plural;
    *can_plural = true;

    std::string str;

    if (this->attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER)
        str = "hind leg";
    else if (!transform_changed_physiology())
    {
        if (player_mutation_level(MUT_HOOVES))
            str = "hoof";
        else if (player_mutation_level(MUT_TALONS))
            str = "talon";
        else if (this->species == SP_NAGA)
        {
            str         = "underbelly";
            *can_plural = false;
        }
        else if (this->species == SP_MERFOLK && this->swimming())
        {
            str         = "tail";
            *can_plural = false;
        }
    }

    if (str.empty())
        return (plural ? "feet" : "foot");

    if (plural && *can_plural)
        str = pluralise(str);

    return str;
}

std::string player::arm_name(bool plural, bool *can_plural) const
{
    if (transform_changed_physiology())
        return hand_name(plural, can_plural);

    if (can_plural != NULL)
        *can_plural = true;

    std::string str = "arm";

    if (player_genus(GENPC_DRACONIAN) || this->species == SP_NAGA)
        str = "scaled arm";
    else if (this->species == SP_KENKU)
        str = "feathered arm";
    else if (this->species == SP_MUMMY)
        str = "bandage wrapped arm";

    if (plural)
        str = pluralise(str);

    return (str);
}

monster_type player::id() const
{
    return (MONS_PLAYER);
}

int player::mindex() const
{
    return (MHITYOU);
}

int player::get_experience_level() const
{
    return (experience_level);
}

bool player::alive() const
{
    // Simplistic, but if the player dies the game is over anyway, so
    // nobody can ask further questions.
    return (true);
}

bool player::is_summoned(int* _duration, int* summon_type) const
{
    if (_duration != NULL)
        *_duration = -1;
    if (summon_type != NULL)
        *summon_type = 0;

    return (false);
}

bool player::fumbles_attack(bool verbose)
{
    // Fumbling in shallow water.
    if (floundering())
    {
        if (x_chance_in_y(4, dex) || one_chance_in(5))
        {
            if (verbose)
                mpr("Unstable footing causes you to fumble your attack.");
            return (true);
        }
    }
    return (false);
}

bool player::cannot_fight() const
{
    return (false);
}

// If you have a randart equipped that has the ARTP_ANGRY property,
// there's a 1/20 chance of it becoming activated whenever you
// attack a monster. (Same as the berserk mutation at level 1.)
// The probabilities for actually going berserk are cumulative!
static bool _equipment_make_berserk()
{
    for (int eq = EQ_WEAPON; eq < NUM_EQUIP; eq++)
    {
         const item_def *item = you.slot_item((equipment_type) eq);
         if (!item)
             continue;

         if (!is_artefact(*item))
             continue;

         if (artefact_wpn_property(*item, ARTP_ANGRY) && one_chance_in(20))
             return (true);
    }

    // nothing found
    return (false);
}

void player::attacking(actor *other)
{
    ASSERT(!crawl_state.arena);

    if (other && other->atype() == ACT_MONSTER)
    {
        const monsters *mon = dynamic_cast<monsters*>(other);
        if (!mon->friendly() && !mon->neutral())
            pet_target = mon->mindex();
    }

    if (player_mutation_level(MUT_BERSERK)
            && x_chance_in_y(player_mutation_level(MUT_BERSERK) * 10 - 5, 100)
        || _equipment_make_berserk())
    {
        go_berserk(false);
    }
}

void player::go_berserk(bool intentional, bool potion)
{
    ::go_berserk(intentional, potion);
}

bool player::can_go_berserk() const
{
    return (can_go_berserk(false));
}

bool player::can_go_berserk(bool intentional, bool potion) const
{
    const bool verbose = intentional || potion;

    if (berserk())
    {
        if (verbose)
            mpr("You're already berserk!");
        // or else you won't notice -- no message here.
        return (false);
    }

    if (duration[DUR_EXHAUSTED])
    {
        if (verbose)
            mpr("You're too exhausted to go berserk.");
        // or else they won't notice -- no message here
        return (false);
    }

    if (beheld())
    {
        if (verbose)
            mpr("You are too mesmerised to rage.");
        // or else they won't notice -- no message here
        return (false);
    }

    if (is_undead
        && (is_undead != US_SEMI_UNDEAD || hunger_state <= HS_SATIATED))
    {
        if (verbose)
            mpr("You cannot raise a blood rage in your lifeless body.");

        // or else you won't notice -- no message here
        return (false);
    }

    // Stasis, but only for identified amulets; unided amulets will
    // trigger when the player attempts to activate berserk,
    // auto-iding at that point, but also killing the berserk and
    // wasting a turn.
    if (wearing_amulet(AMU_STASIS, false))
    {
        if (verbose)
        {
            const item_def *amulet = you.slot_item(EQ_AMULET);
            mprf("You cannot go berserk with %s on.",
                 amulet? amulet->name(DESC_NOCAP_YOUR).c_str() : "your amulet");
        }
        return (false);
    }

    if (!intentional && !potion && player_mental_clarity(true))
    {
        if (verbose)
        {
            mpr("You're too calm and focused to rage.");
            item_def *amu;
            if (!player_mental_clarity(false) && wearing_amulet(AMU_CLARITY)
                && (amu = &you.inv[you.equip[EQ_AMULET]]) && !item_type_known(*amu))
            {
                set_ident_type(amu->base_type, amu->sub_type, ID_KNOWN_TYPE);
                set_ident_flags(*amu, ISFLAG_KNOW_PROPERTIES);
                mprf("You are wearing: %s",
                     amu->name(DESC_INVENTORY_EQUIP).c_str());
            }
        }

        return (false);
    }

    return (true);
}

bool player::berserk() const
{
    return (duration[DUR_BERSERKER]);
}

void player::god_conduct(conduct_type thing_done, int level)
{
    ::did_god_conduct(thing_done, level);
}

void player::banish(const std::string &who)
{
    ASSERT(!crawl_state.arena);

    banished    = true;
    banished_by = who;
}

void player::make_hungry(int hunger_increase, bool silent)
{
    if (hunger_increase > 0)
        ::make_hungry(hunger_increase, silent);
    else if (hunger_increase < 0)
        ::lessen_hunger(-hunger_increase, silent);
}

// For semi-undead species (Vampire!) reduce food cost for spells and abilities
// to 50% (hungry, very hungry) or zero (near starving, starving).
int calc_hunger(int food_cost)
{
    if (you.is_undead == US_SEMI_UNDEAD && you.hunger_state < HS_SATIATED)
    {
        if (you.hunger_state <= HS_NEAR_STARVING)
            return 0;

        return (food_cost/2);
    }
    return (food_cost);
}

int player::warding() const
{
    if (wearing_amulet(AMU_WARDING))
        return (30);

    return (0);
}

bool player::paralysed() const
{
    return (duration[DUR_PARALYSIS]);
}

bool player::cannot_move() const
{
    return (paralysed() || petrified());
}

bool player::confused() const
{
    return (duration[DUR_CONF]);
}

bool player::caught() const
{
    return (attribute[ATTR_HELD]);
}

bool player::petrified() const
{
    return (duration[DUR_PETRIFIED]);
}

int player::shield_block_penalty() const
{
    return (5 * shield_blocks * shield_blocks);
}

int player::shield_bonus() const
{
    const int shield_class = player_shield_class();
    if (shield_class <= 0)
        return (-100);

    return random2avg(shield_class * 2, 2) / 3 - 1;
}

int player::shield_bypass_ability(int tohit) const
{
    return (15 + tohit / 2);
}

void player::shield_block_succeeded(actor *foe)
{
    actor::shield_block_succeeded(foe);

    shield_blocks++;
    if (coinflip())
        exercise(SK_SHIELDS, 1);
}

bool player::wearing_light_armour(bool with_skill) const
{
    return (player_light_armour(with_skill));
}

void player::exercise(skill_type sk, int qty)
{
    ::exercise(sk, qty);
}

int player::skill(skill_type sk, bool bump) const
{
    return (bump? skill_bump(sk) : skills[sk]);
}

int player::armour_class() const
{
    int AC = 0;

    for (int eq = EQ_CLOAK; eq <= EQ_BODY_ARMOUR; ++eq)
    {
        if (eq == EQ_SHIELD)
            continue;

        if (!player_wearing_slot(eq))
            continue;

        const item_def& item = inv[equip[eq]];
        const int ac_value = property(item, PARM_AC ) * 100;
        int racial_bonus   = _player_armour_racial_bonus(item);

        AC += ac_value * (30 + 2 * skills[SK_ARMOUR] + racial_bonus) / 30;

        AC += item.plus * 100;

        // The deformed don't fit into body armour very well.
        // (This includes nagas and centaurs.)
        if (eq == EQ_BODY_ARMOUR && player_mutation_level(MUT_DEFORMED))
            AC -= ac_value / 2;
    }

    AC += player_equip( EQ_RINGS_PLUS, RING_PROTECTION ) * 100;

    if (player_equip_ego_type( EQ_WEAPON, SPWPN_PROTECTION ))
        AC += 500;

    if (player_equip_ego_type( EQ_SHIELD, SPARM_PROTECTION ))
        AC += 300;

    AC += scan_artefacts(ARTP_AC) * 100;

    if (duration[DUR_ICY_ARMOUR])
        AC += 400 + 100 * skills[SK_ICE_MAGIC] / 3;         // max 13

    if (duration[DUR_STONEMAIL])
        AC += 500 + 100 * skills[SK_EARTH_MAGIC] / 2;       // max 18

    if (duration[DUR_STONESKIN])
        AC += 200 + 100 * skills[SK_EARTH_MAGIC] / 5;       // max 7

    if (mutation[MUT_ICEMAIL])
        AC += (100 * ICEMAIL_MAX)
              - (duration[DUR_ICEMAIL_DEPLETED] * 100
                     * ICEMAIL_MAX / ICEMAIL_TIME);

    if (attribute[ATTR_TRANSFORMATION] == TRAN_NONE
        || attribute[ATTR_TRANSFORMATION] == TRAN_LICH
        || attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS)
    {
        // Being a lich doesn't preclude the benefits of hide/scales -- bwr
        //
        // Note: Even though necromutation is a high level spell, it does
        // allow the character full armour (so the bonus is low). -- bwr
        if (attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
            AC += (300 + 100 * skills[SK_NECROMANCY] / 6);   // max 7

        //jmf: only give:
        if (player_genus(GENPC_DRACONIAN))
        {
            if (experience_level < 8)
                AC += 200;
            else if (species == SP_GREY_DRACONIAN)
                AC += 100 + 100 * (experience_level - 4) / 2;  // max 12
            else
                AC += 100 + (100 * experience_level / 4);      // max 7
        }
        else
        {
            switch (species)
            {
            case SP_NAGA:
                AC += 100 * experience_level / 3;              // max 9
                break;

            default:
                break;
            }
        }

        // Scales -- some evil uses of the fact that boolean "true" == 1...
        // I'll spell things out with some comments -- bwr

        // mutations:
        // these give: +1, +2, +3
        AC += 100 * player_mutation_level(MUT_TOUGH_SKIN);
        AC += 100 * player_mutation_level(MUT_GREY_SCALES);
        AC += 100 * player_mutation_level(MUT_SHAGGY_FUR);
        AC += 100 * player_mutation_level(MUT_BLUE_SCALES);
        AC += 100 * player_mutation_level(MUT_SPECKLED_SCALES);
        AC += 100 * player_mutation_level(MUT_IRIDESCENT_SCALES);
        AC += 100 * player_mutation_level(MUT_PATTERNED_SCALES);

        // these give: +1, +3, +5
        if (player_mutation_level(MUT_GREEN_SCALES) > 0)
            AC += (player_mutation_level(MUT_GREEN_SCALES) * 200) - 100;
        if (player_mutation_level(MUT_NACREOUS_SCALES) > 0)
            AC += (player_mutation_level(MUT_NACREOUS_SCALES) * 200) - 100;
        if (player_mutation_level(MUT_BLACK2_SCALES) > 0)
            AC += (player_mutation_level(MUT_BLACK2_SCALES) * 200) - 100;
        if (player_mutation_level(MUT_WHITE_SCALES) > 0)
            AC += (player_mutation_level(MUT_WHITE_SCALES) * 200) - 100;

        // these give: +2, +4, +6
        AC += player_mutation_level(MUT_GREY2_SCALES) * 200;
        AC += player_mutation_level(MUT_YELLOW_SCALES) * 200;
        AC += player_mutation_level(MUT_PURPLE_SCALES) * 200;

        // black gives: +3, +6, +9
        AC += player_mutation_level(MUT_BLACK_SCALES) * 300;

        // boney plates give: +2, +3, +4
        if (player_mutation_level(MUT_BONEY_PLATES) > 0)
            AC += 100 * (player_mutation_level(MUT_BONEY_PLATES) + 1);

        // red gives +1, +2, +4
        AC += 100 * (player_mutation_level(MUT_RED_SCALES)
                     + (player_mutation_level(MUT_RED_SCALES) == 3));

        // indigo gives: +2, +3, +5
        if (player_mutation_level(MUT_INDIGO_SCALES) > 0)
        {
            AC += 100 * (1 + player_mutation_level(MUT_INDIGO_SCALES)
                         + (player_mutation_level(MUT_INDIGO_SCALES) == 3));
        }

        // brown gives: +2, +4, +5
        AC += 100 * ((player_mutation_level(MUT_BROWN_SCALES) * 2)
                     - (player_mutation_level(MUT_BROWN_SCALES) == 3));

        // orange gives: +1, +3, +4
        AC += 100 * (player_mutation_level(MUT_ORANGE_SCALES)
                     + (player_mutation_level(MUT_ORANGE_SCALES) > 1));

        // knobbly red gives: +2, +5, +7
        AC += 100 * ((player_mutation_level(MUT_RED2_SCALES) * 2)
                     + (player_mutation_level(MUT_RED2_SCALES) > 1));

        // metallic gives +3, +7, +10
        AC += 100 * (player_mutation_level(MUT_METALLIC_SCALES) * 3
                     + (player_mutation_level(MUT_METALLIC_SCALES) > 1));
    }
    else
    {
        // transformations:
        switch (attribute[ATTR_TRANSFORMATION])
        {
        case TRAN_NONE:
        case TRAN_BLADE_HANDS:
        case TRAN_LICH:  // can wear normal body armour (small bonus)
            break;

        case TRAN_SPIDER: // low level (small bonus), also gets EV
            AC += (200 + 100 * skills[SK_POISON_MAGIC] / 6); // max 6
            break;

        case TRAN_ICE_BEAST:
            AC += (500 + 100 * (skills[SK_ICE_MAGIC] + 1) / 4); // max 12

            if (duration[DUR_ICY_ARMOUR])
                AC += (100 + 100 * skills[SK_ICE_MAGIC] / 4);   // max +7
            break;

        case TRAN_DRAGON:
            AC += (700 + 100 * skills[SK_FIRE_MAGIC] / 3);      // max 16
            break;

        case TRAN_STATUE: // main ability is armour (high bonus)
            AC += (1700 + 100 * skills[SK_EARTH_MAGIC] / 2);    // max 30

            if (duration[DUR_STONESKIN] || duration[DUR_STONEMAIL])
                AC += (100 + 100 * skills[SK_EARTH_MAGIC] / 4); // max +7
            break;

        default:
            break;
        }
    }

    return (AC / 100);
}

int player::melee_evasion(const actor *act, ev_ignore_type evit) const
{
    return (player_evasion(evit)
            - ((!act || act->visible_to(this)
                || (evit & EV_IGNORE_HELPLESS)) ? 0 : 10)
            - (you_are_delayed()
               && !(evit & EV_IGNORE_HELPLESS)
               && !is_run_delay(current_delay_action())? 5 : 0));
}

bool player::heal(int amount, bool max_too)
{
    ::inc_hp(amount, max_too);
    return true; /* TODO Check whether the player was healed. */
}

mon_holy_type player::holiness() const
{
    if (is_undead)
        return (MH_UNDEAD);

    if (species == SP_DEMONSPAWN)
        return (MH_DEMONIC);

    return (MH_NATURAL);
}

bool player::undead_or_demonic() const
{
    const mon_holy_type holi = holiness();

    return (holi == MH_UNDEAD || holi == MH_DEMONIC);
}

bool player::is_holy() const
{
    if (is_good_god(religion))
        return (true);

    return (false);
}

bool player::is_unholy() const
{
    return (holiness() == MH_DEMONIC);
}

bool player::is_evil() const
{
    if (holiness() == MH_UNDEAD)
        return (true);

    if (is_evil_god(religion))
        return (true);

    return (false);
}

bool player::is_chaotic() const
{
    if (is_chaotic_god(religion))
        return (true);

    return (false);
}

// Output active level of player mutation.
// Might be lower than real mutation for non-"Alive" Vampires.
int player_mutation_level(mutation_type mut)
{
    const int mlevel = you.mutation[mut];

    if (mutation_is_fully_active(mut))
        return (mlevel);

    // For now, dynamic mutations only apply to semi-undead.
    ASSERT(you.is_undead == US_SEMI_UNDEAD);

    // Assumption: stat mutations are physical, and thus always fully active.
    switch (you.hunger_state)
    {
    case HS_ENGORGED:
        return (mlevel);
    case HS_VERY_FULL:
    case HS_FULL:
        return (std::min(mlevel, 2));
    case HS_SATIATED:
        return (std::min(mlevel, 1));
    }
    return (0);
}

int player::res_fire() const
{
    return (player_res_fire());
}

int player::res_steam() const
{
    return (player_res_steam());
}

int player::res_cold() const
{
    return (player_res_cold());
}

int player::res_elec() const
{
    return (player_res_electricity() * 2);
}

int player::res_water_drowning() const
{
    return (res_asphyx() ||
            (you.species == SP_MERFOLK && !transform_changed_physiology()));
}

int player::res_asphyx() const
{
    // The undead are immune to asphyxiation, or so we'll assume.
    if (this->is_undead)
        return 1;

    switch (this->attribute[ATTR_TRANSFORMATION])
    {
    case TRAN_LICH:
    case TRAN_STATUE:
        return 1;
        break;
    }

    return 0;
}

int player::res_poison() const
{
    return (player_res_poison());
}

int player::res_rotting() const
{
    if (is_undead
        && (is_undead != US_SEMI_UNDEAD || hunger_state < HS_SATIATED))
    {
        return (1);
    }

    return (0);
}

int player::res_sticky_flame() const
{
    return (player_res_sticky_flame());
}

int player::res_holy_energy(const actor *attacker) const
{
    if (undead_or_demonic())
        return (-2);

    if (is_evil())
        return (-1);

    if (is_holy())
        return (1);

    return (0);
}

int player::res_negative_energy() const
{
    return (player_prot_life());
}

int player::res_torment() const
{
    return (player_res_torment());
}

int player::res_magic() const
{
    int rm = 0;

    switch (this->species)
    {
    case SP_MOUNTAIN_DWARF:
    case SP_HILL_ORC:
        rm = this->experience_level * 2;
        break;
    default:
        rm = this->experience_level * 3;
        break;
    case SP_HIGH_ELF:
    case SP_SLUDGE_ELF:
    case SP_DEEP_ELF:
    case SP_VAMPIRE:
    case SP_DEMIGOD:
    case SP_OGRE:
        rm = this->experience_level * 4;
        break;
    case SP_NAGA:
        rm = this->experience_level * 5;
        break;
    case SP_PURPLE_DRACONIAN:
    case SP_DEEP_DWARF:
        rm = this->experience_level * 6;
        break;
    case SP_SPRIGGAN:
        rm = this->experience_level * 7;
        break;
    }

    // randarts
    rm += scan_artefacts(ARTP_MAGIC);

    // armour
    rm += 30 * player_equip_ego_type(EQ_ALL_ARMOUR, SPARM_MAGIC_RESISTANCE);

    // rings of magic resistance
    rm += 40 * player_equip(EQ_RINGS, RING_PROTECTION_FROM_MAGIC);

    // Enchantment skill
    rm += 2 * this->skills[SK_ENCHANTMENTS];

    // Mutations
    rm += 30 * player_mutation_level(MUT_MAGIC_RESISTANCE);

    // transformations
    if (this->attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
        rm += 50;

    // Trog's Hand
    if (this->attribute[ATTR_DIVINE_REGENERATION])
        rm += 70;

    // Enchantment effect
    if (this->duration[DUR_LOWERED_MR])
        rm /= 2;

    return (rm);
}

bool player::confusable() const
{
    return (player_mental_clarity() == 0);
}

bool player::slowable() const
{
    return true;
}

flight_type player::flight_mode() const
{
    if (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
        || attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
    {
        return (FL_FLY);
    }
    else if (is_levitating())
    {
        return (this->duration[DUR_CONTROLLED_FLIGHT]
                || wearing_amulet(AMU_CONTROLLED_FLIGHT) ? FL_FLY
                                                         : FL_LEVITATE);
    }
    else
        return (FL_NONE);
}

bool player::permanent_levitation() const
{
    // Boots of levitation keep you with DUR_LEVITATION >= 2 at
    // all times. This is so that you can evoke stop-levitation
    // in order to actually cancel levitation (by setting
    // DUR_LEVITATION to 1.) Note that antimagic() won't do this.
    return (airborne() && player_equip_ego_type(EQ_BOOTS, SPARM_LEVITATION)
            && this->duration[DUR_LEVITATION] > 1);
}

bool player::permanent_flight() const
{
    return (airborne() && wearing_amulet(AMU_CONTROLLED_FLIGHT)
            && this->species == SP_KENKU && this->experience_level >= 15);
}

bool player::light_flight() const
{
    // Only Kenku get perks for flying light.
    return (species == SP_KENKU
            && flight_mode() == FL_FLY && travelling_light());
}

bool player::travelling_light() const
{
    return (this->burden < carrying_capacity(BS_UNENCUMBERED) * 70 / 100);
}

int player::mons_species() const
{
    if (player_genus(GENPC_DRACONIAN))
        return (MONS_DRACONIAN);

    switch (species)
    {
    case SP_HILL_ORC:
        return (MONS_ORC);
    case SP_HIGH_ELF: case SP_DEEP_ELF: case SP_SLUDGE_ELF:
        return (MONS_ELF);

    default:
        return (MONS_HUMAN);
    }
}

void player::poison(actor *agent, int amount)
{
    ::poison_player(amount);
}

void player::expose_to_element(beam_type element, int st)
{
    ::expose_player_to_element(element, st);
}

void player::blink(bool allow_partial_control)
{
    random_blink(allow_partial_control);
}

void player::teleport(bool now, bool abyss_shift, bool wizard_tele)
{
    ASSERT(!crawl_state.arena);

    if (now)
        you_teleport_now(true, abyss_shift, wizard_tele);
    else
        you_teleport();
}

int player::hurt(const actor *agent, int amount, beam_type flavour,
                 bool cleanup_dead)
{
    // We ignore cleanup_dead here.
    if (agent->atype() == ACT_MONSTER)
    {
        const monsters *mon = dynamic_cast<const monsters*>(agent);
        ouch(amount, mon->mindex(),
             KILLED_BY_MONSTER, "", mon->visible_to(&you));
    }
    else
    {
        // Should never happen!
        ASSERT(false);
        ouch(amount, NON_MONSTER, KILLED_BY_SOMETHING);
    }

    if ((flavour == BEAM_NUKE || flavour == BEAM_DISINTEGRATION) && can_bleed())
    {
        blood_spray(pos(), id(), amount / 5);
    }

    return (amount);
}

void player::drain_stat(int stat, int amount, actor *attacker)
{
    if (attacker == NULL)
        lose_stat(stat, amount, false, "");
    else if (attacker->atype() == ACT_MONSTER)
        lose_stat(stat, amount, dynamic_cast<monsters*>(attacker), false);
    else if (attacker->atype() == ACT_PLAYER)
        lose_stat(stat, amount, false, "suicide");
    else
        lose_stat(stat, amount, false, "");
}

bool player::rot(actor *who, int amount, int immediate, bool quiet)
{
    ASSERT(!crawl_state.arena);

    if (amount <= 0 && immediate <= 0)
        return (false);

    if (res_rotting())
    {
        mpr("You feel terrible.");
        return (false);
    }

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

    if (immediate > 0)
        rot_hp(immediate);

    if (this->rotting < 40)
    {
        // Either this, or the actual rotting message should probably
        // be changed so that they're easier to tell apart. -- bwr
        mprf(MSGCH_WARN, "You feel your flesh %s away!",
             this->rotting > 0 ? "rotting" : "start to rot");

        this->rotting += amount;

        learned_something_new(TUT_YOU_ROTTING);
    }

    if (one_chance_in(4))
        sicken(50 + random2(100));

    return (true);
}

bool player::drain_exp(actor *who, bool quiet, int pow)
{
    return (::drain_exp());
}

void player::confuse(actor *who, int str)
{
    confuse_player(str);
}

void player::paralyse(actor *who, int str)
{
    ASSERT(!crawl_state.arena);

    // The shock is too mild to do damage.
    if (stasis_blocks_effect(true, "%s gives you a mild electric shock."))
        return;

    int &paralysis(duration[DUR_PARALYSIS]);

    mprf("You %s the ability to move!",
         paralysis ? "still haven't" : "suddenly lose");

    str *= BASELINE_DELAY;
    if (str > paralysis && (paralysis < 3 || one_chance_in(paralysis)))
        paralysis = str;

    if (paralysis > 13 * BASELINE_DELAY)
        paralysis = 13 * BASELINE_DELAY;
}

void player::petrify(actor *who, int str)
{
    ASSERT(!crawl_state.arena);

    if (stasis_blocks_effect(true, "%s gives you a mild electric shock."))
        return;

    str *= BASELINE_DELAY;
    int &petrif(duration[DUR_PETRIFIED]);

    mprf("You %s the ability to move!",
         petrif ? "still haven't" : "suddenly lose");

    if (str > petrif && (petrif < 3 || one_chance_in(petrif)))
        petrif = str;

    petrif = std::min(13 * BASELINE_DELAY, petrif);
}

void player::slow_down(actor *foe, int str)
{
    ::slow_player(str);
}

int player::has_claws(bool allow_tran) const
{
    if (allow_tran)
    {
        // these transformations bring claws with them
        if (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON)
            return (3);

        // transformations other than these will override claws
        if (attribute[ATTR_TRANSFORMATION] != TRAN_NONE
            && attribute[ATTR_TRANSFORMATION] != TRAN_STATUE
            && attribute[ATTR_TRANSFORMATION] != TRAN_LICH)
        {
            return (0);
        }
    }

    // XXX: Some assumptions about mutations and species in here.
    int sp_claws = species_has_claws(species);
    if (sp_claws > 0)
        return (sp_claws);

    return (player_mutation_level(MUT_CLAWS));
}

bool player::has_usable_claws(bool allow_tran) const
{
    return (equip[EQ_GLOVES] == -1 && has_claws(allow_tran));
}

god_type player::deity() const
{
    return (religion);
}

kill_category player::kill_alignment() const
{
    return (KC_YOU);
}

bool player::sicken(int amount)
{
    ASSERT(!crawl_state.arena);

    if (res_rotting() || amount <= 0)
        return (false);

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

    mpr("You feel ill.");

    disease += amount * BASELINE_DELAY;
    if (disease > 210 * BASELINE_DELAY)
        disease = 210 * BASELINE_DELAY;

    learned_something_new(TUT_YOU_SICK);
    return (true);
}

bool player::can_see_invisible(bool calc_unid) const
{
    int si = 0;

    si += player_equip( EQ_RINGS, RING_SEE_INVISIBLE, calc_unid );

    // armour: (checks head armour only)
    si += player_equip_ego_type( EQ_HELMET, SPARM_SEE_INVISIBLE );

    if (player_mutation_level(MUT_ACUTE_VISION) > 0)
        si += player_mutation_level(MUT_ACUTE_VISION);

    //jmf: added see_invisible spell
    if (this->duration[DUR_SEE_INVISIBLE] > 0)
        si++;

    // randart wpns
    int artefacts = scan_artefacts(ARTP_EYESIGHT, calc_unid);

    if (artefacts > 0)
        si += artefacts;

    if (si > 1)
        si = 1;

    return (si);
}

bool player::can_see_invisible() const
{
    return (can_see_invisible(true));
}

bool player::invisible() const
{
    return (duration[DUR_INVIS] && !backlit());
}

bool player::misled() const
{
    return (duration[DUR_MISLED]);
}

bool player::visible_to(const actor *looker) const
{
    if (crawl_state.arena)
        return (false);

    if (this == looker)
        return (can_see_invisible() || !invisible());

    const monsters* mon = dynamic_cast<const monsters*>(looker);
    return (!invisible()
            || in_water()
            || mon->can_see_invisible()
            || mons_sense_invis(mon));
}

bool player::backlit(bool check_haloed) const
{
    return (get_contamination_level() > 0 || duration[DUR_CORONA]
            || (check_haloed ? haloed() : false)
            || duration[DUR_LIQUID_FLAMES]);
}

// This is the imperative version.
void player::backlight()
{
    if (!this->duration[DUR_INVIS])
    {
        if (this->duration[DUR_CORONA])
            mpr("You glow brighter.");
        else
            mpr("You are outlined in light.");

        you.increase_duration(DUR_CORONA, random_range(15, 35), 250);
    }
    else
    {
        mpr("You feel strangely conspicuous.");

        you.increase_duration(DUR_CORONA, random_range(3, 5), 250);
    }
}

bool player::can_mutate() const
{
    return (true);
}

bool player::can_safely_mutate() const
{
    if (!can_mutate())
        return (false);

    return (!this->is_undead
            || this->is_undead == US_SEMI_UNDEAD
               && this->hunger_state == HS_ENGORGED);
}

bool player::can_bleed() const
{
    if (this->is_undead && (this->species != SP_VAMPIRE
                          || this->hunger_state <= HS_SATIATED))
    {
        return (false);
    }

    const int tran = this->attribute[ATTR_TRANSFORMATION];
    // The corresponding monsters don't bleed either.
    if (tran == TRAN_STATUE || tran == TRAN_ICE_BEAST
        || tran == TRAN_LICH || tran == TRAN_SPIDER)
    {
        return (false);
    }
    return (true);
}

bool player::mutate()
{
    ASSERT(!crawl_state.arena);

    if (!can_mutate())
        return (false);

    if (one_chance_in(5))
    {
        if (::mutate(RANDOM_MUTATION))
        {
            learned_something_new(TUT_YOU_MUTATED);
            return (true);
        }
    }

    return (give_bad_mutation());
}

bool player::is_icy() const
{
    return (attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST);
}

bool player::is_fiery() const
{
    return (false);
}

void player::set_position(const coord_def &c)
{
    ASSERT(!crawl_state.arena);

    const bool real_move = (c != pos());
    actor::set_position(c);

    if (real_move)
    {
        this->reset_prev_move();
        dungeon_events.fire_position_event(DET_PLAYER_MOVED, c);

        // Reset lava/water nearness check to unknown, so it'll be
        // recalculated for the next monster that tries to reach us.
        this->lava_in_sight = this->water_in_sight = -1;
    }
}

void player::moveto(const coord_def &c)
{
    if (c != this->pos())
        clear_trapping_net();

    crawl_view.set_player_at(c);
    set_position(c);
}

bool player::move_to_pos(const coord_def &c)
{
    actor *target = actor_at(c);
    if (!target || target->submerged())
    {
        moveto(c);
        return true;
    }
    return false;
}

void player::apply_location_effects(const coord_def &oldpos,
                                    killer_type killer,
                                    int killernum)
{
    move_player_to_grid(pos(), false, true, true, false);
}

void player::shiftto(const coord_def &c)
{
    crawl_view.shift_player_to(c);
    set_position(c);
}

void player::reset_prev_move()
{
    prev_move.reset();
}

bool player::asleep() const
{
    return (duration[DUR_SLEEP]);
}

bool player::cannot_act() const
{
    return (asleep() || cannot_move());
}

bool player::can_throw_large_rocks() const
{
    return (player_genus(GENPC_OGRE) || species == SP_TROLL);
}

bool player::can_smell() const
{
    return (species != SP_MUMMY);
}

void player::hibernate(int)
{
    ASSERT(!crawl_state.arena);

    if (!can_hibernate())
    {
        mpr("You feel weary for a moment.");
        return;
    }

    mpr("You fall asleep.");

    stop_delay();
    flash_view(DARKGREY);

    // Do this *after* redrawing the view, or viewwindow() will no-op.
    set_duration(DUR_SLEEP, 3 + random2avg(5, 2));
}

void player::put_to_sleep(actor*, int power)
{
    ASSERT(!crawl_state.arena);

    if (duration[DUR_SLEEP])
        return;

    mpr("You fall asleep!");

    stop_delay();
    flash_view(DARKGREY);

    // As above, do this after redraw.
    set_duration(DUR_SLEEP, 5 + random2avg(power/10, 5));
}

void player::awake()
{
    ASSERT(!crawl_state.arena);

    duration[DUR_SLEEP] = 0;
    mpr("You wake up.");
    flash_view(BLACK);
}

void player::check_awaken(int disturbance)
{
    if (asleep() && x_chance_in_y(disturbance + 1, 50))
        awake();
}

////////////////////////////////////////////////////////////////////////////

PlaceInfo::PlaceInfo()
    : level_type(-2), branch(-2), num_visits(0),
      levels_seen(0), mon_kill_exp(0), mon_kill_exp_avail(0),
      turns_total(0), turns_explore(0), turns_travel(0), turns_interlevel(0),
      turns_resting(0), turns_other(0), elapsed_total(0.0),
      elapsed_explore(0.0), elapsed_travel(0.0), elapsed_interlevel(0.0),
      elapsed_resting(0.0), elapsed_other(0.0)
{
    for (int i = 0; i < KC_NCATEGORIES; i++)
        mon_kill_num[i] = 0;
}

bool PlaceInfo::is_global() const
{
    return (level_type == -1 && branch == -1);
}

void PlaceInfo::make_global()
{
    level_type = -1;
    branch     = -1;
}

void PlaceInfo::assert_validity() const
{
    // Check that level_type and branch match up.
    ASSERT(is_global()
           || level_type == LEVEL_DUNGEON && branch >= BRANCH_MAIN_DUNGEON
              && branch < NUM_BRANCHES
           || level_type > LEVEL_DUNGEON && level_type < NUM_LEVEL_AREA_TYPES
              && branch == -1);

    // Can't have visited a place without seeing any of its levels, and
    // vice versa.
    ASSERT(num_visits == 0 && levels_seen == 0
           || num_visits > 0 && levels_seen > 0);

    if (level_type == LEVEL_LABYRINTH || level_type == LEVEL_ABYSS)
        ASSERT(num_visits == levels_seen);
    else if (level_type == LEVEL_PANDEMONIUM)
        // Ziggurats can allow a player to return to the same
        // Pandemonium level.
        // ASSERT(num_visits <= levels_seen);
        ;
    else if (level_type == LEVEL_DUNGEON && branches[branch].depth > 0)
        ASSERT(levels_seen <= (unsigned long) branches[branch].depth);

    ASSERT(turns_total == (turns_explore + turns_travel + turns_interlevel
                           + turns_resting + turns_other));

    ASSERT(elapsed_total == (elapsed_explore + elapsed_travel
                             + elapsed_interlevel + elapsed_resting
                             + elapsed_other));
}

const std::string PlaceInfo::short_name() const
{
    if (level_type == LEVEL_DUNGEON)
        return branches[branch].shortname;
    else
    {
        switch (level_type)
        {
        case LEVEL_ABYSS:
            return "Abyss";

        case LEVEL_PANDEMONIUM:
            return "Pandemonium";

        case LEVEL_LABYRINTH:
            return "Labyrinth";

        case LEVEL_PORTAL_VAULT:
            return "Portal Chamber";

        default:
            return "Bug";
        }
    }
}

const PlaceInfo &PlaceInfo::operator += (const PlaceInfo &other)
{
    num_visits  += other.num_visits;
    levels_seen += other.levels_seen;

    mon_kill_exp       += other.mon_kill_exp;
    mon_kill_exp_avail += other.mon_kill_exp_avail;

    for (int i = 0; i < KC_NCATEGORIES; i++)
        mon_kill_num[i] += other.mon_kill_num[i];

    turns_total      += other.turns_total;
    turns_explore    += other.turns_explore;
    turns_travel     += other.turns_travel;
    turns_interlevel += other.turns_interlevel;
    turns_resting    += other.turns_resting;
    turns_other      += other.turns_other;

    elapsed_total      += other.elapsed_total;
    elapsed_explore    += other.elapsed_explore;
    elapsed_travel     += other.elapsed_travel;
    elapsed_interlevel += other.elapsed_interlevel;
    elapsed_resting    += other.elapsed_resting;
    elapsed_other      += other.elapsed_other;

    return (*this);
}

const PlaceInfo &PlaceInfo::operator -= (const PlaceInfo &other)
{
    num_visits  -= other.num_visits;
    levels_seen -= other.levels_seen;

    mon_kill_exp       -= other.mon_kill_exp;
    mon_kill_exp_avail -= other.mon_kill_exp_avail;

    for (int i = 0; i < KC_NCATEGORIES; i++)
        mon_kill_num[i] -= other.mon_kill_num[i];

    turns_total      -= other.turns_total;
    turns_explore    -= other.turns_explore;
    turns_travel     -= other.turns_travel;
    turns_interlevel -= other.turns_interlevel;
    turns_resting    -= other.turns_resting;
    turns_other      -= other.turns_other;

    elapsed_total      -= other.elapsed_total;
    elapsed_explore    -= other.elapsed_explore;
    elapsed_travel     -= other.elapsed_travel;
    elapsed_interlevel -= other.elapsed_interlevel;
    elapsed_resting    -= other.elapsed_resting;
    elapsed_other      -= other.elapsed_other;

    return (*this);
}

PlaceInfo PlaceInfo::operator + (const PlaceInfo &other) const
{
    PlaceInfo copy = *this;
    copy += other;
    return copy;
}

PlaceInfo PlaceInfo::operator - (const PlaceInfo &other) const
{
    PlaceInfo copy = *this;
    copy -= other;
    return copy;
}

PlaceInfo& player::get_place_info() const
{
    return get_place_info(where_are_you, level_type);
}

PlaceInfo& player::get_place_info(branch_type branch) const
{
    return get_place_info(branch, LEVEL_DUNGEON);
}

PlaceInfo& player::get_place_info(level_area_type level_type2) const
{
    return get_place_info(NUM_BRANCHES, level_type2);
}

PlaceInfo& player::get_place_info(branch_type branch,
                                  level_area_type level_type2) const
{
    ASSERT(level_type2 == LEVEL_DUNGEON && branch >= BRANCH_MAIN_DUNGEON
                && branch < NUM_BRANCHES
           || level_type2 > LEVEL_DUNGEON && level_type < NUM_LEVEL_AREA_TYPES);

    if (level_type2 == LEVEL_DUNGEON)
        return (PlaceInfo&) branch_info[branch];
    else
        return (PlaceInfo&) non_branch_info[level_type2 - 1];
}

void player::set_place_info(PlaceInfo place_info)
{
    place_info.assert_validity();

    if (place_info.is_global())
        global_info = place_info;
    else if (place_info.level_type == LEVEL_DUNGEON)
        branch_info[place_info.branch] = place_info;
    else
        non_branch_info[place_info.level_type - 1] = place_info;
}

std::vector<PlaceInfo> player::get_all_place_info(bool visited_only,
                                                  bool dungeon_only) const
{
    std::vector<PlaceInfo> list;

    for (int i = 0; i < NUM_BRANCHES; i++)
    {
        if (visited_only && branch_info[i].num_visits == 0
            || dungeon_only && branch_info[i].level_type != LEVEL_DUNGEON)
        {
            continue;
        }
        list.push_back(branch_info[i]);
    }

    for (int i = 0; i < (NUM_LEVEL_AREA_TYPES - 1); i++)
    {
        if (visited_only && non_branch_info[i].num_visits == 0
            || dungeon_only && non_branch_info[i].level_type != LEVEL_DUNGEON)
        {
            continue;
        }
        list.push_back(non_branch_info[i]);
    }

    return list;
}

bool player::do_shaft()
{
    dungeon_feature_type force_stair = DNGN_UNSEEN;

    if (!is_valid_shaft_level())
        return (false);

    // Handle instances of do_shaft() being invoked magically when
    // the player isn't standing over a shaft.
    if (get_trap_type(this->pos()) != TRAP_SHAFT)
    {
        switch (grd(this->pos()))
        {
        case DNGN_FLOOR:
        case DNGN_OPEN_DOOR:
        case DNGN_TRAP_MECHANICAL:
        case DNGN_TRAP_MAGICAL:
        case DNGN_TRAP_NATURAL:
        case DNGN_UNDISCOVERED_TRAP:
        case DNGN_ENTER_SHOP:
            break;

        default:
            return (false);
        }

        handle_items_on_shaft(this->pos(), false);

        if (airborne() || total_weight() == 0)
            return (true);

        force_stair = DNGN_TRAP_NATURAL;
    }

    down_stairs(your_level, force_stair);

    return (true);
}

bool player::did_escape_death() const
{
    return (escaped_death_cause != NUM_KILLBY);
}

void player::reset_escaped_death()
{
    escaped_death_cause = NUM_KILLBY;
    escaped_death_aux   = "";
}

void player::add_gold(int delta)
{
    set_gold(gold + delta);
}

void player::del_gold(int delta)
{
    set_gold(gold - delta);
}

void player::set_gold(int amount)
{
    ASSERT(amount >= 0);

    if (amount != gold)
    {
        const int old_gold = gold;
        gold = amount;
        shopping_list.gold_changed(old_gold, gold);
    }
}

void player::increase_duration(duration_type dur, int turns, int cap,
                               const char* msg)
{
    if (msg)
        mpr(msg);
    cap *= BASELINE_DELAY;

    you.duration[dur] += turns * BASELINE_DELAY;
    if (cap && you.duration[dur] > cap)
        you.duration[dur] = cap;
}

void player::set_duration(duration_type dur, int turns,
                          int cap, const char * msg)
{
    you.duration[dur] = 0;
    increase_duration(dur, turns, cap, msg);
}