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



                                                                    


                   

                   
                 
                   


                   
 
                    


                 
           




                      

                 
                            


               
                    
                    
 
                     
                 

                     
                    
                
                  
                  
                    


                     
                     

                  
                    
                 
                     
                      
                      
                      
                  
                   
                   
                   


                     
                    
                  
                  
                     
                 
                  
                

 
                                            
                                    

 
                                       
                                                     

               
                          
 
                                                      
 
                                                    
                                                            

                                                      
                                         



                    






                                                                              
                    


                                                                        
                                       
                                   
         
                                                  

                                   

              
                   


                                                                       
                                       
                                   
         
                                                
                                   



                   
                                                    
                                                                       
                              
                                       
                                   

                                              
                                   



                          



                                                               
                              
                                       

              


                                     
                        
                                                



                                                            
                                       


                           


                                                                            


                                     

                                                  
                                                  
 

                                                                     
                                                 




                                    
                            

                                                                       
                                                                        




                                   
                                            




                    

                                                                       
 
                              
                                                 
                                   

                                             
                                   



                   

                                                                       
 
                              
                                                 
                                   
         
                                                
                                   

              



                              
                                       




                                                                
                              
         
                                       




                       
     
                           

                                                  
                       
                          
                        
                          
                                      


                                       
              
     


              
                                             

                    
 
 
                                                            
 
                
                                                             
 
                                                             
     
                                       

                                             
                                                   
         

                                                                       

     
 
                
     



                                                                         
                                   



                                           
                                                            
         
     
 
 
                                   
 
                                          
 
                                                               



                                          
                                                                           
                                                                       
     
                                                  
                                  
 
 
                            
 


                                   
 




                                                             

                                                                   
                                 


               


                                                                         

                                                                               



                                                                
                           

                    


                                                       






                                 
                      
                                                                        




                                 
 
            
                                     
               




                                           
                                          
 
                                                                           
                                                                         
                                                                            
                

                                                                            
            
                              
 
                                                                      





                                                   



                                                                              




                    
                              
 

                                          
            
                                   
 

                                           
 
                                         

                                    
 
 

                                                                        
                                               
 
                                      
 
                    
     












                                   
                         




                                

     





                                                                             
                                                                      
 

                     
                                                          
                                       
                       
 







                                                                    
                                                                   
                                                                      

                                                                       
                                       
     
                                   

                     


                                                      
         
                                                                       


                                                                
                         
             
 

                                                                          

                                                     




                                           

                                                     



                         
                                                   
                                                         
             
                                                 

                               
 

                                                  
 
                                                    
                              

                                                               
                 



             


                       
                                 


                                   
                         
     




                                                                 
 





                                                                 
 
                  
                                                         






                                                       
     

                                                
 
                  

 
                                                                       
                                          




                                                        
                       
 



                                                                 
                                            
     
                            

                     

                                                                        
         


                                             




                                                           
             


         





                                   
                            
     
                             
         



















                                                             
         
     



                                                
 
 
                                                                       
                                                                      
                                                                       
                                                                    

                                                                   
  
                                                                       
  
                                                                 
                                                              

                                                           
                           


                                                             
                                 
     
                                                      
                                         






                                                                 
     
 



                                                            

 
                 
 

                                                                   
                                  
     
                                                             


                                                            



                                                                
                    
                                                        


                                                                          
                                             







                  

                                       
                                                                        

                                                                        
                                                
                                 
 
                           
 
 
                                  
 
                                              
 
                        
     
                          
                                       

                       


                            
     
                                                             
 
                                                            
                      
     



                                  

                      

     

                                                                         
                                                                       

                                                                       
 
                        

                                                                       
                                                       


                                                                             
 
                                                

                                                             



                                               


                            
     
                                             
                                                      
                                                        




                                 
                              
                                      
                                          
 
                                                           
 
                                                              
                                        
 
                                     


                                                                  

                      
     

                   
 
 

                                                           
 
                                
     

                                              


                                                 
         





                                                                               

                   

                                                                 




                                                               





                                                
                                                
                                                   
                                                     
                                                        


                   
 

                                                      
 

                              
 
                                   




                                                          
 


                                                                
 



                                                                       
 
                                       
                                 
 
                                                         
                                
 


                                                                     
                                

                                                                       
                                
 

                                          
 
                                                    
 

                                          

 

                                                           
                             
     


                                                            

                                            




                                                    



                                            
                             
 
                         





                                      

                                                                         
 
                                                          

                                                                           










































                                                                   







                                                                                
                                                       
 
                              
                                             
                                                                    

                         
                                      
                                      
                                       
                                      
                                       
                         












                                                                         
                                                                            
                                                    
















                                                                              
             




















                                                
      
 


                                                                    
 
                               

                                    
 



                                                                           
                                      




                     
                                     
                                         
 

                              
 
                                                                     
                                         



               
                             
     









                                                                     
 
                          
         








                                                                                

                       

         
                          
 


                                                                        
                                                           
 
                       
         





                                                                        
                                                              
 
                              
                                    

                            
                                                                  


                

                                                                    
                                                                   


                      
                                                                                      
 
                                                    
                                                           
                                                                     
 

                         

     
                                                         



                                                       


                                                         

                                                                        

                                                            


                   

                                                    
                                                 
                                                      

                                       
                   
         
 


                                           
                                                                   
                                            



                                            
                                            
                                       
     





                                                                    
 


                      

                                





                                            



               
                                 

                                                           
             
                                        


                                           
                                           
         

                                                                     

                                
                                                            
 
                                                               
             

                                                               
                                       





                                                               

                                                           
                                   





                                    
                            


                                        


                                                                               
 
                           
                          
 
                              
                                            
                           
                          

     

                                                             


                               
                              
 



                                                
                         
         
     




                 
                                                          



                           
                                                 
 



                                                           



                          

                                 
 
                                                          

                                                                   
     

                                  




                                           

     
                          


                                                                 
                                                                
                                                                 
                                                              
                                                                 
                                                                 

                            
                                                                       
 

                              
               
      
                     
                        
      
                                                            
      
 
                                                                    
 

                                          

                                                     
     






























                                                                   
 



                                                                 
                                                            
                             
                                                                     
 
                                   

                                    


                                                                 


                                                                     

      

                               
 
                           
                           

             
             
                                                   

                                                                               
                                                                  
 
                                                          
 
                                     






                                                                            

                               

           









                                                                
/*
 *  File:       ouch.cc
 *  Summary:    Functions used when Bad Things happen to the player.
 *  Written by: Linley Henzell
 */

