Merge branch 'skillmenu'
[crawl.git] / crawl-ref / source / player.cc
1 /**
2  * @file
3  * @brief Player related functions.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "player.h"
9
10 #include <algorithm>
11 #include <cctype>
12 #include <cmath>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <sstream>
17
18 #include "ability.h"
19 #include "act-iter.h"
20 #include "areas.h"
21 #include "art-enum.h"
22 #include "bloodspatter.h"
23 #include "branch.h"
24 #ifdef DGL_WHEREIS
25  #include "chardump.h"
26 #endif
27 #include "cloud.h"
28 #include "coordit.h"
29 #include "delay.h"
30 #include "dgn-overview.h"
31 #include "dgnevent.h"
32 #include "directn.h"
33 #include "english.h"
34 #include "env.h"
35 #include "errors.h"
36 #include "exercise.h"
37 #include "food.h"
38 #include "godabil.h"
39 #include "godconduct.h"
40 #include "godpassive.h"
41 #include "godwrath.h"
42 #include "hints.h"
43 #include "hiscores.h"
44 #include "invent.h"
45 #include "itemprop.h"
46 #include "item_use.h"
47 #include "kills.h"
48 #include "libutil.h"
49 #include "macro.h"
50 #include "melee_attack.h"
51 #include "message.h"
52 #include "misc.h"
53 #include "mon-place.h"
54 #include "mutation.h"
55 #include "notes.h"
56 #include "output.h"
57 #include "player-stats.h"
58 #include "potion.h"
59 #include "prompt.h"
60 #include "religion.h"
61 #include "shout.h"
62 #include "skills.h"
63 #include "spl-damage.h"
64 #include "spl-transloc.h"
65 #include "spl-util.h"
66 #include "sprint.h"
67 #include "stairs.h"
68 #include "stash.h"
69 #include "state.h"
70 #include "status.h"
71 #include "stepdown.h"
72 #include "stringutil.h"
73 #include "terrain.h"
74 #ifdef USE_TILE
75  #include "tiledef-feat.h"
76  #include "tileview.h"
77 #endif
78 #include "transform.h"
79 #include "traps.h"
80 #include "travel.h"
81 #include "view.h"
82 #include "xom.h"
83
84 #if TAG_MAJOR_VERSION == 34
85 const int DJ_MP_RATE = 2;
86 #endif
87
88 static int _bone_armour_bonus();
89
90 static void _moveto_maybe_repel_stairs()
91 {
92     const dungeon_feature_type new_grid = env.grid(you.pos());
93     const command_type stair_dir = feat_stair_direction(new_grid);
94
95     if (stair_dir == CMD_NO_CMD
96         || new_grid == DNGN_ENTER_SHOP
97         ||  !you.duration[DUR_REPEL_STAIRS_MOVE])
98     {
99         return;
100     }
101
102     int pct = you.duration[DUR_REPEL_STAIRS_CLIMB] ? 29 : 50;
103
104     // When the effect is still strong, the chance to actually catch
105     // a stair is smaller. (Assuming the duration starts out at 1000.)
106     const int dur = max(0, you.duration[DUR_REPEL_STAIRS_MOVE] - 700);
107     pct += dur/10;
108
109     if (x_chance_in_y(pct, 100))
110     {
111         const string stair_str = feature_description_at(you.pos(), false,
112                                                         DESC_THE, false);
113         const string prep = feat_preposition(new_grid, true, &you);
114
115         if (slide_feature_over(you.pos()))
116         {
117             mprf("%s slides away as you move %s it!", stair_str.c_str(),
118                  prep.c_str());
119
120             if (player_in_a_dangerous_place() && one_chance_in(5))
121                 xom_is_stimulated(25);
122         }
123     }
124 }
125
126 bool check_moveto_cloud(const coord_def& p, const string &move_verb,
127                         bool *prompted)
128 {
129     if (you.confused())
130         return true;
131     const cloud_type ctype = cloud_type_at(p);
132     // Don't prompt if already in a cloud of the same type.
133     if (is_damaging_cloud(ctype, true, cloud_is_yours_at(p))
134         && ctype != cloud_type_at(you.pos())
135         && !crawl_state.disables[DIS_CONFIRMATIONS])
136     {
137         // Don't prompt for steam unless we're at uncomfortably low hp.
138         if (ctype == CLOUD_STEAM)
139         {
140             int threshold = 20;
141             if (player_res_steam() < 0)
142                 threshold = threshold * 3 / 2;
143             threshold = threshold * you.time_taken / BASELINE_DELAY;
144             // Do prompt if we'd lose icemail, though.
145             if (you.hp > threshold && !you.mutation[MUT_ICEMAIL])
146                 return true;
147         }
148
149         if (prompted)
150             *prompted = true;
151         string prompt = make_stringf("Really %s into that cloud of %s?",
152                                      move_verb.c_str(),
153                                      cloud_type_name(ctype).c_str());
154         learned_something_new(HINT_CLOUD_WARNING);
155
156         if (!yesno(prompt.c_str(), false, 'n'))
157         {
158             canned_msg(MSG_OK);
159             return false;
160         }
161     }
162     return true;
163 }
164
165 bool check_moveto_trap(const coord_def& p, const string &move_verb,
166                        bool *prompted)
167 {
168     // If there's no trap, let's go.
169     trap_def* trap = find_trap(p);
170     if (!trap || env.grid(p) == DNGN_UNDISCOVERED_TRAP)
171         return true;
172
173     if (trap->type == TRAP_ZOT && !trap->is_safe() && !crawl_state.disables[DIS_CONFIRMATIONS])
174     {
175         string msg = "Do you really want to %s into the Zot trap?";
176         string prompt = make_stringf(msg.c_str(), move_verb.c_str());
177
178         if (prompted)
179             *prompted = true;
180         if (!yes_or_no("%s", prompt.c_str()))
181         {
182             canned_msg(MSG_OK);
183             return false;
184         }
185     }
186     else if (!trap->is_safe() && !crawl_state.disables[DIS_CONFIRMATIONS])
187     {
188         string prompt;
189
190         if (prompted)
191             *prompted = true;
192         prompt = make_stringf("Really %s %s that %s?", move_verb.c_str(),
193                               (trap->type == TRAP_ALARM
194                                || trap->type == TRAP_PLATE) ? "onto"
195                               : "into",
196                               feature_description_at(p, false, DESC_BASENAME,
197                                                      false).c_str());
198         if (!yesno(prompt.c_str(), true, 'n'))
199         {
200             canned_msg(MSG_OK);
201             return false;
202         }
203     }
204     return true;
205 }
206
207 static bool _check_moveto_dangerous(const coord_def& p, const string& msg)
208 {
209     if (you.can_swim() && feat_is_water(env.grid(p))
210         || you.airborne() || !is_feat_dangerous(env.grid(p)))
211     {
212         return true;
213     }
214
215     if (msg != "")
216         mpr(msg);
217     else if (species_likes_water(you.species) && feat_is_water(env.grid(p)))
218         mpr("You cannot enter water in your current form.");
219     else if (species_likes_lava(you.species) && feat_is_lava(env.grid(p)))
220         mpr("You cannot enter lava in your current form.");
221     else
222         canned_msg(MSG_UNTHINKING_ACT);
223     return false;
224 }
225
226 bool check_moveto_terrain(const coord_def& p, const string &move_verb,
227                           const string &msg, bool *prompted)
228 {
229     if (!_check_moveto_dangerous(p, msg))
230         return false
231 ;
232     if (!need_expiration_warning() && need_expiration_warning(p)
233         && !crawl_state.disables[DIS_CONFIRMATIONS])
234     {
235         string prompt;
236
237         if (prompted)
238             *prompted = true;
239
240         if (msg != "")
241             prompt = msg + " ";
242
243         prompt += "Are you sure you want to " + move_verb;
244
245         if (you.ground_level())
246             prompt += " into ";
247         else
248             prompt += " over ";
249
250         prompt += env.grid(p) == DNGN_DEEP_WATER ? "deep water" : "lava";
251
252         prompt += need_expiration_warning(DUR_FLIGHT, p)
253             ? " while you are losing your buoyancy?"
254             : " while your transformation is expiring?";
255
256         if (!yesno(prompt.c_str(), false, 'n'))
257         {
258             canned_msg(MSG_OK);
259             return false;
260         }
261     }
262     return true;
263 }
264
265 bool check_moveto_exclusion(const coord_def& p, const string &move_verb,
266                             bool *prompted)
267 {
268     string prompt;
269
270     if (is_excluded(p)
271         && !is_stair_exclusion(p)
272         && !is_excluded(you.pos())
273         && !crawl_state.disables[DIS_CONFIRMATIONS])
274     {
275         if (prompted)
276             *prompted = true;
277         prompt = make_stringf("Really %s into a travel-excluded area?",
278                               move_verb.c_str());
279
280         if (!yesno(prompt.c_str(), false, 'n'))
281         {
282             canned_msg(MSG_OK);
283             return false;
284         }
285     }
286     return true;
287 }
288
289 bool check_moveto(const coord_def& p, const string &move_verb, const string &msg)
290 {
291     return check_moveto_terrain(p, move_verb, msg)
292            && check_moveto_cloud(p, move_verb)
293            && check_moveto_trap(p, move_verb)
294            && check_moveto_exclusion(p, move_verb);
295 }
296
297 // Returns true if this is a valid swap for this monster. If true, then
298 // the valid location is set in loc. (Otherwise loc becomes garbage.)
299 bool swap_check(monster* mons, coord_def &loc, bool quiet)
300 {
301     loc = you.pos();
302
303     if (you.is_stationary())
304         return false;
305
306     // Don't move onto dangerous terrain.
307     if (is_feat_dangerous(grd(mons->pos())))
308     {
309         canned_msg(MSG_UNTHINKING_ACT);
310         return false;
311     }
312
313     if (mons->is_projectile())
314     {
315         if (!quiet)
316             mpr("It's unwise to walk into this.");
317         return false;
318     }
319
320     if (mons->caught())
321     {
322         if (!quiet)
323         {
324             simple_monster_message(mons,
325                 make_stringf(" is %s!", held_status(mons)).c_str());
326         }
327         return false;
328     }
329
330     if (mons->is_constricted())
331     {
332         if (!quiet)
333             simple_monster_message(mons, " is being constricted!");
334         return false;
335     }
336
337     // prompt when swapping into known zot traps
338     if (!quiet && find_trap(loc) && find_trap(loc)->type == TRAP_ZOT
339         && env.grid(loc) != DNGN_UNDISCOVERED_TRAP
340         && !yes_or_no("Do you really want to swap %s into the Zot trap?",
341                       mons->name(DESC_YOUR).c_str()))
342     {
343         return false;
344     }
345
346     // First try: move monster onto your position.
347     bool swap = !monster_at(loc) && monster_habitable_grid(mons, grd(loc));
348
349     if (monster_at(loc)
350         && monster_at(loc)->type == MONS_TOADSTOOL
351         && mons->type == MONS_WANDERING_MUSHROOM)
352     {
353         swap = monster_habitable_grid(mons, grd(loc));
354     }
355
356     // Choose an appropriate habitat square at random around the target.
357     if (!swap)
358     {
359         int num_found = 0;
360
361         for (adjacent_iterator ai(mons->pos()); ai; ++ai)
362             if (!monster_at(*ai) && monster_habitable_grid(mons, grd(*ai))
363                 && one_chance_in(++num_found))
364             {
365                 loc = *ai;
366             }
367
368         if (num_found)
369             swap = true;
370     }
371
372     if (!swap && !quiet)
373     {
374         // Might not be ideal, but it's better than insta-killing
375         // the monster... maybe try for a short blink instead? - bwr
376         simple_monster_message(mons, " cannot make way for you.");
377         // FIXME: AI_HIT_MONSTER isn't ideal.
378         interrupt_activity(AI_HIT_MONSTER, mons);
379     }
380
381     return swap;
382 }
383
384 static void _splash()
385 {
386     if (you.can_swim())
387         noisy(4, you.pos(), "Floosh!");
388     else if (!beogh_water_walk())
389         noisy(8, you.pos(), "Splash!");
390 }
391
392 void moveto_location_effects(dungeon_feature_type old_feat,
393                              bool stepped, const coord_def& old_pos)
394 {
395     const dungeon_feature_type new_grid = env.grid(you.pos());
396
397     // Terrain effects.
398     if (is_feat_dangerous(new_grid))
399         fall_into_a_pool(new_grid);
400
401     if (you.ground_level())
402     {
403         if (player_likes_lava(false))
404         {
405             if (feat_is_lava(new_grid) && !feat_is_lava(old_feat))
406             {
407                 if (!stepped)
408                     noisy(4, you.pos(), "Gloop!");
409
410                 mprf("You %s lava.",
411                      (stepped) ? "slowly immerse yourself in the" : "fall into the");
412
413                 // Extra time if you stepped in.
414                 if (stepped)
415                     you.time_taken *= 2;
416 #if TAG_MAJOR_VERSION == 34
417                 // This gets called here because otherwise you wouldn't heat
418                 // until your second turn in lava.
419                 if (temperature() < TEMP_FIRE)
420                     mpr("The lava instantly superheats you.");
421                 you.temperature = TEMP_MAX;
422 #endif
423             }
424
425             else if (!feat_is_lava(new_grid) && feat_is_lava(old_feat))
426             {
427                 mpr("You slowly pull yourself out of the lava.");
428                 you.time_taken *= 2;
429             }
430         }
431
432         if (you.species == SP_MERFOLK)
433         {
434             if (feat_is_water(new_grid) // We're entering water
435                 // We're not transformed, or with a form compatible with tail
436                 && (you.form == TRAN_NONE
437                     || you.form == TRAN_APPENDAGE
438                     || you.form == TRAN_BLADE_HANDS))
439             {
440                 merfolk_start_swimming(stepped);
441             }
442             else if (!feat_is_water(new_grid) && !is_feat_dangerous(new_grid))
443                 merfolk_stop_swimming();
444         }
445
446         if (feat_is_water(new_grid) && !stepped)
447             _splash();
448
449         if (feat_is_water(new_grid) && !you.can_swim() && !beogh_water_walk())
450         {
451             if (stepped)
452             {
453                 you.time_taken *= 13 + random2(8);
454                 you.time_taken /= 10;
455             }
456
457             if (!feat_is_water(old_feat))
458             {
459                 mprf("You %s the %s water.",
460                      stepped ? "enter" : "fall into",
461                      new_grid == DNGN_SHALLOW_WATER ? "shallow" : "deep");
462             }
463
464             if (new_grid == DNGN_DEEP_WATER && old_feat != DNGN_DEEP_WATER)
465                 mpr("You sink to the bottom.");
466
467             if (!feat_is_water(old_feat))
468             {
469                 mpr("Moving in this stuff is going to be slow.");
470                 if (you.invisible())
471                     mpr("...and don't expect to remain undetected.");
472             }
473         }
474     }
475
476     // Traps go off.
477     // (But not when losing flight - i.e., moving into the same tile)
478     trap_def* ptrap = find_trap(you.pos());
479     if (ptrap && old_pos != you.pos())
480         ptrap->trigger(you, !stepped); // blinking makes it hard to evade
481
482     if (stepped)
483         _moveto_maybe_repel_stairs();
484 }
485
486 // Use this function whenever the player enters (or lands and thus re-enters)
487 // a grid.
488 //
489 // stepped     - normal walking moves
490 void move_player_to_grid(const coord_def& p, bool stepped)
491 {
492     ASSERT(!crawl_state.game_is_arena());
493     ASSERT_IN_BOUNDS(p);
494
495     if (!stepped)
496         tornado_move(p);
497
498     // assuming that entering the same square means coming from above (flight)
499     const coord_def old_pos = you.pos();
500     const bool from_above = (old_pos == p);
501     const dungeon_feature_type old_grid =
502         (from_above) ? DNGN_FLOOR : grd(old_pos);
503
504     // Really must be clear.
505     ASSERT(you.can_pass_through_feat(grd(p)));
506
507     // Better not be an unsubmerged monster either.
508     ASSERT(!monster_at(p) || monster_at(p)->submerged()
509            || fedhas_passthrough(monster_at(p))
510            || mons_is_player_shadow(monster_at(p)));
511
512     // Move the player to new location.
513     you.moveto(p, true);
514     viewwindow();
515
516     moveto_location_effects(old_grid, stepped, old_pos);
517 }
518
519
520 /**
521  * Check if the given terrain feature is safe for the player to move into.
522  * (Or, at least, not instantly lethal.)
523  *
524  * @param grid          The type of terrain feature under consideration.
525  * @param permanently   Whether to disregard temporary effects (non-permanent
526  *                      flight, forms, etc)
527  * @param ignore_flight Whether to ignore all forms of flight (including
528  *                      permanent flight)
529  * @return              Whether the terrain is safe.
530  */
531 bool is_feat_dangerous(dungeon_feature_type grid, bool permanently,
532                        bool ignore_flight)
533 {
534     if (!ignore_flight
535         && (you.permanent_flight() || you.airborne() && !permanently))
536     {
537         return false;
538     }
539     else if (grid == DNGN_DEEP_WATER && !player_likes_water(permanently)
540              || grid == DNGN_LAVA && !player_likes_lava(permanently))
541     {
542         return true;
543     }
544     else
545         return false;
546 }
547
548 bool is_map_persistent()
549 {
550     return !testbits(env.level_flags, LFLAG_NO_MAP);
551 }
552
553 bool player_in_hell()
554 {
555     return is_hell_subbranch(you.where_are_you);
556 }
557
558 /**
559  * Is the player in the slightly-special version of the abyss that AKs start
560  * in?
561  */
562 bool player_in_starting_abyss()
563 {
564     return you.char_direction == GDT_GAME_START
565            && player_in_branch(BRANCH_ABYSS) && you.depth <= 1;
566 }
567
568 bool player_in_connected_branch()
569 {
570     return is_connected_branch(you.where_are_you);
571 }
572
573 bool player_likes_water(bool permanently)
574 {
575     return !permanently && beogh_water_walk()
576            || (species_likes_water(you.species) || !permanently)
577                && form_likes_water();
578 }
579
580 bool player_likes_lava(bool permanently)
581 {
582     return (species_likes_lava(you.species) || !permanently)
583            && form_likes_lava();
584 }
585
586 bool player_can_open_doors()
587 {
588     return you.form != TRAN_BAT;
589 }
590
591 // If transform is true, compare with current transformation instead
592 // of (or in addition to) underlying species.
593 // (See mon-data.h for species/genus use.)
594 bool is_player_same_genus(const monster_type mon)
595 {
596     // Genus would include necrophage and rotting hulk.
597     if (you.species == SP_GHOUL)
598         return mons_species(mon) == MONS_GHOUL;
599
600     // Note that these are currently considered to be the same genus:
601     // * humans, demigods, and demonspawn
602     // * ogres and two-headed ogres
603     // * trolls, iron trolls, and deep trolls
604     // * kobolds and big kobolds
605     // * dwarves and deep dwarves
606     // * all elf races
607     // * all orc races
608     return mons_genus(mon) == mons_genus(player_mons(false));
609 }
610
611 void update_player_symbol()
612 {
613     you.symbol = Options.show_player_species ? player_mons() : transform_mons();
614 }
615
616 monster_type player_mons(bool transform)
617 {
618     monster_type mons;
619
620     if (transform)
621     {
622         mons = transform_mons();
623         if (mons != MONS_PLAYER)
624             return mons;
625     }
626
627     mons = player_species_to_mons_species(you.species);
628
629     if (mons == MONS_ORC)
630     {
631         if (you_worship(GOD_BEOGH))
632         {
633             mons = (you.piety >= piety_breakpoint(4)) ? MONS_ORC_HIGH_PRIEST
634                                                       : MONS_ORC_PRIEST;
635         }
636     }
637     else if (mons == MONS_OGRE)
638     {
639         const skill_type sk = best_skill(SK_FIRST_SKILL, SK_LAST_SKILL);
640         if (sk >= SK_SPELLCASTING && sk < SK_INVOCATIONS)
641             mons = MONS_OGRE_MAGE;
642     }
643
644     return mons;
645 }
646
647 void update_vision_range()
648 {
649     you.normal_vision = LOS_RADIUS;
650     int nom   = 1;
651     int denom = 1;
652
653     // Nightstalker gives -1/-2/-3.
654     if (player_mutation_level(MUT_NIGHTSTALKER))
655     {
656         nom *= LOS_RADIUS - player_mutation_level(MUT_NIGHTSTALKER);
657         denom *= LOS_RADIUS;
658     }
659
660     // Lantern of shadows.
661     if (you.attribute[ATTR_SHADOWS])
662         nom *= 3, denom *= 4;
663
664     // the Darkness spell.
665     if (you.duration[DUR_DARKNESS])
666         nom *= 3, denom *= 4;
667
668     // robe of Night.
669     if (player_equip_unrand(UNRAND_NIGHT))
670         nom *= 3, denom *= 4;
671
672     you.current_vision = (you.normal_vision * nom + denom / 2) / denom;
673     ASSERT(you.current_vision > 0);
674     set_los_radius(you.current_vision);
675 }
676
677 /**
678  * Ignoring form & most equipment, but not the UNRAND_FINGER_AMULET, can the
679  * player use (usually wear) a given equipment slot?
680  *
681  * @param eq   The slot in question.
682  * @param temp Whether to consider forms.
683  * @return   MB_FALSE if the player can never use the slot;
684  *           MB_MAYBE if the player can only use some items for the slot;
685  *           MB_TRUE  if the player can use any (fsvo any) item for the slot.
686  */
687 maybe_bool you_can_wear(equipment_type eq, bool temp)
688 {
689     if (temp && !get_form()->slot_available(eq))
690         return MB_FALSE;
691
692     switch (eq)
693     {
694     case EQ_LEFT_RING:
695         if (player_mutation_level(MUT_MISSING_HAND))
696             return MB_FALSE;
697         // intentional fallthrough
698     case EQ_RIGHT_RING:
699         return you.species != SP_OCTOPODE ? MB_TRUE : MB_FALSE;
700
701     case EQ_RING_EIGHT:
702         if (player_mutation_level(MUT_MISSING_HAND))
703             return MB_FALSE;
704         // intentional fallthrough
705     case EQ_RING_ONE:
706     case EQ_RING_TWO:
707     case EQ_RING_THREE:
708     case EQ_RING_FOUR:
709     case EQ_RING_FIVE:
710     case EQ_RING_SIX:
711     case EQ_RING_SEVEN:
712         return you.species == SP_OCTOPODE ? MB_TRUE : MB_FALSE;
713
714     case EQ_WEAPON:
715     case EQ_STAFF:
716         return you.species == SP_FELID ? MB_FALSE :
717                you.body_size(PSIZE_TORSO, !temp) < SIZE_MEDIUM ? MB_MAYBE :
718                                          MB_TRUE;
719
720     // You can always wear at least one ring (forms were already handled).
721     case EQ_RINGS:
722     case EQ_ALL_ARMOUR:
723     case EQ_AMULET:
724         return MB_TRUE;
725
726     case EQ_RING_AMULET:
727         return player_equip_unrand(UNRAND_FINGER_AMULET) ? MB_TRUE : MB_FALSE;
728
729     default:
730         break;
731     }
732
733     item_def dummy, alternate;
734     dummy.base_type = alternate.base_type = OBJ_ARMOUR;
735     dummy.sub_type = alternate.sub_type = NUM_ARMOURS;
736     // Make sure can_wear_armour doesn't think it's Lear's.
737     dummy.special = alternate.special = 0;
738
739     switch (eq)
740     {
741     case EQ_CLOAK:
742         dummy.sub_type = ARM_CLOAK;
743         break;
744
745     case EQ_GLOVES:
746         dummy.sub_type = ARM_GLOVES;
747         break;
748
749     case EQ_BOOTS: // And bardings
750         dummy.sub_type = ARM_BOOTS;
751         if (you.species == SP_NAGA)
752             alternate.sub_type = ARM_NAGA_BARDING;
753         if (you.species == SP_CENTAUR)
754             alternate.sub_type = ARM_CENTAUR_BARDING;
755         break;
756
757     case EQ_BODY_ARMOUR:
758         // Assume that anything that can wear any armour at all can wear a robe
759         // and that anything that can wear CPA can wear all armour.
760         dummy.sub_type = ARM_CRYSTAL_PLATE_ARMOUR;
761         alternate.sub_type = ARM_ROBE;
762         break;
763
764     case EQ_SHIELD:
765         // No races right now that can wear ARM_LARGE_SHIELD but not ARM_SHIELD
766         dummy.sub_type = ARM_LARGE_SHIELD;
767         if (you.body_size(PSIZE_TORSO, !temp) < SIZE_MEDIUM)
768             alternate.sub_type = ARM_BUCKLER;
769         break;
770
771     case EQ_HELMET:
772         dummy.sub_type = ARM_HELMET;
773         alternate.sub_type = ARM_HAT;
774         break;
775
776     default:
777         die("unhandled equipment type %d", eq);
778         break;
779     }
780
781     ASSERT(dummy.sub_type != NUM_ARMOURS);
782
783     if (can_wear_armour(dummy, false, !temp))
784         return MB_TRUE;
785     else if (alternate.sub_type != NUM_ARMOURS
786              && can_wear_armour(alternate, false, !temp))
787     {
788         return MB_MAYBE;
789     }
790     else
791         return MB_FALSE;
792 }
793
794 bool player_has_feet(bool temp)
795 {
796     if (you.species == SP_NAGA
797         || you.species == SP_FELID
798         || you.species == SP_OCTOPODE
799 #if TAG_MAJOR_VERSION == 34
800         || you.species == SP_DJINNI
801 #endif
802         || you.fishtail && temp)
803     {
804         return false;
805     }
806
807     if (player_mutation_level(MUT_HOOVES, temp) == 3
808         || player_mutation_level(MUT_TALONS, temp) == 3)
809     {
810         return false;
811     }
812
813     return true;
814 }
815
816 // Returns false if the player is wielding a weapon inappropriate for Berserk.
817 bool berserk_check_wielded_weapon()
818 {
819     const item_def * const wpn = you.weapon();
820     bool penance = false;
821     if (wpn && wpn->defined() && (!is_melee_weapon(*wpn)
822                                    || needs_handle_warning(*wpn,
823                                                            OPER_ATTACK,
824                                                            penance)))
825     {
826         string prompt = "Do you really want to go berserk while wielding "
827                         + wpn->name(DESC_YOUR) + "?";
828         if (penance)
829             prompt += " This could place you under penance!";
830
831         if (!yesno(prompt.c_str(), true, 'n'))
832         {
833             canned_msg(MSG_OK);
834             return false;
835         }
836     }
837
838     return true;
839 }
840
841 // Looks in equipment "slot" to see if there is an equipped "sub_type".
842 // Returns number of matches (in the case of rings, both are checked)
843 int player::wearing(equipment_type slot, int sub_type, bool calc_unid) const
844 {
845     int ret = 0;
846
847     const item_def* item;
848
849     switch (slot)
850     {
851     case EQ_WEAPON:
852         // Hands can have more than just weapons.
853         if (weapon() && weapon()->is_type(OBJ_WEAPONS, sub_type))
854             ret++;
855         break;
856
857     case EQ_STAFF:
858         // Like above, but must be magical staff.
859         if (weapon()
860             && weapon()->is_type(OBJ_STAVES, sub_type)
861             && (calc_unid || item_type_known(*weapon())))
862         {
863             ret++;
864         }
865         break;
866
867     case EQ_RINGS:
868         for (int slots = EQ_LEFT_RING; slots < NUM_EQUIP; slots++)
869         {
870             if (slots == EQ_AMULET)
871                 continue;
872
873             if ((item = slot_item(static_cast<equipment_type>(slots)))
874                 && item->sub_type == sub_type
875                 && (calc_unid
876                     || item_type_known(*item)))
877             {
878                 ret++;
879             }
880         }
881         break;
882
883     case EQ_RINGS_PLUS:
884         for (int slots = EQ_LEFT_RING; slots < NUM_EQUIP; slots++)
885         {
886             if (slots == EQ_AMULET)
887                 continue;
888
889             if ((item = slot_item(static_cast<equipment_type>(slots)))
890                 && item->sub_type == sub_type
891                 && (calc_unid
892                     || item_type_known(*item)))
893             {
894                 ret += item->plus;
895             }
896         }
897         break;
898
899     case EQ_ALL_ARMOUR:
900         // Doesn't make much sense here... be specific. -- bwr
901         die("EQ_ALL_ARMOUR is not a proper slot");
902         break;
903
904     default:
905         if (! (slot > EQ_NONE && slot < NUM_EQUIP))
906             die("invalid slot");
907         if ((item = slot_item(slot))
908             && item->sub_type == sub_type
909             && (calc_unid || item_type_known(*item)))
910         {
911             ret++;
912         }
913         break;
914     }
915
916     return ret;
917 }
918
919 // Looks in equipment "slot" to see if equipped item has "special" ego-type
920 // Returns number of matches (jewellery returns zero -- no ego type).
921 // [ds] There's no equivalent of calc_unid or req_id because as of now, weapons
922 // and armour type-id on wield/wear.
923 int player::wearing_ego(equipment_type slot, int special, bool calc_unid) const
924 {
925     int ret = 0;
926
927     const item_def* item;
928     switch (slot)
929     {
930     case EQ_WEAPON:
931         // Hands can have more than just weapons.
932         if ((item = slot_item(EQ_WEAPON))
933             && item->base_type == OBJ_WEAPONS
934             && get_weapon_brand(*item) == special)
935         {
936             ret++;
937         }
938         break;
939
940     case EQ_LEFT_RING:
941     case EQ_RIGHT_RING:
942     case EQ_AMULET:
943     case EQ_STAFF:
944     case EQ_RINGS:
945     case EQ_RINGS_PLUS:
946         // no ego types for these slots
947         break;
948
949     case EQ_ALL_ARMOUR:
950         // Check all armour slots:
951         for (int i = EQ_MIN_ARMOUR; i <= EQ_MAX_ARMOUR; i++)
952         {
953             if ((item = slot_item(static_cast<equipment_type>(i)))
954                 && get_armour_ego_type(*item) == special
955                 && (calc_unid || item_type_known(*item)))
956             {
957                 ret++;
958             }
959         }
960         break;
961
962     default:
963         if (slot < EQ_MIN_ARMOUR || slot > EQ_MAX_ARMOUR)
964             die("invalid slot: %d", slot);
965         // Check a specific armour slot for an ego type:
966         if ((item = slot_item(static_cast<equipment_type>(slot)))
967             && get_armour_ego_type(*item) == special
968             && (calc_unid || item_type_known(*item)))
969         {
970             ret++;
971         }
972         break;
973     }
974
975     return ret;
976 }
977
978 // Returns true if the indicated unrandart is equipped
979 // [ds] There's no equivalent of calc_unid or req_id because as of now, weapons
980 // and armour type-id on wield/wear.
981 bool player_equip_unrand(int unrand_index)
982 {
983     const unrandart_entry* entry = get_unrand_entry(unrand_index);
984     equipment_type   slot  = get_item_slot(entry->base_type,
985                                            entry->sub_type);
986
987     item_def* item;
988
989     switch (slot)
990     {
991     case EQ_WEAPON:
992         // Hands can have more than just weapons.
993         if ((item = you.slot_item(slot))
994             && item->base_type == OBJ_WEAPONS
995             && is_unrandom_artefact(*item)
996             && item->special == unrand_index)
997         {
998             return true;
999         }
1000         break;
1001
1002     case EQ_RINGS:
1003         for (int slots = EQ_LEFT_RING; slots < NUM_EQUIP; ++slots)
1004         {
1005             if (slots == EQ_AMULET)
1006                 continue;
1007
1008             if ((item = you.slot_item(static_cast<equipment_type>(slots)))
1009                 && is_unrandom_artefact(*item)
1010                 && item->special == unrand_index)
1011             {
1012                 return true;
1013             }
1014         }
1015         break;
1016
1017     case EQ_NONE:
1018     case EQ_STAFF:
1019     case EQ_LEFT_RING:
1020     case EQ_RIGHT_RING:
1021     case EQ_RINGS_PLUS:
1022     case EQ_ALL_ARMOUR:
1023         // no unrandarts for these slots.
1024         break;
1025
1026     default:
1027         if (slot <= EQ_NONE || slot >= NUM_EQUIP)
1028             die("invalid slot: %d", slot);
1029         // Check a specific slot.
1030         if ((item = you.slot_item(slot))
1031             && is_unrandom_artefact(*item)
1032             && item->special == unrand_index)
1033         {
1034             return true;
1035         }
1036         break;
1037     }
1038
1039     return false;
1040 }
1041
1042 // Given an adjacent monster, returns true if the player can hit it (the
1043 // monster should not be submerged, or be submerged in shallow water if
1044 // the player has a polearm).
1045 bool player_can_hit_monster(const monster* mon)
1046 {
1047     if (!mon->submerged())
1048         return true;
1049
1050     if (grd(mon->pos()) != DNGN_SHALLOW_WATER)
1051         return false;
1052
1053     const item_def *weapon = you.weapon();
1054     return weapon && item_attack_skill(*weapon) == SK_POLEARMS;
1055 }
1056
1057 bool player_can_hear(const coord_def& p, int hear_distance)
1058 {
1059     return !silenced(p)
1060            && !silenced(you.pos())
1061            && you.pos().distance_from(p) <= hear_distance;
1062 }
1063
1064 int player_teleport(bool calc_unid)
1065 {
1066     ASSERT(!crawl_state.game_is_arena());
1067
1068     // Don't allow any form of teleportation in Sprint.
1069     if (crawl_state.game_is_sprint())
1070         return 0;
1071
1072     // Short-circuit rings of teleport to prevent spam.
1073     if (you.species == SP_FORMICID)
1074         return 0;
1075
1076     int tp = 0;
1077
1078     // rings (keep in sync with _equip_jewellery_effect)
1079     tp += 8 * you.wearing(EQ_RINGS, RING_TELEPORTATION, calc_unid);
1080
1081     // artefacts
1082     tp += 8 * you.scan_artefacts(ARTP_CAUSE_TELEPORTATION, calc_unid);
1083
1084     // mutations
1085     tp += player_mutation_level(MUT_TELEPORT) * 4;
1086
1087     return tp;
1088 }
1089
1090 // Computes bonuses to regeneration from most sources. Does not handle
1091 // slow healing, vampireness, or Trog's Hand.
1092 static int _player_bonus_regen()
1093 {
1094     int rr = 0;
1095
1096     // Trog's Hand is handled separately so that it will bypass slow healing,
1097     // and it overrides the spell.
1098     if (you.duration[DUR_REGENERATION]
1099         && !you.duration[DUR_TROGS_HAND])
1100     {
1101         rr += 100;
1102     }
1103
1104     // Jewellery.
1105     rr += REGEN_PIP * you.wearing(EQ_AMULET, AMU_REGENERATION);
1106
1107     // Artefacts
1108     rr += REGEN_PIP * you.scan_artefacts(ARTP_REGENERATION);
1109
1110     // Troll leather
1111     if (you.wearing(EQ_BODY_ARMOUR, ARM_TROLL_LEATHER_ARMOUR)
1112         || you.wearing(EQ_BODY_ARMOUR, ARM_TROLL_HIDE))
1113     {
1114         rr += REGEN_PIP;
1115     }
1116
1117     // Fast heal mutation.
1118     rr += player_mutation_level(MUT_REGENERATION) * 20;
1119
1120     // Powered By Death mutation, boosts regen by 10 per corpse in
1121     // a mutation_level * 3 (3/6/9) radius, to a maximum of 7
1122     // corpses. If and only if the duration of the effect is
1123     // still active.
1124     if (you.duration[DUR_POWERED_BY_DEATH])
1125         rr += handle_pbd_corpses() * 100;
1126
1127     return rr;
1128 }
1129
1130 // Slow healing mutation: slows or stops regeneration when monsters are
1131 // visible at level 1 or 2 respectively, stops regeneration at level 3.
1132 static int _slow_heal_rate()
1133 {
1134     if (player_mutation_level(MUT_SLOW_HEALING) == 3)
1135         return 0;
1136
1137     for (monster_near_iterator mi(you.pos(), LOS_NO_TRANS); mi; ++mi)
1138     {
1139         if (!mons_is_firewood(*mi)
1140             && !mi->wont_attack()
1141             && !mi->neutral())
1142         {
1143             return 2 - player_mutation_level(MUT_SLOW_HEALING);
1144         }
1145     }
1146     return 2;
1147 }
1148
1149 int player_regen()
1150 {
1151     int rr = you.hp_max / 3;
1152
1153     if (rr > 20)
1154         rr = 20 + ((rr - 20) / 2);
1155
1156     // Add in miscellaneous bonuses
1157     rr += _player_bonus_regen();
1158
1159     // Before applying other effects, make sure that there's something
1160     // to heal.
1161     rr = max(1, rr);
1162
1163     // Healing depending on satiation.
1164     // The better-fed you are, the faster you heal.
1165     if (you.species == SP_VAMPIRE)
1166     {
1167         if (you.hunger_state == HS_STARVING)
1168             rr = 0;   // No regeneration for starving vampires.
1169         else if (you.hunger_state == HS_ENGORGED)
1170             rr += 20; // More bonus regeneration for engorged vampires.
1171         else if (you.hunger_state < HS_SATIATED)
1172             rr /= 2;  // Halved regeneration for hungry vampires.
1173         else if (you.hunger_state >= HS_FULL)
1174             rr += 10; // Bonus regeneration for full vampires.
1175     }
1176 #if TAG_MAJOR_VERSION == 34
1177
1178     // Compared to other races, a starting djinni would have regen of 4 (hp)
1179     // plus 17 (mp). So let's compensate them early; they can stand getting
1180     // shafted on the total regen rates later on.
1181     if (you.species == SP_DJINNI)
1182         if (you.hp_max < 100)
1183             rr += (100 - you.hp_max) / 6;
1184 #endif
1185
1186     // Slow heal mutation.
1187     if (player_mutation_level(MUT_SLOW_HEALING) > 0)
1188     {
1189         rr *= _slow_heal_rate();
1190         rr /= 2;
1191     }
1192     if (you.stat_zero[STAT_STR])
1193         rr /= 4;
1194
1195     if (you.disease)
1196         rr = 0;
1197
1198     // Trog's Hand. This circumvents the slow healing effect.
1199     if (you.duration[DUR_TROGS_HAND])
1200         rr += 100;
1201
1202     return rr;
1203 }
1204
1205 int player_hunger_rate(bool temp)
1206 {
1207     int hunger = 3;
1208
1209     if (temp && you.form == TRAN_BAT)
1210         return 1;
1211
1212     if (you.species == SP_TROLL)
1213         hunger += 3;            // in addition to the +3 for fast metabolism
1214
1215     if (temp
1216         && (you.duration[DUR_REGENERATION]
1217             || you.duration[DUR_TROGS_HAND])
1218         && you.hp < you.hp_max)
1219     {
1220         hunger += 4;
1221     }
1222
1223     if (temp)
1224     {
1225         if (you.duration[DUR_INVIS])
1226             hunger += 5;
1227
1228         // Berserk has its own food penalty - excluding berserk haste.
1229         // Doubling the hunger cost for haste so that the per turn hunger
1230         // is consistent now that a hasted turn causes 50% the normal hunger
1231         // -cao
1232         if (you.duration[DUR_HASTE])
1233             hunger += haste_mul(5);
1234     }
1235
1236     if (you.species == SP_VAMPIRE)
1237     {
1238         switch (you.hunger_state)
1239         {
1240         case HS_STARVING:
1241         case HS_NEAR_STARVING:
1242             hunger -= 3;
1243             break;
1244         case HS_VERY_HUNGRY:
1245             hunger -= 2;
1246             break;
1247         case HS_HUNGRY:
1248             hunger--;
1249             break;
1250         case HS_SATIATED:
1251             break;
1252         case HS_FULL:
1253             hunger++;
1254             break;
1255         case HS_VERY_FULL:
1256             hunger += 2;
1257             break;
1258         case HS_ENGORGED:
1259             hunger += 3;
1260         }
1261     }
1262     else
1263     {
1264         hunger += player_mutation_level(MUT_FAST_METABOLISM)
1265                 - player_mutation_level(MUT_SLOW_METABOLISM);
1266     }
1267
1268     if (you.hp < you.hp_max
1269         && player_mutation_level(MUT_SLOW_HEALING) < 3)
1270     {
1271         // jewellery
1272         hunger += 3 * you.wearing(EQ_AMULET, AMU_REGENERATION);
1273
1274         // troll leather
1275         if (you.wearing(EQ_BODY_ARMOUR, ARM_TROLL_LEATHER_ARMOUR)
1276             || you.wearing(EQ_BODY_ARMOUR, ARM_TROLL_HIDE))
1277         {
1278             hunger += coinflip() ? 2 : 1;
1279         }
1280     }
1281
1282     // If Cheibriados has slowed your life processes, you will hunger less.
1283     if (you_worship(GOD_CHEIBRIADOS) && you.piety >= piety_breakpoint(0))
1284         hunger = hunger * 3 / 4;
1285
1286     if (hunger < 1)
1287         hunger = 1;
1288
1289     return hunger;
1290 }
1291
1292 int player_spell_levels()
1293 {
1294     int sl = you.experience_level - 1 + you.skill(SK_SPELLCASTING, 2, true);
1295
1296     bool fireball = false;
1297     bool delayed_fireball = false;
1298
1299     if (sl > 99)
1300         sl = 99;
1301
1302     for (const spell_type spell : you.spells)
1303     {
1304         if (spell == SPELL_FIREBALL)
1305             fireball = true;
1306         else if (spell == SPELL_DELAYED_FIREBALL)
1307             delayed_fireball = true;
1308
1309         if (spell != SPELL_NO_SPELL)
1310             sl -= spell_difficulty(spell);
1311     }
1312
1313     // Fireball is free for characters with delayed fireball
1314     if (fireball && delayed_fireball)
1315         sl += spell_difficulty(SPELL_FIREBALL);
1316
1317     // Note: This can happen because of level drain. Maybe we should
1318     // force random spells out when that happens. -- bwr
1319     if (sl < 0)
1320         sl = 0;
1321
1322     return sl;
1323 }
1324
1325 int player_likes_chunks(bool permanently)
1326 {
1327     return you.gourmand(true, !permanently)
1328            ? 3 : player_mutation_level(MUT_CARNIVOROUS);
1329 }
1330
1331 // If temp is set to false, temporary sources or resistance won't be counted.
1332 int player_res_fire(bool calc_unid, bool temp, bool items)
1333 {
1334 #if TAG_MAJOR_VERSION == 34
1335     if (you.species == SP_DJINNI)
1336         return 4; // full immunity
1337
1338 #endif
1339     int rf = 0;
1340
1341     if (items)
1342     {
1343         // rings of fire resistance/fire
1344         rf += you.wearing(EQ_RINGS, RING_PROTECTION_FROM_FIRE, calc_unid);
1345         rf += you.wearing(EQ_RINGS, RING_FIRE, calc_unid);
1346
1347         // rings of ice
1348         rf -= you.wearing(EQ_RINGS, RING_ICE, calc_unid);
1349
1350         // Staves
1351         rf += you.wearing(EQ_STAFF, STAFF_FIRE, calc_unid);
1352
1353         // body armour:
1354         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1355         if (body_armour)
1356             rf += armour_type_prop(body_armour->sub_type, ARMF_RES_FIRE);
1357
1358         // ego armours
1359         rf += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_FIRE_RESISTANCE);
1360         rf += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_RESISTANCE);
1361
1362         // randart weapons:
1363         rf += you.scan_artefacts(ARTP_FIRE, calc_unid);
1364
1365         // dragonskin cloak: 0.5 to draconic resistances
1366         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN)
1367             && coinflip())
1368         {
1369             rf++;
1370         }
1371     }
1372
1373     // species:
1374     if (you.species == SP_MUMMY)
1375         rf--;
1376
1377 #if TAG_MAJOR_VERSION == 34
1378     if (you.species == SP_LAVA_ORC)
1379     {
1380         if (temperature_effect(LORC_FIRE_RES_I))
1381             rf++;
1382         if (temperature_effect(LORC_FIRE_RES_II))
1383             rf++;
1384         if (temperature_effect(LORC_FIRE_RES_III))
1385             rf++;
1386     }
1387 #endif
1388
1389     // mutations:
1390     rf += player_mutation_level(MUT_HEAT_RESISTANCE, temp);
1391     rf -= player_mutation_level(MUT_HEAT_VULNERABILITY, temp);
1392     rf += player_mutation_level(MUT_MOLTEN_SCALES, temp) == 3 ? 1 : 0;
1393
1394     // spells:
1395     if (temp)
1396     {
1397         if (you.duration[DUR_RESISTANCE])
1398             rf++;
1399
1400         if (you.duration[DUR_FIRE_SHIELD])
1401             rf += 2;
1402
1403         if (you.duration[DUR_QAZLAL_FIRE_RES])
1404             rf++;
1405
1406         rf += get_form()->res_fire();
1407     }
1408
1409     if (rf > 3)
1410         rf = 3;
1411     if (temp && you.duration[DUR_FIRE_VULN])
1412         rf--;
1413     if (rf < -3)
1414         rf = -3;
1415
1416     return rf;
1417 }
1418
1419 int player_res_steam(bool calc_unid, bool temp, bool items)
1420 {
1421     int res = 0;
1422     const int rf = player_res_fire(calc_unid, temp, items);
1423
1424     if (you.species == SP_PALE_DRACONIAN)
1425         res += 2;
1426
1427     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1428     if (body_armour)
1429         res += armour_type_prop(body_armour->sub_type, ARMF_RES_STEAM) * 2;
1430
1431     res += (rf < 0) ? rf
1432                     : (rf + 1) / 2;
1433
1434     if (res > 3)
1435         res = 3;
1436
1437     return res;
1438 }
1439
1440 int player_res_cold(bool calc_unid, bool temp, bool items)
1441 {
1442     int rc = 0;
1443
1444     if (temp)
1445     {
1446         if (you.duration[DUR_RESISTANCE])
1447             rc++;
1448
1449         if (you.duration[DUR_FIRE_SHIELD])
1450             rc -= 2;
1451
1452         if (you.duration[DUR_QAZLAL_COLD_RES])
1453             rc++;
1454
1455         rc += get_form()->res_cold();
1456
1457         if (you.species == SP_VAMPIRE)
1458         {
1459             if (you.hunger_state <= HS_NEAR_STARVING)
1460                 rc += 2;
1461             else if (you.hunger_state < HS_SATIATED)
1462                 rc++;
1463         }
1464
1465 #if TAG_MAJOR_VERSION == 34
1466         if (you.species == SP_LAVA_ORC && temperature_effect(LORC_COLD_VULN))
1467             rc--;
1468 #endif
1469     }
1470
1471     if (items)
1472     {
1473         // rings of cold resistance/ice
1474         rc += you.wearing(EQ_RINGS, RING_PROTECTION_FROM_COLD, calc_unid);
1475         rc += you.wearing(EQ_RINGS, RING_ICE, calc_unid);
1476
1477         // rings of fire
1478         rc -= you.wearing(EQ_RINGS, RING_FIRE, calc_unid);
1479
1480         // Staves
1481         rc += you.wearing(EQ_STAFF, STAFF_COLD, calc_unid);
1482
1483         // body armour:
1484         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1485         if (body_armour)
1486             rc += armour_type_prop(body_armour->sub_type, ARMF_RES_COLD);
1487
1488         // ego armours
1489         rc += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_COLD_RESISTANCE);
1490         rc += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_RESISTANCE);
1491
1492         // randart weapons:
1493         rc += you.scan_artefacts(ARTP_COLD, calc_unid);
1494
1495         // dragonskin cloak: 0.5 to draconic resistances
1496         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1497             rc++;
1498     }
1499
1500 #if TAG_MAJOR_VERSION == 34
1501     // species:
1502     if (you.species == SP_DJINNI)
1503         rc--;
1504 #endif
1505     // mutations:
1506     rc += player_mutation_level(MUT_COLD_RESISTANCE, temp);
1507     rc -= player_mutation_level(MUT_COLD_VULNERABILITY, temp);
1508     rc += player_mutation_level(MUT_ICY_BLUE_SCALES, temp) == 3 ? 1 : 0;
1509     rc += player_mutation_level(MUT_SHAGGY_FUR, temp) == 3 ? 1 : 0;
1510
1511     if (rc < -3)
1512         rc = -3;
1513     else if (rc > 3)
1514         rc = 3;
1515
1516     return rc;
1517 }
1518
1519 bool player::res_corr(bool calc_unid, bool items) const
1520 {
1521     if (religion == GOD_JIYVA && piety >= piety_breakpoint(2))
1522         return true;
1523
1524     if (get_form()->res_acid())
1525         return true;
1526
1527     if (you.duration[DUR_RESISTANCE])
1528         return true;
1529
1530     if (items)
1531     {
1532         // dragonskin cloak: 0.5 to draconic resistances
1533         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN)
1534             && coinflip())
1535         {
1536             return true;
1537         }
1538     }
1539
1540     if ((form_keeps_mutations() || form == TRAN_DRAGON)
1541         && species == SP_YELLOW_DRACONIAN)
1542     {
1543         return true;
1544     }
1545
1546     if (form_keeps_mutations()
1547         && player_mutation_level(MUT_YELLOW_SCALES) >= 3)
1548     {
1549         return true;
1550     }
1551
1552     return actor::res_corr(calc_unid, items);
1553 }
1554
1555 int player_res_acid(bool calc_unid, bool items)
1556 {
1557     return you.res_corr(calc_unid, items) ? 1 : 0;
1558 }
1559
1560 int player_res_electricity(bool calc_unid, bool temp, bool items)
1561 {
1562     int re = 0;
1563
1564     if (items)
1565     {
1566         // staff
1567         re += you.wearing(EQ_STAFF, STAFF_AIR, calc_unid);
1568
1569         // body armour:
1570         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1571         if (body_armour)
1572             re += armour_type_prop(body_armour->sub_type, ARMF_RES_ELEC);
1573
1574         // randart weapons:
1575         re += you.scan_artefacts(ARTP_ELECTRICITY, calc_unid);
1576
1577         // dragonskin cloak: 0.5 to draconic resistances
1578         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1579             re++;
1580     }
1581
1582     // mutations:
1583     re += player_mutation_level(MUT_THIN_METALLIC_SCALES, temp) == 3 ? 1 : 0;
1584     re += player_mutation_level(MUT_SHOCK_RESISTANCE, temp);
1585     re -= player_mutation_level(MUT_SHOCK_VULNERABILITY, temp);
1586
1587     if (temp)
1588     {
1589         if (you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION])
1590             return 3;
1591
1592         if (you.duration[DUR_RESISTANCE])
1593             re++;
1594
1595         if (you.duration[DUR_QAZLAL_ELEC_RES])
1596             re++;
1597
1598         // transformations:
1599         if (get_form()->res_elec())
1600             re++;
1601     }
1602
1603     if (re > 1)
1604         re = 1;
1605
1606     return re;
1607 }
1608
1609 bool player_control_teleport(bool temp)
1610 {
1611     return (temp && you.duration[DUR_CONTROL_TELEPORT])
1612            || crawl_state.game_is_zotdef();
1613 }
1614
1615 /**
1616  * Is the player character immune to torment?
1617  *
1618  * @param random    Whether to include unreliable effects (stochastic resist)
1619  * @param temp      Whether to include temporary effects (forms, statuses...)
1620  * @return          Whether the player resists a given instance of torment; if
1621  *                  random is passed, the result may vary from call to call.
1622  */
1623 bool player_res_torment(bool random, bool temp)
1624 {
1625     if (player_mutation_level(MUT_TORMENT_RESISTANCE))
1626         return true;
1627
1628     if (random
1629         && player_mutation_level(MUT_STOCHASTIC_TORMENT_RESISTANCE)
1630         && coinflip())
1631     {
1632         return true;
1633     }
1634
1635     if (!temp)
1636         return false;
1637
1638     return get_form()->res_neg() == 3
1639            || you.species == SP_VAMPIRE && you.hunger_state == HS_STARVING
1640            || you.petrified();
1641 }
1642
1643 // Kiku protects you from torment to a degree.
1644 bool player_kiku_res_torment()
1645 {
1646     return in_good_standing(GOD_KIKUBAAQUDGHA, 3)
1647            && !you.gift_timeout; // no protection during pain branding weapon
1648 }
1649
1650 // If temp is set to false, temporary sources or resistance won't be counted.
1651 int player_res_poison(bool calc_unid, bool temp, bool items)
1652 {
1653     switch (you.undead_state(temp))
1654     {
1655         case US_ALIVE:
1656             break;
1657         case US_HUNGRY_DEAD: //ghouls
1658         case US_UNDEAD: // mummies & lichform
1659             return 3;
1660         case US_SEMI_UNDEAD: // vampire
1661             if (you.hunger_state == HS_STARVING) // XXX: && temp?
1662                 return 3;
1663             break;
1664     }
1665
1666     if (you.is_artificial(temp)
1667         || temp && get_form()->res_pois() == 3
1668         || items && player_equip_unrand(UNRAND_OLGREB)
1669         || temp && you.duration[DUR_DIVINE_STAMINA])
1670     {
1671         return 3;
1672     }
1673
1674     int rp = 0;
1675
1676     if (items)
1677     {
1678         // rings of poison resistance
1679         rp += you.wearing(EQ_RINGS, RING_POISON_RESISTANCE, calc_unid);
1680
1681         // Staves
1682         rp += you.wearing(EQ_STAFF, STAFF_POISON, calc_unid);
1683
1684         // ego armour:
1685         rp += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_POISON_RESISTANCE);
1686
1687         // body armour:
1688         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1689         if (body_armour)
1690             rp += armour_type_prop(body_armour->sub_type, ARMF_RES_POISON);
1691
1692         // rPois+ artefacts
1693         rp += you.scan_artefacts(ARTP_POISON, calc_unid);
1694
1695         // dragonskin cloak: 0.5 to draconic resistances
1696         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1697             rp++;
1698     }
1699
1700     // mutations:
1701     rp += player_mutation_level(MUT_POISON_RESISTANCE, temp);
1702     rp += player_mutation_level(MUT_SLIMY_GREEN_SCALES, temp) == 3 ? 1 : 0;
1703
1704     // Only thirsty vampires are naturally poison resistant.
1705     // XXX: && temp?
1706     if (you.species == SP_VAMPIRE && you.hunger_state < HS_SATIATED)
1707         rp++;
1708
1709     if (temp)
1710     {
1711         // potions/cards:
1712         if (you.duration[DUR_RESISTANCE])
1713             rp++;
1714
1715         if (get_form()->res_pois() > 0)
1716             rp++;
1717     }
1718
1719     // Cap rPois at + before vulnerability effects are applied
1720     // (so carrying multiple rPois effects is never useful)
1721     rp = min(1, rp);
1722
1723     if (temp)
1724     {
1725         if (get_form()->res_pois() < 0)
1726             rp--;
1727
1728         if (you.duration[DUR_POISON_VULN])
1729             rp--;
1730     }
1731
1732     // don't allow rPois--, etc.
1733     rp = max(-1, rp);
1734
1735     return rp;
1736 }
1737
1738 int player_res_sticky_flame(bool calc_unid, bool temp, bool items)
1739 {
1740     int rsf = 0;
1741
1742     if (you.species == SP_MOTTLED_DRACONIAN)
1743         rsf++;
1744
1745     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1746     if (body_armour)
1747         rsf += armour_type_prop(body_armour->sub_type, ARMF_RES_STICKY_FLAME);
1748
1749     // dragonskin cloak: 0.5 to draconic resistances
1750     if (items && calc_unid
1751         && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1752     {
1753         rsf++;
1754     }
1755
1756     if (get_form()->res_sticky_flame())
1757         rsf++;
1758
1759     if (rsf > 1)
1760         rsf = 1;
1761
1762     return rsf;
1763 }
1764
1765 int player_spec_death()
1766 {
1767     int sd = 0;
1768
1769     // Staves
1770     sd += you.wearing(EQ_STAFF, STAFF_DEATH);
1771
1772     // species:
1773     sd += player_mutation_level(MUT_NECRO_ENHANCER);
1774
1775     // transformations:
1776     if (you.form == TRAN_LICH)
1777         sd++;
1778
1779     return sd;
1780 }
1781
1782 int player_spec_fire()
1783 {
1784     int sf = 0;
1785
1786     // staves:
1787     sf += you.wearing(EQ_STAFF, STAFF_FIRE);
1788
1789     // rings of fire:
1790     sf += you.wearing(EQ_RINGS, RING_FIRE);
1791
1792 #if TAG_MAJOR_VERSION == 34
1793     if (you.species == SP_LAVA_ORC && temperature_effect(LORC_FIRE_BOOST))
1794         sf++;
1795 #endif
1796
1797     if (you.duration[DUR_FIRE_SHIELD])
1798         sf++;
1799
1800     return sf;
1801 }
1802
1803 int player_spec_cold()
1804 {
1805     int sc = 0;
1806
1807     // staves:
1808     sc += you.wearing(EQ_STAFF, STAFF_COLD);
1809
1810     // rings of ice:
1811     sc += you.wearing(EQ_RINGS, RING_ICE);
1812
1813 #if TAG_MAJOR_VERSION == 34
1814     if (you.species == SP_LAVA_ORC
1815         && (temperature_effect(LORC_LAVA_BOOST)
1816             || temperature_effect(LORC_FIRE_BOOST)))
1817     {
1818         sc--;
1819     }
1820 #endif
1821
1822     return sc;
1823 }
1824
1825 int player_spec_earth()
1826 {
1827     int se = 0;
1828
1829     // Staves
1830     se += you.wearing(EQ_STAFF, STAFF_EARTH);
1831
1832     return se;
1833 }
1834
1835 int player_spec_air()
1836 {
1837     int sa = 0;
1838
1839     // Staves
1840     sa += you.wearing(EQ_STAFF, STAFF_AIR);
1841
1842     return sa;
1843 }
1844
1845 int player_spec_conj()
1846 {
1847     int sc = 0;
1848
1849     // Staves
1850     sc += you.wearing(EQ_STAFF, STAFF_CONJURATION);
1851
1852     return sc;
1853 }
1854
1855 int player_spec_hex()
1856 {
1857     return 0;
1858 }
1859
1860 int player_spec_charm()
1861 {
1862     // Nothing, for the moment.
1863     return 0;
1864 }
1865
1866 int player_spec_summ()
1867 {
1868     int ss = 0;
1869
1870     // Staves
1871     ss += you.wearing(EQ_STAFF, STAFF_SUMMONING);
1872
1873     return ss;
1874 }
1875
1876 int player_spec_poison()
1877 {
1878     int sp = 0;
1879
1880     // Staves
1881     sp += you.wearing(EQ_STAFF, STAFF_POISON);
1882
1883     if (player_equip_unrand(UNRAND_OLGREB))
1884         sp++;
1885
1886     return sp;
1887 }
1888
1889 int player_energy()
1890 {
1891     int pe = 0;
1892
1893     // Staves
1894     pe += you.wearing(EQ_STAFF, STAFF_ENERGY);
1895
1896     return pe;
1897 }
1898
1899 // If temp is set to false, temporary sources of resistance won't be
1900 // counted.
1901 int player_prot_life(bool calc_unid, bool temp, bool items)
1902 {
1903     int pl = 0;
1904
1905     // Hunger is temporary, true, but that's something you can control,
1906     // especially as life protection only increases the hungrier you
1907     // get.
1908     if (you.species == SP_VAMPIRE)
1909     {
1910         switch (you.hunger_state)
1911         {
1912         case HS_STARVING:
1913         case HS_NEAR_STARVING:
1914             pl = 3;
1915             break;
1916         case HS_VERY_HUNGRY:
1917         case HS_HUNGRY:
1918             pl = 2;
1919             break;
1920         case HS_SATIATED:
1921             pl = 1;
1922             break;
1923         default:
1924             break;
1925         }
1926     }
1927
1928     // Same here. Your piety status, and, hence, TSO's protection, is
1929     // something you can more or less control.
1930     if (you_worship(GOD_SHINING_ONE))
1931     {
1932         if (you.piety >= piety_breakpoint(1))
1933             pl++;
1934         if (you.piety >= piety_breakpoint(3))
1935             pl++;
1936         if (you.piety >= piety_breakpoint(5))
1937             pl++;
1938     }
1939
1940     if (temp)
1941     {
1942         pl += get_form()->res_neg();
1943
1944         // completely stoned, unlike statue which has some life force
1945         if (you.petrified())
1946             pl += 3;
1947     }
1948
1949     if (items)
1950     {
1951         if (you.wearing(EQ_AMULET, AMU_WARDING, calc_unid))
1952             pl++;
1953
1954         // rings
1955         pl += you.wearing(EQ_RINGS, RING_LIFE_PROTECTION, calc_unid);
1956
1957         // armour (checks body armour only)
1958         pl += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_POSITIVE_ENERGY);
1959
1960         // pearl dragon counts
1961         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1962         if (body_armour)
1963             pl += armour_type_prop(body_armour->sub_type, ARMF_RES_NEG);
1964
1965         // randart wpns
1966         pl += you.scan_artefacts(ARTP_NEGATIVE_ENERGY, calc_unid);
1967
1968         // dragonskin cloak: 0.5 to draconic resistances
1969         // this one is dubious (no pearl draconians)
1970         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1971             pl++;
1972
1973         pl += you.wearing(EQ_STAFF, STAFF_DEATH, calc_unid);
1974     }
1975
1976     // undead/demonic power
1977     pl += player_mutation_level(MUT_NEGATIVE_ENERGY_RESISTANCE, temp);
1978
1979     pl = min(3, pl);
1980
1981     return pl;
1982 }
1983
1984 // New player movement speed system... allows for a bit more than
1985 // "player runs fast" and "player walks slow" in that the speed is
1986 // actually calculated (allowing for centaurs to get a bonus from
1987 // swiftness and other such things). Levels of the mutation now
1988 // also have meaning (before they all just meant fast). Most of
1989 // this isn't as fast as it used to be (6 for having anything), but
1990 // even a slight speed advantage is very good... and we certainly don't
1991 // want to go past 6 (see below). -- bwr
1992 int player_movement_speed()
1993 {
1994     int mv = 10;
1995
1996     // transformations
1997     if (you.form == TRAN_BAT)
1998         mv = 5; // but allowed minimum is six
1999     else if (you.form == TRAN_PIG)
2000         mv = 7;
2001     else if (you.form == TRAN_PORCUPINE || you.form == TRAN_WISP)
2002         mv = 8;
2003     else if (you.fishtail || you.form == TRAN_HYDRA && you.in_water())
2004         mv = 6;
2005
2006     // moving on liquefied ground takes longer
2007     if (you.liquefied_ground())
2008         mv += 3;
2009
2010     // armour
2011     if (you.run())
2012         mv -= 1;
2013
2014     mv += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_PONDEROUSNESS);
2015
2016     // Cheibriados
2017     if (you_worship(GOD_CHEIBRIADOS))
2018         mv += 2 + min(div_rand_round(you.piety, 20), 8);
2019
2020     // Tengu can move slightly faster when flying.
2021     if (you.tengu_flight())
2022         mv--;
2023
2024     if (you.duration[DUR_FROZEN])
2025         mv += 4;
2026
2027     if (you.duration[DUR_GRASPING_ROOTS])
2028         mv += 3;
2029
2030     // Mutations: -2, -3, -4, unless innate and shapechanged.
2031     if (int fast = player_mutation_level(MUT_FAST))
2032         mv -= fast + 1;
2033
2034     if (int slow = player_mutation_level(MUT_SLOW))
2035     {
2036         mv *= 10 + slow * 2;
2037         mv /= 10;
2038     }
2039
2040     if (you.duration[DUR_SWIFTNESS] > 0 && !you.in_liquid())
2041     {
2042         if (you.attribute[ATTR_SWIFTNESS] > 0)
2043           mv = div_rand_round(3*mv, 4);
2044         else if (mv >= 8)
2045           mv = div_rand_round(3*mv, 2);
2046         else if (mv == 7)
2047           mv = div_rand_round(7*6, 5); // balance for the cap at 6
2048     }
2049
2050     // We'll use the old value of six as a minimum, with haste this could
2051     // end up as a speed of three, which is about as fast as we want
2052     // the player to be able to go (since 3 is 3.33x as fast and 2 is 5x,
2053     // which is a bit of a jump, and a bit too fast) -- bwr
2054     // Currently Haste takes 6 to 4, which is 2.5x as fast as delay 10
2055     // and still seems plenty fast. -- elliptic
2056     if (mv < FASTEST_PLAYER_MOVE_SPEED)
2057         mv = FASTEST_PLAYER_MOVE_SPEED;
2058
2059     return mv;
2060 }
2061
2062 // This function differs from the above in that it's used to set the
2063 // initial time_taken value for the turn. Everything else (movement,
2064 // spellcasting, combat) applies a ratio to this value.
2065 int player_speed()
2066 {
2067     int ps = 10;
2068
2069     // When paralysed, speed is irrelevant.
2070     if (you.cannot_act())
2071         return ps;
2072
2073     if (you.duration[DUR_SLOW] || have_stat_zero())
2074         ps = haste_mul(ps);
2075
2076     if (you.duration[DUR_BERSERK] && !you_worship(GOD_CHEIBRIADOS))
2077         ps = berserk_div(ps);
2078     else if (you.duration[DUR_HASTE])
2079         ps = haste_div(ps);
2080
2081     if (you.form == TRAN_STATUE || you.duration[DUR_PETRIFYING])
2082     {
2083         ps *= 15;
2084         ps /= 10;
2085     }
2086
2087     return ps;
2088 }
2089
2090 // Get level of player mutation, ignoring mutations with an activity level
2091 // less than minact.
2092 static int _mut_level(mutation_type mut, mutation_activity_type minact)
2093 {
2094     const int mlevel = you.mutation[mut];
2095
2096     const mutation_activity_type active = mutation_activity_level(mut);
2097
2098     if (active >= minact)
2099         return mlevel;
2100
2101     return 0;
2102 }
2103
2104 // Output level of player mutation. If temp is true (the default), take into
2105 // account the suppression of mutations by changes of form.
2106 int player_mutation_level(mutation_type mut, bool temp)
2107 {
2108     return _mut_level(mut, temp ? MUTACT_PARTIAL : MUTACT_INACTIVE);
2109 }
2110
2111 static int _player_armour_beogh_bonus(const item_def& item)
2112 {
2113     if (item.base_type != OBJ_ARMOUR)
2114         return 0;
2115
2116     int bonus = 0;
2117
2118     if (in_good_standing(GOD_BEOGH))
2119     {
2120         if (you.piety >= piety_breakpoint(5))
2121             bonus = 10;
2122         else if (you.piety >= piety_breakpoint(4))
2123             bonus = 8;
2124         else if (you.piety >= piety_breakpoint(2))
2125             bonus = 6;
2126         else if (you.piety >= piety_breakpoint(0))
2127             bonus = 4;
2128         else
2129             bonus = 2;
2130     }
2131
2132     return bonus;
2133 }
2134
2135 bool is_effectively_light_armour(const item_def *item)
2136 {
2137     return !item
2138            || (abs(property(*item, PARM_EVASION)) / 10 < 5);
2139 }
2140
2141 bool player_effectively_in_light_armour()
2142 {
2143     const item_def *armour = you.slot_item(EQ_BODY_ARMOUR, false);
2144     return is_effectively_light_armour(armour);
2145 }
2146
2147 // This function returns true if the player has a radically different
2148 // shape... minor changes like blade hands don't count, also note
2149 // that lich transformation doesn't change the character's shape
2150 // (so we end up with Naga-liches, Spriggan-liches, Minotaur-liches)
2151 // it just makes the character undead (with the benefits that implies). - bwr
2152 bool player_is_shapechanged()
2153 {
2154     if (you.form == TRAN_NONE
2155         || you.form == TRAN_BLADE_HANDS
2156         || you.form == TRAN_LICH
2157         || you.form == TRAN_SHADOW
2158         || you.form == TRAN_APPENDAGE)
2159     {
2160         return false;
2161     }
2162
2163     return true;
2164 }
2165
2166 // An evasion factor based on the player's body size, smaller == higher
2167 // evasion size factor.
2168 static int _player_evasion_size_factor()
2169 {
2170     // XXX: you.body_size() implementations are incomplete, fix.
2171     const size_type size = you.body_size(PSIZE_BODY);
2172     return 2 * (SIZE_MEDIUM - size);
2173 }
2174
2175 // Determines racial shield penalties (formicids get a bonus compared to
2176 // other medium-sized races)
2177 int player_shield_racial_factor()
2178 {
2179     return max(1, 5 + (you.species == SP_FORMICID ? -2 // Same as trolls/centaurs/etc.
2180                                                   : _player_evasion_size_factor()));
2181 }
2182
2183 // The total EV penalty to the player for all their worn armour items
2184 // with a base EV penalty (i.e. EV penalty as a base armour property,
2185 // not as a randart property).
2186 static int _player_adjusted_evasion_penalty(const int scale)
2187 {
2188     int piece_armour_evasion_penalty = 0;
2189
2190     // Some lesser armours have small penalties now (barding).
2191     for (int i = EQ_MIN_ARMOUR; i < EQ_MAX_ARMOUR; i++)
2192     {
2193         if (i == EQ_SHIELD || !you.slot_item(static_cast<equipment_type>(i)))
2194             continue;
2195
2196         // [ds] Evasion modifiers for armour are negatives, change
2197         // those to positive for penalty calc.
2198         const int penalty = (-property(you.inv[you.equip[i]], PARM_EVASION))/3;
2199         if (penalty > 0)
2200             piece_armour_evasion_penalty += penalty;
2201     }
2202
2203     return piece_armour_evasion_penalty * scale / 10 +
2204            you.adjusted_body_armour_penalty(scale) ;
2205 }
2206
2207 // EV bonuses that work even when helpless.
2208 static int _player_para_evasion_bonuses(ev_ignore_type evit)
2209 {
2210     int evbonus = 0;
2211
2212     if (you.duration[DUR_PHASE_SHIFT] && !(evit & EV_IGNORE_PHASESHIFT))
2213         evbonus += 8;
2214
2215     if (player_mutation_level(MUT_DISTORTION_FIELD) > 0)
2216         evbonus += player_mutation_level(MUT_DISTORTION_FIELD) + 1;
2217
2218     return evbonus;
2219 }
2220
2221 // Player EV bonuses for various effects and transformations. This
2222 // does not include tengu/merfolk EV bonuses for flight/swimming.
2223 static int _player_evasion_bonuses(ev_ignore_type evit)
2224 {
2225     int evbonus = _player_para_evasion_bonuses(evit);
2226
2227     if (you.duration[DUR_AGILITY])
2228         evbonus += AGILITY_BONUS;
2229
2230     evbonus += you.wearing(EQ_RINGS_PLUS, RING_EVASION);
2231
2232     if (you.wearing_ego(EQ_WEAPON, SPWPN_EVASION))
2233         evbonus += 5;
2234
2235     evbonus += you.scan_artefacts(ARTP_EVASION);
2236
2237     // mutations
2238     if (_mut_level(MUT_ICY_BLUE_SCALES, MUTACT_FULL) > 1)
2239         evbonus--;
2240     if (_mut_level(MUT_MOLTEN_SCALES, MUTACT_FULL) > 1)
2241         evbonus--;
2242     evbonus += max(0, player_mutation_level(MUT_GELATINOUS_BODY) - 1);
2243
2244     // transformation penalties/bonuses not covered by size alone:
2245     if (player_mutation_level(MUT_SLOW_REFLEXES))
2246         evbonus -= player_mutation_level(MUT_SLOW_REFLEXES) * 3;
2247
2248     return evbonus;
2249 }
2250
2251 // Player EV scaling for being flying tengu or swimming merfolk.
2252 static int _player_scale_evasion(int prescaled_ev, const int scale)
2253 {
2254     if (you.duration[DUR_PETRIFYING] || you.caught())
2255         prescaled_ev /= 2;
2256     else if (you.duration[DUR_GRASPING_ROOTS])
2257         prescaled_ev = prescaled_ev * 2 / 3;
2258
2259     // Merfolk get an evasion bonus in water.
2260     if (you.fishtail)
2261     {
2262         const int ev_bonus = min(9 * scale,
2263                                  max(2 * scale, prescaled_ev / 4));
2264         return prescaled_ev + ev_bonus;
2265     }
2266
2267     // Flying Tengu get an evasion bonus.
2268     if (you.tengu_flight())
2269     {
2270         const int ev_bonus = min(9 * scale,
2271                                  max(1 * scale, prescaled_ev / 5));
2272         return prescaled_ev + ev_bonus;
2273     }
2274
2275     return prescaled_ev;
2276 }
2277
2278 /**
2279  * What is the player's bonus to EV from dodging when not paralyzed, after
2280  * accounting for size & body armour penalties?
2281  *
2282  * First, calculate base dodge bonus (linear with dodging * stepdowned dex),
2283  * and armour dodge penalty (base armour evp, increased for small races &
2284  * decreased for large, then with a magic "3" subtracted from it to make the
2285  * penalties not too harsh).
2286  *
2287  * If the player's strength is greater than the armour dodge penalty, return
2288  *      base dodge * (1 - dodge_pen / (str*2)).
2289  * E.g., if str is twice dodge penalty, return 3/4 of base dodge. If
2290  * str = dodge_pen * 4, return 7/8...
2291  *
2292  * If str is less than dodge penalty, return
2293  *      base_dodge * str / (dodge_pen * 2).
2294  * E.g., if str = dodge_pen / 2, return 1/4 of base dodge. if
2295  * str = dodge_pen / 4, return 1/8...
2296  *
2297  * For either equation, if str = dodge_pen, the result is base_dodge/2.
2298  *
2299  * @param scale     A scale to multiply the result by, to avoid precision loss.
2300  * @return          A bonus to EV, multiplied by the scale.
2301  */
2302 static int _player_armour_adjusted_dodge_bonus(int scale)
2303 {
2304     // stepdowns at 10 and 24 dex; the last two parameters are not important.
2305     const int ev_dex = stepdown_value(you.dex(), 10, 24, 72, 72);
2306
2307     const int dodge_bonus =
2308         (70 + you.skill(SK_DODGING, 10) * ev_dex) * scale
2309         / (20 - _player_evasion_size_factor()) / 10;
2310
2311     const int armour_dodge_penalty = you.unadjusted_body_armour_penalty() - 3;
2312     if (armour_dodge_penalty <= 0)
2313         return dodge_bonus;
2314
2315     const int str = max(1, you.strength());
2316     if (armour_dodge_penalty >= str)
2317         return dodge_bonus * str / (armour_dodge_penalty * 2);
2318     return dodge_bonus - dodge_bonus * armour_dodge_penalty / (str * 2);
2319 }
2320
2321 // Total EV for player using the revised 0.6 evasion model.
2322 static int _player_evasion(ev_ignore_type evit)
2323 {
2324     const int size_factor = _player_evasion_size_factor();
2325     // Repulsion fields and size are all that matters when paralysed or
2326     // at 0 dex.
2327     if ((you.cannot_move() || you.stat_zero[STAT_DEX] || you.form == TRAN_TREE)
2328         && !(evit & EV_IGNORE_HELPLESS))
2329     {
2330         const int paralysed_base_ev = 2 + size_factor / 2;
2331         const int repulsion_ev = _player_para_evasion_bonuses(evit);
2332         return max(1, paralysed_base_ev + repulsion_ev);
2333     }
2334
2335     const int scale = 100;
2336     const int size_base_ev = (10 + size_factor) * scale;
2337
2338     const int prestepdown_evasion =
2339         size_base_ev
2340         + _player_armour_adjusted_dodge_bonus(scale)
2341         - _player_adjusted_evasion_penalty(scale)
2342         - you.adjusted_shield_penalty(scale);
2343
2344     const int poststepdown_evasion =
2345         stepdown_value(prestepdown_evasion, 20*scale, 30*scale, 60*scale, -1);
2346
2347     const int evasion_bonuses = _player_evasion_bonuses(evit) * scale;
2348
2349     const int prescaled_evasion =
2350         poststepdown_evasion + evasion_bonuses;
2351
2352     const int final_evasion =
2353         _player_scale_evasion(prescaled_evasion, scale);
2354
2355     return unscale_round_up(final_evasion, scale);
2356 }
2357
2358 // Returns the spellcasting penalty (increase in spell failure) for the
2359 // player's worn body armour and shield.
2360 int player_armour_shield_spell_penalty()
2361 {
2362     const int scale = 100;
2363
2364     const int body_armour_penalty =
2365         max(19 * you.adjusted_body_armour_penalty(scale), 0);
2366
2367     const int total_penalty = body_armour_penalty
2368                  + 19 * you.adjusted_shield_penalty(scale);
2369
2370     return max(total_penalty, 0) / scale;
2371 }
2372
2373 /**
2374  * How many spell-success-chance-boosting ('wizardry') effects can the player
2375  * apply to the given spell?
2376  *
2377  * @param spell     The type of spell being cast.
2378  * @return          The number of relevant wizardry effects.
2379  */
2380 int player_wizardry(spell_type spell)
2381 {
2382     return you.wearing(EQ_RINGS, RING_WIZARDRY)
2383            + you.wearing(EQ_STAFF, STAFF_WIZARDRY);
2384 }
2385
2386 /**
2387  * Calculate the SH value used internally.
2388  *
2389  * Exactly twice the value displayed to players, for legacy reasons.
2390  * @return      The player's current SH value.
2391  */
2392 int player_shield_class()
2393 {
2394     int shield = 0;
2395
2396     if (you.incapacitated())
2397         return 0;
2398
2399     if (you.shield())
2400     {
2401         const item_def& item = you.inv[you.equip[EQ_SHIELD]];
2402         int size_factor = (you.body_size(PSIZE_TORSO) - SIZE_MEDIUM)
2403                         * (item.sub_type - ARM_LARGE_SHIELD);
2404         int base_shield = property(item, PARM_AC) * 2 + size_factor;
2405
2406         int beogh_bonus = _player_armour_beogh_bonus(item);
2407
2408         // bonus applied only to base, see above for effect:
2409         shield += base_shield * 50;
2410         shield += base_shield * you.skill(SK_SHIELDS, 5) / 2;
2411         shield += base_shield * beogh_bonus * 10 / 6;
2412
2413         shield += item.plus * 200;
2414
2415         shield += you.skill(SK_SHIELDS, 38)
2416                 + min(you.skill(SK_SHIELDS, 38), 3 * 38);
2417
2418         int stat = 0;
2419         if (item.sub_type == ARM_BUCKLER)
2420             stat = you.dex() * 38;
2421         else if (item.sub_type == ARM_LARGE_SHIELD)
2422             stat = you.dex() * 12 + you.strength() * 26;
2423         else
2424             stat = you.dex() * 19 + you.strength() * 19;
2425         stat = stat * (base_shield + 13) / 26;
2426
2427         shield += stat;
2428     }
2429     else
2430     {
2431         if (you.duration[DUR_MAGIC_SHIELD])
2432             shield += 900 + you.skill(SK_EVOCATIONS, 75);
2433
2434         if (you.duration[DUR_CONDENSATION_SHIELD])
2435             shield += 800 + you.props[CONDENSATION_SHIELD_KEY].get_int() * 15;
2436     }
2437
2438     // mutations
2439     // +2, +3, +4 (displayed values)
2440     shield += (player_mutation_level(MUT_LARGE_BONE_PLATES) > 0
2441                ? player_mutation_level(MUT_LARGE_BONE_PLATES) * 200 + 200
2442                : 0);
2443
2444     shield += qazlal_sh_boost() * 100;
2445     shield += tso_sh_boost() * 100;
2446     shield += _bone_armour_bonus() * 2;
2447
2448     return (shield + 50) / 100;
2449 }
2450
2451 /**
2452  * Calculate the SH value that should be displayed to players.
2453  *
2454  * Exactly half the internal value, for legacy reasons.
2455  * @return      The SH value to be displayed.
2456  */
2457 int player_displayed_shield_class()
2458 {
2459     return player_shield_class() / 2;
2460 }
2461
2462 /**
2463  * Does the player take halved ability damage?
2464  *
2465  * @param calc_unid     Whether to include properties of worn but unidentified
2466  *                      items in the calculation. (Probably irrelevant.)
2467  * @return              Whether the player has SustAb.
2468  */
2469 bool player_sust_abil(bool calc_unid)
2470 {
2471     return you.wearing(EQ_RINGS, RING_SUSTAIN_ABILITIES, calc_unid)
2472            || you.scan_artefacts(ARTP_SUSTAB)
2473            || player_mutation_level(MUT_SUSTAIN_ABILITIES);
2474 }
2475
2476 void forget_map(bool rot)
2477 {
2478     ASSERT(!crawl_state.game_is_arena());
2479
2480     // If forgetting was intentional, clear the travel trail.
2481     if (!rot)
2482         clear_travel_trail();
2483
2484     // Labyrinth and the Abyss use special rotting rules.
2485     const bool rot_resist = player_in_branch(BRANCH_LABYRINTH)
2486                                 && you.species == SP_MINOTAUR
2487                             || player_in_branch(BRANCH_ABYSS)
2488                                 && you_worship(GOD_LUGONU);
2489     const double geometric_chance = 0.99;
2490     const int radius = (rot_resist ? 200 : 100);
2491
2492     const int scalar = 0xFF;
2493     for (rectangle_iterator ri(0); ri; ++ri)
2494     {
2495         const coord_def &p = *ri;
2496         if (!env.map_knowledge(p).known() || you.see_cell(p))
2497             continue;
2498
2499         if (rot)
2500         {
2501             const int dist = distance2(you.pos(), p);
2502             int chance = pow(geometric_chance,
2503                              max(1, (dist - radius) / 40)) * scalar;
2504             if (x_chance_in_y(chance, scalar))
2505                 continue;
2506         }
2507
2508         if (you.see_cell(p))
2509             continue;
2510
2511         env.map_knowledge(p).clear();
2512         if (env.map_forgotten.get())
2513             (*env.map_forgotten.get())(p).clear();
2514         StashTrack.update_stash(p);
2515 #ifdef USE_TILE
2516         tile_forget_map(p);
2517 #endif
2518     }
2519
2520     ash_detect_portals(is_map_persistent());
2521 #ifdef USE_TILE
2522     tiles.update_minimap_bounds();
2523 #endif
2524 }
2525
2526 static void _remove_temp_mutation()
2527 {
2528     int num_remove = min(you.attribute[ATTR_TEMP_MUTATIONS],
2529         max(you.attribute[ATTR_TEMP_MUTATIONS] * 5 / 12 - random2(3),
2530         1 + random2(3)));
2531
2532     if (num_remove >= you.attribute[ATTR_TEMP_MUTATIONS])
2533         mprf(MSGCH_DURATION, "You feel the corruption within you wane completely.");
2534     else
2535         mprf(MSGCH_DURATION, "You feel the corruption within you wane somewhat.");
2536
2537     for (int i = 0; i < num_remove; ++i)
2538         delete_temp_mutation();
2539
2540     if (you.attribute[ATTR_TEMP_MUTATIONS] > 0)
2541         you.attribute[ATTR_TEMP_MUT_XP] += temp_mutation_roll();
2542 }
2543
2544 int get_exp_progress()
2545 {
2546     if (you.experience_level >= you.get_max_xl())
2547         return 0;
2548
2549     const int current = exp_needed(you.experience_level);
2550     const int next    = exp_needed(you.experience_level + 1);
2551     if (next == current)
2552         return 0;
2553     return (you.experience - current) * 100 / (next - current);
2554 }
2555
2556 static void _recharge_xp_evokers(int exp)
2557 {
2558     FixedVector<item_def*, NUM_MISCELLANY> evokers(nullptr);
2559     list_charging_evokers(evokers);
2560
2561     int xp_factor = max(min((int)exp_needed(you.experience_level+1, 0) * 2 / 7,
2562                              you.experience_level * 425),
2563                         you.experience_level*4 + 30)
2564                     / (3 + you.skill_rdiv(SK_EVOCATIONS, 2, 13));
2565
2566     for (int i = 0; i < NUM_MISCELLANY; ++i)
2567     {
2568         item_def* evoker = evokers[i];
2569         if (!evoker)
2570             continue;
2571
2572         int &debt = evoker_debt(evoker->sub_type);
2573         if (debt == 0)
2574             continue;
2575
2576         debt = max(0, debt - div_rand_round(exp, xp_factor));
2577         if (debt == 0)
2578             mprf("%s has recharged.", evoker->name(DESC_YOUR).c_str());
2579     }
2580 }
2581
2582 void gain_exp(unsigned int exp_gained, unsigned int* actual_gain)
2583 {
2584     if (crawl_state.game_is_arena())
2585         return;
2586
2587     if (crawl_state.game_is_zotdef())
2588     {
2589         you.zot_points += exp_gained;
2590         // All XP, for some reason Sprint speeds up only skill training,
2591         // but not levelling, Ash skill transfer, etc.
2592         exp_gained *= 2;
2593     }
2594
2595     vector<god_type> xp_gods;
2596     for (int i = GOD_NO_GOD; i < NUM_GODS; ++i)
2597     {
2598         if (xp_penance((god_type) i))
2599             xp_gods.push_back((god_type) i);
2600     }
2601
2602     if (!xp_gods.empty())
2603     {
2604         god_type god = xp_gods[random2(xp_gods.size())];
2605         reduce_xp_penance(god, exp_gained);
2606     }
2607
2608     const unsigned int old_exp = you.experience;
2609
2610     dprf("gain_exp: %d", exp_gained);
2611
2612     if (you.transfer_skill_points > 0)
2613     {
2614         // Can happen if the game got interrupted during target skill choice.
2615         if (is_invalid_skill(you.transfer_to_skill))
2616         {
2617             you.transfer_from_skill = SK_NONE;
2618             you.transfer_skill_points = 0;
2619             you.transfer_total_skill_points = 0;
2620         }
2621         else
2622         {
2623             int amount = exp_gained * 10
2624                                 / calc_skill_cost(you.skill_cost_level);
2625             if (amount >= 20 || one_chance_in(20 - amount))
2626             {
2627                 amount = max(20, amount);
2628                 transfer_skill_points(you.transfer_from_skill,
2629                                       you.transfer_to_skill, amount, false);
2630             }
2631         }
2632     }
2633
2634     if (you.experience + exp_gained > (unsigned int)MAX_EXP_TOTAL)
2635         you.experience = MAX_EXP_TOTAL;
2636     else
2637         you.experience += exp_gained;
2638
2639     you.attribute[ATTR_EVOL_XP] += exp_gained;
2640     for (int i = GOD_NO_GOD; i < NUM_GODS; ++i)
2641     {
2642         if (active_penance((god_type) i))
2643         {
2644             you.attribute[ATTR_GOD_WRATH_XP] -= exp_gained;
2645             while (you.attribute[ATTR_GOD_WRATH_XP] < 0)
2646             {
2647                 you.attribute[ATTR_GOD_WRATH_COUNT]++;
2648                 set_penance_xp_timeout();
2649             }
2650             break;
2651         }
2652     }
2653
2654     if (crawl_state.game_is_sprint())
2655         exp_gained = sprint_modify_exp(exp_gained);
2656
2657     you.exp_available += exp_gained;
2658
2659     train_skills();
2660     while (check_selected_skills()
2661            && you.exp_available >= calc_skill_cost(you.skill_cost_level))
2662     {
2663         train_skills();
2664     }
2665
2666     if (you.exp_available >= calc_skill_cost(you.skill_cost_level))
2667         you.exp_available = calc_skill_cost(you.skill_cost_level);
2668
2669     level_change();
2670
2671     if (actual_gain != nullptr)
2672         *actual_gain = you.experience - old_exp;
2673
2674     if (you.attribute[ATTR_TEMP_MUTATIONS] > 0)
2675     {
2676         you.attribute[ATTR_TEMP_MUT_XP] -= exp_gained;
2677         if (you.attribute[ATTR_TEMP_MUT_XP] <= 0)
2678             _remove_temp_mutation();
2679     }
2680
2681     _recharge_xp_evokers(exp_gained);
2682
2683     if (you.attribute[ATTR_XP_DRAIN])
2684     {
2685         int loss = div_rand_round(exp_gained * 3 / 2,
2686                                   calc_skill_cost(you.skill_cost_level));
2687
2688         // Make it easier to recover from very heavy levels of draining
2689         // (they're nasty enough as it is)
2690         loss = loss * (1 + (you.attribute[ATTR_XP_DRAIN] / 250.0f));
2691
2692         dprf("Lost %d of %d draining points", loss, you.attribute[ATTR_XP_DRAIN]);
2693
2694         you.attribute[ATTR_XP_DRAIN] -= loss;
2695         // Regaining skills may affect AC/EV.
2696         you.redraw_armour_class = true;
2697         you.redraw_evasion = true;
2698         if (you.attribute[ATTR_XP_DRAIN] <= 0)
2699         {
2700             you.attribute[ATTR_XP_DRAIN] = 0;
2701             mprf(MSGCH_RECOVERY, "Your life force feels restored.");
2702         }
2703     }
2704 }
2705
2706 bool will_gain_life(int lev)
2707 {
2708     if (lev < you.attribute[ATTR_LIFE_GAINED] - 2)
2709         return false;
2710
2711     return you.lives + you.deaths < (lev - 1) / 3;
2712 }
2713
2714 static void _felid_extra_life()
2715 {
2716     if (will_gain_life(you.max_level)
2717         && you.lives < 2)
2718     {
2719         you.lives++;
2720         mprf(MSGCH_INTRINSIC_GAIN, "Extra life!");
2721         you.attribute[ATTR_LIFE_GAINED] = you.max_level;
2722         // Should play the 1UP sound from SMB...
2723     }
2724 }
2725
2726 /**
2727  * Handle the effects from a player's change in XL.
2728  * @param aux                     A string describing the cause of the level
2729  *                                change.
2730  * @param skip_attribute_increase If true and XL has increased, don't process
2731  *                                stat gains. Currently only used by wizmode
2732  *                                commands.
2733  */
2734 void level_change(bool skip_attribute_increase)
2735 {
2736     // necessary for the time being, as level_change() is called
2737     // directly sometimes {dlb}
2738     you.redraw_experience = true;
2739
2740     while (you.experience < exp_needed(you.experience_level))
2741         lose_level();
2742
2743     while (you.experience_level < you.get_max_xl()
2744            && you.experience >= exp_needed(you.experience_level + 1))
2745     {
2746         if (!skip_attribute_increase)
2747         {
2748             crawl_state.cancel_cmd_all();
2749
2750             if (is_processing_macro())
2751                 flush_input_buffer(FLUSH_ABORT_MACRO);
2752         }
2753
2754         // [ds] Make sure we increment you.experience_level and apply
2755         // any stat/hp increases only after we've cleared all prompts
2756         // for this experience level. If we do part of the work before
2757         // the prompt, and a player on telnet gets disconnected, the
2758         // SIGHUP will save Crawl in the in-between state and rob the
2759         // player of their level-up perks.
2760
2761         const int new_exp = you.experience_level + 1;
2762
2763         if (new_exp <= you.max_level)
2764         {
2765             mprf(MSGCH_INTRINSIC_GAIN,
2766                  "Welcome back to level %d!", new_exp);
2767
2768             // No more prompts for this XL past this point.
2769
2770             you.experience_level = new_exp;
2771         }
2772         else  // Character has gained a new level
2773         {
2774             // Don't want to see the dead creature at the prompt.
2775             redraw_screen();
2776             // There may be more levels left to gain.
2777             you.redraw_experience = true;
2778
2779             if (new_exp == 27)
2780                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level 27, the final one!");
2781             else if (new_exp == you.get_max_xl())
2782                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d, the highest you will ever reach!",
2783                         you.get_max_xl());
2784             else
2785             {
2786                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d!",
2787                      new_exp);
2788             }
2789
2790             const bool manual_stat_level = new_exp % 3 == 0;  // 3,6,9,12...
2791
2792             if (manual_stat_level && !skip_attribute_increase)
2793                 if (!attribute_increase())
2794                     return; // abort level gain, the xp is still there
2795
2796             crawl_state.stat_gain_prompt = false;
2797             you.experience_level = new_exp;
2798             you.max_level = you.experience_level;
2799
2800 #ifdef USE_TILE_LOCAL
2801             // In case of intrinsic ability changes.
2802             tiles.layout_statcol();
2803             redraw_screen();
2804 #endif
2805             if (!skip_attribute_increase)
2806                 species_stat_gain(you.species);
2807
2808             switch (you.species)
2809             {
2810             case SP_VAMPIRE:
2811                 if (you.experience_level == 3)
2812                 {
2813                     if (you.hunger_state > HS_SATIATED)
2814                     {
2815                         mprf(MSGCH_INTRINSIC_GAIN, "If you weren't so full, "
2816                              "you could now transform into a vampire bat.");
2817                     }
2818                     else
2819                     {
2820                         mprf(MSGCH_INTRINSIC_GAIN,
2821                              "You can now transform into a vampire bat.");
2822                     }
2823                 }
2824                 break;
2825
2826             case SP_NAGA:
2827                 if (!(you.experience_level % 3))
2828                 {
2829                     mprf(MSGCH_INTRINSIC_GAIN, "Your skin feels tougher.");
2830                     you.redraw_armour_class = true;
2831                 }
2832                 break;
2833
2834             case SP_BASE_DRACONIAN:
2835                 if (you.experience_level >= 7)
2836                 {
2837                     you.species = static_cast<species_type>(
2838                                        random_range(SP_FIRST_NONBASE_DRACONIAN,
2839                                                     SP_LAST_NONBASE_DRACONIAN));
2840
2841                     // We just changed our aptitudes, so some skills may now
2842                     // be at the wrong level (with negative progress); if we
2843                     // print anything in this condition, we might trigger a
2844                     // --More--, a redraw, and a crash (#6376 on Mantis).
2845                     //
2846                     // Hence we first fix up our skill levels silently (passing
2847                     // do_level_up = false) but save the old values; then when
2848                     // we want the messages later, we restore the old skill
2849                     // levels and call check_skill_level_change() again, this
2850                     // time passing do_update = true.
2851
2852                     uint8_t saved_skills[NUM_SKILLS];
2853                     for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
2854                     {
2855                         saved_skills[sk] = you.skills[sk];
2856                         check_skill_level_change(sk, false);
2857                     }
2858                     // The player symbol depends on species.
2859                     update_player_symbol();
2860 #ifdef USE_TILE
2861                     init_player_doll();
2862 #endif
2863                     // Produce messages about skill increases/decreases. We
2864                     // restore one skill level at a time so that at most the
2865                     // skill being checked is at the wrong level.
2866                     for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
2867                     {
2868                         you.skills[sk] = saved_skills[sk];
2869                         check_skill_level_change(sk);
2870                     }
2871
2872                     redraw_screen();
2873
2874                     mprf(MSGCH_INTRINSIC_GAIN,
2875                          "Your scales start taking on a %s colour.",
2876                          scale_type(you.species));
2877                 }
2878                 break;
2879
2880             case SP_DEMONSPAWN:
2881             {
2882                 bool gave_message = false;
2883                 int level = 0;
2884                 mutation_type first_body_facet = NUM_MUTATIONS;
2885
2886                 for (const player::demon_trait trait : you.demonic_traits)
2887                 {
2888                     if (is_body_facet(trait.mutation))
2889                     {
2890                         if (first_body_facet < NUM_MUTATIONS
2891                             && trait.mutation != first_body_facet)
2892                         {
2893                             if (you.experience_level == level)
2894                             {
2895                                 mprf(MSGCH_MUTATION, "You feel monstrous as "
2896                                      "your demonic heritage exerts itself.");
2897                                 mark_milestone("monstrous", "discovered their "
2898                                                "monstrous ancestry!");
2899                             }
2900                             break;
2901                         }
2902
2903                         if (first_body_facet == NUM_MUTATIONS)
2904                         {
2905                             first_body_facet = trait.mutation;
2906                             level = trait.level_gained;
2907                         }
2908                     }
2909                 }
2910
2911                 for (const player::demon_trait trait : you.demonic_traits)
2912                 {
2913                     if (trait.level_gained == you.experience_level)
2914                     {
2915                         if (!gave_message)
2916                         {
2917                             mprf(MSGCH_INTRINSIC_GAIN,
2918                                  "Your demonic ancestry asserts itself...");
2919
2920                             gave_message = true;
2921                         }
2922                         perma_mutate(trait.mutation, 1, "demonic ancestry");
2923                     }
2924                 }
2925
2926                 break;
2927             }
2928
2929             case SP_FELID:
2930                 _felid_extra_life();
2931                 break;
2932
2933             default:
2934                 break;
2935             }
2936
2937             give_level_mutations(you.species, you.experience_level);
2938
2939         }
2940
2941         if (species_is_draconian(you.species) && !(you.experience_level % 3))
2942         {
2943             mprf(MSGCH_INTRINSIC_GAIN, "Your scales feel tougher.");
2944             you.redraw_armour_class = true;
2945         }
2946
2947         // zot defence abilities; must also be updated in ability.cc when these levels are changed
2948         if (crawl_state.game_is_zotdef())
2949         {
2950             if (you.experience_level == 2)
2951                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of oklob saplings.");
2952             if (you.experience_level == 3)
2953                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of arrow traps.");
2954             if (you.experience_level == 4)
2955                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of plants.");
2956             if (you.experience_level == 4)
2957                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through removing curses.");
2958             if (you.experience_level == 5)
2959                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of burning bushes.");
2960             if (you.experience_level == 6)
2961                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of altars and grenades.");
2962             if (you.experience_level == 7)
2963                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of oklob plants.");
2964             if (you.experience_level == 8)
2965                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of net traps.");
2966             if (you.experience_level == 9)
2967                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of ice statues.");
2968             if (you.experience_level == 10)
2969                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of spear traps.");
2970             if (you.experience_level == 11)
2971                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of alarm traps.");
2972             if (you.experience_level == 12)
2973                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of mushroom circles.");
2974             if (you.experience_level == 13)
2975                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of bolt traps.");
2976             if (you.experience_level == 14)
2977                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of orange crystal statues.");
2978             if (you.experience_level == 15)
2979                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of needle traps.");
2980             if (you.experience_level == 16)
2981                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through self-teleportation.");
2982             if (you.experience_level == 17)
2983                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through making water.");
2984             if (you.experience_level == 19)
2985                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of lightning spires.");
2986             if (you.experience_level == 20)
2987                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of obsidian statues.");
2988             // gold and bazaars gained together
2989             if (you.experience_level == 21)
2990                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of bazaars.");
2991             if (you.experience_level == 21)
2992                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through acquiring gold.");
2993             if (you.experience_level == 22)
2994                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of oklob circles.");
2995             if (you.experience_level == 24)
2996                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through acquirement.");
2997             if (you.experience_level == 25)
2998                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of blade traps.");
2999             if (you.experience_level == 26)
3000                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of curse skulls.");
3001         }
3002
3003         const int old_hp = you.hp;
3004         const int old_maxhp = you.hp_max;
3005         const int old_mp = you.magic_points;
3006         const int old_maxmp = you.max_magic_points;
3007
3008         // recalculate for game
3009         calc_hp();
3010         calc_mp();
3011
3012         set_hp(old_hp * you.hp_max / old_maxhp);
3013         set_mp(old_maxmp > 0 ? old_mp * you.max_magic_points / old_maxmp
3014                : you.max_magic_points);
3015
3016         // Get "real" values for note-taking, i.e. ignore Berserk,
3017         // transformations or equipped items.
3018         const int note_maxhp = get_real_hp(false, false);
3019         const int note_maxmp = get_real_mp(false);
3020
3021         char buf[200];
3022 #if TAG_MAJOR_VERSION == 34
3023         if (you.species == SP_DJINNI)
3024             // Djinn don't HP/MP
3025             sprintf(buf, "EP: %d/%d",
3026                     min(you.hp, note_maxhp + note_maxmp),
3027                     note_maxhp + note_maxmp);
3028         else
3029 #endif
3030             sprintf(buf, "HP: %d/%d MP: %d/%d",
3031                     min(you.hp, note_maxhp), note_maxhp,
3032                     min(you.magic_points, note_maxmp), note_maxmp);
3033         take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, buf));
3034
3035         xom_is_stimulated(12);
3036
3037         learned_something_new(HINT_NEW_LEVEL);
3038     }
3039
3040     while (you.experience >= exp_needed(you.max_level + 1))
3041     {
3042         ASSERT(you.experience_level == you.get_max_xl());
3043         ASSERT(you.max_level < 127); // marshalled as an 1-byte value
3044         you.max_level++;
3045         if (you.species == SP_FELID)
3046             _felid_extra_life();
3047     }
3048
3049     you.redraw_title = true;
3050
3051 #ifdef DGL_WHEREIS
3052     whereis_record();
3053 #endif
3054
3055     // Hints mode arbitrarily ends at xp 7.
3056     if (crawl_state.game_is_hints() && you.experience_level >= 7)
3057         hints_finished();
3058 }
3059
3060 void adjust_level(int diff, bool just_xp)
3061 {
3062     ASSERT((uint64_t)you.experience <= (uint64_t)MAX_EXP_TOTAL);
3063     const int max_exp_level = you.get_max_xl();
3064     if (you.experience_level + diff < 1)
3065         you.experience = 0;
3066     else if (you.experience_level + diff >= max_exp_level)
3067         you.experience = max(you.experience,
3068                 exp_needed(max_exp_level));
3069     else
3070     {
3071         while (diff < 0 && you.experience >=
3072                 exp_needed(max_exp_level))
3073         {
3074             // Having XP for level 53 and going back to 26 due to a single
3075             // card would mean your felid is not going to get any extra lives
3076             // in foreseable future.
3077             you.experience -= exp_needed(max_exp_level)
3078                     - exp_needed(max_exp_level - 1);
3079             diff++;
3080         }
3081         int old_min = exp_needed(you.experience_level);
3082         int old_max = exp_needed(you.experience_level + 1);
3083         int new_min = exp_needed(you.experience_level + diff);
3084         int new_max = exp_needed(you.experience_level + 1 + diff);
3085         dprf("XP before: %d\n", you.experience);
3086         dprf("%4.2f of %d..%d to %d..%d",
3087              (you.experience - old_min) * 1.0 / (old_max - old_min),
3088              old_min, old_max, new_min, new_max);
3089
3090         you.experience = ((int64_t)(new_max - new_min))
3091                        * (you.experience - old_min)
3092                        / (old_max - old_min)
3093                        + new_min;
3094         dprf("XP after: %d\n", you.experience);
3095     }
3096
3097     ASSERT((uint64_t)you.experience <= (uint64_t)MAX_EXP_TOTAL);
3098
3099     if (!just_xp)
3100         level_change();
3101 }
3102
3103 /**
3104  * Return a multiplier for skill when calculating stealth values, based on the
3105  * player's species & form.
3106  *
3107  * @return The player's current stealth multiplier value.
3108  */
3109 static int _stealth_mod()
3110 {
3111     const int form_stealth_mod = get_form()->get_stealth_mod();
3112
3113     if (form_stealth_mod != 0)
3114         return form_stealth_mod;
3115
3116     int species_stealth_mod = species_stealth_modifier(you.species);
3117     if (you.form == TRAN_STATUE)
3118         species_stealth_mod -= 3;
3119     // Thirsty vampires are (much) more stealthy
3120     if (you.species == SP_VAMPIRE)
3121     {
3122         switch (you.hunger_state)
3123         {
3124         case HS_STARVING:
3125             species_stealth_mod += 3;
3126             break;
3127
3128         case HS_NEAR_STARVING:
3129             species_stealth_mod += 2;
3130             break;
3131
3132         case HS_VERY_HUNGRY:
3133         case HS_HUNGRY:
3134             species_stealth_mod += 1;
3135             break;
3136         default:
3137             break;
3138         }
3139     }
3140     return species_stealth_mod;
3141 }
3142
3143 /**
3144  * Get the player's current stealth value.
3145  *
3146  * XXX: rename this to something more reasonable
3147
3148  *
3149  * (Keep in mind, while tweaking this function: the order in which stealth
3150  * modifiers are applied is significant!)
3151  *
3152  * @return  The player's current stealth value.
3153  */
3154 int check_stealth()
3155 {
3156     ASSERT(!crawl_state.game_is_arena());
3157     // Extreme stealthiness can be enforced by wizmode stealth setting.
3158     if (crawl_state.disables[DIS_MON_SIGHT])
3159         return 1000;
3160
3161     // lantern of shadows, berserking, "clumsy" (0-dex).
3162     if (you.attribute[ATTR_SHADOWS] || you.berserk()
3163         || you.stat_zero[STAT_DEX] || player_mutation_level(MUT_NO_STEALTH))
3164     {
3165         return 0;
3166     }
3167
3168     int stealth = you.dex() * 3;
3169
3170     if (you.form == TRAN_BLADE_HANDS && you.species == SP_FELID
3171         && !you.airborne())
3172     {
3173         stealth -= 50; // klack klack klack go the blade paws
3174         // this is an absurd special case but also it's really funny so w/e
3175     }
3176
3177     stealth += you.skill(SK_STEALTH, _stealth_mod());
3178
3179     if (you.confused())
3180         stealth /= 3;
3181
3182     const item_def *arm = you.slot_item(EQ_BODY_ARMOUR, false);
3183     const item_def *boots = you.slot_item(EQ_BOOTS, false);
3184
3185     if (arm)
3186     {
3187         // [ds] New stealth penalty formula from rob: SP = 6 * (EP^2)
3188         // Now 2 * EP^2 / 3 after EP rescaling.
3189         const int evp = you.unadjusted_body_armour_penalty();
3190         const int penalty = evp * evp * 2 / 3;
3191 #if 0
3192         dprf("Stealth penalty for armour (ep: %d): %d", ep, penalty);
3193 #endif
3194         stealth -= penalty;
3195     }
3196
3197     stealth += STEALTH_PIP * you.scan_artefacts(ARTP_STEALTH);
3198
3199     stealth += STEALTH_PIP * you.wearing(EQ_RINGS, RING_STEALTH);
3200     stealth -= STEALTH_PIP * you.wearing(EQ_RINGS, RING_LOUDNESS);
3201
3202     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
3203     if (body_armour)
3204     {
3205         const int pips = armour_type_prop(body_armour->sub_type, ARMF_STEALTH);
3206         stealth += pips * STEALTH_PIP;
3207     }
3208
3209     if (you.duration[DUR_STEALTH])
3210         stealth += STEALTH_PIP * 2;
3211
3212     if (you.duration[DUR_AGILITY])
3213         stealth += STEALTH_PIP;
3214
3215     if (!you.airborne())
3216     {
3217         if (you.in_water())
3218         {
3219             // Merfolk can sneak up on monsters underwater -- bwr
3220             if (you.fishtail || you.species == SP_OCTOPODE)
3221                 stealth += STEALTH_PIP;
3222             else if (!you.can_swim() && !you.extra_balanced())
3223                 stealth /= 2;       // splashy-splashy
3224         }
3225
3226         else if (boots && get_armour_ego_type(*boots) == SPARM_STEALTH)
3227             stealth += STEALTH_PIP;
3228
3229         else if (you.has_usable_hooves())
3230             stealth -= 5 + 5 * player_mutation_level(MUT_HOOVES);
3231
3232         else if (you.species == SP_FELID && (!you.form || you.form == TRAN_APPENDAGE))
3233             stealth += 20;  // paws
3234     }
3235
3236     // Radiating silence is the negative complement of shouting all the
3237     // time... a sudden change from background noise to no noise is going
3238     // to clue anything in to the fact that something is very wrong...
3239     // a personal silence spell would naturally be different, but this
3240     // silence radiates for a distance and prevents monster spellcasting,
3241     // which pretty much gives away the stealth game.
3242     // this penalty is dependent on the actual amount of ambient noise
3243     // in the level -doy
3244     if (you.duration[DUR_SILENCE])
3245         stealth -= STEALTH_PIP + current_level_ambient_noise();
3246
3247     // Mutations.
3248     stealth += STEALTH_PIP * player_mutation_level(MUT_NIGHTSTALKER);
3249     stealth += (STEALTH_PIP / 2)
3250                 * player_mutation_level(MUT_THIN_SKELETAL_STRUCTURE);
3251     stealth += STEALTH_PIP * player_mutation_level(MUT_CAMOUFLAGE);
3252     const int how_transparent = player_mutation_level(MUT_TRANSLUCENT_SKIN);
3253     if (how_transparent)
3254         stealth += 15 * (how_transparent);
3255
3256     // it's easier to be stealthy when there's a lot of background noise
3257     stealth += 2 * current_level_ambient_noise();
3258
3259     // If you've been tagged with Corona or are Glowing, the glow
3260     // makes you extremely unstealthy.
3261     if (you.backlit())
3262         stealth = stealth * 2 / 5;
3263
3264     // On the other hand, shrouding has the reverse effect, if you know
3265     // how to make use of it:
3266     if (you.umbra())
3267     {
3268         int umbra_mul = 1, umbra_div = 1;
3269         if (you_worship(GOD_DITHMENOS) || you_worship(GOD_YREDELEMNUL))
3270         {
3271             umbra_mul = you.piety + MAX_PIETY;
3272             umbra_div = MAX_PIETY;
3273         }
3274         if (player_equip_unrand(UNRAND_SHADOWS))
3275             if (2 * umbra_mul < 3 * umbra_div)
3276             {
3277                 umbra_mul = 3;
3278                 umbra_div = 2;
3279             }
3280         stealth *= umbra_mul;
3281         stealth /= umbra_div;
3282     }
3283
3284     // If you're surrounded by a storm, you're inherently pretty conspicuous.
3285     if (in_good_standing(GOD_QAZLAL, 0))
3286     {
3287         stealth = stealth
3288                   * (MAX_PIETY - min((int)you.piety, piety_breakpoint(5)))
3289                   / (MAX_PIETY - piety_breakpoint(0));
3290     }
3291     // The shifting glow from the Orb, while too unstable to negate invis
3292     // or affect to-hit, affects stealth even more than regular glow.
3293     if (orb_haloed(you.pos()))
3294         stealth /= 3;
3295
3296     stealth = max(0, stealth);
3297
3298     return stealth;
3299 }
3300
3301 // Returns the medium duration value which is usually announced by a special
3302 // message ("XY is about to time out") or a change of colour in the
3303 // status display.
3304 // Note that these values cannot be relied on when playing since there are
3305 // random decrements precisely to avoid this.
3306 int get_expiration_threshold(duration_type dur)
3307 {
3308     switch (dur)
3309     {
3310     case DUR_PETRIFYING:
3311         return 1 * BASELINE_DELAY;
3312
3313     case DUR_QUAD_DAMAGE:
3314         return 3 * BASELINE_DELAY; // per client.qc
3315
3316     case DUR_FIRE_SHIELD:
3317     case DUR_SILENCE: // no message
3318         return 5 * BASELINE_DELAY;
3319
3320     case DUR_REGENERATION:
3321     case DUR_RESISTANCE:
3322     case DUR_SWIFTNESS:
3323     case DUR_INVIS:
3324     case DUR_HASTE:
3325     case DUR_BERSERK:
3326     case DUR_ICY_ARMOUR:
3327     case DUR_CONDENSATION_SHIELD:
3328     case DUR_PHASE_SHIFT:
3329     case DUR_CONTROL_TELEPORT:
3330     case DUR_DEATH_CHANNEL:
3331     case DUR_SHROUD_OF_GOLUBRIA:
3332     case DUR_INFUSION:
3333     case DUR_SONG_OF_SLAYING:
3334     case DUR_TROGS_HAND:
3335     case DUR_QAZLAL_FIRE_RES:
3336     case DUR_QAZLAL_COLD_RES:
3337     case DUR_QAZLAL_ELEC_RES:
3338     case DUR_QAZLAL_AC:
3339         return 6 * BASELINE_DELAY;
3340
3341     case DUR_FLIGHT:
3342     case DUR_TRANSFORMATION: // not on status
3343     case DUR_DEATHS_DOOR:    // not on status
3344     case DUR_SLIMIFY:
3345         return 10 * BASELINE_DELAY;
3346
3347     // These get no messages when they "flicker".
3348     case DUR_CONFUSING_TOUCH:
3349         return 20 * BASELINE_DELAY;
3350
3351     case DUR_ANTIMAGIC:
3352         return you.hp_max; // not so severe anymore
3353
3354     default:
3355         return 0;
3356     }
3357 }
3358
3359 // Is a given duration about to expire?
3360 bool dur_expiring(duration_type dur)
3361 {
3362     const int value = you.duration[dur];
3363     if (value <= 0)
3364         return false;
3365
3366     return value <= get_expiration_threshold(dur);
3367 }
3368
3369 static void _output_expiring_message(duration_type dur, const char* msg)
3370 {
3371     if (you.duration[dur])
3372     {
3373         const bool expires = dur_expiring(dur);
3374         mprf("%s%s", expires ? "Expiring: " : "", msg);
3375     }
3376 }
3377
3378 static void _display_char_status(int value, const char *fmt, ...)
3379 {
3380     va_list argp;
3381     va_start(argp, fmt);
3382
3383     string msg = vmake_stringf(fmt, argp);
3384
3385     if (you.wizard)
3386         mprf("%s (%d).", msg.c_str(), value);
3387     else
3388         mprf("%s.", msg.c_str());
3389
3390     va_end(argp);
3391 }
3392
3393 static void _display_vampire_status()
3394 {
3395     string msg = "At your current hunger state you ";
3396     vector<const char *> attrib;
3397
3398     switch (you.hunger_state)
3399     {
3400         case HS_STARVING:
3401             attrib.push_back("are immune to poison");
3402             attrib.push_back("significantly resist cold");
3403             attrib.push_back("strongly resist negative energy");
3404             attrib.push_back("resist torment");
3405             attrib.push_back("do not heal.");
3406             break;
3407         case HS_NEAR_STARVING:
3408             attrib.push_back("resist poison");
3409             attrib.push_back("significantly resist cold");
3410             attrib.push_back("strongly resist negative energy");
3411             attrib.push_back("have an extremely slow metabolism");
3412             attrib.push_back("heal slowly.");
3413             break;
3414         case HS_VERY_HUNGRY:
3415         case HS_HUNGRY:
3416             attrib.push_back("resist poison");
3417             attrib.push_back("resist cold");
3418             attrib.push_back("significantly resist negative energy");
3419             if (you.hunger_state == HS_HUNGRY)
3420                 attrib.push_back("have a slow metabolism");
3421             else
3422                 attrib.push_back("have a very slow metabolism");
3423             attrib.push_back("heal slowly.");
3424             break;
3425         case HS_SATIATED:
3426             attrib.push_back("resist negative energy.");
3427             break;
3428         case HS_FULL:
3429             attrib.push_back("have a fast metabolism");
3430             attrib.push_back("heal quickly.");
3431             break;
3432         case HS_VERY_FULL:
3433             attrib.push_back("have a very fast metabolism");
3434             attrib.push_back("heal quickly.");
3435             break;
3436         case HS_ENGORGED:
3437             attrib.push_back("have an extremely fast metabolism");
3438             attrib.push_back("heal extremely quickly.");
3439             break;
3440     }
3441
3442     if (!attrib.empty())
3443     {
3444         msg += comma_separated_line(attrib.begin(), attrib.end());
3445         mpr(msg);
3446     }
3447 }
3448
3449 static void _display_movement_speed()
3450 {
3451     const int move_cost = (player_speed() * player_movement_speed()) / 10;
3452
3453     const bool water  = you.in_liquid();
3454     const bool swim   = you.swimming();
3455
3456     const bool fly    = you.flight_mode();
3457     const bool swift  = (you.duration[DUR_SWIFTNESS] > 0
3458                          && you.attribute[ATTR_SWIFTNESS] >= 0);
3459     const bool antiswift = (you.duration[DUR_SWIFTNESS] > 0
3460                             && you.attribute[ATTR_SWIFTNESS] < 0);
3461
3462     _display_char_status(move_cost, "Your %s speed is %s%s%s",
3463           // order is important for these:
3464           (swim)    ? "swimming" :
3465           (water)   ? "wading" :
3466           (fly)     ? "flying"
3467                     : "movement",
3468
3469           (water && !swim)  ? "uncertain and " :
3470           (!water && swift) ? "aided by the wind" :
3471           (!water && antiswift) ? "hindered by the wind" : "",
3472
3473           (!water && swift) ? ((move_cost >= 10) ? ", but still "
3474                                                  : " and ") :
3475           (!water && antiswift) ? ((move_cost <= 10) ? ", but still "
3476                                                      : " and ")
3477                             : "",
3478
3479           (move_cost <   8) ? "very quick" :
3480           (move_cost <  10) ? "quick" :
3481           (move_cost == 10) ? "average" :
3482           (move_cost <  13) ? "slow"
3483                             : "very slow");
3484 }
3485
3486 static void _display_tohit()
3487 {
3488 #ifdef DEBUG_DIAGNOSTICS
3489     melee_attack attk(&you, nullptr);
3490
3491     const int to_hit = attk.calc_to_hit(false);
3492
3493     dprf("To-hit: %d", to_hit);
3494 #endif
3495 }
3496
3497 static const char* _attack_delay_desc(int attack_delay)
3498 {
3499     return (attack_delay >= 200) ? "extremely slow" :
3500            (attack_delay >= 155) ? "very slow" :
3501            (attack_delay >= 125) ? "quite slow" :
3502            (attack_delay >= 105) ? "below average" :
3503            (attack_delay >=  95) ? "average" :
3504            (attack_delay >=  75) ? "above average" :
3505            (attack_delay >=  55) ? "quite fast" :
3506            (attack_delay >=  45) ? "very fast" :
3507            (attack_delay >=  35) ? "extremely fast" :
3508                                    "blindingly fast";
3509 }
3510
3511 /**
3512  * Print a message indicating the player's attack delay with their current
3513  * weapon & quivered ammo (if applicable).
3514  *
3515  * Uses melee attack delay for ranged weapons if no appropriate ammo is
3516  * is quivered, purely for simplicity of implementation; XXX fix this
3517  */
3518 static void _display_attack_delay()
3519 {
3520     const item_def* ammo = nullptr;
3521     you.m_quiver->get_desired_item(&ammo, nullptr);
3522     const bool uses_ammo = ammo && you.weapon()
3523                            && ammo->launched_by(*you.weapon());
3524     const int delay = you.attack_delay(you.weapon(), uses_ammo ? ammo : nullptr,
3525                                        false, false);
3526
3527     const int no_shield_delay = you.attack_delay(you.weapon(),
3528                                                  uses_ammo ? ammo : nullptr,
3529                                                  false, false, false);
3530     const bool at_min_delay = you.weapon()
3531                               && weapon_min_delay(*you.weapon())
3532                                  == no_shield_delay;
3533
3534     // Scale to fit the displayed weapon base delay, i.e.,
3535     // normal speed is 100 (as in 100%).
3536     int avg = 10 * delay;
3537
3538     // Haste shouldn't be counted, but let's show finesse.
3539     if (you.duration[DUR_FINESSE])
3540         avg = max(20, avg / 2);
3541
3542     _display_char_status(avg, "Your attack speed is %s%s",
3543                          _attack_delay_desc(avg),
3544                          at_min_delay ?
3545                             " (and cannot be increased with skill)" : "");
3546 }
3547
3548 // forward declaration
3549 static string _constriction_description();
3550
3551 void display_char_status()
3552 {
3553     if (you.undead_state() == US_SEMI_UNDEAD
3554         && you.hunger_state == HS_ENGORGED)
3555     {
3556         mpr("You feel almost alive.");
3557     }
3558     else if (you.undead_state())
3559         mpr("You are undead.");
3560     else if (you.duration[DUR_DEATHS_DOOR])
3561     {
3562         _output_expiring_message(DUR_DEATHS_DOOR,
3563                                  "You are standing in death's doorway.");
3564     }
3565     else
3566         mpr("You are alive.");
3567
3568     const int halo_size = you.halo_radius2();
3569     if (halo_size >= 0)
3570     {
3571         if (halo_size > 37)
3572             mpr("You are illuminated by a large divine halo.");
3573         else if (halo_size > 10)
3574             mpr("You are illuminated by a divine halo.");
3575         else
3576             mpr("You are illuminated by a small divine halo.");
3577     }
3578     else if (you.haloed())
3579         mpr("An external divine halo illuminates you.");
3580
3581     if (you.species == SP_VAMPIRE)
3582         _display_vampire_status();
3583
3584     // A hard-coded duration/status list used to be used here. This list is no
3585     // longer hard-coded. May 2014. -reaverb
3586     status_info inf;
3587     for (unsigned i = 0; i <= STATUS_LAST_STATUS; ++i)
3588     {
3589         if (fill_status_info(i, &inf) && !inf.long_text.empty())