Give survival hints in tutorial death message.
[crawl.git] / crawl-ref / source / ouch.cc
blob8cf02d83b7a20dd3eb2bc8a96c0f5dd6c66f1dbb
1 /*
2 * File: ouch.cc
3 * Summary: Functions used when Bad Things happen to the player.
4 * Written by: Linley Henzell
5 */
7 #include "AppHdr.h"
9 #include <cstring>
10 #include <string>
11 #include <iostream>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cctype>
15 #include <cmath>
17 #ifdef TARGET_OS_DOS
18 #include <file.h>
19 #endif
21 #ifdef UNIX
22 #include <sys/types.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #endif
27 #include "ouch.h"
29 #ifdef TARGET_COMPILER_MINGW
30 #include <io.h>
31 #endif
33 #include "externs.h"
34 #include "options.h"
36 #include "artefact.h"
37 #include "beam.h"
38 #include "chardump.h"
39 #include "coord.h"
40 #include "delay.h"
41 #include "dgnevent.h"
42 #include "effects.h"
43 #include "env.h"
44 #include "files.h"
45 #include "fight.h"
46 #include "fineff.h"
47 #include "godabil.h"
48 #include "hints.h"
49 #include "hiscores.h"
50 #include "invent.h"
51 #include "itemname.h"
52 #include "itemprop.h"
53 #include "items.h"
54 #include "macro.h"
55 #include "message.h"
56 #include "mgen_data.h"
57 #include "misc.h"
58 #include "mon-util.h"
59 #include "mon-place.h"
60 #include "mon-stuff.h"
61 #include "notes.h"
62 #include "output.h"
63 #include "player.h"
64 #include "player-stats.h"
65 #include "random.h"
66 #include "religion.h"
67 #include "shopping.h"
68 #include "skills2.h"
69 #include "spl-selfench.h"
70 #include "spl-other.h"
71 #include "state.h"
72 #include "stuff.h"
73 #include "transform.h"
74 #include "tutorial.h"
75 #include "view.h"
76 #include "shout.h"
77 #include "xom.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)
100 remove_ice_armour();
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)
108 int resist;
109 int original = hurted;
111 dprf("checking resistance: flavour=%d", flavour);
113 std::string kaux = "";
114 if (beam)
116 source = beam->get_source_name();
117 kaux = beam->name;
120 if (doEffects)
121 _maybe_melt_player_enchantments(flavour);
123 switch (flavour)
125 case BEAM_WATER:
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.");
130 break;
132 case BEAM_STEAM:
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);
142 break;
144 case BEAM_FIRE:
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);
154 break;
156 case BEAM_COLD:
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);
166 break;
168 case BEAM_ELECTRICITY:
169 hurted = resist_adjust_damage(&you, flavour,
170 player_res_electricity(),
171 hurted, true);
173 if (hurted < original && doEffects)
174 canned_msg(MSG_YOU_RESIST);
175 break;
177 case BEAM_POISON:
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,
184 hurted, true);
185 if (resist > 0 && doEffects)
186 canned_msg(MSG_YOU_RESIST);
187 break;
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
192 // arbitrary.
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);
203 break;
205 case BEAM_NEG:
206 resist = player_prot_life();
208 // TSO's protection.
209 if (you.religion == GOD_SHINING_ONE && you.piety > resist * 50)
211 int unhurted = std::min(hurted, (you.piety * hurted) / 150);
213 if (unhurted > 0)
214 hurted -= unhurted;
216 else if (resist > 0)
217 hurted -= (resist * hurted) / 3;
219 if (doEffects)
220 drain_exp();
221 break;
223 case BEAM_ICE:
224 hurted = resist_adjust_damage(&you, flavour, player_res_cold(),
225 hurted, true);
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);
234 break;
236 case BEAM_LAVA:
237 hurted = resist_adjust_damage(&you, flavour, player_res_fire(),
238 hurted, true);
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);
247 break;
249 case BEAM_ACID:
250 if (player_res_acid())
252 if (doEffects)
253 canned_msg(MSG_YOU_RESIST);
254 hurted = hurted * player_acid_resist_factor() / 100;
256 break;
258 case BEAM_MIASMA:
259 if (you.res_rotting())
261 if (doEffects)
262 canned_msg(MSG_YOU_RESIST);
263 hurted = 0;
265 break;
267 case BEAM_HOLY:
269 // Cleansing flame.
270 const int rhe = you.res_holy_energy(NULL);
271 if (rhe > 0)
272 hurted = 0;
273 else if (rhe == 0)
274 hurted /= 2;
275 else if (rhe < -1)
276 hurted = (hurted * 3) / 2;
278 if (hurted == 0 && doEffects)
279 canned_msg(MSG_YOU_RESIST);
280 break;
283 case BEAM_LIGHT:
284 if (you.invisible())
285 hurted = 0;
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);
296 break;
298 case BEAM_AIR:
300 // Airstrike.
301 if (you.res_wind() > 0)
302 hurted = 0;
303 else if (you.flight_mode())
304 hurted += hurted / 2;
305 break;
307 default:
308 break;
309 } // end switch
311 return (hurted);
314 void splash_with_acid(int acid_strength, bool corrode_items,
315 std::string hurt_message)
317 int dam = 0;
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;
325 if (!cloak_protects)
327 if (!player_wearing_slot(slot) && slot != EQ_SHIELD)
328 dam++;
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
344 dam += 2;
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))
384 return;
386 // Anti-corrosion items protect against 90% of corrosion.
387 if (wearing_amulet(AMU_RESIST_CORROSION) && !one_chance_in(10))
389 dprf("Amulet protects.");
390 return;
393 int how_rusty = ((item.base_type == OBJ_WEAPONS) ? item.plus2 : item.plus);
394 // Already very rusty.
395 if (how_rusty < -5)
396 return;
398 // determine possibility of resistance by object type {dlb}:
399 switch (item.base_type)
401 case OBJ_ARMOUR:
402 if ((item.sub_type == ARM_CRYSTAL_PLATE_MAIL
403 || get_equip_race(item) == ISFLAG_DWARVEN)
404 && !one_chance_in(5))
406 it_resists = true;
407 suppress_msg = false;
409 break;
411 case OBJ_WEAPONS:
412 case OBJ_MISSILES:
413 if (get_equip_race(item) == ISFLAG_DWARVEN && !one_chance_in(5))
415 it_resists = true;
416 suppress_msg = false;
418 break;
420 default:
421 // Other items can't corrode.
422 return;
425 // determine chance of corrosion {dlb}:
426 if (!it_resists)
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.
433 // {dlb}
434 if (chance >= 0 && chance <= 4)
435 it_resists = x_chance_in_y(2 + (4 << chance) + chance * 8, 100);
436 else
437 it_resists = true;
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}:
444 if (!suppress_msg)
446 if (it_resists)
447 mprf("%s resists.", item.name(DESC_CAP_YOUR).c_str());
448 else
449 mprf("The acid corrodes %s!", item.name(DESC_NOCAP_YOUR).c_str());
452 if (!it_resists)
454 how_rusty--;
455 xom_is_stimulated(64);
457 if (item.base_type == OBJ_WEAPONS)
458 item.plus2 = how_rusty;
459 else
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;
476 switch (flavour)
478 case BEAM_FIRE:
479 case BEAM_LAVA:
480 case BEAM_NAPALM:
481 case BEAM_HELLFIRE:
482 target_class = OBJ_SCROLLS;
483 break;
485 case BEAM_COLD:
486 case BEAM_FRAG:
487 target_class = OBJ_POTIONS;
488 break;
490 case BEAM_SPORE:
491 case BEAM_DEVOUR_FOOD:
492 target_class = OBJ_FOOD;
493 break;
495 default:
496 break;
499 return (target_class);
502 static std::string _part_stack_string(const int num, const int total)
504 if (num == total)
505 return "Your";
507 std::string ret = uppercase_first(number_in_words(num));
508 ret += " of your";
510 return ret;
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)
518 int num_dest = 0;
519 int total_dest = 0;
520 int jiyva_block = 0;
522 const int target_class = _get_target_class(flavour);
523 if (target_class == OBJ_UNASSIGNED)
524 return (false);
526 // Fedhas worshipers are exempt from the food destruction effect
527 // of spores.
528 if (flavour == BEAM_SPORE
529 && you.religion == GOD_FEDHAS)
531 simple_god_message(" protects your food from the spores.",
532 GOD_FEDHAS);
533 return (false);
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())
543 continue;
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))
553 continue;
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))
561 continue;
564 if (target_class == OBJ_POTIONS
565 && you.mutation[MUT_CONSERVE_POTIONS]
566 && !one_chance_in(10))
568 continue;
571 if (you.religion == GOD_JIYVA && !player_under_penance()
572 && x_chance_in_y(you.piety, MAX_PIETY))
574 ++jiyva_block;
575 continue;
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;
581 num_dest = 0;
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))
588 num_dest++;
590 if (i == you.equip[EQ_WEAPON])
591 you.wield_change = true;
593 if (dec_inv_item_quantity(i, 1))
594 break;
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.
602 if (num_dest > 0)
604 switch (target_class)
606 case OBJ_SCROLLS:
607 mprf("%s %s catch%s fire!",
608 _part_stack_string(num_dest, quantity).c_str(),
609 item_name.c_str(),
610 (num_dest == 1) ? "es" : "");
611 break;
613 case OBJ_POTIONS:
614 mprf("%s %s freeze%s and shatter%s!",
615 _part_stack_string(num_dest, quantity).c_str(),
616 item_name.c_str(),
617 (num_dest == 1) ? "s" : "",
618 (num_dest == 1) ? "s" : "");
619 break;
621 case OBJ_FOOD:
622 // Message handled elsewhere.
623 if (flavour == BEAM_DEVOUR_FOOD)
624 break;
625 mprf("%s %s %s covered with spores!",
626 _part_stack_string(num_dest, quantity).c_str(),
627 item_name.c_str(),
628 (num_dest == 1) ? "is" : "are");
629 break;
631 default:
632 mprf("%s %s %s destroyed!",
633 _part_stack_string(num_dest, quantity).c_str(),
634 item_name.c_str(),
635 (num_dest == 1) ? "is" : "are");
636 break;
639 total_dest += num_dest;
644 if (jiyva_block)
646 mprf("%s shields %s delectables from destruction.",
647 god_name(GOD_JIYVA).c_str(),
648 (total_dest > 0) ? "some of your" : "your");
651 if (!total_dest)
652 return (false);
654 // Message handled elsewhere.
655 if (flavour == BEAM_DEVOUR_FOOD)
656 return (true);
658 xom_is_stimulated((num_dest > 1) ? 32 : 16);
660 return (true);
663 bool expose_items_to_element(beam_type flavour, const coord_def& where,
664 int strength)
666 int num_dest = 0;
668 const int target_class = _get_target_class(flavour);
669 if (target_class == OBJ_UNASSIGNED)
670 return (false);
672 // Beams fly *over* water and lava.
673 if (grd(where) == DNGN_LAVA || grd(where) == DNGN_DEEP_WATER)
674 return (false);
676 for (stack_iterator si(where); si; ++si)
678 if (!si->defined())
679 continue;
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))
686 num_dest++;
687 if (!dec_mitm_item_quantity(si->index(), 1)
688 && is_blood_potion(*si))
690 remove_oldest_blood_potion(*si);
696 if (!num_dest)
697 return (false);
699 if (flavour == BEAM_DEVOUR_FOOD)
700 return (true);
702 if (you.see_cell(where))
704 switch (target_class)
706 case OBJ_SCROLLS:
707 mprf("You see %s of smoke.",
708 (num_dest > 1) ? "some puffs" : "a puff");
709 break;
711 case OBJ_POTIONS:
712 mprf("You see %s shatter.",
713 (num_dest > 1) ? "some glass" : "glass");
714 break;
716 case OBJ_FOOD:
717 mprf("You see %s of spores.",
718 (num_dest > 1) ? "some clouds" : "a cloud");
719 break;
721 default:
722 mprf("%s on the floor %s destroyed!",
723 (num_dest > 1) ? "Some items" : "An item",
724 (num_dest > 1) ? "were" : "was");
725 break;
729 xom_is_stimulated((num_dest > 1) ? 32 : 16);
731 return (true);
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
739 // else.
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);
748 if (strength <= 0)
749 return (false);
751 return (_expose_invent_to_element(flavour, strength));
754 void lose_level()
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
762 return;
765 you.experience = exp_needed(you.experience_level + 1) - 1;
766 you.experience_level--;
768 mprf(MSGCH_WARN,
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);
774 dec_max_hp(4);
776 dec_mp(1);
777 dec_max_mp(1);
779 calc_hp();
780 calc_mp();
782 char buf[200];
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();
801 if (protection == 3)
803 if (announce_full)
804 canned_msg(MSG_YOU_RESIST);
806 return (false);
809 if (you.experience == 0)
811 ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_DRAINING);
813 // Return in case death was escaped via wizard mode.
814 return (true);
817 if (you.experience_level == 1)
819 you.experience = 0;
821 return (true);
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);
830 // TSO's protection.
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!");
841 if (undrained > 0)
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;
854 if (exp_drained > 0)
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))
869 lose_level();
871 return (true);
874 return (false);
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)
892 amusement /= 5;
893 xom_is_stimulated(amusement);
894 return;
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);
901 return;
903 else if (death_type == KILLED_BY_DISINT)
905 // flying chunks...
906 xom_is_stimulated(128);
907 return;
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))
914 return;
917 int amusementvalue = 1;
918 const monster* mons = &menv[death_source];
920 if (!mons->alive())
921 return;
923 if (mons->wont_attack())
925 // Xom thinks collateral damage is funny.
926 xom_is_stimulated(255 * dam / (dam + you.hp));
927 return;
930 int leveldif = mons->hit_dice - you.experience_level;
931 if (leveldif == 0)
932 leveldif = 1;
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())
943 amusementvalue += 8;
945 if (death_type != KILLED_BY_BEAM
946 && you.skills[SK_THROWING] <= (you.experience_level / 4))
948 amusementvalue += 2;
950 else if (you.skills[SK_FIGHTING] <= (you.experience_level / 4))
951 amusementvalue += 2;
953 if (player_in_a_dangerous_place())
954 amusementvalue += 2;
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))
967 return;
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
978 // will crash later.
979 if (death_source == NON_MONSTER)
980 return;
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)
988 int how_many = 0;
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)
994 how_many = 1;
996 if (how_many > 0)
998 if (x_chance_in_y(how_many, 8)
999 && !lose_stat(STAT_STR, 1, true, "spawning slimes"))
1001 canned_msg(MSG_NOTHING_HAPPENS);
1002 return;
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))
1010 foe = MHITNOT;
1011 mgen_data mg(mon, BEH_FRIENDLY, &you, 2, 0, you.pos(),
1012 foe, 0, GOD_JIYVA);
1014 if (create_monster(mg) != -1)
1015 count_created++;
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()))
1032 return;
1034 item_def corpse;
1035 if (fill_out_corpse(0, player_mons(), corpse) == MONS_NO_MONSTER)
1036 return;
1038 if (explode && explode_corpse(corpse, you.pos()))
1039 return;
1041 int o = get_item_slot();
1042 if (o == NON_ITEM)
1044 item_was_destroyed(corpse);
1045 return;
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();
1055 mitm[o] = corpse;
1057 move_item_to_grid(&o, you.pos(), !you.in_water());
1061 #if defined(WIZARD) || defined(DEBUG)
1062 static void _wizard_restore_life()
1064 if (you.hp <= 0)
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;
1075 #endif
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])
1090 return;
1092 if (you.dead) // ... but eligible for revival
1093 return;
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);
1100 dam -= shave;
1101 if (dam <= 0)
1103 // Rotting and costs may lower hp directly.
1104 if (you.hp > 0)
1105 return;
1106 dam = 0;
1110 ait_hp_loss hpl(dam, death_type);
1111 interrupt_activity(AI_HP_LOSS, &hpl);
1113 if (dam > 0)
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)
1123 return;
1126 if (dam != INSTANT_DEATH)
1128 if (player_spirit_shield() && death_type != KILLED_BY_POISON)
1130 if (dam <= you.magic_points)
1132 dec_mp(dam);
1133 return;
1135 dam -= you.magic_points;
1136 dec_mp(you.magic_points);
1139 if (dam >= you.hp)
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));
1150 return;
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;
1162 dec_hp(dam, true);
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);
1169 if (you.hp > 0)
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);
1182 // for note taking
1183 std::string damage_desc;
1184 if (!see_source)
1186 damage_desc = make_stringf("something (%d)", dam);
1188 else
1190 damage_desc = scorefile_entry(dam, death_source,
1191 death_type, aux, true)
1192 .death_description(scorefile_entry::DDV_TERSE);
1195 take_note(
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);
1201 return;
1202 } // else hp <= 0
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
1214 // or Xom is bored.
1215 if (you.religion == GOD_XOM && !you.penance[GOD_XOM]
1216 && you.gift_timeout > 0)
1218 return;
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)
1226 return;
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)
1236 aux = "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,
1243 see_source))
1245 return;
1248 #if defined(WIZARD) || defined(DEBUG)
1249 if (you.never_die)
1251 _wizard_restore_life();
1252 return;
1254 #endif
1256 crawl_state.cancel_cmd_all();
1258 // Construct scorefile entry.
1259 scorefile_entry se(dam, death_source, death_type, aux, false,
1260 death_source_name);
1262 #ifdef WIZARD
1263 if (!non_death)
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();
1277 return;
1281 #endif // WIZARD
1283 if (crawl_state.game_is_tutorial())
1285 if (!non_death)
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()),
1294 true);
1295 if (you.lives && !non_death)
1297 mark_milestone("death", lowercase_first(
1298 se.death_description(scorefile_entry::DDV_NORMAL)).c_str());
1300 you.deaths++;
1301 you.lives--;
1302 you.dead = true;
1304 stop_delay(true);
1306 mprnojoin("You die...");
1307 xom_death_message((kill_method_type) se.get_death_type());
1308 more();
1310 _place_player_corpse(death_type == KILLED_BY_DISINT);
1311 return;
1314 // The game's over.
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);
1325 #else
1327 // Only add non-wizards to the score file.
1328 // Never generate bones files of wizard or tutorial characters -- bwr
1329 if (!you.wizard)
1331 hiscores_new_entry(se);
1332 logfile_new_entry(se);
1334 if (!non_death && !crawl_state.game_is_tutorial())
1335 save_ghost();
1337 #endif
1339 _end_game(se);
1342 static std::string _morgue_name(time_t when_crawl_got_even)
1344 #ifdef SHORT_FILE_NAMES
1345 return "morgue";
1346 #else // !SHORT_FILE_NAMES
1347 std::string name = "morgue-" + you.your_name;
1349 std::string time = make_file_time(when_crawl_got_even);
1350 if (!time.empty())
1351 name += "-" + time;
1353 return (name);
1354 #endif // SHORT_FILE_NAMES
1357 // Delete save files on game end.
1358 static void _delete_files()
1360 you.save->unlink();
1361 delete you.save;
1362 you.save = 0;
1365 void screen_end_game(std::string text)
1367 crawl_state.cancel_cmd_all();
1368 _delete_files();
1370 if (!text.empty())
1372 clrscr();
1373 linebreak_string2(text, get_number_of_cols());
1374 display_tagged_block(text);
1376 if (!crawl_state.seen_hups)
1377 get_ch();
1380 game_ended();
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())
1392 continue;
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]);
1398 if (inscr != "")
1399 add_autoinscription(you.inv[i], inscr);
1403 _delete_files();
1405 // death message
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)
1415 case GOD_FEDHAS:
1416 simple_god_message(" appreciates your contribution to the "
1417 "ecosystem.");
1418 break;
1420 case GOD_NEMELEX_XOBEH:
1421 nemelex_death_message();
1422 break;
1424 case GOD_KIKUBAAQUDGHA:
1425 if (you.is_undead
1426 && you.form != TRAN_LICH)
1428 simple_god_message(" rasps: \"You have failed me! "
1429 "Welcome... oblivion!\"");
1431 else
1433 simple_god_message(" rasps: \"You have failed me! "
1434 "Welcome... death!\"");
1436 break;
1438 case GOD_YREDELEMNUL:
1439 if (you.is_undead)
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.",
1445 MSGCH_GOD);
1447 // No message if you're not undead and your corpse is lost.
1448 break;
1450 default:
1451 break;
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)
1465 more();
1466 clrscr();
1469 #ifdef DGL_WHEREIS
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"
1473 : "dead");
1474 #endif
1476 if (!crawl_state.seen_hups)
1477 more();
1479 browse_inventory(true);
1480 textcolor(LIGHTGREY);
1482 // Prompt for saving macros.
1483 if (crawl_state.unsaved_macros && yesno("Save macros?", true, 'n'))
1484 macro_save();
1486 clrscr();
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)
1504 get_ch();
1506 game_ended();
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());
1515 else
1516 return (NON_MONSTER);