#include "AppHdr.h"

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

#ifdef TARGET_OS_DOS
#include <file.h>
#endif

#ifdef UNIX
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#endif

#include "ouch.h"

#ifdef TARGET_COMPILER_MINGW
#include <io.h>
#endif

#include "externs.h"
#include "options.h"

#include "artefact.h"
#include "beam.h"
#include "chardump.h"
#include "delay.h"
#include "effects.h"
#include "env.h"
#include "files.h"
#include "fight.h"
#include "godabil.h"
#include "hiscores.h"
#include "invent.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
#include "macro.h"
#include "message.h"
#include "misc.h"
#include "mon-util.h"
#include "mon-place.h"
#include "mgen_data.h"
#include "mon-stuff.h"
#include "notes.h"
#include "output.h"
#include "player.h"
#include "random.h"
#include "religion.h"
#include "shopping.h"
#include "skills2.h"
#include "spells4.h"
#include "state.h"
#include "stuff.h"
#include "tutorial.h"
#include "view.h"
#include "shout.h"
#include "xom.h"


static void end_game( scorefile_entry &se );
static void _item_corrode(int slot);


// NOTE: DOES NOT check for hellfire!!!
int check_your_resists(int hurted, beam_type flavour)
{
    int resist;
    int original = hurted;

    dprf("checking resistance: flavour=%d", flavour );

    if (flavour == BEAM_FIRE || flavour == BEAM_LAVA
        || flavour == BEAM_HELLFIRE || flavour == BEAM_FRAG)
    {
        if (you.duration[DUR_CONDENSATION_SHIELD] > 0)
            remove_condensation_shield();
    }

    switch (flavour)
    {
    case BEAM_WATER:
        hurted = resist_adjust_damage(&you, flavour,
                                      you.res_water_drowning(), hurted, true);
        if (!hurted)
            mpr("You shrug off the wave.");
        break;

    case BEAM_STEAM:
        hurted = resist_adjust_damage(&you, flavour,
                                      player_res_steam(), hurted, true);
        if (hurted < original)
            canned_msg(MSG_YOU_RESIST);
        else if (hurted > original)
        {
            mpr("The steam scalds you terribly!");
            xom_is_stimulated(200);
        }
        break;

    case BEAM_FIRE:
        hurted = resist_adjust_damage(&you, flavour,
                                      player_res_fire(), hurted, true);
        if (hurted < original)
            canned_msg(MSG_YOU_RESIST);
        else if (hurted > original)
        {
            mpr("The fire burns you terribly!");
            xom_is_stimulated(200);
        }
        break;

    case BEAM_COLD:
        hurted = resist_adjust_damage(&you, flavour,
                                      player_res_cold(), hurted, true);
        if (hurted < original)
            canned_msg(MSG_YOU_RESIST);
        else if (hurted > original)
        {
            mpr("You feel a terrible chill!");
            xom_is_stimulated(200);
        }
        break;

    case BEAM_ELECTRICITY:
        hurted = resist_adjust_damage(&you, flavour,
                                      player_res_electricity(),
                                      hurted, true);

        if (hurted < original)
            canned_msg(MSG_YOU_RESIST);
        break;

    case BEAM_POISON:
        resist = player_res_poison();

        if (resist <= 0)
            poison_player( coinflip() ? 2 : 1 );

        hurted = resist_adjust_damage(&you, flavour, resist,
                                      hurted, true);
        if (resist > 0)
            canned_msg(MSG_YOU_RESIST);
        break;

    case BEAM_POISON_ARROW:
        // [dshaligram] NOT importing uber-poison arrow from 4.1. Giving no
        // bonus to poison resistant players seems strange and unnecessarily
        // arbitrary.
        resist = player_res_poison();

        if (!resist)
            poison_player( 4 + random2(3), true );
        else if (!you.is_undead)
            poison_player( 2 + random2(3), true );

        hurted = resist_adjust_damage(&you, flavour, resist, hurted);
        if (hurted < original)
            canned_msg(MSG_YOU_PARTIALLY_RESIST);
        break;

    case BEAM_NEG:
        resist = player_prot_life();

        // TSO's protection.
        if (you.religion == GOD_SHINING_ONE && you.piety > resist * 50)
        {
            int unhurted = std::min(hurted, (you.piety * hurted) / 150);

            if (unhurted > 0)
                hurted -= unhurted;
        }
        else if (resist > 0)
            hurted -= (resist * hurted) / 3;

        drain_exp();
        break;

    case BEAM_ICE:
        hurted = resist_adjust_damage(&you, flavour, player_res_cold(),
                                      hurted, true);

        if (hurted < original)
            canned_msg(MSG_YOU_PARTIALLY_RESIST);
        else if (hurted > original)
        {
            mpr("You feel a painful chill!");
            xom_is_stimulated(200);
        }
        break;

    case BEAM_LAVA:
        hurted = resist_adjust_damage(&you, flavour, player_res_fire(),
                                      hurted, true);

        if (hurted < original)
            canned_msg(MSG_YOU_PARTIALLY_RESIST);
        else if (hurted > original)
        {
            mpr("The lava burns you terribly!");
            xom_is_stimulated(200);
        }
        break;

    case BEAM_ACID:
        if (player_res_acid())
        {
            canned_msg(MSG_YOU_RESIST);
            hurted = hurted * player_acid_resist_factor() / 100;
        }
        break;

    case BEAM_MIASMA:
        if (you.res_rotting())
        {
            canned_msg(MSG_YOU_RESIST);
            hurted = 0;
        }
        break;

    case BEAM_HOLY:
    {
        // Cleansing flame.
        const int rhe = you.res_holy_energy(NULL);
        if (rhe > 0)
            hurted = 0;
        else if (rhe == 0)
            hurted /= 2;
        else if (rhe < -1)
            hurted = (hurted * 3) / 2;

        if (hurted == 0)
            canned_msg(MSG_YOU_RESIST);
        break;
    }

    default:
        break;
    }                           // end switch

    return (hurted);
}

