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