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