void splash_with_acid(int acid_strength, bool corrode_items)
{
    int dam = 0;
    const bool wearing_cloak = player_wearing_slot(EQ_CLOAK);

    for (int slot = EQ_CLOAK; slot <= EQ_BODY_ARMOUR; slot++)
    {
        if (!player_wearing_slot(slot))
        {
            if (!wearing_cloak || coinflip())
                dam += roll_dice(1, acid_strength);
        }
        else if (corrode_items && x_chance_in_y(acid_strength + 1, 20))
            _item_corrode(you.equip[slot]);
    }


    if (dam > 0)
    {
        const int post_res_dam = dam * player_acid_resist_factor() / 100;

        if (post_res_dam > 0)
        {
            mpr("The acid burns!");

            if (post_res_dam < dam)
                canned_msg(MSG_YOU_RESIST);

            ouch(post_res_dam, NON_MONSTER, KILLED_BY_ACID);
        }
    }
}

void weapon_acid(int acid_strength)
{
    int hand_thing = you.equip[EQ_WEAPON];

    if (hand_thing == -1 && you_tran_can_wear(EQ_GLOVES, true))
        hand_thing = you.equip[EQ_GLOVES];

    if (hand_thing == -1)
    {
        msg::stream << "Your " << your_hand(true) << " burn!" << std::endl;
        ouch(roll_dice(1, acid_strength), NON_MONSTER, KILLED_BY_ACID);
    }
    else if (x_chance_in_y(acid_strength + 1, 20))
        _item_corrode(hand_thing);
}

void _item_corrode(int slot)
{
    bool it_resists = false;
    bool suppress_msg = false;
    item_def& item = you.inv[slot];

    // Artefacts don't corrode.
    if (is_artefact(item))
        return;

    // Anti-corrosion items protect against 90% of corrosion.
    if (wearing_amulet(AMU_RESIST_CORROSION) && !one_chance_in(10))
    {
        dprf("Amulet protects.");
        return;
    }

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

    int how_rusty = ((item.base_type == OBJ_WEAPONS) ? item.plus2 : item.plus);
    // Already very rusty.
    if (how_rusty < -5)
        return;

    // determine possibility of resistance by object type {dlb}:
    switch (item.base_type)
    {
    case OBJ_ARMOUR:
        if ((item.sub_type == ARM_CRYSTAL_PLATE_MAIL
             || get_equip_race(item) == ISFLAG_DWARVEN)
            && !one_chance_in(5))
        {
            it_resists = true;
            suppress_msg = false;
        }
        break;

    case OBJ_WEAPONS:
    case OBJ_MISSILES:
        if (get_equip_race(item) == ISFLAG_DWARVEN && !one_chance_in(5))
        {
            it_resists = true;
            suppress_msg = false;
        }
        break;

    default:
        // Other items can't corrode.
        return;
    }

    // determine chance of corrosion {dlb}:
    if (!it_resists)
    {
        const int chance = abs(how_rusty);

        // The embedded equation may look funny, but it actually works well
        // to generate a pretty probability ramp {6%, 18%, 34%, 58%, 98%}
        // for values [0,4] which closely matches the original, ugly switch.
        // {dlb}
        if (chance >= 0 && chance <= 4)
            it_resists = x_chance_in_y(2 + (4 << chance) + chance * 8, 100);
        else
            it_resists = true;

        // If the checks get this far, you should hear about it. {dlb}
        suppress_msg = false;
    }

    // handle message output and item damage {dlb}:
    if (!suppress_msg)
    {
        if (it_resists)
            mprf("%s resists.", item.name(DESC_CAP_YOUR).c_str());
        else
            mprf("The acid corrodes %s!", item.name(DESC_NOCAP_YOUR).c_str());
    }

    if (!it_resists)
    {
        how_rusty--;
        xom_is_stimulated(64);

        if (item.base_type == OBJ_WEAPONS)
            item.plus2 = how_rusty;
        else
            item.plus  = how_rusty;

        if (item.base_type == OBJ_ARMOUR)
            you.redraw_armour_class = true;

        if (you.equip[EQ_WEAPON] == slot)
            you.wield_change = true;
    }
}

