Add two new Ru sacrifices: resistance and eye
[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_TEMPERATURE_SENSITIVITY, temp);
1400     rf += player_mutation_level(MUT_MOLTEN_SCALES, temp) == 3 ? 1 : 0;
1401
1402     // spells:
1403     if (temp)
1404     {
1405         if (you.duration[DUR_RESISTANCE])
1406             rf++;
1407
1408         if (you.duration[DUR_FIRE_SHIELD])
1409             rf += 2;
1410
1411         if (you.duration[DUR_QAZLAL_FIRE_RES])
1412             rf++;
1413
1414         rf += get_form()->res_fire();
1415     }
1416
1417     if (rf > 3)
1418         rf = 3;
1419     if (temp && you.duration[DUR_FIRE_VULN])
1420         rf--;
1421     if (rf < -3)
1422         rf = -3;
1423
1424     return rf;
1425 }
1426
1427 int player_res_steam(bool calc_unid, bool temp, bool items)
1428 {
1429     int res = 0;
1430     const int rf = player_res_fire(calc_unid, temp, items);
1431
1432     if (you.species == SP_PALE_DRACONIAN)
1433         res += 2;
1434
1435     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1436     if (body_armour)
1437         res += armour_type_prop(body_armour->sub_type, ARMF_RES_STEAM) * 2;
1438
1439     res += (rf < 0) ? rf
1440                     : (rf + 1) / 2;
1441
1442     if (res > 3)
1443         res = 3;
1444
1445     return res;
1446 }
1447
1448 int player_res_cold(bool calc_unid, bool temp, bool items)
1449 {
1450     int rc = 0;
1451
1452     if (temp)
1453     {
1454         if (you.duration[DUR_RESISTANCE])
1455             rc++;
1456
1457         if (you.duration[DUR_FIRE_SHIELD])
1458             rc -= 2;
1459
1460         if (you.duration[DUR_QAZLAL_COLD_RES])
1461             rc++;
1462
1463         rc += get_form()->res_cold();
1464
1465         if (you.species == SP_VAMPIRE)
1466         {
1467             if (you.hunger_state <= HS_NEAR_STARVING)
1468                 rc += 2;
1469             else if (you.hunger_state < HS_SATIATED)
1470                 rc++;
1471         }
1472
1473 #if TAG_MAJOR_VERSION == 34
1474         if (you.species == SP_LAVA_ORC && temperature_effect(LORC_COLD_VULN))
1475             rc--;
1476 #endif
1477     }
1478
1479     if (items)
1480     {
1481         // rings of cold resistance/ice
1482         rc += you.wearing(EQ_RINGS, RING_PROTECTION_FROM_COLD, calc_unid);
1483         rc += you.wearing(EQ_RINGS, RING_ICE, calc_unid);
1484
1485         // rings of fire
1486         rc -= you.wearing(EQ_RINGS, RING_FIRE, calc_unid);
1487
1488         // Staves
1489         rc += you.wearing(EQ_STAFF, STAFF_COLD, calc_unid);
1490
1491         // body armour:
1492         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1493         if (body_armour)
1494             rc += armour_type_prop(body_armour->sub_type, ARMF_RES_COLD);
1495
1496         // ego armours
1497         rc += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_COLD_RESISTANCE);
1498         rc += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_RESISTANCE);
1499
1500         // randart weapons:
1501         rc += you.scan_artefacts(ARTP_COLD, calc_unid);
1502
1503         // dragonskin cloak: 0.5 to draconic resistances
1504         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1505             rc++;
1506     }
1507
1508 #if TAG_MAJOR_VERSION == 34
1509     // species:
1510     if (you.species == SP_DJINNI)
1511         rc--;
1512 #endif
1513     // mutations:
1514     rc += player_mutation_level(MUT_COLD_RESISTANCE, temp);
1515     rc -= player_mutation_level(MUT_COLD_VULNERABILITY, temp);
1516     rc -= player_mutation_level(MUT_TEMPERATURE_SENSITIVITY, temp);
1517     rc += player_mutation_level(MUT_ICY_BLUE_SCALES, temp) == 3 ? 1 : 0;
1518     rc += player_mutation_level(MUT_SHAGGY_FUR, temp) == 3 ? 1 : 0;
1519
1520     if (rc < -3)
1521         rc = -3;
1522     else if (rc > 3)
1523         rc = 3;
1524
1525     return rc;
1526 }
1527
1528 bool player::res_corr(bool calc_unid, bool items) const
1529 {
1530     if (religion == GOD_JIYVA && piety >= piety_breakpoint(2))
1531         return true;
1532
1533     if (get_form()->res_acid())
1534         return true;
1535
1536     if (you.duration[DUR_RESISTANCE])
1537         return true;
1538
1539     if (items)
1540     {
1541         // dragonskin cloak: 0.5 to draconic resistances
1542         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN)
1543             && coinflip())
1544         {
1545             return true;
1546         }
1547     }
1548
1549     if ((form_keeps_mutations() || form == TRAN_DRAGON)
1550         && species == SP_YELLOW_DRACONIAN)
1551     {
1552         return true;
1553     }
1554
1555     if (form_keeps_mutations()
1556         && player_mutation_level(MUT_YELLOW_SCALES) >= 3)
1557     {
1558         return true;
1559     }
1560
1561     return actor::res_corr(calc_unid, items);
1562 }
1563
1564 int player_res_acid(bool calc_unid, bool items)
1565 {
1566     return you.res_corr(calc_unid, items) ? 1 : 0;
1567 }
1568
1569 int player_res_electricity(bool calc_unid, bool temp, bool items)
1570 {
1571     int re = 0;
1572
1573     if (items)
1574     {
1575         // staff
1576         re += you.wearing(EQ_STAFF, STAFF_AIR, calc_unid);
1577
1578         // body armour:
1579         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1580         if (body_armour)
1581             re += armour_type_prop(body_armour->sub_type, ARMF_RES_ELEC);
1582
1583         // randart weapons:
1584         re += you.scan_artefacts(ARTP_ELECTRICITY, calc_unid);
1585
1586         // dragonskin cloak: 0.5 to draconic resistances
1587         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1588             re++;
1589     }
1590
1591     // mutations:
1592     re += player_mutation_level(MUT_THIN_METALLIC_SCALES, temp) == 3 ? 1 : 0;
1593     re += player_mutation_level(MUT_SHOCK_RESISTANCE, temp);
1594     re -= player_mutation_level(MUT_SHOCK_VULNERABILITY, temp);
1595
1596     if (temp)
1597     {
1598         if (you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION])
1599             return 3;
1600
1601         if (you.duration[DUR_RESISTANCE])
1602             re++;
1603
1604         if (you.duration[DUR_QAZLAL_ELEC_RES])
1605             re++;
1606
1607         // transformations:
1608         if (get_form()->res_elec())
1609             re++;
1610     }
1611
1612     if (re > 1)
1613         re = 1;
1614
1615     return re;
1616 }
1617
1618 bool player_control_teleport(bool temp)
1619 {
1620     return (temp && you.duration[DUR_CONTROL_TELEPORT])
1621            || crawl_state.game_is_zotdef();
1622 }
1623
1624 /**
1625  * Is the player character immune to torment?
1626  *
1627  * @param random    Whether to include unreliable effects (stochastic resist)
1628  * @return          Whether the player resists a given instance of torment; if
1629  *                  random is passed, the result may vary from call to call.
1630  */
1631 bool player_res_torment(bool random)
1632 {
1633     if (player_mutation_level(MUT_TORMENT_RESISTANCE))
1634         return true;
1635
1636     if (random
1637         && player_mutation_level(MUT_STOCHASTIC_TORMENT_RESISTANCE)
1638         && coinflip())
1639     {
1640         return true;
1641     }
1642
1643     return get_form()->res_neg() == 3
1644            || you.species == SP_VAMPIRE && you.hunger_state == HS_STARVING
1645            || you.petrified()
1646            || player_equip_unrand(UNRAND_ETERNAL_TORMENT);
1647 }
1648
1649 // Kiku protects you from torment to a degree.
1650 bool player_kiku_res_torment()
1651 {
1652     return in_good_standing(GOD_KIKUBAAQUDGHA, 3)
1653            && !you.gift_timeout; // no protection during pain branding weapon
1654 }
1655
1656 // If temp is set to false, temporary sources or resistance won't be counted.
1657 int player_res_poison(bool calc_unid, bool temp, bool items)
1658 {
1659     switch (you.undead_state(temp))
1660     {
1661         case US_ALIVE:
1662             break;
1663         case US_HUNGRY_DEAD: //ghouls
1664         case US_UNDEAD: // mummies & lichform
1665             return 3;
1666         case US_SEMI_UNDEAD: // vampire
1667             if (you.hunger_state == HS_STARVING) // XXX: && temp?
1668                 return 3;
1669             break;
1670     }
1671
1672     if (you.is_artificial(temp)
1673         || temp && get_form()->res_pois() == 3
1674         || items && player_equip_unrand(UNRAND_OLGREB)
1675         || temp && you.duration[DUR_DIVINE_STAMINA])
1676     {
1677         return 3;
1678     }
1679
1680     int rp = 0;
1681
1682     if (items)
1683     {
1684         // rings of poison resistance
1685         rp += you.wearing(EQ_RINGS, RING_POISON_RESISTANCE, calc_unid);
1686
1687         // Staves
1688         rp += you.wearing(EQ_STAFF, STAFF_POISON, calc_unid);
1689
1690         // ego armour:
1691         rp += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_POISON_RESISTANCE);
1692
1693         // body armour:
1694         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1695         if (body_armour)
1696             rp += armour_type_prop(body_armour->sub_type, ARMF_RES_POISON);
1697
1698         // rPois+ artefacts
1699         rp += you.scan_artefacts(ARTP_POISON, calc_unid);
1700
1701         // dragonskin cloak: 0.5 to draconic resistances
1702         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1703             rp++;
1704     }
1705
1706     // mutations:
1707     rp += player_mutation_level(MUT_POISON_RESISTANCE, temp);
1708     rp += player_mutation_level(MUT_SLIMY_GREEN_SCALES, temp) == 3 ? 1 : 0;
1709
1710     // Only thirsty vampires are naturally poison resistant.
1711     // XXX: && temp?
1712     if (you.species == SP_VAMPIRE && you.hunger_state < HS_SATIATED)
1713         rp++;
1714
1715     if (temp)
1716     {
1717         // potions/cards:
1718         if (you.duration[DUR_RESISTANCE])
1719             rp++;
1720
1721         if (get_form()->res_pois() > 0)
1722             rp++;
1723     }
1724
1725     // Cap rPois at + before vulnerability effects are applied
1726     // (so carrying multiple rPois effects is never useful)
1727     rp = min(1, rp);
1728
1729     if (temp)
1730     {
1731         if (get_form()->res_pois() < 0)
1732             rp--;
1733
1734         if (you.duration[DUR_POISON_VULN])
1735             rp--;
1736     }
1737
1738     // don't allow rPois--, etc.
1739     rp = max(-1, rp);
1740
1741     return rp;
1742 }
1743
1744 int player_res_sticky_flame(bool calc_unid, bool temp, bool items)
1745 {
1746     int rsf = 0;
1747
1748     if (you.species == SP_MOTTLED_DRACONIAN)
1749         rsf++;
1750
1751     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1752     if (body_armour)
1753         rsf += armour_type_prop(body_armour->sub_type, ARMF_RES_STICKY_FLAME);
1754
1755     // dragonskin cloak: 0.5 to draconic resistances
1756     if (items && calc_unid
1757         && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1758     {
1759         rsf++;
1760     }
1761
1762     if (get_form()->res_sticky_flame())
1763         rsf++;
1764
1765     if (rsf > 1)
1766         rsf = 1;
1767
1768     return rsf;
1769 }
1770
1771 int player_spec_death()
1772 {
1773     int sd = 0;
1774
1775     // Staves
1776     sd += you.wearing(EQ_STAFF, STAFF_DEATH);
1777
1778     // species:
1779     sd += player_mutation_level(MUT_NECRO_ENHANCER);
1780
1781     // transformations:
1782     if (you.form == TRAN_LICH)
1783         sd++;
1784
1785     return sd;
1786 }
1787
1788 int player_spec_fire()
1789 {
1790     int sf = 0;
1791
1792     // staves:
1793     sf += you.wearing(EQ_STAFF, STAFF_FIRE);
1794
1795     // rings of fire:
1796     sf += you.wearing(EQ_RINGS, RING_FIRE);
1797
1798 #if TAG_MAJOR_VERSION == 34
1799     if (you.species == SP_LAVA_ORC && temperature_effect(LORC_FIRE_BOOST))
1800         sf++;
1801 #endif
1802
1803     if (you.duration[DUR_FIRE_SHIELD])
1804         sf++;
1805
1806     return sf;
1807 }
1808
1809 int player_spec_cold()
1810 {
1811     int sc = 0;
1812
1813     // staves:
1814     sc += you.wearing(EQ_STAFF, STAFF_COLD);
1815
1816     // rings of ice:
1817     sc += you.wearing(EQ_RINGS, RING_ICE);
1818
1819 #if TAG_MAJOR_VERSION == 34
1820     if (you.species == SP_LAVA_ORC
1821         && (temperature_effect(LORC_LAVA_BOOST)
1822             || temperature_effect(LORC_FIRE_BOOST)))
1823     {
1824         sc--;
1825     }
1826 #endif
1827
1828     return sc;
1829 }
1830
1831 int player_spec_earth()
1832 {
1833     int se = 0;
1834
1835     // Staves
1836     se += you.wearing(EQ_STAFF, STAFF_EARTH);
1837
1838     return se;
1839 }
1840
1841 int player_spec_air()
1842 {
1843     int sa = 0;
1844
1845     // Staves
1846     sa += you.wearing(EQ_STAFF, STAFF_AIR);
1847
1848     return sa;
1849 }
1850
1851 int player_spec_conj()
1852 {
1853     int sc = 0;
1854
1855     // Staves
1856     sc += you.wearing(EQ_STAFF, STAFF_CONJURATION);
1857
1858     return sc;
1859 }
1860
1861 int player_spec_hex()
1862 {
1863     return 0;
1864 }
1865
1866 int player_spec_charm()
1867 {
1868     // Nothing, for the moment.
1869     return 0;
1870 }
1871
1872 int player_spec_summ()
1873 {
1874     int ss = 0;
1875
1876     // Staves
1877     ss += you.wearing(EQ_STAFF, STAFF_SUMMONING);
1878
1879     return ss;
1880 }
1881
1882 int player_spec_poison()
1883 {
1884     int sp = 0;
1885
1886     // Staves
1887     sp += you.wearing(EQ_STAFF, STAFF_POISON);
1888
1889     if (player_equip_unrand(UNRAND_OLGREB))
1890         sp++;
1891
1892     return sp;
1893 }
1894
1895 int player_energy()
1896 {
1897     int pe = 0;
1898
1899     // Staves
1900     pe += you.wearing(EQ_STAFF, STAFF_ENERGY);
1901
1902     return pe;
1903 }
1904
1905 // If temp is set to false, temporary sources of resistance won't be
1906 // counted.
1907 int player_prot_life(bool calc_unid, bool temp, bool items)
1908 {
1909     int pl = 0;
1910
1911     // Hunger is temporary, true, but that's something you can control,
1912     // especially as life protection only increases the hungrier you
1913     // get.
1914     if (you.species == SP_VAMPIRE)
1915     {
1916         switch (you.hunger_state)
1917         {
1918         case HS_STARVING:
1919         case HS_NEAR_STARVING:
1920             pl = 3;
1921             break;
1922         case HS_VERY_HUNGRY:
1923         case HS_HUNGRY:
1924             pl = 2;
1925             break;
1926         case HS_SATIATED:
1927             pl = 1;
1928             break;
1929         default:
1930             break;
1931         }
1932     }
1933
1934     // Same here. Your piety status, and, hence, TSO's protection, is
1935     // something you can more or less control.
1936     if (you_worship(GOD_SHINING_ONE))
1937     {
1938         if (you.piety >= piety_breakpoint(1))
1939             pl++;
1940         if (you.piety >= piety_breakpoint(3))
1941             pl++;
1942         if (you.piety >= piety_breakpoint(5))
1943             pl++;
1944     }
1945
1946     if (temp)
1947     {
1948         pl += get_form()->res_neg();
1949
1950         // completely stoned, unlike statue which has some life force
1951         if (you.petrified())
1952             pl += 3;
1953     }
1954
1955     if (items)
1956     {
1957         if (you.wearing(EQ_AMULET, AMU_WARDING, calc_unid))
1958             pl++;
1959
1960         // rings
1961         pl += you.wearing(EQ_RINGS, RING_LIFE_PROTECTION, calc_unid);
1962
1963         // armour (checks body armour only)
1964         pl += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_POSITIVE_ENERGY);
1965
1966         // pearl dragon counts
1967         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1968         if (body_armour)
1969             pl += armour_type_prop(body_armour->sub_type, ARMF_RES_NEG);
1970
1971         // randart wpns
1972         pl += you.scan_artefacts(ARTP_NEGATIVE_ENERGY, calc_unid);
1973
1974         // dragonskin cloak: 0.5 to draconic resistances
1975         // this one is dubious (no pearl draconians)
1976         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1977             pl++;
1978
1979         pl += you.wearing(EQ_STAFF, STAFF_DEATH, calc_unid);
1980     }
1981
1982     // undead/demonic power
1983     pl += player_mutation_level(MUT_NEGATIVE_ENERGY_RESISTANCE, temp);
1984
1985     pl = min(3, pl);
1986
1987     return pl;
1988 }
1989
1990 // New player movement speed system... allows for a bit more than
1991 // "player runs fast" and "player walks slow" in that the speed is
1992 // actually calculated (allowing for centaurs to get a bonus from
1993 // swiftness and other such things). Levels of the mutation now
1994 // also have meaning (before they all just meant fast). Most of
1995 // this isn't as fast as it used to be (6 for having anything), but
1996 // even a slight speed advantage is very good... and we certainly don't
1997 // want to go past 6 (see below). -- bwr
1998 int player_movement_speed()
1999 {
2000     int mv = 10;
2001
2002     // transformations
2003     if (you.form == TRAN_BAT)
2004         mv = 5; // but allowed minimum is six
2005     else if (you.form == TRAN_PIG)
2006         mv = 7;
2007     else if (you.form == TRAN_PORCUPINE || you.form == TRAN_WISP)
2008         mv = 8;
2009     else if (you.fishtail || you.form == TRAN_HYDRA && you.in_water())
2010         mv = 6;
2011
2012     // moving on liquefied ground takes longer
2013     if (you.liquefied_ground())
2014         mv += 3;
2015
2016     // armour
2017     if (you.run())
2018         mv -= 1;
2019
2020     mv += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_PONDEROUSNESS);
2021
2022     // Cheibriados
2023     if (you_worship(GOD_CHEIBRIADOS))
2024         mv += 2 + min(div_rand_round(you.piety, 20), 8);
2025     else if (player_under_penance(GOD_CHEIBRIADOS))
2026         mv += 2 + min(div_rand_round(you.piety_max[GOD_CHEIBRIADOS], 20), 8);
2027
2028     // Tengu can move slightly faster when flying.
2029     if (you.tengu_flight())
2030         mv--;
2031
2032     if (you.duration[DUR_FROZEN])
2033         mv += 4;
2034
2035     if (you.duration[DUR_GRASPING_ROOTS])
2036         mv += 3;
2037
2038     // Mutations: -2, -3, -4, unless innate and shapechanged.
2039     if (int fast = player_mutation_level(MUT_FAST))
2040         mv -= fast + 1;
2041
2042     if (int slow = player_mutation_level(MUT_SLOW))
2043     {
2044         mv *= 10 + slow * 2;
2045         mv /= 10;
2046     }
2047
2048     if (you.duration[DUR_SWIFTNESS] > 0 && !you.in_liquid())
2049     {
2050         if (you.attribute[ATTR_SWIFTNESS] > 0)
2051           mv = div_rand_round(3*mv, 4);
2052         else if (mv >= 8)
2053           mv = div_rand_round(3*mv, 2);
2054         else if (mv == 7)
2055           mv = div_rand_round(7*6, 5); // balance for the cap at 6
2056     }
2057
2058     // We'll use the old value of six as a minimum, with haste this could
2059     // end up as a speed of three, which is about as fast as we want
2060     // the player to be able to go (since 3 is 3.33x as fast and 2 is 5x,
2061     // which is a bit of a jump, and a bit too fast) -- bwr
2062     // Currently Haste takes 6 to 4, which is 2.5x as fast as delay 10
2063     // and still seems plenty fast. -- elliptic
2064     if (mv < FASTEST_PLAYER_MOVE_SPEED)
2065         mv = FASTEST_PLAYER_MOVE_SPEED;
2066
2067     return mv;
2068 }
2069
2070 // This function differs from the above in that it's used to set the
2071 // initial time_taken value for the turn. Everything else (movement,
2072 // spellcasting, combat) applies a ratio to this value.
2073 int player_speed()
2074 {
2075     int ps = 10;
2076
2077     // When paralysed, speed is irrelevant.
2078     if (you.cannot_act())
2079         return ps;
2080
2081     if (you.duration[DUR_SLOW] || have_stat_zero())
2082         ps = haste_mul(ps);
2083
2084     if (you.duration[DUR_BERSERK] && !you_worship(GOD_CHEIBRIADOS))
2085         ps = berserk_div(ps);
2086     else if (you.duration[DUR_HASTE])
2087         ps = haste_div(ps);
2088
2089     if (you.form == TRAN_STATUE || you.duration[DUR_PETRIFYING])
2090     {
2091         ps *= 15;
2092         ps /= 10;
2093     }
2094
2095     return ps;
2096 }
2097
2098 // Get level of player mutation, ignoring mutations with an activity level
2099 // less than minact.
2100 static int _mut_level(mutation_type mut, mutation_activity_type minact)
2101 {
2102     const int mlevel = you.mutation[mut];
2103
2104     const mutation_activity_type active = mutation_activity_level(mut);
2105
2106     if (active >= minact)
2107         return mlevel;
2108
2109     return 0;
2110 }
2111
2112 // Output level of player mutation. If temp is true (the default), take into
2113 // account the suppression of mutations by changes of form.
2114 int player_mutation_level(mutation_type mut, bool temp)
2115 {
2116     return _mut_level(mut, temp ? MUTACT_PARTIAL : MUTACT_INACTIVE);
2117 }
2118
2119 static int _player_armour_beogh_bonus(const item_def& item)
2120 {
2121     if (item.base_type != OBJ_ARMOUR)
2122         return 0;
2123
2124     int bonus = 0;
2125
2126     if (in_good_standing(GOD_BEOGH))
2127     {
2128         if (you.piety >= piety_breakpoint(5))
2129             bonus = 10;
2130         else if (you.piety >= piety_breakpoint(4))
2131             bonus = 8;
2132         else if (you.piety >= piety_breakpoint(2))
2133             bonus = 6;
2134         else if (you.piety >= piety_breakpoint(0))
2135             bonus = 4;
2136         else
2137             bonus = 2;
2138     }
2139
2140     return bonus;
2141 }
2142
2143 bool is_effectively_light_armour(const item_def *item)
2144 {
2145     return !item
2146            || (abs(property(*item, PARM_EVASION)) / 10 < 5);
2147 }
2148
2149 bool player_effectively_in_light_armour()
2150 {
2151     const item_def *armour = you.slot_item(EQ_BODY_ARMOUR, false);
2152     return is_effectively_light_armour(armour);
2153 }
2154
2155 // This function returns true if the player has a radically different
2156 // shape... minor changes like blade hands don't count, also note
2157 // that lich transformation doesn't change the character's shape
2158 // (so we end up with Naga-liches, Spriggan-liches, Minotaur-liches)
2159 // it just makes the character undead (with the benefits that implies). - bwr
2160 bool player_is_shapechanged()
2161 {
2162     if (you.form == TRAN_NONE
2163         || you.form == TRAN_BLADE_HANDS
2164         || you.form == TRAN_LICH
2165         || you.form == TRAN_SHADOW
2166         || you.form == TRAN_APPENDAGE)
2167     {
2168         return false;
2169     }
2170
2171     return true;
2172 }
2173
2174 // An evasion factor based on the player's body size, smaller == higher
2175 // evasion size factor.
2176 static int _player_evasion_size_factor()
2177 {
2178     // XXX: you.body_size() implementations are incomplete, fix.
2179     const size_type size = you.body_size(PSIZE_BODY);
2180     return 2 * (SIZE_MEDIUM - size);
2181 }
2182
2183 // Determines racial shield penalties (formicids get a bonus compared to
2184 // other medium-sized races)
2185 int player_shield_racial_factor()
2186 {
2187     return max(1, 5 + (you.species == SP_FORMICID ? -2 // Same as trolls/centaurs/etc.
2188                                                   : _player_evasion_size_factor()));
2189 }
2190
2191 // The total EV penalty to the player for all their worn armour items
2192 // with a base EV penalty (i.e. EV penalty as a base armour property,
2193 // not as a randart property).
2194 static int _player_adjusted_evasion_penalty(const int scale)
2195 {
2196     int piece_armour_evasion_penalty = 0;
2197
2198     // Some lesser armours have small penalties now (barding).
2199     for (int i = EQ_MIN_ARMOUR; i < EQ_MAX_ARMOUR; i++)
2200     {
2201         if (i == EQ_SHIELD || !you.slot_item(static_cast<equipment_type>(i)))
2202             continue;
2203
2204         // [ds] Evasion modifiers for armour are negatives, change
2205         // those to positive for penalty calc.
2206         const int penalty = (-property(you.inv[you.equip[i]], PARM_EVASION))/3;
2207         if (penalty > 0)
2208             piece_armour_evasion_penalty += penalty;
2209     }
2210
2211     return piece_armour_evasion_penalty * scale / 10 +
2212            you.adjusted_body_armour_penalty(scale) ;
2213 }
2214
2215 // EV bonuses that work even when helpless.
2216 static int _player_para_evasion_bonuses(ev_ignore_type evit)
2217 {
2218     int evbonus = 0;
2219
2220     if (you.duration[DUR_PHASE_SHIFT] && !(evit & EV_IGNORE_PHASESHIFT))
2221         evbonus += 8;
2222
2223     if (player_mutation_level(MUT_DISTORTION_FIELD) > 0)
2224         evbonus += player_mutation_level(MUT_DISTORTION_FIELD) + 1;
2225
2226     return evbonus;
2227 }
2228
2229 // Player EV bonuses for various effects and transformations. This
2230 // does not include tengu/merfolk EV bonuses for flight/swimming.
2231 static int _player_evasion_bonuses(ev_ignore_type evit)
2232 {
2233     int evbonus = _player_para_evasion_bonuses(evit);
2234
2235     if (you.duration[DUR_AGILITY])
2236         evbonus += AGILITY_BONUS;
2237
2238     evbonus += you.wearing(EQ_RINGS_PLUS, RING_EVASION);
2239
2240     if (you.wearing_ego(EQ_WEAPON, SPWPN_EVASION))
2241         evbonus += 5;
2242
2243     evbonus += you.scan_artefacts(ARTP_EVASION);
2244
2245     // mutations
2246     if (_mut_level(MUT_ICY_BLUE_SCALES, MUTACT_FULL) > 1)
2247         evbonus--;
2248     if (_mut_level(MUT_MOLTEN_SCALES, MUTACT_FULL) > 1)
2249         evbonus--;
2250     evbonus += max(0, player_mutation_level(MUT_GELATINOUS_BODY) - 1);
2251
2252     // transformation penalties/bonuses not covered by size alone:
2253     if (player_mutation_level(MUT_SLOW_REFLEXES))
2254         evbonus -= player_mutation_level(MUT_SLOW_REFLEXES) * 3;
2255
2256     return evbonus;
2257 }
2258
2259 // Player EV scaling for being flying tengu or swimming merfolk.
2260 static int _player_scale_evasion(int prescaled_ev, const int scale)
2261 {
2262     if (you.duration[DUR_PETRIFYING] || you.caught())
2263         prescaled_ev /= 2;
2264     else if (you.duration[DUR_GRASPING_ROOTS])
2265         prescaled_ev = prescaled_ev * 2 / 3;
2266
2267     // Merfolk get an evasion bonus in water.
2268     if (you.fishtail)
2269     {
2270         const int ev_bonus = min(9 * scale,
2271                                  max(2 * scale, prescaled_ev / 4));
2272         return prescaled_ev + ev_bonus;
2273     }
2274
2275     // Flying Tengu get an evasion bonus.
2276     if (you.tengu_flight())
2277     {
2278         const int ev_bonus = min(9 * scale,
2279                                  max(1 * scale, prescaled_ev / 5));
2280         return prescaled_ev + ev_bonus;
2281     }
2282
2283     return prescaled_ev;
2284 }
2285
2286 /**
2287  * What is the player's bonus to EV from dodging when not paralyzed, after
2288  * accounting for size & body armour penalties?
2289  *
2290  * First, calculate base dodge bonus (linear with dodging * stepdowned dex),
2291  * and armour dodge penalty (base armour evp, increased for small races &
2292  * decreased for large, then with a magic "3" subtracted from it to make the
2293  * penalties not too harsh).
2294  *
2295  * If the player's strength is greater than the armour dodge penalty, return
2296  *      base dodge * (1 - dodge_pen / (str*2)).
2297  * E.g., if str is twice dodge penalty, return 3/4 of base dodge. If
2298  * str = dodge_pen * 4, return 7/8...
2299  *
2300  * If str is less than dodge penalty, return
2301  *      base_dodge * str / (dodge_pen * 2).
2302  * E.g., if str = dodge_pen / 2, return 1/4 of base dodge. if
2303  * str = dodge_pen / 4, return 1/8...
2304  *
2305  * For either equation, if str = dodge_pen, the result is base_dodge/2.
2306  *
2307  * @param scale     A scale to multiply the result by, to avoid precision loss.
2308  * @return          A bonus to EV, multiplied by the scale.
2309  */
2310 static int _player_armour_adjusted_dodge_bonus(int scale)
2311 {
2312     // stepdowns at 10 and 24 dex; the last two parameters are not important.
2313     const int ev_dex = stepdown_value(you.dex(), 10, 24, 72, 72);
2314
2315     const int dodge_bonus =
2316         (70 + you.skill(SK_DODGING, 10) * ev_dex) * scale
2317         / (20 - _player_evasion_size_factor()) / 10;
2318
2319     const int armour_dodge_penalty = you.unadjusted_body_armour_penalty() - 3;
2320     if (armour_dodge_penalty <= 0)
2321         return dodge_bonus;
2322
2323     const int str = max(1, you.strength());
2324     if (armour_dodge_penalty >= str)
2325         return dodge_bonus * str / (armour_dodge_penalty * 2);
2326     return dodge_bonus - dodge_bonus * armour_dodge_penalty / (str * 2);
2327 }
2328
2329 // Total EV for player using the revised 0.6 evasion model.
2330 static int _player_evasion(ev_ignore_type evit)
2331 {
2332     const int size_factor = _player_evasion_size_factor();
2333     // Repulsion fields and size are all that matters when paralysed or
2334     // at 0 dex.
2335     if ((you.cannot_move() || you.duration[DUR_CLUMSY] || you.form == TRAN_TREE)
2336         && !(evit & EV_IGNORE_HELPLESS))
2337     {
2338         const int paralysed_base_ev = 2 + size_factor / 2;
2339         const int repulsion_ev = _player_para_evasion_bonuses(evit);
2340         return max(1, paralysed_base_ev + repulsion_ev);
2341     }
2342
2343     const int scale = 100;
2344     const int size_base_ev = (10 + size_factor) * scale;
2345
2346     const int prestepdown_evasion =
2347         size_base_ev
2348         + _player_armour_adjusted_dodge_bonus(scale)
2349         - _player_adjusted_evasion_penalty(scale)
2350         - you.adjusted_shield_penalty(scale);
2351
2352     const int poststepdown_evasion =
2353         stepdown_value(prestepdown_evasion, 20*scale, 30*scale, 60*scale, -1);
2354
2355     const int evasion_bonuses = _player_evasion_bonuses(evit) * scale;
2356
2357     const int prescaled_evasion =
2358         poststepdown_evasion + evasion_bonuses;
2359
2360     const int final_evasion =
2361         _player_scale_evasion(prescaled_evasion, scale);
2362
2363     return unscale_round_up(final_evasion, scale);
2364 }
2365
2366 // Returns the spellcasting penalty (increase in spell failure) for the
2367 // player's worn body armour and shield.
2368 int player_armour_shield_spell_penalty()
2369 {
2370     const int scale = 100;
2371
2372     const int body_armour_penalty =
2373         max(19 * you.adjusted_body_armour_penalty(scale), 0);
2374
2375     const int total_penalty = body_armour_penalty
2376                  + 19 * you.adjusted_shield_penalty(scale);
2377
2378     return max(total_penalty, 0) / scale;
2379 }
2380
2381 /**
2382  * How many spell-success-chance-boosting ('wizardry') effects can the player
2383  * apply to the given spell?
2384  *
2385  * @param spell     The type of spell being cast.
2386  * @return          The number of relevant wizardry effects.
2387  */
2388 int player_wizardry(spell_type spell)
2389 {
2390     return you.wearing(EQ_RINGS, RING_WIZARDRY)
2391            + you.wearing(EQ_STAFF, STAFF_WIZARDRY);
2392 }
2393
2394 /**
2395  * Calculate the SH value used internally.
2396  *
2397  * Exactly twice the value displayed to players, for legacy reasons.
2398  * @return      The player's current SH value.
2399  */
2400 int player_shield_class()
2401 {
2402     int shield = 0;
2403
2404     if (you.incapacitated())
2405         return 0;
2406
2407     if (you.shield())
2408     {
2409         const item_def& item = you.inv[you.equip[EQ_SHIELD]];
2410         int size_factor = (you.body_size(PSIZE_TORSO) - SIZE_MEDIUM)
2411                         * (item.sub_type - ARM_LARGE_SHIELD);
2412         int base_shield = property(item, PARM_AC) * 2 + size_factor;
2413
2414         int beogh_bonus = _player_armour_beogh_bonus(item);
2415
2416         // bonus applied only to base, see above for effect:
2417         shield += base_shield * 50;
2418         shield += base_shield * you.skill(SK_SHIELDS, 5) / 2;
2419         shield += base_shield * beogh_bonus * 10 / 6;
2420
2421         shield += item.plus * 200;
2422
2423         shield += you.skill(SK_SHIELDS, 38)
2424                 + min(you.skill(SK_SHIELDS, 38), 3 * 38);
2425
2426         int stat = 0;
2427         if (item.sub_type == ARM_BUCKLER)
2428             stat = you.dex() * 38;
2429         else if (item.sub_type == ARM_LARGE_SHIELD)
2430             stat = you.dex() * 12 + you.strength() * 26;
2431         else
2432             stat = you.dex() * 19 + you.strength() * 19;
2433         stat = stat * (base_shield + 13) / 26;
2434
2435         shield += stat;
2436     }
2437     else
2438     {
2439         if (you.duration[DUR_MAGIC_SHIELD])
2440             shield += 900 + you.skill(SK_EVOCATIONS, 75);
2441
2442         if (you.duration[DUR_CONDENSATION_SHIELD])
2443             shield += 800 + you.props[CONDENSATION_SHIELD_KEY].get_int() * 15;
2444     }
2445
2446     // mutations
2447     // +2, +3, +4 (displayed values)
2448     shield += (player_mutation_level(MUT_LARGE_BONE_PLATES) > 0
2449                ? player_mutation_level(MUT_LARGE_BONE_PLATES) * 200 + 200
2450                : 0);
2451
2452     shield += qazlal_sh_boost() * 100;
2453     shield += tso_sh_boost() * 100;
2454     shield += _bone_armour_bonus() * 2;
2455
2456     return (shield + 50) / 100;
2457 }
2458
2459 /**
2460  * Calculate the SH value that should be displayed to players.
2461  *
2462  * Exactly half the internal value, for legacy reasons.
2463  * @return      The SH value to be displayed.
2464  */
2465 int player_displayed_shield_class()
2466 {
2467     return player_shield_class() / 2;
2468 }
2469
2470 /**
2471  * Does the player take halved ability damage?
2472  *
2473  * @param calc_unid     Whether to include properties of worn but unidentified
2474  *                      items in the calculation. (Probably irrelevant.)
2475  * @return              Whether the player has SustAb.
2476  */
2477 bool player_sust_abil(bool calc_unid)
2478 {
2479     return you.wearing(EQ_RINGS, RING_SUSTAIN_ABILITIES, calc_unid)
2480            || you.scan_artefacts(ARTP_SUSTAB)
2481            || player_mutation_level(MUT_SUSTAIN_ABILITIES);
2482 }
2483
2484 void forget_map(bool rot)
2485 {
2486     ASSERT(!crawl_state.game_is_arena());
2487
2488     // If forgetting was intentional, clear the travel trail.
2489     if (!rot)
2490         clear_travel_trail();
2491
2492     // Labyrinth and the Abyss use special rotting rules.
2493     const bool rot_resist = player_in_branch(BRANCH_LABYRINTH)
2494                                 && you.species == SP_MINOTAUR
2495                             || player_in_branch(BRANCH_ABYSS)
2496                                 && you_worship(GOD_LUGONU);
2497     const double geometric_chance = 0.99;
2498     const int radius = (rot_resist ? 200 : 100);
2499
2500     const int scalar = 0xFF;
2501     for (rectangle_iterator ri(0); ri; ++ri)
2502     {
2503         const coord_def &p = *ri;
2504         if (!env.map_knowledge(p).known() || you.see_cell(p))
2505             continue;
2506
2507         if (rot)
2508         {
2509             const int dist = distance2(you.pos(), p);
2510             int chance = pow(geometric_chance,
2511                              max(1, (dist - radius) / 40)) * scalar;
2512             if (x_chance_in_y(chance, scalar))
2513                 continue;
2514         }
2515
2516         if (you.see_cell(p))
2517             continue;
2518
2519         env.map_knowledge(p).clear();
2520         if (env.map_forgotten.get())
2521             (*env.map_forgotten.get())(p).clear();
2522         StashTrack.update_stash(p);
2523 #ifdef USE_TILE
2524         tile_forget_map(p);
2525 #endif
2526     }
2527
2528     ash_detect_portals(is_map_persistent());
2529 #ifdef USE_TILE
2530     tiles.update_minimap_bounds();
2531 #endif
2532 }
2533
2534 static void _remove_temp_mutation()
2535 {
2536     int num_remove = min(you.attribute[ATTR_TEMP_MUTATIONS],
2537         max(you.attribute[ATTR_TEMP_MUTATIONS] * 5 / 12 - random2(3),
2538         1 + random2(3)));
2539
2540     if (num_remove >= you.attribute[ATTR_TEMP_MUTATIONS])
2541         mprf(MSGCH_DURATION, "You feel the corruption within you wane completely.");
2542     else
2543         mprf(MSGCH_DURATION, "You feel the corruption within you wane somewhat.");
2544
2545     for (int i = 0; i < num_remove; ++i)
2546         delete_temp_mutation();
2547
2548     if (you.attribute[ATTR_TEMP_MUTATIONS] > 0)
2549         you.attribute[ATTR_TEMP_MUT_XP] += temp_mutation_roll();
2550 }
2551
2552 int get_exp_progress()
2553 {
2554     if (you.experience_level >= you.get_max_xl())
2555         return 0;
2556
2557     const int current = exp_needed(you.experience_level);
2558     const int next    = exp_needed(you.experience_level + 1);
2559     if (next == current)
2560         return 0;
2561     return (you.experience - current) * 100 / (next - current);
2562 }
2563
2564 static void _recharge_xp_evokers(int exp)
2565 {
2566     FixedVector<item_def*, NUM_MISCELLANY> evokers(nullptr);
2567     list_charging_evokers(evokers);
2568
2569     int xp_factor = max(min((int)exp_needed(you.experience_level+1, 0) * 2 / 7,
2570                              you.experience_level * 425),
2571                         you.experience_level*4 + 30)
2572                     / (3 + you.skill_rdiv(SK_EVOCATIONS, 2, 13));
2573
2574     for (int i = 0; i < NUM_MISCELLANY; ++i)
2575     {
2576         item_def* evoker = evokers[i];
2577         if (!evoker)
2578             continue;
2579
2580         int &debt = evoker_debt(evoker->sub_type);
2581         if (debt == 0)
2582             continue;
2583
2584         debt = max(0, debt - div_rand_round(exp, xp_factor));
2585         if (debt == 0)
2586             mprf("%s has recharged.", evoker->name(DESC_YOUR).c_str());
2587     }
2588 }
2589
2590 void gain_exp(unsigned int exp_gained, unsigned int* actual_gain)
2591 {
2592     if (crawl_state.game_is_arena())
2593         return;
2594
2595     if (crawl_state.game_is_zotdef())
2596     {
2597         you.zot_points += exp_gained;
2598         // All XP, for some reason Sprint speeds up only skill training,
2599         // but not levelling, Ash skill transfer, etc.
2600         exp_gained *= 2;
2601     }
2602
2603     vector<god_type> xp_gods;
2604     for (int i = GOD_NO_GOD; i < NUM_GODS; ++i)
2605     {
2606         if (xp_penance((god_type) i))
2607             xp_gods.push_back((god_type) i);
2608     }
2609
2610     if (!xp_gods.empty())
2611     {
2612         god_type god = xp_gods[random2(xp_gods.size())];
2613         reduce_xp_penance(god, exp_gained);
2614     }
2615
2616     const unsigned int old_exp = you.experience;
2617
2618     dprf("gain_exp: %d", exp_gained);
2619
2620     if (you.transfer_skill_points > 0)
2621     {
2622         // Can happen if the game got interrupted during target skill choice.
2623         if (is_invalid_skill(you.transfer_to_skill))
2624         {
2625             you.transfer_from_skill = SK_NONE;
2626             you.transfer_skill_points = 0;
2627             you.transfer_total_skill_points = 0;
2628         }
2629         else
2630         {
2631             int amount = exp_gained * 10
2632                                 / calc_skill_cost(you.skill_cost_level);
2633             if (amount >= 20 || one_chance_in(20 - amount))
2634             {
2635                 amount = max(20, amount);
2636                 transfer_skill_points(you.transfer_from_skill,
2637                                       you.transfer_to_skill, amount, false);
2638             }
2639         }
2640     }
2641
2642     if (you.experience + exp_gained > (unsigned int)MAX_EXP_TOTAL)
2643         you.experience = MAX_EXP_TOTAL;
2644     else
2645         you.experience += exp_gained;
2646
2647     you.attribute[ATTR_EVOL_XP] += exp_gained;
2648     for (int i = GOD_NO_GOD; i < NUM_GODS; ++i)
2649     {
2650         if (active_penance((god_type) i))
2651         {
2652             you.attribute[ATTR_GOD_WRATH_XP] -= exp_gained;
2653             while (you.attribute[ATTR_GOD_WRATH_XP] < 0)
2654             {
2655                 you.attribute[ATTR_GOD_WRATH_COUNT]++;
2656                 set_penance_xp_timeout();
2657             }
2658             break;
2659         }
2660     }
2661
2662     if (crawl_state.game_is_sprint())
2663         exp_gained = sprint_modify_exp(exp_gained);
2664
2665     you.exp_available += exp_gained;
2666
2667     train_skills();
2668     while (check_selected_skills()
2669            && you.exp_available >= calc_skill_cost(you.skill_cost_level))
2670     {
2671         train_skills();
2672     }
2673
2674     if (you.exp_available >= calc_skill_cost(you.skill_cost_level))
2675         you.exp_available = calc_skill_cost(you.skill_cost_level);
2676
2677     level_change();
2678
2679     if (actual_gain != nullptr)
2680         *actual_gain = you.experience - old_exp;
2681
2682     if (you.attribute[ATTR_TEMP_MUTATIONS] > 0)
2683     {
2684         you.attribute[ATTR_TEMP_MUT_XP] -= exp_gained;
2685         if (you.attribute[ATTR_TEMP_MUT_XP] <= 0)
2686             _remove_temp_mutation();
2687     }
2688
2689     _recharge_xp_evokers(exp_gained);
2690
2691     if (you.attribute[ATTR_XP_DRAIN])
2692     {
2693         int loss = div_rand_round(exp_gained * 3 / 2,
2694                                   calc_skill_cost(you.skill_cost_level));
2695
2696         // Make it easier to recover from very heavy levels of draining
2697         // (they're nasty enough as it is)
2698         loss = loss * (1 + (you.attribute[ATTR_XP_DRAIN] / 250.0f));
2699
2700         dprf("Lost %d of %d draining points", loss, you.attribute[ATTR_XP_DRAIN]);
2701
2702         you.attribute[ATTR_XP_DRAIN] -= loss;
2703         // Regaining skills may affect AC/EV.
2704         you.redraw_armour_class = true;
2705         you.redraw_evasion = true;
2706         if (you.attribute[ATTR_XP_DRAIN] <= 0)
2707         {
2708             you.attribute[ATTR_XP_DRAIN] = 0;
2709             mprf(MSGCH_RECOVERY, "Your life force feels restored.");
2710         }
2711     }
2712 }
2713
2714 bool will_gain_life(int lev)
2715 {
2716     if (lev < you.attribute[ATTR_LIFE_GAINED] - 2)
2717         return false;
2718
2719     return you.lives + you.deaths < (lev - 1) / 3;
2720 }
2721
2722 static void _felid_extra_life()
2723 {
2724     if (will_gain_life(you.max_level)
2725         && you.lives < 2)
2726     {
2727         you.lives++;
2728         mprf(MSGCH_INTRINSIC_GAIN, "Extra life!");
2729         you.attribute[ATTR_LIFE_GAINED] = you.max_level;
2730         // Should play the 1UP sound from SMB...
2731     }
2732 }
2733
2734 /**
2735  * Handle the effects from a player's change in XL.
2736  * @param aux                     A string describing the cause of the level
2737  *                                change.
2738  * @param skip_attribute_increase If true and XL has increased, don't process
2739  *                                stat gains. Currently only used by wizmode
2740  *                                commands.
2741  */
2742 void level_change(bool skip_attribute_increase)
2743 {
2744     // necessary for the time being, as level_change() is called
2745     // directly sometimes {dlb}
2746     you.redraw_experience = true;
2747
2748     while (you.experience < exp_needed(you.experience_level))
2749         lose_level();
2750
2751     while (you.experience_level < you.get_max_xl()
2752            && you.experience >= exp_needed(you.experience_level + 1))
2753     {
2754         if (!skip_attribute_increase)
2755         {
2756             crawl_state.cancel_cmd_all();
2757
2758             if (is_processing_macro())
2759                 flush_input_buffer(FLUSH_ABORT_MACRO);
2760         }
2761
2762         // [ds] Make sure we increment you.experience_level and apply
2763         // any stat/hp increases only after we've cleared all prompts
2764         // for this experience level. If we do part of the work before
2765         // the prompt, and a player on telnet gets disconnected, the
2766         // SIGHUP will save Crawl in the in-between state and rob the
2767         // player of their level-up perks.
2768
2769         const int new_exp = you.experience_level + 1;
2770
2771         if (new_exp <= you.max_level)
2772         {
2773             mprf(MSGCH_INTRINSIC_GAIN,
2774                  "Welcome back to level %d!", new_exp);
2775
2776             // No more prompts for this XL past this point.
2777
2778             you.experience_level = new_exp;
2779         }
2780         else  // Character has gained a new level
2781         {
2782             // Don't want to see the dead creature at the prompt.
2783             redraw_screen();
2784             // There may be more levels left to gain.
2785             you.redraw_experience = true;
2786
2787             if (new_exp == 27)
2788                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level 27, the final one!");
2789             else if (new_exp == you.get_max_xl())
2790                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d, the highest you will ever reach!",
2791                         you.get_max_xl());
2792             else
2793             {
2794                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d!",
2795                      new_exp);
2796             }
2797
2798             const bool manual_stat_level = new_exp % 3 == 0;  // 3,6,9,12...
2799
2800             if (manual_stat_level && !skip_attribute_increase)
2801                 if (!attribute_increase())
2802                     return; // abort level gain, the xp is still there
2803
2804             crawl_state.stat_gain_prompt = false;
2805             you.experience_level = new_exp;
2806             you.max_level = you.experience_level;
2807
2808 #ifdef USE_TILE_LOCAL
2809             // In case of intrinsic ability changes.
2810             tiles.layout_statcol();
2811             redraw_screen();
2812 #endif
2813             if (!skip_attribute_increase)
2814                 species_stat_gain(you.species);
2815
2816             switch (you.species)
2817             {
2818             case SP_VAMPIRE:
2819                 if (you.experience_level == 3)
2820                 {
2821                     if (you.hunger_state > HS_SATIATED)
2822                     {
2823                         mprf(MSGCH_INTRINSIC_GAIN, "If you weren't so full, "
2824                              "you could now transform into a vampire bat.");
2825                     }
2826                     else
2827                     {
2828                         mprf(MSGCH_INTRINSIC_GAIN,
2829                              "You can now transform into a vampire bat.");
2830                     }
2831                 }
2832                 break;
2833
2834             case SP_NAGA:
2835                 if (!(you.experience_level % 3))
2836                 {
2837                     mprf(MSGCH_INTRINSIC_GAIN, "Your skin feels tougher.");
2838                     you.redraw_armour_class = true;
2839                 }
2840                 break;
2841
2842             case SP_BASE_DRACONIAN:
2843                 if (you.experience_level >= 7)
2844                 {
2845                     you.species = static_cast<species_type>(
2846                                        random_range(SP_FIRST_NONBASE_DRACONIAN,
2847                                                     SP_LAST_NONBASE_DRACONIAN));
2848
2849                     // We just changed our aptitudes, so some skills may now
2850                     // be at the wrong level (with negative progress); if we
2851                     // print anything in this condition, we might trigger a
2852                     // --More--, a redraw, and a crash (#6376 on Mantis).
2853                     //
2854                     // Hence we first fix up our skill levels silently (passing
2855                     // do_level_up = false) but save the old values; then when
2856                     // we want the messages later, we restore the old skill
2857                     // levels and call check_skill_level_change() again, this
2858                     // time passing do_update = true.
2859
2860                     uint8_t saved_skills[NUM_SKILLS];
2861                     for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
2862                     {
2863                         saved_skills[sk] = you.skills[sk];
2864                         check_skill_level_change(sk, false);
2865                     }
2866                     // The player symbol depends on species.
2867                     update_player_symbol();
2868 #ifdef USE_TILE
2869                     init_player_doll();
2870 #endif
2871                     // Produce messages about skill increases/decreases. We
2872                     // restore one skill level at a time so that at most the
2873                     // skill being checked is at the wrong level.
2874                     for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
2875                     {
2876                         you.skills[sk] = saved_skills[sk];
2877                         check_skill_level_change(sk);
2878                     }
2879
2880                     redraw_screen();
2881
2882                     mprf(MSGCH_INTRINSIC_GAIN,
2883                          "Your scales start taking on a %s colour.",
2884                          scale_type(you.species));
2885                 }
2886                 break;
2887
2888             case SP_DEMONSPAWN:
2889             {
2890                 bool gave_message = false;
2891                 int level = 0;
2892                 mutation_type first_body_facet = NUM_MUTATIONS;
2893
2894                 for (const player::demon_trait trait : you.demonic_traits)
2895                 {
2896                     if (is_body_facet(trait.mutation))
2897                     {
2898                         if (first_body_facet < NUM_MUTATIONS
2899                             && trait.mutation != first_body_facet)
2900                         {
2901                             if (you.experience_level == level)
2902                             {
2903                                 mprf(MSGCH_MUTATION, "You feel monstrous as "
2904                                      "your demonic heritage exerts itself.");
2905                                 mark_milestone("monstrous", "discovered their "
2906                                                "monstrous ancestry!");
2907                             }
2908                             break;
2909                         }
2910
2911                         if (first_body_facet == NUM_MUTATIONS)
2912                         {
2913                             first_body_facet = trait.mutation;
2914                             level = trait.level_gained;
2915                         }
2916                     }
2917                 }
2918
2919                 for (const player::demon_trait trait : you.demonic_traits)
2920                 {
2921                     if (trait.level_gained == you.experience_level)
2922                     {
2923                         if (!gave_message)
2924                         {
2925                             mprf(MSGCH_INTRINSIC_GAIN,
2926                                  "Your demonic ancestry asserts itself...");
2927
2928                             gave_message = true;
2929                         }
2930                         perma_mutate(trait.mutation, 1, "demonic ancestry");
2931                     }
2932                 }
2933
2934                 break;
2935             }
2936
2937             case SP_FELID:
2938                 _felid_extra_life();
2939                 break;
2940
2941             default:
2942                 break;
2943             }
2944
2945             give_level_mutations(you.species, you.experience_level);
2946
2947         }
2948
2949         if (species_is_draconian(you.species) && !(you.experience_level % 3))
2950         {
2951             mprf(MSGCH_INTRINSIC_GAIN, "Your scales feel tougher.");
2952             you.redraw_armour_class = true;
2953         }
2954
2955         // zot defence abilities; must also be updated in ability.cc when these levels are changed
2956         if (crawl_state.game_is_zotdef())
2957         {
2958             if (you.experience_level == 2)
2959                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of oklob saplings.");
2960             if (you.experience_level == 3)
2961                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of arrow traps.");
2962             if (you.experience_level == 4)
2963                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of plants.");
2964             if (you.experience_level == 4)
2965                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through removing curses.");
2966             if (you.experience_level == 5)
2967                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of burning bushes.");
2968             if (you.experience_level == 6)
2969                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of altars and grenades.");
2970             if (you.experience_level == 7)
2971                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of oklob plants.");
2972             if (you.experience_level == 8)
2973                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of net traps.");
2974             if (you.experience_level == 9)
2975                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of ice statues.");
2976             if (you.experience_level == 10)
2977                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of spear traps.");
2978             if (you.experience_level == 11)
2979                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of alarm traps.");
2980             if (you.experience_level == 12)
2981                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of mushroom circles.");
2982             if (you.experience_level == 13)
2983                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of bolt traps.");
2984             if (you.experience_level == 14)
2985                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of orange crystal statues.");
2986             if (you.experience_level == 15)
2987                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of needle traps.");
2988             if (you.experience_level == 16)
2989                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through self-teleportation.");
2990             if (you.experience_level == 17)
2991                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through making water.");
2992             if (you.experience_level == 19)
2993                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of lightning spires.");
2994             if (you.experience_level == 20)
2995                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of obsidian statues.");
2996             // gold and bazaars gained together
2997             if (you.experience_level == 21)
2998                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of bazaars.");
2999             if (you.experience_level == 21)
3000                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through acquiring gold.");
3001             if (you.experience_level == 22)
3002                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of oklob circles.");
3003             if (you.experience_level == 24)
3004                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through acquirement.");
3005             if (you.experience_level == 25)
3006                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of blade traps.");
3007             if (you.experience_level == 26)
3008                 mprf(MSGCH_INTRINSIC_GAIN, "Your Zot abilities now extend through the making of curse skulls.");
3009         }
3010
3011         const int old_hp = you.hp;
3012         const int old_maxhp = you.hp_max;
3013         const int old_mp = you.magic_points;
3014         const int old_maxmp = you.max_magic_points;
3015
3016         // recalculate for game
3017         calc_hp();
3018         calc_mp();
3019
3020         set_hp(old_hp * you.hp_max / old_maxhp);
3021         set_mp(old_maxmp > 0 ? old_mp * you.max_magic_points / old_maxmp
3022                : you.max_magic_points);
3023
3024         // Get "real" values for note-taking, i.e. ignore Berserk,
3025         // transformations or equipped items.
3026         const int note_maxhp = get_real_hp(false, false);
3027         const int note_maxmp = get_real_mp(false);
3028
3029         char buf[200];
3030 #if TAG_MAJOR_VERSION == 34
3031         if (you.species == SP_DJINNI)
3032             // Djinn don't HP/MP
3033             sprintf(buf, "EP: %d/%d",
3034                     min(you.hp, note_maxhp + note_maxmp),
3035                     note_maxhp + note_maxmp);
3036         else
3037 #endif
3038             sprintf(buf, "HP: %d/%d MP: %d/%d",
3039                     min(you.hp, note_maxhp), note_maxhp,
3040                     min(you.magic_points, note_maxmp), note_maxmp);
3041         take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, buf));
3042
3043         xom_is_stimulated(12);
3044
3045         learned_something_new(HINT_NEW_LEVEL);
3046     }
3047
3048     while (you.experience >= exp_needed(you.max_level + 1))
3049     {
3050         ASSERT(you.experience_level == you.get_max_xl());
3051         ASSERT(you.max_level < 127); // marshalled as an 1-byte value
3052         you.max_level++;
3053         if (you.species == SP_FELID)
3054             _felid_extra_life();
3055     }
3056
3057     you.redraw_title = true;
3058
3059 #ifdef DGL_WHEREIS
3060     whereis_record();
3061 #endif
3062
3063     // Hints mode arbitrarily ends at xp 7.
3064     if (crawl_state.game_is_hints() && you.experience_level >= 7)
3065         hints_finished();
3066 }
3067
3068 void adjust_level(int diff, bool just_xp)
3069 {
3070     ASSERT((uint64_t)you.experience <= (uint64_t)MAX_EXP_TOTAL);
3071     const int max_exp_level = you.get_max_xl();
3072     if (you.experience_level + diff < 1)
3073         you.experience = 0;
3074     else if (you.experience_level + diff >= max_exp_level)
3075         you.experience = max(you.experience,
3076                 exp_needed(max_exp_level));
3077     else
3078     {
3079         while (diff < 0 && you.experience >=
3080                 exp_needed(max_exp_level))
3081         {
3082             // Having XP for level 53 and going back to 26 due to a single
3083             // card would mean your felid is not going to get any extra lives
3084             // in foreseable future.
3085             you.experience -= exp_needed(max_exp_level)
3086                     - exp_needed(max_exp_level - 1);
3087             diff++;
3088         }
3089         int old_min = exp_needed(you.experience_level);
3090         int old_max = exp_needed(you.experience_level + 1);
3091         int new_min = exp_needed(you.experience_level + diff);
3092         int new_max = exp_needed(you.experience_level + 1 + diff);
3093         dprf("XP before: %d\n", you.experience);
3094         dprf("%4.2f of %d..%d to %d..%d",
3095              (you.experience - old_min) * 1.0 / (old_max - old_min),
3096              old_min, old_max, new_min, new_max);
3097
3098         you.experience = ((int64_t)(new_max - new_min))
3099                        * (you.experience - old_min)
3100                        / (old_max - old_min)
3101                        + new_min;
3102         dprf("XP after: %d\n", you.experience);
3103     }
3104
3105     ASSERT((uint64_t)you.experience <= (uint64_t)MAX_EXP_TOTAL);
3106
3107     if (!just_xp)
3108         level_change();
3109 }
3110
3111 /**
3112  * Return a multiplier for skill when calculating stealth values, based on the
3113  * player's species & form.
3114  *
3115  * @return The player's current stealth multiplier value.
3116  */
3117 static int _stealth_mod()
3118 {
3119     const int form_stealth_mod = get_form()->get_stealth_mod();
3120
3121     if (form_stealth_mod != 0)
3122         return form_stealth_mod;
3123
3124     int species_stealth_mod = species_stealth_modifier(you.species);
3125     if (you.form == TRAN_STATUE)
3126         species_stealth_mod -= 3;
3127     // Thirsty vampires are (much) more stealthy
3128     if (you.species == SP_VAMPIRE)
3129     {
3130         switch (you.hunger_state)
3131         {
3132         case HS_STARVING:
3133             species_stealth_mod += 3;
3134             break;
3135
3136         case HS_NEAR_STARVING:
3137             species_stealth_mod += 2;
3138             break;
3139
3140         case HS_VERY_HUNGRY:
3141         case HS_HUNGRY:
3142             species_stealth_mod += 1;
3143             break;
3144         default:
3145             break;
3146         }
3147     }
3148     return species_stealth_mod;
3149 }
3150
3151 /**
3152  * Get the player's current stealth value.
3153  *
3154  * XXX: rename this to something more reasonable
3155
3156  *
3157  * (Keep in mind, while tweaking this function: the order in which stealth
3158  * modifiers are applied is significant!)
3159  *
3160  * @return  The player's current stealth value.
3161  */
3162 int check_stealth()
3163 {
3164     ASSERT(!crawl_state.game_is_arena());
3165     // Extreme stealthiness can be enforced by wizmode stealth setting.
3166     if (crawl_state.disables[DIS_MON_SIGHT])
3167         return 1000;
3168
3169     // lantern of shadows, berserking, "clumsy" (0-dex).
3170     if (you.attribute[ATTR_SHADOWS] || you.berserk()
3171         || you.duration[DUR_CLUMSY] || player_mutation_level(MUT_NO_STEALTH))
3172     {
3173         return 0;
3174     }
3175
3176     int stealth = you.dex() * 3;
3177
3178     if (you.form == TRAN_BLADE_HANDS && you.species == SP_FELID
3179         && !you.airborne())
3180     {
3181         stealth -= 50; // klack klack klack go the blade paws
3182         // this is an absurd special case but also it's really funny so w/e
3183     }
3184
3185     stealth += you.skill(SK_STEALTH, _stealth_mod());
3186
3187     if (you.confused())
3188         stealth /= 3;
3189
3190     const item_def *arm = you.slot_item(EQ_BODY_ARMOUR, false);
3191     const item_def *boots = you.slot_item(EQ_BOOTS, false);
3192
3193     if (arm)
3194     {
3195         // [ds] New stealth penalty formula from rob: SP = 6 * (EP^2)
3196         // Now 2 * EP^2 / 3 after EP rescaling.
3197         const int evp = you.unadjusted_body_armour_penalty();
3198         const int penalty = evp * evp * 2 / 3;
3199 #if 0
3200         dprf("Stealth penalty for armour (ep: %d): %d", ep, penalty);
3201 #endif
3202         stealth -= penalty;
3203     }
3204
3205     stealth += STEALTH_PIP * you.scan_artefacts(ARTP_STEALTH);
3206
3207     stealth += STEALTH_PIP * you.wearing(EQ_RINGS, RING_STEALTH);
3208     stealth -= STEALTH_PIP * you.wearing(EQ_RINGS, RING_LOUDNESS);
3209
3210     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
3211     if (body_armour)
3212     {
3213         const int pips = armour_type_prop(body_armour->sub_type, ARMF_STEALTH);
3214         stealth += pips * STEALTH_PIP;
3215     }
3216
3217     if (you.duration[DUR_STEALTH])
3218         stealth += STEALTH_PIP * 2;
3219
3220     if (you.duration[DUR_AGILITY])
3221         stealth += STEALTH_PIP;
3222
3223     if (!you.airborne())
3224     {
3225         if (you.in_water())
3226         {
3227             // Merfolk can sneak up on monsters underwater -- bwr
3228             if (you.fishtail || you.species == SP_OCTOPODE)
3229                 stealth += STEALTH_PIP;
3230             else if (!you.can_swim() && !you.extra_balanced())
3231                 stealth /= 2;       // splashy-splashy
3232         }
3233
3234         else if (boots && get_armour_ego_type(*boots) == SPARM_STEALTH)
3235             stealth += STEALTH_PIP;
3236
3237         else if (you.has_usable_hooves())
3238             stealth -= 5 + 5 * player_mutation_level(MUT_HOOVES);
3239
3240         else if (you.species == SP_FELID && (!you.form || you.form == TRAN_APPENDAGE))
3241             stealth += 20;  // paws
3242     }
3243
3244     // Radiating silence is the negative complement of shouting all the
3245     // time... a sudden change from background noise to no noise is going
3246     // to clue anything in to the fact that something is very wrong...
3247     // a personal silence spell would naturally be different, but this
3248     // silence radiates for a distance and prevents monster spellcasting,
3249     // which pretty much gives away the stealth game.
3250     // this penalty is dependent on the actual amount of ambient noise
3251     // in the level -doy
3252     if (you.duration[DUR_SILENCE])
3253         stealth -= STEALTH_PIP + current_level_ambient_noise();
3254
3255     // Mutations.
3256     stealth += STEALTH_PIP * player_mutation_level(MUT_NIGHTSTALKER);
3257     stealth += (STEALTH_PIP / 2)
3258                 * player_mutation_level(MUT_THIN_SKELETAL_STRUCTURE);
3259     stealth += STEALTH_PIP * player_mutation_level(MUT_CAMOUFLAGE);
3260     const int how_transparent = player_mutation_level(MUT_TRANSLUCENT_SKIN);
3261     if (how_transparent)
3262         stealth += 15 * (how_transparent);
3263
3264     // it's easier to be stealthy when there's a lot of background noise
3265     stealth += 2 * current_level_ambient_noise();
3266
3267     // If you've been tagged with Corona or are Glowing, the glow
3268     // makes you extremely unstealthy.
3269     if (you.backlit())
3270         stealth = stealth * 2 / 5;
3271
3272     // On the other hand, shrouding has the reverse effect, if you know
3273     // how to make use of it:
3274     if (you.umbra())
3275     {
3276         int umbra_mul = 1, umbra_div = 1;
3277         if (you_worship(GOD_DITHMENOS) || you_worship(GOD_YREDELEMNUL))
3278         {
3279             umbra_mul = you.piety + MAX_PIETY;
3280             umbra_div = MAX_PIETY;
3281         }
3282         if (player_equip_unrand(UNRAND_SHADOWS))
3283             if (2 * umbra_mul < 3 * umbra_div)
3284             {
3285                 umbra_mul = 3;
3286                 umbra_div = 2;
3287             }
3288         stealth *= umbra_mul;
3289         stealth /= umbra_div;
3290     }
3291
3292     // If you're surrounded by a storm, you're inherently pretty conspicuous.
3293     if (in_good_standing(GOD_QAZLAL, 0))
3294     {
3295         stealth = stealth
3296                   * (MAX_PIETY - min((int)you.piety, piety_breakpoint(5)))
3297                   / (MAX_PIETY - piety_breakpoint(0));
3298     }
3299     // The shifting glow from the Orb, while too unstable to negate invis
3300     // or affect to-hit, affects stealth even more than regular glow.
3301     if (orb_haloed(you.pos()))
3302         stealth /= 3;
3303
3304     stealth = max(0, stealth);
3305
3306     return stealth;
3307 }
3308
3309 // Returns the medium duration value which is usually announced by a special
3310 // message ("XY is about to time out") or a change of colour in the
3311 // status display.
3312 // Note that these values cannot be relied on when playing since there are
3313 // random decrements precisely to avoid this.
3314 int get_expiration_threshold(duration_type dur)
3315 {
3316     switch (dur)
3317     {
3318     case DUR_PETRIFYING:
3319         return 1 * BASELINE_DELAY;
3320
3321     case DUR_QUAD_DAMAGE:
3322         return 3 * BASELINE_DELAY; // per client.qc
3323
3324     case DUR_FIRE_SHIELD:
3325     case DUR_SILENCE: // no message
3326         return 5 * BASELINE_DELAY;
3327
3328     case DUR_REGENERATION:
3329     case DUR_RESISTANCE:
3330     case DUR_SWIFTNESS:
3331     case DUR_INVIS:
3332     case DUR_HASTE:
3333     case DUR_BERSERK:
3334     case DUR_ICY_ARMOUR:
3335     case DUR_CONDENSATION_SHIELD:
3336     case DUR_PHASE_SHIFT:
3337     case DUR_CONTROL_TELEPORT:
3338     case DUR_DEATH_CHANNEL:
3339     case DUR_SHROUD_OF_GOLUBRIA:
3340     case DUR_INFUSION:
3341     case DUR_SONG_OF_SLAYING:
3342     case DUR_TROGS_HAND:
3343     case DUR_QAZLAL_FIRE_RES:
3344     case DUR_QAZLAL_COLD_RES:
3345     case DUR_QAZLAL_ELEC_RES:
3346     case DUR_QAZLAL_AC:
3347         return 6 * BASELINE_DELAY;
3348
3349     case DUR_FLIGHT:
3350     case DUR_TRANSFORMATION: // not on status
3351     case DUR_DEATHS_DOOR:    // not on status
3352     case DUR_SLIMIFY:
3353         return 10 * BASELINE_DELAY;
3354
3355     // These get no messages when they "flicker".
3356     case DUR_CONFUSING_TOUCH:
3357         return 20 * BASELINE_DELAY;
3358
3359     case DUR_ANTIMAGIC:
3360         return you.hp_max; // not so severe anymore
3361
3362     default:
3363         return 0;
3364     }
3365 }
3366
3367 // Is a given duration about to expire?
3368 bool dur_expiring(duration_type dur)
3369 {
3370     const int value = you.duration[dur];
3371     if (value <= 0)
3372         return false;
3373
3374     return value <= get_expiration_threshold(dur);
3375 }
3376
3377 static void _output_expiring_message(duration_type dur, const char* msg)
3378 {
3379     if (you.duration[dur])
3380     {
3381         const bool expires = dur_expiring(dur);
3382         mprf("%s%s", expires ? "Expiring: " : "", msg);
3383     }
3384 }
3385
3386 static void _display_char_status(int value, const char *fmt, ...)
3387 {
3388     va_list argp;
3389     va_start(argp, fmt);
3390
3391     string msg = vmake_stringf(fmt, argp);
3392
3393     if (you.wizard)
3394         mprf("%s (%d).", msg.c_str(), value);
3395     else
3396         mprf("%s.", msg.c_str());
3397
3398     va_end(argp);
3399 }
3400
3401 static void _display_vampire_status()
3402 {
3403     string msg = "At your current hunger state you ";
3404     vector<const char *> attrib;
3405
3406     switch (you.hunger_state)
3407     {
3408         case HS_STARVING:
3409             attrib.push_back("are immune to poison");
3410             attrib.push_back("significantly resist cold");
3411             attrib.push_back("strongly resist negative energy");
3412             attrib.push_back("resist torment");
3413             attrib.push_back("do not heal.");
3414             break;
3415         case HS_NEAR_STARVING:
3416             attrib.push_back("resist poison");
3417             attrib.push_back("significantly resist cold");
3418             attrib.push_back("strongly resist negative energy");
3419             attrib.push_back("have an extremely slow metabolism");
3420             attrib.push_back("heal slowly.");
3421             break;
3422         case HS_VERY_HUNGRY:
3423         case HS_HUNGRY:
3424             attrib.push_back("resist poison");
3425             attrib.push_back("resist cold");
3426             attrib.push_back("significantly resist negative energy");
3427             if (you.hunger_state == HS_HUNGRY)
3428                 attrib.push_back("have a slow metabolism");
3429             else
3430                 attrib.push_back("have a very slow metabolism");
3431             attrib.push_back("heal slowly.");
3432             break;
3433         case HS_SATIATED:
3434             attrib.push_back("resist negative energy.");
3435             break;
3436         case HS_FULL:
3437             attrib.push_back("have a fast metabolism");
3438             attrib.push_back("heal quickly.");
3439             break;
3440         case HS_VERY_FULL:
3441             attrib.push_back("have a very fast metabolism");
3442             attrib.push_back("heal quickly.");
3443             break;
3444         case HS_ENGORGED:
3445             attrib.push_back("have an extremely fast metabolism");
3446             attrib.push_back("heal extremely quickly.");
3447             break;
3448     }
3449
3450     if (!attrib.empty())
3451     {
3452         msg += comma_separated_line(attrib.begin(), attrib.end());
3453         mpr(msg);
3454     }
3455 }
3456
3457 static void _display_movement_speed()
3458 {
3459     const int move_cost = (player_speed() * player_movement_speed()) / 10;
3460
3461     const bool water  = you.in_liquid();
3462     const bool swim   = you.swimming();
3463
3464     const bool fly    = you.airborne();
3465     const bool swift  = (you.duration[DUR_SWIFTNESS] > 0
3466                          && you.attribute[ATTR_SWIFTNESS] >= 0);
3467     const bool antiswift = (you.duration[DUR_SWIFTNESS] > 0
3468                             && you.attribute[ATTR_SWIFTNESS] < 0);
3469
3470     _display_char_status(move_cost, "Your %s speed is %s%s%s",
3471           // order is important for these:
3472           (swim)    ? "swimming" :
3473           (water)   ? "wading" :
3474           (fly)     ? "flying"
3475                     : "movement",
3476
3477           (water && !swim)  ? "uncertain and " :
3478           (!water && swift) ? "aided by the wind" :
3479           (!water && antiswift) ? "hindered by the wind" : "",
3480
3481           (!water && swift) ? ((move_cost >= 10) ? ", but still "
3482                                                  : " and ") :
3483           (!water && antiswift) ? ((move_cost <= 10) ? ", but still "
3484                                                      : " and ")
3485                             : "",
3486
3487           (move_cost <   8) ? "very quick" :
3488           (move_cost <  10) ? "quick" :
3489           (move_cost == 10) ? "average" :
3490           (move_cost <  13) ? "slow"
3491                             : "very slow");
3492 }
3493
3494 static void _display_tohit()
3495 {
3496 #ifdef DEBUG_DIAGNOSTICS
3497     melee_attack attk(&you, nullptr);
3498
3499     const int to_hit = attk.calc_to_hit(false);
3500
3501     dprf("To-hit: %d", to_hit);
3502 #endif
3503 }
3504
3505 static const char* _attack_delay_desc(int attack_delay)
3506 {
3507     return (attack_delay >= 200) ? "extremely slow" :
3508            (attack_delay >= 155) ? "very slow" :
3509            (attack_delay >= 125) ? "quite slow" :
3510            (attack_delay >= 105) ? "below average" :
3511            (attack_delay >=  95) ? "average" :
3512            (attack_delay >=  75) ? "above average" :
3513            (attack_delay >=  55) ? "quite fast" :
3514            (attack_delay >=  45) ? "very fast" :
3515            (attack_delay >=  35) ? "extremely fast" :
3516                                    "blindingly fast";
3517 }
3518
3519 /**
3520  * Print a message indicating the player's attack delay with their current
3521  * weapon & quivered ammo (if applicable).
3522  *
3523  * Uses melee attack delay for ranged weapons if no appropriate ammo is
3524  * is quivered, purely for simplicity of implementation; XXX fix this
3525  */
3526 static void _display_attack_delay()
3527 {
3528     const item_def* ammo = nullptr;
3529     you.m_quiver->get_desired_item(&ammo, nullptr);
3530     const bool uses_ammo = ammo && you.weapon()
3531                            && ammo->launched_by(*you.weapon());
3532     const int delay = you.attack_delay(you.weapon(), uses_ammo ? ammo : nullptr,
3533                                        false, false);
3534
3535     const int no_shield_delay = you.attack_delay(you.weapon(),
3536                                                  uses_ammo ? ammo : nullptr,
3537                                                  false, false, false);
3538     const bool at_min_delay = you.weapon()
3539                               && weapon_min_delay(*you.weapon())
3540                                  == no_shield_delay;
3541
3542     // Scale to fit the displayed weapon base delay, i.e.,
3543     // normal speed is 100 (as in 100%).
3544     int avg = 10 * delay;
3545
3546     // Haste shouldn't be counted, but let's show finesse.
3547     if (you.duration[DUR_FINESSE])
3548         avg = max(20, avg / 2);
3549
3550     _display_char_status(avg, "Your attack speed is %s%s",
3551                          _attack_delay_desc(avg),
3552                          at_min_delay ?
3553                             " (and cannot be increased with skill)" : "");
3554 }
3555
3556 // forward declaration
3557 static string _constriction_description();
3558
3559 void display_char_status()
3560 {
3561     if (you.undead_state() == US_SEMI_UNDEAD
3562         && you.hunger_state == HS_ENGORGED)
3563     {
3564         mpr("You feel almost alive.");
3565     }
3566     else if (you.undead_state())
3567         mpr("You are undead.");
3568     else if (you.duration[DUR_DEATHS_DOOR])
3569     {
3570         _output_expiring_message(DUR_DEATHS_DOOR,
3571                                  "You are standing in death's doorway.");
3572     }
3573     else
3574         mpr("You are alive.");
3575
3576     const int halo_size = you.halo_radius2();
3577     if (halo_size >= 0)
3578     {
3579         if (halo_size > 37)
3580             mpr("You are illuminated by a large divine halo.");
3581         else if (halo_size > 10)
3582             mpr("You are illuminated by a divine halo.");
3583         else
3584             mpr("You are illuminated by a small divine halo.");
3585     }
3586     else if (you.haloed())
3587         mpr("An external divine halo illuminates you.");
3588
3589     if (you.species == SP_VAMPIRE)
<