3 * Summary: Functions used when Bad Things happen to the player.
4 * Written by: Linley Henzell
22 #include <sys/types.h>
29 #ifdef TARGET_COMPILER_MINGW
56 #include "mgen_data.h"
59 #include "mon-place.h"
60 #include "mon-stuff.h"
64 #include "player-stats.h"
69 #include "spl-selfench.h"
70 #include "spl-other.h"
73 #include "transform.h"
80 static void _end_game(scorefile_entry
&se
);
81 static void _item_corrode(int slot
);
83 static void _maybe_melt_player_enchantments(beam_type flavour
)
85 if (flavour
== BEAM_FIRE
|| flavour
== BEAM_LAVA
86 || flavour
== BEAM_HELLFIRE
|| flavour
== BEAM_NAPALM
87 || flavour
== BEAM_STEAM
)
89 if (you
.duration
[DUR_CONDENSATION_SHIELD
] > 0)
90 remove_condensation_shield();
92 if (you
.mutation
[MUT_ICEMAIL
])
94 mpr("Your icy envelope dissipates!", MSGCH_DURATION
);
95 you
.duration
[DUR_ICEMAIL_DEPLETED
] = ICEMAIL_TIME
;
96 you
.redraw_armour_class
= true;
99 if (you
.duration
[DUR_ICY_ARMOUR
] > 0)
104 // NOTE: DOES NOT check for hellfire!!!
105 int check_your_resists(int hurted
, beam_type flavour
, std::string source
,
106 bolt
*beam
, bool doEffects
)
109 int original
= hurted
;
111 dprf("checking resistance: flavour=%d", flavour
);
113 std::string kaux
= "";
116 source
= beam
->get_source_name();
121 _maybe_melt_player_enchantments(flavour
);
126 hurted
= resist_adjust_damage(&you
, flavour
,
127 you
.res_water_drowning(), hurted
, true);
128 if (!hurted
&& doEffects
)
129 mpr("You shrug off the wave.");
133 hurted
= resist_adjust_damage(&you
, flavour
,
134 player_res_steam(), hurted
, true);
135 if (hurted
< original
&& doEffects
)
136 canned_msg(MSG_YOU_RESIST
);
137 else if (hurted
> original
&& doEffects
)
139 mpr("The steam scalds you terribly!");
140 xom_is_stimulated(200);
145 hurted
= resist_adjust_damage(&you
, flavour
,
146 player_res_fire(), hurted
, true);
147 if (hurted
< original
&& doEffects
)
148 canned_msg(MSG_YOU_RESIST
);
149 else if (hurted
> original
&& doEffects
)
151 mpr("The fire burns you terribly!");
152 xom_is_stimulated(200);
157 hurted
= resist_adjust_damage(&you
, flavour
,
158 player_res_cold(), hurted
, true);
159 if (hurted
< original
&& doEffects
)
160 canned_msg(MSG_YOU_RESIST
);
161 else if (hurted
> original
&& doEffects
)
163 mpr("You feel a terrible chill!");
164 xom_is_stimulated(200);
168 case BEAM_ELECTRICITY
:
169 hurted
= resist_adjust_damage(&you
, flavour
,
170 player_res_electricity(),
173 if (hurted
< original
&& doEffects
)
174 canned_msg(MSG_YOU_RESIST
);
178 resist
= player_res_poison();
180 if (resist
<= 0 && doEffects
)
181 poison_player(coinflip() ? 2 : 1, source
, kaux
);
183 hurted
= resist_adjust_damage(&you
, flavour
, resist
,
185 if (resist
> 0 && doEffects
)
186 canned_msg(MSG_YOU_RESIST
);
189 case BEAM_POISON_ARROW
:
190 // [dshaligram] NOT importing uber-poison arrow from 4.1. Giving no
191 // bonus to poison resistant players seems strange and unnecessarily
193 resist
= player_res_poison();
195 if (!resist
&& doEffects
)
196 poison_player(4 + random2(3), source
, kaux
, true);
197 else if (!you
.is_undead
&& doEffects
)
198 poison_player(2 + random2(3), source
, kaux
, true);
200 hurted
= resist_adjust_damage(&you
, flavour
, resist
, hurted
);
201 if (hurted
< original
&& doEffects
)
202 canned_msg(MSG_YOU_PARTIALLY_RESIST
);
206 resist
= player_prot_life();
209 if (you
.religion
== GOD_SHINING_ONE
&& you
.piety
> resist
* 50)
211 int unhurted
= std::min(hurted
, (you
.piety
* hurted
) / 150);
217 hurted
-= (resist
* hurted
) / 3;
224 hurted
= resist_adjust_damage(&you
, flavour
, player_res_cold(),
227 if (hurted
< original
&& doEffects
)
228 canned_msg(MSG_YOU_PARTIALLY_RESIST
);
229 else if (hurted
> original
&& doEffects
)
231 mpr("You feel a painful chill!");
232 xom_is_stimulated(200);
237 hurted
= resist_adjust_damage(&you
, flavour
, player_res_fire(),
240 if (hurted
< original
&& doEffects
)
241 canned_msg(MSG_YOU_PARTIALLY_RESIST
);
242 else if (hurted
> original
&& doEffects
)
244 mpr("The lava burns you terribly!");
245 xom_is_stimulated(200);
250 if (player_res_acid())
253 canned_msg(MSG_YOU_RESIST
);
254 hurted
= hurted
* player_acid_resist_factor() / 100;
259 if (you
.res_rotting())
262 canned_msg(MSG_YOU_RESIST
);
270 const int rhe
= you
.res_holy_energy(NULL
);
276 hurted
= (hurted
* 3) / 2;
278 if (hurted
== 0 && doEffects
)
279 canned_msg(MSG_YOU_RESIST
);
286 else if (you
.species
== SP_VAMPIRE
)
287 hurted
+= hurted
/ 2;
289 if (original
&& !hurted
&& doEffects
)
290 mpr("The beam of light passes harmlessly through you.");
291 else if (hurted
> original
&& doEffects
)
293 mpr("The light scorches you terribly!");
294 xom_is_stimulated(200);
301 if (you
.res_wind() > 0)
303 else if (you
.flight_mode())
304 hurted
+= hurted
/ 2;
314 void splash_with_acid(int acid_strength
, bool corrode_items
,
315 std::string hurt_message
)
318 const bool wearing_cloak
= player_wearing_slot(EQ_CLOAK
);
320 for (int slot
= EQ_MIN_ARMOUR
; slot
<= EQ_MAX_ARMOUR
; slot
++)
322 const bool cloak_protects
= wearing_cloak
&& coinflip()
323 && slot
!= EQ_SHIELD
&& slot
!= EQ_CLOAK
;
327 if (!player_wearing_slot(slot
) && slot
!= EQ_SHIELD
)
330 if (player_wearing_slot(slot
) && corrode_items
331 && x_chance_in_y(acid_strength
+ 1, 20))
333 _item_corrode(you
.equip
[slot
]);
338 // Without fur, clothed people have dam 0 (+2 later), Sp/Tr/Dr/Og ~1
339 // (randomized), Fe 5. Fur helps only against naked spots.
340 const int fur
= player_mutation_level(MUT_SHAGGY_FUR
);
341 dam
-= fur
* dam
/ 5;
343 // two extra virtual slots so players can't be immune
345 dam
= roll_dice(dam
, acid_strength
);
347 const int post_res_dam
= dam
* player_acid_resist_factor() / 100;
349 if (post_res_dam
> 0)
351 mpr(hurt_message
.empty() ? "The acid burns!" : hurt_message
);
353 if (post_res_dam
< dam
)
354 canned_msg(MSG_YOU_RESIST
);
356 ouch(post_res_dam
, NON_MONSTER
, KILLED_BY_ACID
);
360 void weapon_acid(int acid_strength
)
362 int hand_thing
= you
.equip
[EQ_WEAPON
];
364 if (hand_thing
== -1 && !you
.melded
[EQ_GLOVES
])
365 hand_thing
= you
.equip
[EQ_GLOVES
];
367 if (hand_thing
== -1)
369 msg::stream
<< "Your " << your_hand(true) << " burn!" << std::endl
;
370 ouch(roll_dice(1, acid_strength
), NON_MONSTER
, KILLED_BY_ACID
);
372 else if (x_chance_in_y(acid_strength
+ 1, 20))
373 _item_corrode(hand_thing
);
376 static void _item_corrode(int slot
)
378 bool it_resists
= false;
379 bool suppress_msg
= false;
380 item_def
& item
= you
.inv
[slot
];
382 // Artefacts don't corrode.
383 if (is_artefact(item
))
386 // Anti-corrosion items protect against 90% of corrosion.
387 if (wearing_amulet(AMU_RESIST_CORROSION
) && !one_chance_in(10))
389 dprf("Amulet protects.");
393 int how_rusty
= ((item
.base_type
== OBJ_WEAPONS
) ? item
.plus2
: item
.plus
);
394 // Already very rusty.
398 // determine possibility of resistance by object type {dlb}:
399 switch (item
.base_type
)
402 if ((item
.sub_type
== ARM_CRYSTAL_PLATE_MAIL
403 || get_equip_race(item
) == ISFLAG_DWARVEN
)
404 && !one_chance_in(5))
407 suppress_msg
= false;
413 if (get_equip_race(item
) == ISFLAG_DWARVEN
&& !one_chance_in(5))
416 suppress_msg
= false;
421 // Other items can't corrode.
425 // determine chance of corrosion {dlb}:
428 const int chance
= abs(how_rusty
);
430 // The embedded equation may look funny, but it actually works well
431 // to generate a pretty probability ramp {6%, 18%, 34%, 58%, 98%}
432 // for values [0,4] which closely matches the original, ugly switch.
434 if (chance
>= 0 && chance
<= 4)
435 it_resists
= x_chance_in_y(2 + (4 << chance
) + chance
* 8, 100);
439 // If the checks get this far, you should hear about it. {dlb}
440 suppress_msg
= false;
443 // handle message output and item damage {dlb}:
447 mprf("%s resists.", item
.name(DESC_CAP_YOUR
).c_str());
449 mprf("The acid corrodes %s!", item
.name(DESC_NOCAP_YOUR
).c_str());
455 xom_is_stimulated(64);
457 if (item
.base_type
== OBJ_WEAPONS
)
458 item
.plus2
= how_rusty
;
460 item
.plus
= how_rusty
;
462 if (item
.base_type
== OBJ_ARMOUR
)
463 you
.redraw_armour_class
= true;
465 if (you
.equip
[EQ_WEAPON
] == slot
)
466 you
.wield_change
= true;
470 // Helper function for the expose functions below.
471 // This currently works because elements only target a single type each.
472 static int _get_target_class(beam_type flavour
)
474 int target_class
= OBJ_UNASSIGNED
;
482 target_class
= OBJ_SCROLLS
;
487 target_class
= OBJ_POTIONS
;
491 case BEAM_DEVOUR_FOOD
:
492 target_class
= OBJ_FOOD
;
499 return (target_class
);
502 static std::string
_part_stack_string(const int num
, const int total
)
507 std::string ret
= uppercase_first(number_in_words(num
));
513 // XXX: These expose functions could use being reworked into a real system...
514 // the usage and implementation is currently very hacky.
515 // Handles the destruction of inventory items from the elements.
516 static bool _expose_invent_to_element(beam_type flavour
, int strength
)
522 const int target_class
= _get_target_class(flavour
);
523 if (target_class
== OBJ_UNASSIGNED
)
526 // Fedhas worshipers are exempt from the food destruction effect
528 if (flavour
== BEAM_SPORE
529 && you
.religion
== GOD_FEDHAS
)
531 simple_god_message(" protects your food from the spores.",
536 // Currently we test against each stack (and item in the stack)
537 // independently at strength%... perhaps we don't want that either
538 // because it makes the system very fair and removes the protection
539 // factor of junk (which might be more desirable for game play).
540 for (int i
= 0; i
< ENDOFPACK
; ++i
)
542 if (!you
.inv
[i
].defined())
545 if (you
.inv
[i
].base_type
== target_class
546 || target_class
== OBJ_FOOD
547 && you
.inv
[i
].base_type
== OBJ_CORPSES
)
549 // Conservation doesn't help against harpies' devouring food.
550 if (flavour
!= BEAM_DEVOUR_FOOD
551 && player_item_conserve() && !one_chance_in(10))
556 // These stack with conservation; they're supposed to be good.
557 if (target_class
== OBJ_SCROLLS
558 && you
.mutation
[MUT_CONSERVE_SCROLLS
]
559 && !one_chance_in(10))
564 if (target_class
== OBJ_POTIONS
565 && you
.mutation
[MUT_CONSERVE_POTIONS
]
566 && !one_chance_in(10))
571 if (you
.religion
== GOD_JIYVA
&& !player_under_penance()
572 && x_chance_in_y(you
.piety
, MAX_PIETY
))
578 // Get name and quantity before destruction.
579 const std::string item_name
= you
.inv
[i
].name(DESC_PLAIN
);
580 const int quantity
= you
.inv
[i
].quantity
;
583 // Loop through all items in the stack.
584 for (int j
= 0; j
< you
.inv
[i
].quantity
; ++j
)
586 if (x_chance_in_y(strength
, 100))
590 if (i
== you
.equip
[EQ_WEAPON
])
591 you
.wield_change
= true;
593 if (dec_inv_item_quantity(i
, 1))
595 else if (is_blood_potion(you
.inv
[i
]))
596 remove_oldest_blood_potion(you
.inv
[i
]);
600 // Name destroyed items.
601 // TODO: Combine messages using a vector.
604 switch (target_class
)
607 mprf("%s %s catch%s fire!",
608 _part_stack_string(num_dest
, quantity
).c_str(),
610 (num_dest
== 1) ? "es" : "");
614 mprf("%s %s freeze%s and shatter%s!",
615 _part_stack_string(num_dest
, quantity
).c_str(),
617 (num_dest
== 1) ? "s" : "",
618 (num_dest
== 1) ? "s" : "");
622 // Message handled elsewhere.
623 if (flavour
== BEAM_DEVOUR_FOOD
)
625 mprf("%s %s %s covered with spores!",
626 _part_stack_string(num_dest
, quantity
).c_str(),
628 (num_dest
== 1) ? "is" : "are");
632 mprf("%s %s %s destroyed!",
633 _part_stack_string(num_dest
, quantity
).c_str(),
635 (num_dest
== 1) ? "is" : "are");
639 total_dest
+= num_dest
;
646 mprf("%s shields %s delectables from destruction.",
647 god_name(GOD_JIYVA
).c_str(),
648 (total_dest
> 0) ? "some of your" : "your");
654 // Message handled elsewhere.
655 if (flavour
== BEAM_DEVOUR_FOOD
)
658 xom_is_stimulated((num_dest
> 1) ? 32 : 16);
663 bool expose_items_to_element(beam_type flavour
, const coord_def
& where
,
668 const int target_class
= _get_target_class(flavour
);
669 if (target_class
== OBJ_UNASSIGNED
)
672 // Beams fly *over* water and lava.
673 if (grd(where
) == DNGN_LAVA
|| grd(where
) == DNGN_DEEP_WATER
)
676 for (stack_iterator
si(where
); si
; ++si
)
681 if (si
->base_type
== target_class
682 || target_class
== OBJ_FOOD
&& si
->base_type
== OBJ_CORPSES
)
684 if (x_chance_in_y(strength
, 100))
687 if (!dec_mitm_item_quantity(si
->index(), 1)
688 && is_blood_potion(*si
))
690 remove_oldest_blood_potion(*si
);
699 if (flavour
== BEAM_DEVOUR_FOOD
)
702 if (you
.see_cell(where
))
704 switch (target_class
)
707 mprf("You see %s of smoke.",
708 (num_dest
> 1) ? "some puffs" : "a puff");
712 mprf("You see %s shatter.",
713 (num_dest
> 1) ? "some glass" : "glass");
717 mprf("You see %s of spores.",
718 (num_dest
> 1) ? "some clouds" : "a cloud");
722 mprf("%s on the floor %s destroyed!",
723 (num_dest
> 1) ? "Some items" : "An item",
724 (num_dest
> 1) ? "were" : "was");
729 xom_is_stimulated((num_dest
> 1) ? 32 : 16);
734 // Handle side-effects for exposure to element other than damage. This
735 // function exists because some code calculates its own damage instead
736 // of using check_your_resists() and we want to isolate all the special
737 // code they keep having to do... namely condensation shield checks,
738 // you really can't expect this function to even be called for much
741 // This function now calls _expose_invent_to_element() if strength > 0.
743 // XXX: This function is far from perfect and a work in progress.
744 bool expose_player_to_element(beam_type flavour
, int strength
)
746 _maybe_melt_player_enchantments(flavour
);
751 return (_expose_invent_to_element(flavour
, strength
));
756 // Because you.experience is unsigned long, if it's going to be
757 // negative, must die straightaway.
758 if (you
.experience_level
== 1)
760 ouch(INSTANT_DEATH
, NON_MONSTER
, KILLED_BY_DRAINING
);
761 // Return in case death was canceled via wizard mode
765 you
.experience
= exp_needed(you
.experience_level
+ 1) - 1;
766 you
.experience_level
--;
769 "You are now level %d!", you
.experience_level
);
771 // Constant value to avoid grape jelly trick... see level_change() for
772 // where these HPs and MPs are given back. -- bwr
773 ouch(4, NON_MONSTER
, KILLED_BY_DRAINING
);
783 sprintf(buf
, "HP: %d/%d MP: %d/%d",
784 you
.hp
, you
.hp_max
, you
.magic_points
, you
.max_magic_points
);
785 take_note(Note(NOTE_XP_LEVEL_CHANGE
, you
.experience_level
, 0, buf
));
787 redraw_skill(you
.your_name
, player_title());
788 you
.redraw_experience
= true;
790 xom_is_stimulated(255);
792 // Kill the player if maxhp <= 0. We can't just move the ouch() call past
793 // dec_max_hp() since it would decrease hp twice, so here's another one.
794 ouch(0, NON_MONSTER
, KILLED_BY_DRAINING
);
797 bool drain_exp(bool announce_full
)
799 const int protection
= player_prot_life();
804 canned_msg(MSG_YOU_RESIST
);
809 if (you
.experience
== 0)
811 ouch(INSTANT_DEATH
, NON_MONSTER
, KILLED_BY_DRAINING
);
813 // Return in case death was escaped via wizard mode.
817 if (you
.experience_level
== 1)
824 unsigned int total_exp
= exp_needed(you
.experience_level
+ 2)
825 - exp_needed(you
.experience_level
+ 1);
826 unsigned int exp_drained
= (total_exp
* (10 + random2(11))) / 100;
827 unsigned int pool_drained
= std::min(exp_drained
,
828 (unsigned int)you
.exp_available
);
831 if (you
.religion
== GOD_SHINING_ONE
&& you
.piety
> protection
* 50)
833 unsigned int undrained
= std::min(exp_drained
,
834 (you
.piety
* exp_drained
) / 150);
835 unsigned int pool_undrained
= std::min(pool_drained
,
836 (you
.piety
* pool_drained
) / 150);
838 if (undrained
> 0 || pool_undrained
> 0)
840 simple_god_message(" protects your life force!");
842 exp_drained
-= undrained
;
843 if (pool_undrained
> 0)
844 pool_drained
-= pool_undrained
;
847 else if (protection
> 0)
849 canned_msg(MSG_YOU_PARTIALLY_RESIST
);
850 exp_drained
-= (protection
* exp_drained
) / 3;
851 pool_drained
-= (protection
* pool_drained
) / 3;
856 mpr("You feel drained.");
857 xom_is_stimulated(20);
858 you
.experience
-= exp_drained
;
859 you
.exp_available
-= pool_drained
;
861 you
.exp_available
= std::max(0, you
.exp_available
);
863 dprf("You lose %d experience points, %d from pool.",
864 exp_drained
, pool_drained
);
866 you
.redraw_experience
= true;
868 if (you
.experience
< exp_needed(you
.experience_level
+ 1))
877 static void _xom_checks_damage(kill_method_type death_type
,
878 int dam
, int death_source
)
880 if (you
.religion
== GOD_XOM
)
882 if (death_type
== KILLED_BY_TARGETING
883 || death_type
== KILLED_BY_BOUNCE
884 || death_type
== KILLED_BY_REFLECTION
885 || death_type
== KILLED_BY_SELF_AIMED
886 && player_in_a_dangerous_place())
888 // Xom thinks the player accidentally hurting him/herself is funny.
889 // Deliberate damage is only amusing if it's dangerous.
890 int amusement
= 255 * dam
/ (dam
+ you
.hp
);
891 if (death_type
== KILLED_BY_SELF_AIMED
)
893 xom_is_stimulated(amusement
);
896 else if (death_type
== KILLED_BY_FALLING_DOWN_STAIRS
897 || death_type
== KILLED_BY_FALLING_THROUGH_GATE
)
899 // Xom thinks falling down the stairs is hilarious.
900 xom_is_stimulated(255);
903 else if (death_type
== KILLED_BY_DISINT
)
906 xom_is_stimulated(128);
909 else if (death_type
!= KILLED_BY_MONSTER
910 && death_type
!= KILLED_BY_BEAM
911 && death_type
!= KILLED_BY_DISINT
912 || invalid_monster_index(death_source
))
917 int amusementvalue
= 1;
918 const monster
* mons
= &menv
[death_source
];
923 if (mons
->wont_attack())
925 // Xom thinks collateral damage is funny.
926 xom_is_stimulated(255 * dam
/ (dam
+ you
.hp
));
930 int leveldif
= mons
->hit_dice
- you
.experience_level
;
934 // Note that Xom is amused when you are significantly hurt by a
935 // creature of higher level than yourself, as well as by a
936 // creature of lower level than yourself.
937 amusementvalue
+= leveldif
* leveldif
* dam
;
939 if (!mons
->visible_to(&you
))
940 amusementvalue
+= 10;
942 if (mons
->speed
< 100/player_movement_speed())
945 if (death_type
!= KILLED_BY_BEAM
946 && you
.skills
[SK_THROWING
] <= (you
.experience_level
/ 4))
950 else if (you
.skills
[SK_FIGHTING
] <= (you
.experience_level
/ 4))
953 if (player_in_a_dangerous_place())
956 amusementvalue
/= (you
.hp
> 0) ? you
.hp
: 1;
958 xom_is_stimulated(amusementvalue
);
962 static void _yred_mirrors_injury(int dam
, int death_source
)
964 if (yred_injury_mirror())
966 if (dam
<= 0 || invalid_monster_index(death_source
))
969 add_final_effect(FINEFF_MIRROR_DAMAGE
, &menv
[death_source
], &you
,
970 coord_def(0, 0), dam
);
974 static void _maybe_spawn_jellies(int dam
, const char* aux
,
975 kill_method_type death_type
, int death_source
)
977 // We need to exclude acid damage and similar things or this function
979 if (death_source
== NON_MONSTER
)
982 monster_type mon
= royal_jelly_ejectable_monster();
984 // Exclude torment damage.
985 const char *ptr
= strstr(aux
, "torment");
986 if (you
.religion
== GOD_JIYVA
&& you
.piety
> 160 && ptr
== NULL
)
989 if (dam
>= you
.hp_max
* 3 / 4)
990 how_many
= random2(4) + 2;
991 else if (dam
>= you
.hp_max
/ 2)
992 how_many
= random2(2) + 2;
993 else if (dam
>= you
.hp_max
/ 4)
998 if (x_chance_in_y(how_many
, 8)
999 && !lose_stat(STAT_STR
, 1, true, "spawning slimes"))
1001 canned_msg(MSG_NOTHING_HAPPENS
);
1005 int count_created
= 0;
1006 for (int i
= 0; i
< how_many
; ++i
)
1008 int foe
= death_source
;
1009 if (invalid_monster_index(foe
))
1011 mgen_data
mg(mon
, BEH_FRIENDLY
, &you
, 2, 0, you
.pos(),
1014 if (create_monster(mg
) != -1)
1018 if (count_created
> 0)
1020 mprf("You shudder from the %s and a %s!",
1021 death_type
== KILLED_BY_MONSTER
? "blow" : "blast",
1022 count_created
> 1 ? "flood of jellies pours out from you"
1023 : "jelly pops out");
1029 static void _place_player_corpse(bool explode
)
1031 if (!in_bounds(you
.pos()))
1035 if (fill_out_corpse(0, player_mons(), corpse
) == MONS_NO_MONSTER
)
1038 if (explode
&& explode_corpse(corpse
, you
.pos()))
1041 int o
= get_item_slot();
1044 item_was_destroyed(corpse
);
1048 corpse
.props
[MONSTER_HIT_DICE
].get_short() = you
.experience_level
;
1049 corpse
.props
[CORPSE_NAME_KEY
] = you
.your_name
;
1050 corpse
.props
[CORPSE_NAME_TYPE_KEY
].get_int() = 0;
1051 corpse
.props
["ev"].get_int() = player_evasion(static_cast<ev_ignore_type
>(
1052 EV_IGNORE_HELPLESS
| EV_IGNORE_PHASESHIFT
));
1053 // mostly mutations here. At least there's no need to handle armour.
1054 corpse
.props
["ac"].get_int() = you
.armour_class();
1057 move_item_to_grid(&o
, you
.pos(), !you
.in_water());
1061 #if defined(WIZARD) || defined(DEBUG)
1062 static void _wizard_restore_life()
1065 set_hp(you
.hp_max
, false);
1066 for (int i
= 0; i
< NUM_STATS
; ++i
)
1068 if (you
.stat(static_cast<stat_type
>(i
)) <= 0)
1070 you
.stat_loss
[i
] = 0;
1071 you
.redraw_stats
[i
] = true;
1077 void reset_damage_counters()
1079 you
.turn_damage
= 0;
1080 you
.damage_source
= NON_MONSTER
;
1081 you
.source_damage
= 0;
1084 // death_source should be set to NON_MONSTER for non-monsters. {dlb}
1085 void ouch(int dam
, int death_source
, kill_method_type death_type
,
1086 const char *aux
, bool see_source
, const char *death_source_name
)
1088 ASSERT(!crawl_state
.game_is_arena());
1089 if (you
.duration
[DUR_TIME_STEP
])
1092 if (you
.dead
) // ... but eligible for revival
1095 if (dam
!= INSTANT_DEATH
&& you
.species
== SP_DEEP_DWARF
)
1097 // Deep Dwarves get to shave any hp loss.
1098 int shave
= 1 + random2(2 + random2(1 + you
.experience_level
/ 3));
1099 dprf("HP shaved: %d.", shave
);
1103 // Rotting and costs may lower hp directly.
1110 ait_hp_loss
hpl(dam
, death_type
);
1111 interrupt_activity(AI_HP_LOSS
, &hpl
);
1114 you
.check_awaken(500);
1116 const bool non_death
= death_type
== KILLED_BY_QUITTING
1117 || death_type
== KILLED_BY_WINNING
1118 || death_type
== KILLED_BY_LEAVING
;
1120 if (you
.duration
[DUR_DEATHS_DOOR
] && death_type
!= KILLED_BY_LAVA
1121 && death_type
!= KILLED_BY_WATER
&& !non_death
&& you
.hp_max
> 0)
1126 if (dam
!= INSTANT_DEATH
)
1128 if (player_spirit_shield() && death_type
!= KILLED_BY_POISON
)
1130 if (dam
<= you
.magic_points
)
1135 dam
-= you
.magic_points
;
1136 dec_mp(you
.magic_points
);
1141 if (harm_protection_type hpt
= god_protects_from_harm(you
.religion
))
1143 simple_god_message(" protects you from harm!");
1145 if (you
.duration
[DUR_PRAYER
]
1146 && hpt
== HPT_RELIABLE_PRAYING_PLUS_ANYTIME
)
1148 lose_piety(21 + random2(20));
1154 you
.turn_damage
+= dam
;
1155 if (you
.damage_source
!= death_source
)
1157 you
.damage_source
= death_source
;
1158 you
.source_damage
= 0;
1160 you
.source_damage
+= dam
;
1164 // Even if we have low HP messages off, we'll still give a
1165 // big hit warning (in this case, a hit for half our HPs) -- bwr
1166 if (dam
> 0 && you
.hp_max
<= dam
* 2)
1167 mpr("Ouch! That really hurt!", MSGCH_DANGER
);
1171 if (Options
.hp_warning
1172 && you
.hp
<= (you
.hp_max
* Options
.hp_warning
) / 100)
1174 mpr("* * * LOW HITPOINT WARNING * * *", MSGCH_DANGER
);
1175 dungeon_events
.fire_event(DET_HP_WARNING
);
1178 hints_healing_check();
1180 _xom_checks_damage(death_type
, dam
, death_source
);
1183 std::string damage_desc
;
1186 damage_desc
= make_stringf("something (%d)", dam
);
1190 damage_desc
= scorefile_entry(dam
, death_source
,
1191 death_type
, aux
, true)
1192 .death_description(scorefile_entry::DDV_TERSE
);
1196 Note(NOTE_HP_CHANGE
, you
.hp
, you
.hp_max
, damage_desc
.c_str()));
1198 _yred_mirrors_injury(dam
, death_source
);
1199 _maybe_spawn_jellies(dam
, aux
, death_type
, death_source
);
1205 // Is the player being killed by a direct act of Xom?
1206 if (crawl_state
.is_god_acting()
1207 && crawl_state
.which_god_acting() == GOD_XOM
1208 && crawl_state
.other_gods_acting().size() == 0)
1210 you
.escaped_death_cause
= death_type
;
1211 you
.escaped_death_aux
= aux
== NULL
? "" : aux
;
1213 // Xom should only kill his worshippers if they're under penance
1215 if (you
.religion
== GOD_XOM
&& !you
.penance
[GOD_XOM
]
1216 && you
.gift_timeout
> 0)
1221 // Also don't kill wizards testing Xom acts.
1222 if ((crawl_state
.repeat_cmd
== CMD_WIZARD
1223 || crawl_state
.prev_cmd
== CMD_WIZARD
)
1224 && you
.religion
!= GOD_XOM
)
1229 // Okay, you *didn't* escape death.
1230 you
.reset_escaped_death();
1232 // Ensure some minimal information about Xom's involvement.
1233 if (aux
== NULL
|| strlen(aux
) == 0)
1235 if (death_type
!= KILLED_BY_XOM
)
1238 else if (strstr(aux
, "Xom") == NULL
)
1239 death_type
= KILLED_BY_XOM
;
1241 // Xom may still try to save your life.
1242 else if (xom_saves_your_life(dam
, death_source
, death_type
, aux
,
1248 #if defined(WIZARD) || defined(DEBUG)
1251 _wizard_restore_life();
1256 crawl_state
.cancel_cmd_all();
1258 // Construct scorefile entry.
1259 scorefile_entry
se(dam
, death_source
, death_type
, aux
, false,
1265 if (crawl_state
.test
|| you
.wizard
)
1267 const std::string death_desc
1268 = se
.death_description(scorefile_entry::DDV_VERBOSE
);
1270 dprf("Damage: %d; Hit points: %d", dam
, you
.hp
);
1272 if (crawl_state
.test
|| !yesno("Die?", false, 'n'))
1274 take_note(Note(NOTE_DEATH
, you
.hp
, you
.hp_max
,
1275 death_desc
.c_str()), true);
1276 _wizard_restore_life();
1283 if (crawl_state
.game_is_tutorial())
1286 tutorial_death_message();
1288 screen_end_game("");
1291 // Okay, so you're dead.
1292 take_note(Note(NOTE_DEATH
, you
.hp
, you
.hp_max
,
1293 se
.death_description(scorefile_entry::DDV_NORMAL
).c_str()),
1295 if (you
.lives
&& !non_death
)
1297 mark_milestone("death", lowercase_first(
1298 se
.death_description(scorefile_entry::DDV_NORMAL
)).c_str());
1306 mprnojoin("You die...");
1307 xom_death_message((kill_method_type
) se
.get_death_type());
1310 _place_player_corpse(death_type
== KILLED_BY_DISINT
);
1315 crawl_state
.need_save
= false;
1316 crawl_state
.updating_scores
= true;
1318 // Prevent bogus notes.
1319 activate_notes(false);
1321 #ifdef SCORE_WIZARD_CHARACTERS
1322 // Add this highscore to the score file.
1323 hiscores_new_entry(se
);
1324 logfile_new_entry(se
);
1327 // Only add non-wizards to the score file.
1328 // Never generate bones files of wizard or tutorial characters -- bwr
1331 hiscores_new_entry(se
);
1332 logfile_new_entry(se
);
1334 if (!non_death
&& !crawl_state
.game_is_tutorial())
1342 static std::string
_morgue_name(time_t when_crawl_got_even
)
1344 #ifdef SHORT_FILE_NAMES
1346 #else // !SHORT_FILE_NAMES
1347 std::string name
= "morgue-" + you
.your_name
;
1349 std::string time
= make_file_time(when_crawl_got_even
);
1354 #endif // SHORT_FILE_NAMES
1357 // Delete save files on game end.
1358 static void _delete_files()
1365 void screen_end_game(std::string text
)
1367 crawl_state
.cancel_cmd_all();
1373 linebreak_string2(text
, get_number_of_cols());
1374 display_tagged_block(text
);
1376 if (!crawl_state
.seen_hups
)
1383 void _end_game(scorefile_entry
&se
)
1385 for (int i
= 0; i
< ENDOFPACK
; i
++)
1386 if (item_type_unknown(you
.inv
[i
]))
1387 add_inscription(you
.inv
[i
], "unknown");
1389 for (int i
= 0; i
< ENDOFPACK
; i
++)
1391 if (!you
.inv
[i
].defined())
1393 set_ident_flags(you
.inv
[i
], ISFLAG_IDENT_MASK
);
1394 set_ident_type(you
.inv
[i
], ID_KNOWN_TYPE
);
1395 if (Options
.autoinscribe_artefacts
&& is_artefact(you
.inv
[i
]))
1397 std::string inscr
= artefact_auto_inscription(you
.inv
[i
]);
1399 add_autoinscription(you
.inv
[i
], inscr
);
1406 if (se
.get_death_type() != KILLED_BY_LEAVING
1407 && se
.get_death_type() != KILLED_BY_QUITTING
1408 && se
.get_death_type() != KILLED_BY_WINNING
)
1410 mprnojoin("You die..."); // insert player name here? {dlb}
1411 xom_death_message((kill_method_type
) se
.get_death_type());
1413 switch (you
.religion
)
1416 simple_god_message(" appreciates your contribution to the "
1420 case GOD_NEMELEX_XOBEH
:
1421 nemelex_death_message();
1424 case GOD_KIKUBAAQUDGHA
:
1426 && you
.form
!= TRAN_LICH
)
1428 simple_god_message(" rasps: \"You have failed me! "
1429 "Welcome... oblivion!\"");
1433 simple_god_message(" rasps: \"You have failed me! "
1434 "Welcome... death!\"");
1438 case GOD_YREDELEMNUL
:
1440 simple_god_message(" claims you as an undead slave.");
1441 else if (se
.get_death_type() != KILLED_BY_DISINT
1442 && se
.get_death_type() != KILLED_BY_LAVA
)
1444 mpr("Your body rises from the dead as a mindless zombie.",
1447 // No message if you're not undead and your corpse is lost.
1454 flush_prev_message();
1455 viewwindow(); // don't do for leaving/winning characters
1457 if (crawl_state
.game_is_hints())
1458 hints_death_screen();
1461 if (!dump_char(_morgue_name(se
.get_death_time()), false, true, &se
))
1463 mpr("Char dump unsuccessful! Sorry about that.");
1464 if (!crawl_state
.seen_hups
)
1470 whereis_record(se
.get_death_type() == KILLED_BY_QUITTING
? "quit" :
1471 se
.get_death_type() == KILLED_BY_WINNING
? "won" :
1472 se
.get_death_type() == KILLED_BY_LEAVING
? "bailed out"
1476 if (!crawl_state
.seen_hups
)
1479 browse_inventory(true);
1480 textcolor(LIGHTGREY
);
1482 // Prompt for saving macros.
1483 if (crawl_state
.unsaved_macros
&& yesno("Save macros?", true, 'n'))
1487 cprintf("Goodbye, %s.", you
.your_name
.c_str());
1488 cprintf("\n\n "); // Space padding where # would go in list format
1490 std::string hiscore
= hiscores_format_single_long(se
, true);
1492 const int lines
= count_occurrences(hiscore
, "\n") + 1;
1494 cprintf("%s", hiscore
.c_str());
1496 cprintf("\nBest Crawlers - %s\n",
1497 crawl_state
.game_type_name().c_str());
1499 // "- 5" gives us an extra line in case the description wraps on a line.
1500 hiscores_print_list(get_number_of_lines() - lines
- 5);
1502 // just to pause, actual value returned does not matter {dlb}
1503 if (!crawl_state
.seen_hups
)
1509 int actor_to_death_source(const actor
* agent
)
1511 if (agent
->atype() == ACT_PLAYER
)
1512 return (NON_MONSTER
);
1513 else if (agent
->atype() == ACT_MONSTER
)
1514 return (agent
->as_monster()->mindex());
1516 return (NON_MONSTER
);