// Helper function for the expose functions below.
// This currently works because elements only target a single type each.
static int _get_target_class(beam_type flavour)
{
    int target_class = OBJ_UNASSIGNED;

    switch (flavour)
    {
    case BEAM_FIRE:
    case BEAM_LAVA:
    case BEAM_NAPALM:
    case BEAM_HELLFIRE:
        target_class = OBJ_SCROLLS;
        break;

    case BEAM_COLD:
    case BEAM_FRAG:
        target_class = OBJ_POTIONS;
        break;

    case BEAM_SPORE:
    case BEAM_STEAL_FOOD:
        target_class = OBJ_FOOD;
        break;

    default:
        break;
    }

    return (target_class);
}

// XXX: These expose functions could use being reworked into a real system...
// the usage and implementation is currently very hacky.
// Handles the destruction of inventory items from the elements.
static bool _expose_invent_to_element(beam_type flavour, int strength)
{
    int num_dest = 0;

    const int target_class = _get_target_class( flavour );
    if (target_class == OBJ_UNASSIGNED)
        return (false);

    // Fedhas worshipers are exempt from the food destruction effect
    // of spores.
    if(flavour == BEAM_SPORE
       && you.religion == GOD_FEDHAS)
    {
        return false;
    }

    // Currently we test against each stack (and item in the stack)
    // independently at strength%... perhaps we don't want that either
    // because it makes the system very fair and removes the protection
    // factor of junk (which might be more desirable for game play).
    for (int i = 0; i < ENDOFPACK; ++i)
    {
        if (!you.inv[i].is_valid())
            continue;

        if (you.inv[i].base_type == target_class
            || target_class == OBJ_FOOD
               && you.inv[i].base_type == OBJ_CORPSES)
        {
            // Conservation doesn't help against harpies stealing food.
            if (flavour != BEAM_STEAL_FOOD
                && player_item_conserve() && !one_chance_in(10))
            {
                continue;
            }

            // These stack with conservation; they're supposed to be good.
            if (target_class == OBJ_SCROLLS
                && you.mutation[MUT_CONSERVE_SCROLLS]
                && !one_chance_in(10))
            {
                continue;
            }

            if (target_class == OBJ_POTIONS
                && you.mutation[MUT_CONSERVE_POTIONS]
                && !one_chance_in(10))
            {
                continue;
            }

            // Loop through all items in the stack.
            for (int j = 0; j < you.inv[i].quantity; ++j)
            {
                if (x_chance_in_y(strength, 100))
                {
                    num_dest++;

                    if (i == you.equip[EQ_WEAPON])
                        you.wield_change = true;

                    if (dec_inv_item_quantity(i, 1))
                        break;
                    else if (is_blood_potion(you.inv[i]))
                        remove_oldest_blood_potion(you.inv[i]);
                }
            }
        }
    }

    if (!num_dest)
        return (false);

    // Message handled elsewhere.
    if (flavour == BEAM_STEAL_FOOD)
        return (true);

    switch (target_class)
    {
    case OBJ_SCROLLS:
        mprf("%s you are carrying %s fire!",
             (num_dest > 1) ? "Some of the scrolls" : "A scroll",
             (num_dest > 1) ? "catch" : "catches" );
        break;

    case OBJ_POTIONS:
        mprf("%s you are carrying %s and %s!",
             (num_dest > 1) ? "Some of the potions" : "A potion",
             (num_dest > 1) ? "freeze" : "freezes",
             (num_dest > 1) ? "shatter" : "shatters" );
        break;

    case OBJ_FOOD:
        mpr("Some of your food is covered with spores!");
        break;

    default:
        mprf("%s you are carrying %s destroyed!",
             (num_dest > 1) ? "Some items" : "An item",
             (num_dest > 1) ? "were" : "was" );
        break;
    }

    xom_is_stimulated((num_dest > 1) ? 32 : 16);

    return (true);
}

bool expose_items_to_element(beam_type flavour, const coord_def& where,
                             int strength)
{
    int num_dest = 0;

    const int target_class = _get_target_class(flavour);
    if (target_class == OBJ_UNASSIGNED)
        return (false);

    // Beams fly *over* water and lava.
    if (grd(where) == DNGN_LAVA || grd(where) == DNGN_DEEP_WATER)
        return (false);

    for (stack_iterator si(where); si; ++si)
    {
        if (!si->is_valid())
            continue;

        if (si->base_type == target_class
            || target_class == OBJ_FOOD && si->base_type == OBJ_CORPSES)
        {
            if (x_chance_in_y(strength, 100))
            {
                num_dest++;
                if (!dec_mitm_item_quantity(si->index(), 1)
                    && is_blood_potion(*si))
                {
                    remove_oldest_blood_potion(*si);
                }
            }
        }
    }

    if (!num_dest)
        return (false);

    if (flavour == BEAM_STEAL_FOOD)
        return (true);

    if (you.see_cell(where))
    {
        switch (target_class)
        {
        case OBJ_SCROLLS:
            mprf("You see %s of smoke.",
                 (num_dest > 1) ? "some puffs" : "a puff");
            break;

        case OBJ_POTIONS:
            mprf("You see %s shatter.",
                 (num_dest > 1) ? "some glass" : "glass");
            break;

        case OBJ_FOOD:
            mprf("You see %s of spores.",
                 (num_dest > 1) ? "some clouds" : "a cloud");
            break;

        default:
            mprf("%s on the floor %s destroyed!",
                 (num_dest > 1) ? "Some items" : "An item",
                 (num_dest > 1) ? "were" : "was" );
            break;
        }
    }

    xom_is_stimulated((num_dest > 1) ? 32 : 16);

    return (true);
}

// Handle side-effects for exposure to element other than damage.  This
// function exists because some code calculates its own damage instead
// of using check_your_resists() and we want to isolate all the special
// code they keep having to do... namely condensation shield checks,
// you really can't expect this function to even be called for much
// else.
//
// This function now calls _expose_invent_to_element() if strength > 0.
//
// XXX: This function is far from perfect and a work in progress.
bool expose_player_to_element(beam_type flavour, int strength)
{
    // Note that BEAM_TELEPORT is sent here when the player
    // blinks or teleports.
    if (flavour == BEAM_FIRE || flavour == BEAM_LAVA
        || flavour == BEAM_HELLFIRE || flavour == BEAM_FRAG
        || flavour == BEAM_TELEPORT || flavour == BEAM_NAPALM
        || flavour == BEAM_STEAM)
    {
        if (you.duration[DUR_CONDENSATION_SHIELD] > 0)
            remove_condensation_shield();

        if (you.mutation[MUT_ICEMAIL])
        {
            mpr("Your icy envelope dissipates!", MSGCH_DURATION);
            you.duration[DUR_ICEMAIL_DEPLETED] = ICEMAIL_TIME;
            you.redraw_armour_class = true;
        }
    }

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

    return (_expose_invent_to_element( flavour, strength ));
}

void lose_level()
{
    // Because you.experience is unsigned long, if it's going to be
    // negative, must die straightaway.
    if (you.experience_level == 1)
    {
        ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_DRAINING);
        // Return in case death was canceled via wizard mode
        return;
    }

    you.experience = exp_needed( you.experience_level + 1 ) - 1;
    you.experience_level--;

    mprf(MSGCH_WARN,
         "You are now level %d!", you.experience_level);

    // Constant value to avoid grape jelly trick... see level_change() for
    // where these HPs and MPs are given back.  -- bwr
    ouch(4, NON_MONSTER, KILLED_BY_DRAINING);
    dec_max_hp(4);

    dec_mp(1);
    dec_max_mp(1);

    calc_hp();
    calc_mp();

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

    redraw_skill(you.your_name, player_title());
    you.redraw_experience = true;

    xom_is_stimulated(255);
}

bool drain_exp(bool announce_full)
{
    const int protection = player_prot_life();

    if (protection == 3)
    {
        if (announce_full)
            canned_msg(MSG_YOU_RESIST);

        return (false);
    }

    if (you.experience == 0)
    {
        ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_DRAINING);

        // Return in case death was escaped via wizard mode.
        return (true);
    }

    if (you.experience_level == 1)
    {
        you.experience = 0;

        return (true);
    }

    unsigned long total_exp = exp_needed(you.experience_level + 2)
                                  - exp_needed(you.experience_level + 1);
    unsigned long exp_drained = (total_exp * (10 + random2(11))) / 100;
    unsigned long pool_drained = std::min(exp_drained,
                                     (unsigned long)you.exp_available);

    // TSO's protection.
    if (you.religion == GOD_SHINING_ONE && you.piety > protection * 50)
    {
        unsigned long undrained = std::min(exp_drained,
                                      (you.piety * exp_drained) / 150);
        unsigned long pool_undrained = std::min(pool_drained,
                                           (you.piety * pool_drained) / 150);

        if (undrained > 0 || pool_undrained > 0)
        {
            simple_god_message(" protects your life force!");
            if (undrained > 0)
                exp_drained -= undrained;
            if (pool_undrained > 0)
                pool_drained -= pool_undrained;
        }
    }
    else if (protection > 0)
    {
        canned_msg(MSG_YOU_PARTIALLY_RESIST);
        exp_drained -= (protection * exp_drained) / 3;
        pool_drained -= (protection * pool_drained) / 3;
    }

    if (exp_drained > 0)
    {
        mpr("You feel drained.");
        xom_is_stimulated(20);
        you.experience -= exp_drained;
        you.exp_available -= pool_drained;

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

        dprf("You lose %ld experience points, %ld from pool.",
             exp_drained, pool_drained);

        you.redraw_experience = true;

        if (you.experience < exp_needed(you.experience_level + 1))
            lose_level();

        return (true);
    }

    return (false);
}

static void _xom_checks_damage(kill_method_type death_type,
                               int dam, int death_source)
{
    if (you.religion == GOD_XOM)
    {
        if (death_type == KILLED_BY_TARGETTING
            || death_type == KILLED_BY_BOUNCE
            || death_type == KILLED_BY_REFLECTION
            || death_type == KILLED_BY_SELF_AIMED
               && player_in_a_dangerous_place())
        {
            // Xom thinks the player accidentally hurting him/herself is funny.
            // Deliberate damage is only amusing if it's dangerous.
            int amusement = 255 * dam / (dam + you.hp);
            if (death_type == KILLED_BY_SELF_AIMED)
                amusement /= 5;
            xom_is_stimulated(amusement);
            return;
        }
        else if (death_type == KILLED_BY_FALLING_DOWN_STAIRS
                 || death_type == KILLED_BY_FALLING_THROUGH_GATE)
        {
            // Xom thinks falling down the stairs is hilarious.
            xom_is_stimulated(255);
            return;
        }
        else if (death_type == KILLED_BY_DISINT)
        {
            // flying chunks...
            xom_is_stimulated(128);
            return;
        }
        else if (death_type != KILLED_BY_MONSTER
                    && death_type != KILLED_BY_BEAM
                    && death_type != KILLED_BY_DISINT
                 || invalid_monster_index(death_source))
        {
            return;
        }

        int amusementvalue = 1;
        const monsters *monster = &menv[death_source];

        if (!monster->alive())
            return;

        if (monster->wont_attack())
        {
            // Xom thinks collateral damage is funny.
            xom_is_stimulated(255 * dam / (dam + you.hp));
            return;
        }

        int leveldif = monster->hit_dice - you.experience_level;
        if (leveldif == 0)
            leveldif = 1;

        // Note that Xom is amused when you are significantly hurt by a
        // creature of higher level than yourself, as well as by a
        // creature of lower level than yourself.
        amusementvalue += leveldif * leveldif * dam;

        if (!monster->visible_to(&you))
            amusementvalue += 10;

        if (monster->speed < 100/player_movement_speed())
            amusementvalue += 8;

        if (death_type != KILLED_BY_BEAM
            && you.skills[SK_THROWING] <= (you.experience_level / 4))
        {
            amusementvalue += 2;
        }
        else if (you.skills[SK_FIGHTING] <= (you.experience_level / 4))
            amusementvalue += 2;

        if (player_in_a_dangerous_place())
            amusementvalue += 2;

        amusementvalue /= (you.hp > 0) ? you.hp : 1;

        xom_is_stimulated(amusementvalue);
    }
}

static void _yred_mirrors_injury(int dam, int death_source)
{
    if (yred_injury_mirror())
    {
        if (dam <= 0 || invalid_monster_index(death_source))
            return;

        monsters *mon = &menv[death_source];

        if (!mon->alive())
            return;

        simple_god_message(" mirrors your injury!");

#ifndef USE_TILE
        flash_monster_colour(mon, RED, 200);
#endif

        mon->hurt(&you, dam);

        if (mon->alive())
            print_wounds(mon);

        lose_piety(integer_sqrt(dam));
    }
}

static void _passive_freeze(kill_method_type death_type, const char *aux,
                            int death_source)
{
    const char *ptr = aux ? strstr(aux, "torment") : NULL;
    if (you.mutation[MUT_PASSIVE_FREEZE] && death_type == KILLED_BY_MONSTER
        && ptr == NULL)
    {
        if (invalid_monster_index(death_source))
            return;

        monsters *mon = &menv[death_source];

        if (!mon->alive())
            return;

        bolt beam;
        beam.flavour = BEAM_COLD;
        beam.thrower = KILL_YOU;

        const int orig_hurted = roll_dice(1, 11);
        int hurted = mons_adjust_flavoured(mon, beam, orig_hurted);

        if (!hurted)
            return;

        simple_monster_message(mon, " is very cold.");

#ifndef USE_TILE
        flash_monster_colour(mon, LIGHTBLUE, 200);
#endif

        mon->hurt(&you, hurted);

        if (mon->alive())
        {
            mon->expose_to_element(BEAM_COLD, orig_hurted);
            print_wounds(mon);

            const int cold_res = mon->res_cold();

            if (cold_res <= 0)
            {
                const int stun = (1 - cold_res) * random2(7);
                mon->speed_increment -= stun;
            }
        }
    }
}

static void _maybe_spawn_jellies(int dam, const char* aux,
                                  kill_method_type death_type, int death_source)
{
    // We need to exclude acid damage and similar things or this function
    // will crash later.
    if (death_source == NON_MONSTER)
        return;

    monster_type mon = royal_jelly_ejectable_monster();

    // Exclude torment damage.
    const char *ptr = strstr(aux, "torment");
    if (you.religion == GOD_JIYVA && you.piety > 160 && ptr == NULL)
    {
        int how_many = 0;
        if (dam >= you.hp_max * 3 / 4)
            how_many = random2(4) + 2;
        else if (dam >= you.hp_max / 2)
            how_many = random2(2) + 2;
        else if (dam >= you.hp_max / 4)
            how_many = 1;

        if (how_many > 0)
        {
            if (x_chance_in_y(how_many, 8)
                && !lose_stat(STAT_STRENGTH, 1, true, "spawning slimes"))
            {
                canned_msg(MSG_NOTHING_HAPPENS);
                return;
            }

            int count_created = 0;
            for (int i = 0; i < how_many; ++i)
            {
                mgen_data mg(mon, BEH_STRICT_NEUTRAL, &you, 0, 0, you.pos(),
                             MHITNOT, 0, GOD_JIYVA);

                if (create_monster(mg) != -1)
                    count_created++;
            }

            if (count_created > 0)
            {
                mprf("You shudder from the %s and a %s!",
                     death_type == KILLED_BY_MONSTER ? "blow" : "blast",
                     count_created > 1 ? "flood of jellies pours out from you"
                                       : "jelly pops out");
            }
        }
    }
}


#ifdef WIZARD
static void _wizard_restore_life()
{
    if (you.hp <= 0)
        set_hp(you.hp_max, false);
    if (you.strength <= 0)
    {
        you.strength        = you.max_strength;
        you.redraw_strength = true;
    }
    if (you.dex <= 0)
    {
        you.dex              = you.max_dex;
        you.redraw_dexterity = true;
        you.redraw_evasion   = true;
    }
    if (you.intel <= 0)
    {
        you.intel               = you.max_intel;
        you.redraw_intelligence = true;
    }
}
#endif

// death_source should be set to NON_MONSTER for non-monsters. {dlb}
void ouch(int dam, int death_source, kill_method_type death_type,
          const char *aux, bool see_source)
{
    ASSERT(!crawl_state.arena);
    if (you.duration[DUR_TIME_STEP])
        return;

    if (dam != INSTANT_DEATH && you.species == SP_DEEP_DWARF)
    {
        // Deep Dwarves get to shave _any_ hp loss.
        int shave = 1 + random2(2 + random2(1 + you.experience_level / 3));
        dprf("HP shaved: %d.", shave);
        dam -= shave;
        if (dam <= 0)
            return;
    }

    ait_hp_loss hpl(dam, death_type);
    interrupt_activity(AI_HP_LOSS, &hpl);

    if (dam > 0)
        you.check_awaken(500);

    if (you.duration[DUR_DEATHS_DOOR] && death_type != KILLED_BY_LAVA
        && death_type != KILLED_BY_WATER)
    {
        return;
    }

    if (dam != INSTANT_DEATH)
    {
        if (player_spirit_shield() && death_type != KILLED_BY_POISON)
        {
            if (dam <= you.magic_points)
            {
                dec_mp(dam);
                return;
            }
            dam -= you.magic_points;
            dec_mp(you.magic_points);
        }

        if (dam >= you.hp)
        {
            if (harm_protection_type hpt = god_protects_from_harm(you.religion))
            {
                simple_god_message(" protects you from harm!");

                if (you.duration[DUR_PRAYER]
                    && hpt == HPT_RELIABLE_PRAYING_PLUS_ANYTIME)
                {
                    lose_piety(21 + random2(20));
                }
                return;
            }
        }

        dec_hp(dam, true);

        // Even if we have low HP messages off, we'll still give a
        // big hit warning (in this case, a hit for half our HPs) -- bwr
        if (dam > 0 && you.hp_max <= dam * 2)
            mpr( "Ouch! That really hurt!", MSGCH_DANGER );

        if (you.hp > 0)
        {
            if (Options.hp_warning
                && you.hp <= (you.hp_max * Options.hp_warning) / 100)
            {
                mpr( "* * * LOW HITPOINT WARNING * * *", MSGCH_DANGER );
            }

            _xom_checks_damage(death_type, dam, death_source);

            // for note taking
            std::string damage_desc;
            if (!see_source)
            {
                damage_desc = make_stringf("something (%d)", dam);
            }
            else
            {
                damage_desc = scorefile_entry(dam, death_source,
                                              death_type, aux, true)
                    .death_description(scorefile_entry::DDV_TERSE);
            }

            take_note(
                      Note(NOTE_HP_CHANGE, you.hp, you.hp_max, damage_desc.c_str()) );

            _yred_mirrors_injury(dam, death_source);
            _passive_freeze(death_type, aux, death_source);
            _maybe_spawn_jellies(dam, aux, death_type, death_source);

            return;
        } // else hp <= 0
    }

    // Is the player being killed by a direct act of Xom?
    if (crawl_state.is_god_acting()
        && crawl_state.which_god_acting() == GOD_XOM
        && crawl_state.other_gods_acting().size() == 0)
    {
        you.escaped_death_cause = death_type;
        you.escaped_death_aux   = aux == NULL ? "" : aux;

        // Xom should only kill his worshippers if they're under penance
        // or Xom is bored.
        if (you.religion == GOD_XOM && !you.penance[GOD_XOM]
            && you.gift_timeout > 0)
        {
            return;
        }

        // Also don't kill wizards testing Xom acts.
        if ((crawl_state.repeat_cmd == CMD_WIZARD
                || crawl_state.prev_cmd == CMD_WIZARD)
            && you.religion != GOD_XOM)
        {
            return;
        }

        // Okay, you *didn't* escape death.
        you.reset_escaped_death();

        // Ensure some minimal information about Xom's involvement.
        if (aux == NULL || strlen(aux) == 0)
        {
            if (death_type != KILLED_BY_XOM)
                aux = "Xom";
        }
        else if (strstr(aux, "Xom") == NULL)
            death_type = KILLED_BY_XOM;
    }
    // Xom may still try to save your life.
    else if (xom_saves_your_life(dam, death_source, death_type, aux,
                                 see_source))
    {
        return;
    }

#if WIZARD || DEBUG
    if (you.never_die)
    {
        if (you.hp <= 0)
            you.hp = you.hp_max;
        if (you.strength <= 0)
            you.strength = you.max_strength;
        if (you.dex <= 0)
            you.dex = you.max_dex;
        if (you.intel <= 0)
            you.intel = you.max_intel;
        return;
    }
#endif

    // Construct scorefile entry.
    scorefile_entry se(dam, death_source, death_type, aux);

#ifdef WIZARD
    if (death_type != KILLED_BY_QUITTING
        && death_type != KILLED_BY_WINNING
        && death_type != KILLED_BY_LEAVING)
    {
        if (crawl_state.test || you.wizard)
        {
            const std::string death_desc
                = se.death_description(scorefile_entry::DDV_VERBOSE);
#ifdef USE_OPTIONAL_WIZARD_DEATH

            dprf("Damage: %d; Hit points: %d", dam, you.hp);

            if (crawl_state.test || !yesno("Die?", false, 'n'))
            {
                take_note(Note( NOTE_DEATH, you.hp, you.hp_max,
                                death_desc.c_str()), true);
                _wizard_restore_life();
                return;
            }
#else  // !def USE_OPTIONAL_WIZARD_DEATH
            mpr("Since you're a debugger, I'll let you live.");
            mpr("Be more careful next time, okay?");

            take_note(Note( NOTE_DEATH, you.hp, you.hp_max,
                            death_desc.c_str()), true);
            _wizard_restore_life();
            return;
#endif  // USE_OPTIONAL_WIZARD_DEATH
        }
    }
#endif  // WIZARD

    // Okay, so you're dead.
    crawl_state.need_save       = false;
    crawl_state.updating_scores = true;

    take_note(Note( NOTE_DEATH, you.hp, you.hp_max,
                    se.death_description(scorefile_entry::DDV_NORMAL).c_str()),
              true);

    // Prevent bogus notes.
    activate_notes(false);

#ifdef SCORE_WIZARD_CHARACTERS
    // Add this highscore to the score file.
    hiscores_new_entry(se);
    logfile_new_entry(se);
#else

    // Only add non-wizards to the score file.
    // Never generate bones files of wizard characters -- bwr
    if (!you.wizard)
    {
        hiscores_new_entry(se);
        logfile_new_entry(se);

        if (death_type != KILLED_BY_LEAVING
            && death_type != KILLED_BY_WINNING
            && death_type != KILLED_BY_QUITTING)
        {
            save_ghost();
        }
    }
#endif

    end_game(se);
}

static std::string morgue_name(time_t when_crawl_got_even)
{
#ifdef SHORT_FILE_NAMES
    return "morgue";
#else  // !SHORT_FILE_NAMES
    std::string name = "morgue-" + you.your_name;

    std::string time = make_file_time(when_crawl_got_even);
    if (!time.empty())
        name += "-" + time;

    return (name);
#endif // SHORT_FILE_NAMES
}

// Delete save files on game end.
static void delete_files()
{
    // clean all levels that we think we have ever visited
    for (level_id_set::const_iterator i = Generated_Levels.begin();
         i != Generated_Levels.end(); ++i)
    {
        const level_id &place(*i);
        unlink(
            make_filename(you.your_name,
                          place.absdepth(),
                          place.branch,
                          place.level_type,
                          false).c_str());
    }

    // temp levels, if any
    unlink( make_filename( you.your_name, 0, BRANCH_MAIN_DUNGEON,
                           LEVEL_ABYSS, false ).c_str() );
    unlink( make_filename( you.your_name, 0, BRANCH_MAIN_DUNGEON,
                           LEVEL_PANDEMONIUM, false ).c_str() );
    unlink( make_filename( you.your_name, 0, BRANCH_MAIN_DUNGEON,
                           LEVEL_LABYRINTH, false ).c_str() );
    unlink( make_filename( you.your_name, 0, BRANCH_MAIN_DUNGEON,
                           LEVEL_PORTAL_VAULT, false ).c_str() );

    // create base file name
    std::string basename = get_savedir_filename(you.your_name, "", "");

    const char* suffixes[] = {
#ifdef CLUA_BINDINGS
        ".lua",
#endif
#ifdef PACKAGE_SUFFIX
        PACKAGE_SUFFIX ,
#endif
        ".st", ".kil", ".tc", ".nts", ".tut", ".sav", ".msg"
    };

    const int num_suffixes = sizeof(suffixes) / sizeof(const char*);

    for (int i = 0; i < num_suffixes; ++i)
    {
        std::string tmpname = basename + suffixes[i];
        unlink( tmpname.c_str() );
    }
}

void end_game(scorefile_entry &se)
{
    bool dead = true;

    for (int i = 0; i < ENDOFPACK; i++)
        set_ident_flags( you.inv[i], ISFLAG_IDENT_MASK );

    for (int i = 0; i < ENDOFPACK; i++)
    {
        if (you.inv[i].base_type != 0)
            set_ident_type( you.inv[i], ID_KNOWN_TYPE );
    }

    delete_files();

    if (!dump_char( morgue_name(se.death_time), !dead, true, &se ))
    {
        mpr("Char dump unsuccessful! Sorry about that.");
        if (!crawl_state.seen_hups)
            more();
        clrscr();
    }

    if (se.death_type == KILLED_BY_LEAVING
        || se.death_type == KILLED_BY_QUITTING
        || se.death_type == KILLED_BY_WINNING)
    {
        dead = false;
    }

    // death message
    if (dead)
    {
        mpr("You die...");      // insert player name here? {dlb}
        xom_death_message((kill_method_type) se.death_type);
        flush_prev_message();
        viewwindow(false); // don't do for leaving/winning characters

        if (Tutorial.tutorial_left)
            tutorial_death_screen();
    }

#ifdef DGL_WHEREIS
    whereis_record( se.death_type == KILLED_BY_QUITTING? "quit" :
                    se.death_type == KILLED_BY_WINNING ? "won"  :
                    se.death_type == KILLED_BY_LEAVING ? "bailed out"
                                                       : "dead" );
#endif

    if (!crawl_state.seen_hups)
        more();

    browse_inventory(true);
    textcolor( LIGHTGREY );
    clrscr();

    clrscr();
    cprintf("Goodbye, %s.", you.your_name.c_str());
    cprintf( EOL EOL "    " ); // Space padding where # would go in list format

    std::string hiscore = hiscores_format_single_long( se, true );

    const int lines = count_occurrences(hiscore, EOL) + 1;

    cprintf( "%s", hiscore.c_str() );

    cprintf( EOL "Best Crawlers -" EOL );

    // "- 5" gives us an extra line in case the description wraps on a line.
    hiscores_print_list( get_number_of_lines() - lines - 5 );

    // just to pause, actual value returned does not matter {dlb}
    if (!crawl_state.seen_hups)
        get_ch();
    end(0);
}

int actor_to_death_source(const actor* agent)
{
    if (agent->atype() == ACT_PLAYER)
        return (NON_MONSTER);
    else if (agent->atype() == ACT_MONSTER)
        return (dynamic_cast<const monsters*>(agent)->mindex());
    else
        return (NON_MONSTER);
}