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 "abyss.h"
20 #include "act-iter.h"
21 #include "areas.h"
22 #include "art-enum.h"
23 #include "attack.h"
24 #include "bloodspatter.h"
25 #include "branch.h"
26 #include "chardump.h"
27 #include "cloud.h"
28 #include "coordit.h"
29 #include "delay.h"
30 #include "dgn-overview.h"
31 #include "dgn-event.h"
32 #include "directn.h"
33 #include "english.h"
34 #include "env.h"
35 #include "tile-env.h"
36 #include "errors.h"
37 #include "exercise.h"
38 #include "files.h"
39 #include "god-abil.h"
40 #include "god-conduct.h"
41 #include "god-passive.h"
42 #include "god-wrath.h"
43 #include "hints.h"
44 #include "hiscores.h"
45 #include "invent.h"
46 #include "item-prop.h"
47 #include "items.h"
48 #include "item-use.h"
49 #include "kills.h"
50 #include "level-state-type.h"
51 #include "libutil.h"
52 #include "macro.h"
53 #include "melee-attack.h"
54 #include "message.h"
55 #include "mon-place.h"
56 #include "movement.h"
57 #include "mutation.h"
58 #include "nearby-danger.h"
59 #include "notes.h"
60 #include "output.h"
61 #include "player-equip.h"
62 #include "player-save-info.h"
63 #include "player-stats.h"
64 #include "prompt.h"
65 #include "religion.h"
66 #include "shout.h"
67 #include "skills.h"
68 #include "species.h" // random_starting_species
69 #include "spl-damage.h"
70 #include "spl-selfench.h"
71 #include "spl-summoning.h"
72 #include "spl-transloc.h"
73 #include "spl-util.h"
74 #include "sprint.h"
75 #include "stairs.h"
76 #include "stash.h"
77 #include "state.h"
78 #include "status.h"
79 #include "stepdown.h"
80 #include "stringutil.h"
81 #include "tag-version.h"
82 #include "terrain.h"
83 #ifdef USE_TILE
84  #include "tilepick.h"
85  #include "tileview.h"
86 #endif
87 #include "transform.h"
88 #include "traps.h"
89 #include "travel.h"
90 #include "view.h"
91 #include "wizard-option-type.h"
92 #include "xom.h"
93 
_moveto_maybe_repel_stairs()94 static void _moveto_maybe_repel_stairs()
95 {
96     const dungeon_feature_type new_grid = env.grid(you.pos());
97     const command_type stair_dir = feat_stair_direction(new_grid);
98 
99     if (stair_dir == CMD_NO_CMD
100         || new_grid == DNGN_ENTER_SHOP
101         ||  !you.duration[DUR_REPEL_STAIRS_MOVE])
102     {
103         return;
104     }
105 
106     int pct = you.duration[DUR_REPEL_STAIRS_CLIMB] ? 29 : 50;
107 
108     // When the effect is still strong, the chance to actually catch
109     // a stair is smaller. (Assuming the duration starts out at 1000.)
110     const int dur = max(0, you.duration[DUR_REPEL_STAIRS_MOVE] - 700);
111     pct += dur/10;
112 
113     if (x_chance_in_y(pct, 100))
114     {
115         const string stair_str = feature_description_at(you.pos(), false,
116                                                         DESC_THE);
117         const string prep = feat_preposition(new_grid, true, &you);
118 
119         if (slide_feature_over(you.pos()))
120         {
121             mprf("%s slides away as you move %s it!", stair_str.c_str(),
122                  prep.c_str());
123 
124             if (player_in_a_dangerous_place() && one_chance_in(5))
125                 xom_is_stimulated(25);
126         }
127     }
128 }
129 
check_moveto_cloud(const coord_def & p,const string & move_verb,bool * prompted)130 bool check_moveto_cloud(const coord_def& p, const string &move_verb,
131                         bool *prompted)
132 {
133     if (you.confused())
134         return true;
135 
136     const cloud_type ctype = env.map_knowledge(p).cloud();
137     // Don't prompt if already in a cloud of the same type.
138     if (is_damaging_cloud(ctype, true, cloud_is_yours_at(p))
139         && ctype != cloud_type_at(you.pos())
140         && !crawl_state.disables[DIS_CONFIRMATIONS])
141     {
142         // Don't prompt for steam unless we're at uncomfortably low hp.
143         if (ctype == CLOUD_STEAM)
144         {
145             int threshold = 20;
146             if (player_res_steam() < 0)
147                 threshold = threshold * 3 / 2;
148             threshold = threshold * you.time_taken / BASELINE_DELAY;
149             // Do prompt if we'd lose icemail, though.
150             if (you.hp > threshold && !you.has_mutation(MUT_CONDENSATION_SHIELD))
151                 return true;
152         }
153         // Don't prompt for meph if we have clarity, unless at very low HP.
154         if (ctype == CLOUD_MEPHITIC && you.clarity(false)
155             && you.hp > 2 * you.time_taken / BASELINE_DELAY)
156         {
157             return true;
158         }
159 
160         if (prompted)
161             *prompted = true;
162         string prompt = make_stringf("Really %s into that cloud of %s?",
163                                      move_verb.c_str(),
164                                      cloud_type_name(ctype).c_str());
165         learned_something_new(HINT_CLOUD_WARNING);
166 
167         if (!yesno(prompt.c_str(), false, 'n'))
168         {
169             canned_msg(MSG_OK);
170             return false;
171         }
172     }
173     return true;
174 }
175 
check_moveto_trap(const coord_def & p,const string & move_verb,bool * prompted)176 bool check_moveto_trap(const coord_def& p, const string &move_verb,
177                        bool *prompted)
178 {
179     // Boldly go into the unknown (for shadow step and other ranged move
180     // prompts)
181     if (env.map_knowledge(p).trap() == TRAP_UNASSIGNED)
182         return true;
183 
184     // If there's no trap, let's go.
185     trap_def* trap = trap_at(p);
186     if (!trap)
187         return true;
188 
189     if (trap->type == TRAP_ZOT && !trap->is_safe() && !crawl_state.disables[DIS_CONFIRMATIONS])
190     {
191         string msg = "Do you really want to %s into the Zot trap?";
192         string prompt = make_stringf(msg.c_str(), move_verb.c_str());
193 
194         if (prompted)
195             *prompted = true;
196         if (!yes_or_no("%s", prompt.c_str()))
197         {
198             canned_msg(MSG_OK);
199             return false;
200         }
201     }
202     else if (!trap->is_safe() && !crawl_state.disables[DIS_CONFIRMATIONS])
203     {
204         string prompt;
205 
206         if (prompted)
207             *prompted = true;
208         prompt = make_stringf("Really %s %s that %s?", move_verb.c_str(),
209                               (trap->type == TRAP_ALARM
210                                || trap->type == TRAP_PLATE) ? "onto"
211                               : "into",
212                               feature_description_at(p, false, DESC_BASENAME).c_str());
213         if (!yesno(prompt.c_str(), true, 'n'))
214         {
215             canned_msg(MSG_OK);
216             return false;
217         }
218     }
219     return true;
220 }
221 
_check_moveto_dangerous(const coord_def & p,const string & msg)222 static bool _check_moveto_dangerous(const coord_def& p, const string& msg)
223 {
224     if (you.can_swim() && feat_is_water(env.grid(p))
225         || you.airborne() || !is_feat_dangerous(env.grid(p)))
226     {
227         return true;
228     }
229 
230     if (!msg.empty())
231         mpr(msg);
232     else if (species::likes_water(you.species) && feat_is_water(env.grid(p)))
233     {
234         // player normally likes water, but is in a form that doesn't
235         mpr("You cannot enter water in your current form.");
236     }
237     else
238         canned_msg(MSG_UNTHINKING_ACT);
239     return false;
240 }
241 
check_moveto_terrain(const coord_def & p,const string & move_verb,const string & msg,bool * prompted)242 bool check_moveto_terrain(const coord_def& p, const string &move_verb,
243                           const string &msg, bool *prompted)
244 {
245     // Boldly go into the unknown (for shadow step and other ranged move
246     // prompts)
247     if (!env.map_knowledge(p).known())
248         return true;
249 
250     if (!_check_moveto_dangerous(p, msg))
251         return false;
252     if (!you.airborne() && env.grid(you.pos()) != DNGN_TOXIC_BOG
253         && env.grid(p) == DNGN_TOXIC_BOG)
254     {
255         string prompt;
256 
257         if (prompted)
258             *prompted = true;
259 
260         if (!msg.empty())
261             prompt = msg + " ";
262 
263         prompt += "Are you sure you want to " + move_verb
264                 + " into a toxic bog?";
265 
266         if (!yesno(prompt.c_str(), false, 'n'))
267         {
268             canned_msg(MSG_OK);
269             return false;
270         }
271     }
272     if (!need_expiration_warning() && need_expiration_warning(p)
273         && !crawl_state.disables[DIS_CONFIRMATIONS])
274     {
275         string prompt;
276 
277         if (prompted)
278             *prompted = true;
279 
280         if (!msg.empty())
281             prompt = msg + " ";
282 
283         prompt += "Are you sure you want to " + move_verb;
284 
285         if (you.ground_level())
286             prompt += " into ";
287         else
288             prompt += " over ";
289 
290         prompt += env.grid(p) == DNGN_DEEP_WATER ? "deep water" : "lava";
291 
292         prompt += need_expiration_warning(DUR_FLIGHT, p)
293             ? " while you are losing your buoyancy?"
294             : " while your transformation is expiring?";
295 
296         if (!yesno(prompt.c_str(), false, 'n'))
297         {
298             canned_msg(MSG_OK);
299             return false;
300         }
301     }
302     return true;
303 }
304 
check_moveto_exclusions(const vector<coord_def> & areas,const string & move_verb,bool * prompted)305 bool check_moveto_exclusions(const vector<coord_def> &areas,
306                              const string &move_verb,
307                              bool *prompted)
308 {
309     const bool you_pos_excluded = is_excluded(you.pos())
310             && !is_stair_exclusion(you.pos());
311     if (you_pos_excluded || crawl_state.disables[DIS_CONFIRMATIONS])
312         return true;
313 
314     int count = 0;
315     for (auto p : areas)
316     {
317         if (is_excluded(p) && !is_stair_exclusion(p))
318             count++;
319     }
320     if (count == 0)
321         return true;
322     const string prompt = make_stringf((count == (int) areas.size() ?
323                     "Really %s into a travel-excluded area?" :
324                     "You might %s into a travel-excluded area, are you sure?"),
325                               move_verb.c_str());
326 
327     if (prompted)
328         *prompted = true;
329     if (!yesno(prompt.c_str(), false, 'n'))
330     {
331         canned_msg(MSG_OK);
332         return false;
333     }
334     return true;
335 }
336 
check_moveto_exclusion(const coord_def & p,const string & move_verb,bool * prompted)337 bool check_moveto_exclusion(const coord_def& p, const string &move_verb,
338                             bool *prompted)
339 {
340     return check_moveto_exclusions({p}, move_verb, prompted);
341 }
342 
343 /**
344  * Confirm that the player really does want to go to the indicated place.
345  * May give many prompts, or no prompts if the move is safe.
346  *
347  * @param p          The location the player wants to go to
348  * @param move_verb  The method of locomotion the player is using
349  * @param physically Whether the player is considered to be "walking" for the
350  *                   purposes of barbs causing damage and ice spells expiring
351  * @return If true, continue with the move, otherwise cancel it
352  */
check_moveto(const coord_def & p,const string & move_verb,bool physically)353 bool check_moveto(const coord_def& p, const string &move_verb, bool physically)
354 {
355     return !(physically && cancel_harmful_move(physically, move_verb == "rampage"))
356            && check_moveto_terrain(p, move_verb, "")
357            && check_moveto_cloud(p, move_verb)
358            && check_moveto_trap(p, move_verb)
359            && check_moveto_exclusion(p, move_verb);
360 }
361 
362 // Returns true if this is a valid swap for this monster. If true, then
363 // the valid location is set in loc. (Otherwise loc becomes garbage.)
swap_check(monster * mons,coord_def & loc,bool quiet)364 bool swap_check(monster* mons, coord_def &loc, bool quiet)
365 {
366     loc = you.pos();
367 
368     if (you.is_stationary())
369         return false;
370 
371     // Don't move onto dangerous terrain.
372     if (is_feat_dangerous(env.grid(mons->pos())))
373     {
374         canned_msg(MSG_UNTHINKING_ACT);
375         return false;
376     }
377 
378     if (mons_is_projectile(*mons))
379     {
380         if (!quiet)
381             mpr("It's unwise to walk into this.");
382         return false;
383     }
384 
385     if (mons->caught())
386     {
387         if (!quiet)
388         {
389             simple_monster_message(*mons,
390                 make_stringf(" is %s!", held_status(mons)).c_str());
391         }
392         return false;
393     }
394 
395     if (mons->is_constricted())
396     {
397         if (!quiet)
398             simple_monster_message(*mons, " is being constricted!");
399         return false;
400     }
401 
402     if (mons->is_stationary() || mons->asleep() || mons->cannot_move())
403     {
404         if (!quiet)
405             simple_monster_message(*mons, " cannot move out of your way!");
406         return false;
407     }
408 
409     // prompt when swapping into known zot traps
410     if (!quiet && trap_at(loc) && trap_at(loc)->type == TRAP_ZOT
411         && !yes_or_no("Do you really want to swap %s into the Zot trap?",
412                       mons->name(DESC_YOUR).c_str()))
413     {
414         return false;
415     }
416 
417     // First try: move monster onto your position.
418     bool swap = !monster_at(loc) && monster_habitable_grid(mons, env.grid(loc));
419 
420     // Choose an appropriate habitat square at random around the target.
421     if (!swap)
422     {
423         int num_found = 0;
424 
425         for (adjacent_iterator ai(mons->pos()); ai; ++ai)
426             if (!monster_at(*ai) && monster_habitable_grid(mons, env.grid(*ai))
427                 && one_chance_in(++num_found))
428             {
429                 loc = *ai;
430             }
431 
432         if (num_found)
433             swap = true;
434     }
435 
436     if (!swap && !quiet)
437     {
438         // Might not be ideal, but it's better than insta-killing
439         // the monster... maybe try for a short blink instead? - bwr
440         simple_monster_message(*mons, " cannot make way for you.");
441         // FIXME: activity_interrupt::hit_monster isn't ideal.
442         interrupt_activity(activity_interrupt::hit_monster, mons);
443     }
444 
445     return swap;
446 }
447 
_splash()448 static void _splash()
449 {
450     if (you.can_swim())
451         noisy(4, you.pos(), "Floosh!");
452     else if (!you.can_water_walk())
453         noisy(8, you.pos(), "Splash!");
454 }
455 
moveto_location_effects(dungeon_feature_type old_feat,bool stepped,const coord_def & old_pos)456 void moveto_location_effects(dungeon_feature_type old_feat,
457                              bool stepped, const coord_def& old_pos)
458 {
459     const dungeon_feature_type new_grid = env.grid(you.pos());
460 
461     // Terrain effects.
462     if (is_feat_dangerous(new_grid))
463         fall_into_a_pool(new_grid);
464 
465     // called after fall_into_a_pool, in case of emergency untransform
466     if (you.has_innate_mutation(MUT_MERTAIL))
467         merfolk_check_swimming(stepped);
468 
469     if (you.ground_level())
470     {
471         if (feat_is_water(new_grid))
472         {
473             if (!stepped)
474                 _splash();
475 
476             if (!you.can_swim() && !you.can_water_walk())
477             {
478                 if (!feat_is_water(old_feat))
479                 {
480                     if (new_grid == DNGN_TOXIC_BOG)
481                     {
482                         mprf("You %s the toxic bog.",
483                                 stepped ? "enter" : "fall into");
484                     }
485                     else
486                     {
487                         mprf("You %s the %s water.",
488                              stepped ? "enter" : "fall into",
489                              new_grid == DNGN_SHALLOW_WATER ? "shallow"
490                              : "deep");
491                     }
492                 }
493 
494                 if (new_grid == DNGN_DEEP_WATER && old_feat != DNGN_DEEP_WATER)
495                     mpr("You sink to the bottom.");
496 
497                 if (!feat_is_water(old_feat))
498                 {
499                     mpr("Moving in this stuff is going to be slow.");
500                     if (you.invisible())
501                         mpr("...and don't expect to remain undetected.");
502                 }
503             }
504 
505             if ((you.can_swim() || you.extra_balanced())
506                 && !feat_is_water(old_feat)
507                 && you.invisible())
508             {
509                 mpr("Don't expect to remain undetected while in the water.");
510             }
511         }
512         else if (you.props.exists(TEMP_WATERWALK_KEY))
513             you.props.erase(TEMP_WATERWALK_KEY);
514     }
515 
516     id_floor_items();
517 
518     // Falling into a toxic bog, take the damage
519     if (old_pos == you.pos() && stepped)
520         actor_apply_toxic_bog(&you);
521 
522     // Traps go off.
523     // (But not when losing flight - i.e., moving into the same tile)
524     trap_def* ptrap = trap_at(you.pos());
525     if (ptrap && old_pos != you.pos())
526         ptrap->trigger(you);
527 
528     if (stepped)
529         _moveto_maybe_repel_stairs();
530 
531     update_monsters_in_view();
532     if (check_for_interesting_features() && you.running.is_explore())
533         stop_running();
534 }
535 
536 // Use this function whenever the player enters (or lands and thus re-enters)
537 // a grid.
538 //
539 // stepped     - normal walking moves
move_player_to_grid(const coord_def & p,bool stepped)540 void move_player_to_grid(const coord_def& p, bool stepped)
541 {
542     ASSERT(!crawl_state.game_is_arena());
543     ASSERT_IN_BOUNDS(p);
544 
545     // assuming that entering the same square means coming from above (flight)
546     const coord_def old_pos = you.pos();
547     const bool from_above = (old_pos == p);
548     const dungeon_feature_type old_grid =
549         (from_above) ? DNGN_FLOOR : env.grid(old_pos);
550 
551     // Really must be clear.
552     ASSERT(you.can_pass_through_feat(env.grid(p)));
553 
554     // Better not be an unsubmerged monster either.
555     ASSERT(!monster_at(p) || monster_at(p)->submerged()
556            || fedhas_passthrough(monster_at(p))
557            || mons_is_player_shadow(*monster_at(p))
558            || mons_is_wrath_avatar(*monster_at(p)));
559 
560     // Move the player to new location.
561     you.moveto(p, true);
562     viewwindow();
563     update_screen();
564 
565     moveto_location_effects(old_grid, stepped, old_pos);
566 }
567 
568 
569 /**
570  * Check if the given terrain feature is safe for the player to move into.
571  * (Or, at least, not instantly lethal.)
572  *
573  * @param grid          The type of terrain feature under consideration.
574  * @param permanently   Whether to disregard temporary effects (non-permanent
575  *                      flight, forms, etc)
576  * @param ignore_flight Whether to ignore all forms of flight (including
577  *                      permanent flight)
578  * @return              Whether the terrain is safe.
579  */
is_feat_dangerous(dungeon_feature_type grid,bool permanently,bool ignore_flight)580 bool is_feat_dangerous(dungeon_feature_type grid, bool permanently,
581                        bool ignore_flight)
582 {
583     if (!ignore_flight
584         && (you.permanent_flight() || you.airborne() && !permanently))
585     {
586         return false;
587     }
588     else if (grid == DNGN_DEEP_WATER && !player_likes_water(permanently)
589              || grid == DNGN_LAVA)
590     {
591         return true;
592     }
593     else
594         return false;
595 }
596 
is_map_persistent()597 bool is_map_persistent()
598 {
599     return !testbits(your_branch().branch_flags, brflag::no_map)
600            || env.properties.exists(FORCE_MAPPABLE_KEY);
601 }
602 
player_in_hell(bool vestibule)603 bool player_in_hell(bool vestibule)
604 {
605     return vestibule ? is_hell_branch(you.where_are_you) :
606                        is_hell_subbranch(you.where_are_you);
607 }
608 
609 /**
610  * Is the player in the slightly-special version of the abyss that AKs start
611  * in?
612  */
player_in_starting_abyss()613 bool player_in_starting_abyss()
614 {
615     return you.chapter == CHAPTER_POCKET_ABYSS
616            && player_in_branch(BRANCH_ABYSS) && you.depth <= 1;
617 }
618 
player_in_connected_branch()619 bool player_in_connected_branch()
620 {
621     return is_connected_branch(you.where_are_you);
622 }
623 
player_likes_water(bool permanently)624 bool player_likes_water(bool permanently)
625 {
626     return !permanently && you.can_water_walk()
627            || (species::likes_water(you.species) || !permanently)
628                && form_likes_water();
629 }
630 
631 /**
632  * Is the player considered to be closely related, if not the same species, to
633  * the given monster? (See mon-data.h for species/genus info.)
634  *
635  * @param mon   The type of monster to be compared.
636  * @return      Whether the player's species is related to the one given.
637  */
is_player_same_genus(const monster_type mon)638 bool is_player_same_genus(const monster_type mon)
639 {
640     return mons_genus(mon) == mons_genus(player_mons(false));
641 }
642 
update_player_symbol()643 void update_player_symbol()
644 {
645     you.symbol = Options.show_player_species ? player_mons() : transform_mons();
646 }
647 
player_mons(bool transform)648 monster_type player_mons(bool transform)
649 {
650     monster_type mons;
651 
652     if (transform)
653     {
654         mons = transform_mons();
655         if (mons != MONS_PLAYER)
656             return mons;
657     }
658 
659     mons = you.mons_species();
660 
661     if (mons == MONS_ORC)
662     {
663         if (you_worship(GOD_BEOGH))
664         {
665             mons = (you.piety >= piety_breakpoint(4)) ? MONS_ORC_HIGH_PRIEST
666                                                       : MONS_ORC_PRIEST;
667         }
668     }
669     else if (mons == MONS_OGRE)
670     {
671         const skill_type sk = best_skill(SK_FIRST_SKILL, SK_LAST_SKILL);
672         if (sk >= SK_SPELLCASTING && sk <= SK_LAST_MAGIC)
673             mons = MONS_OGRE_MAGE;
674     }
675 
676     return mons;
677 }
678 
update_vision_range()679 void update_vision_range()
680 {
681     you.normal_vision = LOS_DEFAULT_RANGE;
682 
683     // Daystalker gives +1 base LOS. (currently capped to one level for
684     // console reasons, a modular hud might someday permit more levels)
685     if (you.get_mutation_level(MUT_DAYSTALKER))
686         you.normal_vision += you.get_mutation_level(MUT_DAYSTALKER);
687 
688     // Nightstalker gives -1/-2/-3 to base LOS.
689     if (you.get_mutation_level(MUT_NIGHTSTALKER))
690         you.normal_vision -= you.get_mutation_level(MUT_NIGHTSTALKER);
691 
692     // Halo and umbra radius scale with you.normal_vision, so to avoid
693     // penalizing players with low LOS from items, don't shrink normal_vision.
694     you.current_vision = you.normal_vision;
695 
696     // scarf of shadows gives -1.
697     if (you.wearing_ego(EQ_CLOAK, SPARM_SHADOWS))
698         you.current_vision -= 1;
699 
700     // robe of Night.
701     if (player_equip_unrand(UNRAND_NIGHT))
702         you.current_vision = you.current_vision * 3 / 4;
703 
704     ASSERT(you.current_vision > 0);
705     set_los_radius(you.current_vision);
706 }
707 
708 /**
709  * Ignoring form & most equipment, but not the UNRAND_FINGER_AMULET, can the
710  * player use (usually wear) a given equipment slot?
711  *
712  * @param eq   The slot in question.
713  * @param temp Whether to consider forms.
714  * @return   MB_FALSE if the player can never use the slot;
715  *           MB_MAYBE if the player can only use some items for the slot;
716  *           MB_TRUE  if the player can use any (fsvo any) item for the slot.
717  */
you_can_wear(equipment_type eq,bool temp)718 maybe_bool you_can_wear(equipment_type eq, bool temp)
719 {
720     if (temp && !get_form()->slot_available(eq))
721         return MB_FALSE;
722 
723     // handles incorrect ring slots vs species
724     if (species::bans_eq(you.species, eq))
725         return MB_FALSE;
726 
727     switch (eq)
728     {
729     case EQ_RING_EIGHT:
730     case EQ_LEFT_RING:
731         if (you.get_mutation_level(MUT_MISSING_HAND))
732             return MB_FALSE;
733         // intentional fallthrough
734     case EQ_RIGHT_RING:
735     case EQ_RING_ONE:
736     case EQ_RING_TWO:
737     case EQ_RING_THREE:
738     case EQ_RING_FOUR:
739     case EQ_RING_FIVE:
740     case EQ_RING_SIX:
741     case EQ_RING_SEVEN:
742         return MB_TRUE;
743 
744     case EQ_WEAPON:
745     case EQ_STAFF:
746         return you.has_mutation(MUT_NO_GRASPING) ? MB_FALSE :
747                you.body_size(PSIZE_TORSO, !temp) < SIZE_MEDIUM ? MB_MAYBE :
748                                          MB_TRUE;
749 
750     // You can always wear at least one ring (forms were already handled).
751     case EQ_RINGS:
752     case EQ_ALL_ARMOUR:
753     case EQ_AMULET:
754         return MB_TRUE;
755 
756     case EQ_RING_AMULET:
757         return player_equip_unrand(UNRAND_FINGER_AMULET) ? MB_TRUE : MB_FALSE;
758 
759     default:
760         break;
761     }
762 
763     item_def dummy, alternate;
764     dummy.base_type = alternate.base_type = OBJ_ARMOUR;
765     dummy.sub_type = alternate.sub_type = NUM_ARMOURS;
766     // Make sure can_wear_armour doesn't think it's Lear's.
767     dummy.unrand_idx = alternate.unrand_idx = 0;
768 
769     switch (eq)
770     {
771     case EQ_CLOAK:
772         dummy.sub_type = ARM_CLOAK;
773         alternate.sub_type = ARM_SCARF;
774         break;
775 
776     case EQ_GLOVES:
777         dummy.sub_type = ARM_GLOVES;
778         break;
779 
780     case EQ_BOOTS: // And bardings
781         dummy.sub_type = ARM_BOOTS;
782         if (you.wear_barding())
783             alternate.sub_type = ARM_BARDING;
784         break;
785 
786     case EQ_BODY_ARMOUR:
787         // Assume that anything that can wear any armour at all can wear a robe
788         // and that anything that can wear CPA can wear all armour.
789         dummy.sub_type = ARM_CRYSTAL_PLATE_ARMOUR;
790         alternate.sub_type = ARM_ROBE;
791         break;
792 
793     case EQ_SHIELD:
794         // No races right now that can wear ARM_TOWER_SHIELD but not ARM_KITE_SHIELD
795         dummy.sub_type = ARM_TOWER_SHIELD;
796         if (you.body_size(PSIZE_TORSO, !temp) < SIZE_MEDIUM)
797             alternate.sub_type = ARM_BUCKLER;
798         break;
799 
800     case EQ_HELMET:
801         dummy.sub_type = ARM_HELMET;
802         alternate.sub_type = ARM_HAT;
803         break;
804 
805     default:
806         die("unhandled equipment type %d", eq);
807         break;
808     }
809 
810     ASSERT(dummy.sub_type != NUM_ARMOURS);
811 
812     if (can_wear_armour(dummy, false, !temp))
813         return MB_TRUE;
814     else if (alternate.sub_type != NUM_ARMOURS
815              && can_wear_armour(alternate, false, !temp))
816     {
817         return MB_MAYBE;
818     }
819     else
820         return MB_FALSE;
821 }
822 
player_has_feet(bool temp,bool include_mutations)823 bool player_has_feet(bool temp, bool include_mutations)
824 {
825     if (you.has_innate_mutation(MUT_CONSTRICTING_TAIL)
826         || you.has_innate_mutation(MUT_FLOAT)
827         || you.has_innate_mutation(MUT_PAWS) // paws are not feet?
828         || you.has_tentacles(temp)
829         || you.fishtail && temp)
830     {
831         return false;
832     }
833 
834     if (include_mutations &&
835         (you.get_mutation_level(MUT_HOOVES, temp) == 3
836          || you.get_mutation_level(MUT_TALONS, temp) == 3))
837     {
838         return false;
839     }
840 
841     return true;
842 }
843 
844 // Returns false if the player is wielding a weapon inappropriate for Berserk.
berserk_check_wielded_weapon()845 bool berserk_check_wielded_weapon()
846 {
847     const item_def * const wpn = you.weapon();
848     bool penance = false;
849     if (wpn && wpn->defined()
850         && (!is_melee_weapon(*wpn)
851             || needs_handle_warning(*wpn, OPER_ATTACK, penance)))
852     {
853         string prompt = "Do you really want to go berserk while wielding "
854                         + wpn->name(DESC_YOUR) + "?";
855         if (penance)
856             prompt += " This could place you under penance!";
857 
858         if (!yesno(prompt.c_str(), true, 'n'))
859         {
860             canned_msg(MSG_OK);
861             return false;
862         }
863     }
864 
865     return true;
866 }
867 
868 // Looks in equipment "slot" to see if there is an equipped "sub_type".
869 // Returns number of matches (in the case of rings, both are checked)
wearing(equipment_type slot,int sub_type,bool calc_unid) const870 int player::wearing(equipment_type slot, int sub_type, bool calc_unid) const
871 {
872     int ret = 0;
873 
874     const item_def* item;
875 
876     switch (slot)
877     {
878     case EQ_WEAPON:
879         // Hands can have more than just weapons.
880         if (weapon() && weapon()->is_type(OBJ_WEAPONS, sub_type))
881             ret++;
882         break;
883 
884     case EQ_STAFF:
885         // Like above, but must be magical staff.
886         if (weapon()
887             && weapon()->is_type(OBJ_STAVES, sub_type)
888             && (calc_unid || item_type_known(*weapon())))
889         {
890             ret++;
891         }
892         break;
893 
894     case EQ_AMULET:
895         if ((item = slot_item(static_cast<equipment_type>(EQ_AMULET)))
896             && item->sub_type == sub_type
897             && (calc_unid
898                 || item_type_known(*item)))
899         {
900             ret++;
901         }
902         break;
903 
904     case EQ_RINGS:
905     case EQ_RINGS_PLUS:
906         for (int slots = EQ_FIRST_JEWELLERY; slots <= EQ_LAST_JEWELLERY; slots++)
907         {
908             if (slots == EQ_AMULET)
909                 continue;
910 
911             if ((item = slot_item(static_cast<equipment_type>(slots)))
912                 && item->sub_type == sub_type
913                 && (calc_unid
914                     || item_type_known(*item)))
915             {
916                 ret += (slot == EQ_RINGS_PLUS ? item->plus : 1);
917             }
918         }
919         break;
920 
921     case EQ_ALL_ARMOUR:
922         // Doesn't make much sense here... be specific. -- bwr
923         die("EQ_ALL_ARMOUR is not a proper slot");
924         break;
925 
926     default:
927         if (! (slot >= EQ_FIRST_EQUIP && slot < NUM_EQUIP))
928             die("invalid slot");
929         if ((item = slot_item(slot))
930             && item->sub_type == sub_type
931             && (calc_unid || item_type_known(*item)))
932         {
933             ret++;
934         }
935         break;
936     }
937 
938     return ret;
939 }
940 
941 // Looks in equipment "slot" to see if equipped item has "special" ego-type
942 // Returns number of matches (jewellery returns zero -- no ego type).
943 // [ds] There's no equivalent of calc_unid or req_id because as of now, weapons
944 // and armour type-id on wield/wear.
wearing_ego(equipment_type slot,int special,bool calc_unid) const945 int player::wearing_ego(equipment_type slot, int special, bool calc_unid) const
946 {
947     int ret = 0;
948 
949     const item_def* item;
950     switch (slot)
951     {
952     case EQ_WEAPON:
953         // Hands can have more than just weapons.
954         if ((item = slot_item(EQ_WEAPON))
955             && item->base_type == OBJ_WEAPONS
956             && get_weapon_brand(*item) == special)
957         {
958             ret++;
959         }
960         break;
961 
962     case EQ_LEFT_RING:
963     case EQ_RIGHT_RING:
964     case EQ_AMULET:
965     case EQ_STAFF:
966     case EQ_RINGS:
967     case EQ_RINGS_PLUS:
968         // no ego types for these slots
969         break;
970 
971     case EQ_ALL_ARMOUR:
972         // Check all armour slots:
973         for (int i = EQ_MIN_ARMOUR; i <= EQ_MAX_ARMOUR; i++)
974         {
975             if ((item = slot_item(static_cast<equipment_type>(i)))
976                 && get_armour_ego_type(*item) == special
977                 && (calc_unid || item_type_known(*item)))
978             {
979                 ret++;
980             }
981         }
982         break;
983 
984     default:
985         if (slot < EQ_MIN_ARMOUR || slot > EQ_MAX_ARMOUR)
986             die("invalid slot: %d", slot);
987         // Check a specific armour slot for an ego type:
988         if ((item = slot_item(static_cast<equipment_type>(slot)))
989             && get_armour_ego_type(*item) == special
990             && (calc_unid || item_type_known(*item)))
991         {
992             ret++;
993         }
994         break;
995     }
996 
997     return ret;
998 }
999 
1000 // Returns true if the indicated unrandart is equipped
1001 // [ds] There's no equivalent of calc_unid or req_id because as of now, weapons
1002 // and armour type-id on wield/wear.
player_equip_unrand(int unrand_index,bool include_melded)1003 bool player_equip_unrand(int unrand_index, bool include_melded)
1004 {
1005     const unrandart_entry* entry = get_unrand_entry(unrand_index);
1006     equipment_type   slot  = get_item_slot(entry->base_type,
1007                                            entry->sub_type);
1008 
1009     item_def* item;
1010 
1011     switch (slot)
1012     {
1013     case EQ_WEAPON:
1014         // Hands can have more than just weapons.
1015         if ((item = you.slot_item(slot, include_melded))
1016             && item->base_type == OBJ_WEAPONS
1017             && is_unrandom_artefact(*item)
1018             && item->unrand_idx == unrand_index)
1019         {
1020             return true;
1021         }
1022         break;
1023 
1024     case EQ_RINGS:
1025         for (int slots = EQ_FIRST_JEWELLERY; slots <= EQ_LAST_JEWELLERY; ++slots)
1026         {
1027             if (slots == EQ_AMULET)
1028                 continue;
1029 
1030             if ((item = you.slot_item(static_cast<equipment_type>(slots), include_melded))
1031                 && is_unrandom_artefact(*item)
1032                 && item->unrand_idx == unrand_index)
1033             {
1034                 return true;
1035             }
1036         }
1037         break;
1038 
1039     case EQ_NONE:
1040     case EQ_STAFF:
1041     case EQ_LEFT_RING:
1042     case EQ_RIGHT_RING:
1043     case EQ_RINGS_PLUS:
1044     case EQ_ALL_ARMOUR:
1045         // no unrandarts for these slots.
1046         break;
1047 
1048     default:
1049         if (slot <= EQ_NONE || slot >= NUM_EQUIP)
1050             die("invalid slot: %d", slot);
1051         // Check a specific slot.
1052         if ((item = you.slot_item(slot, include_melded))
1053             && is_unrandom_artefact(*item)
1054             && item->unrand_idx == unrand_index)
1055         {
1056             return true;
1057         }
1058         break;
1059     }
1060 
1061     return false;
1062 }
1063 
player_can_hear(const coord_def & p,int hear_distance)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 
player_teleport(bool calc_unid)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 or Gauntlets.
1076     if (crawl_state.game_is_sprint() || player_in_branch(BRANCH_GAUNTLET))
1077         return 0;
1078 
1079     // Short-circuit rings of teleport to prevent spam.
1080     if (you.stasis())
1081         return 0;
1082 
1083     int tp = 0;
1084 
1085     // artefacts
1086     tp += 8 * you.scan_artefacts(ARTP_CAUSE_TELEPORTATION, calc_unid);
1087 
1088     // mutations
1089     tp += you.get_mutation_level(MUT_TELEPORT) * 4;
1090 
1091     return tp;
1092 }
1093 
1094 // Computes bonuses to regeneration from most sources. Does not handle
1095 // slow regeneration, vampireness, or Trog's Hand.
_player_bonus_regen()1096 static int _player_bonus_regen()
1097 {
1098     int rr = 0;
1099 
1100     // Amulets, troll leather armour, and artefacts.
1101     for (int slot = EQ_MIN_ARMOUR; slot <= EQ_MAX_WORN; ++slot)
1102     {
1103         if (you.melded[slot] || you.equip[slot] == -1 || !you.activated[slot])
1104             continue;
1105         const item_def &arm = you.inv[you.equip[slot]];
1106         if (arm.base_type == OBJ_ARMOUR
1107             && armour_type_prop(arm.sub_type, ARMF_REGENERATION))
1108         {
1109             rr += REGEN_PIP;
1110         }
1111         if (arm.is_type(OBJ_JEWELLERY, AMU_REGENERATION))
1112             rr += REGEN_PIP;
1113         if (is_artefact(arm))
1114             rr += REGEN_PIP * artefact_property(arm, ARTP_REGENERATION);
1115     }
1116 
1117     // Fast heal mutation.
1118     rr += you.get_mutation_level(MUT_REGENERATION) * REGEN_PIP;
1119 
1120     // Powered By Death mutation, boosts regen by variable strength
1121     // if the duration of the effect is still active.
1122     if (you.duration[DUR_POWERED_BY_DEATH])
1123         rr += you.props[POWERED_BY_DEATH_KEY].get_int() * REGEN_PIP;
1124 
1125     return rr;
1126 }
1127 
1128 /// Is the player's hp regeneration inhibited by nearby monsters?
regeneration_is_inhibited()1129 bool regeneration_is_inhibited()
1130 {
1131     // used mainly for resting: don't add anything here that can be waited off
1132     if (you.get_mutation_level(MUT_INHIBITED_REGENERATION) == 1
1133         || (you.has_mutation(MUT_VAMPIRISM) && !you.vampire_alive))
1134     {
1135         for (monster_near_iterator mi(you.pos(), LOS_NO_TRANS); mi; ++mi)
1136         {
1137             if (mons_is_threatening(**mi)
1138                 && !mi->wont_attack()
1139                 && !mi->neutral()
1140                 && !mi->submerged())
1141             {
1142                 return true;
1143             }
1144         }
1145     }
1146 
1147     return false;
1148 }
1149 
player_regen()1150 int player_regen()
1151 {
1152     // Note: if some condition can set rr = 0, can't be rested off, and
1153     // would allow travel, please update is_sufficiently_rested.
1154 
1155     int rr = you.hp_max / 3;
1156 
1157     if (rr > 20)
1158         rr = 20 + ((rr - 20) / 2);
1159 
1160     // Add in miscellaneous bonuses
1161     rr += _player_bonus_regen();
1162 
1163     // Before applying other effects, make sure that there's something
1164     // to heal.
1165     rr = max(1, rr);
1166 
1167     // Bonus regeneration for alive vampires.
1168     if (you.has_mutation(MUT_VAMPIRISM) && you.vampire_alive)
1169         rr += 20;
1170 
1171     if (you.duration[DUR_COLLAPSE])
1172         rr /= 4;
1173 
1174     if (you.duration[DUR_SICKNESS]
1175         || !player_regenerates_hp())
1176     {
1177         rr = 0;
1178     }
1179 
1180     // Trog's Hand. This circumvents sickness or inhibited regeneration.
1181     if (you.duration[DUR_TROGS_HAND])
1182         rr += REGEN_PIP;
1183 
1184     return rr;
1185 }
1186 
player_mp_regen()1187 int player_mp_regen()
1188 {
1189     if (you.has_mutation(MUT_HP_CASTING))
1190         return 0;
1191 
1192     int regen_amount = 7 + you.max_magic_points / 2;
1193 
1194     if (you.get_mutation_level(MUT_MANA_REGENERATION))
1195         regen_amount *= 2;
1196 
1197     if (you.props[MANA_REGEN_AMULET_ACTIVE].get_int() == 1)
1198         regen_amount += 25;
1199 
1200     return regen_amount;
1201 }
1202 
1203 /**
1204  * How many spell levels does the player have total, including those used up
1205  * by memorised spells?
1206  */
player_total_spell_levels()1207 int player_total_spell_levels()
1208 {
1209     return you.experience_level - 1 + you.skill(SK_SPELLCASTING, 2, false, false);
1210 }
1211 
1212 /**
1213  * How many spell levels does the player currently have available for
1214  * memorising new spells?
1215  */
player_spell_levels()1216 int player_spell_levels()
1217 {
1218     int sl = min(player_total_spell_levels(), 99);
1219 
1220     for (const spell_type spell : you.spells)
1221     {
1222         if (spell != SPELL_NO_SPELL)
1223             sl -= spell_difficulty(spell);
1224     }
1225 
1226     if (sl < 0)
1227         sl = 0;
1228 
1229     return sl;
1230 }
1231 
1232 // If temp is set to false, temporary sources or resistance won't be counted.
player_res_fire(bool calc_unid,bool temp,bool items)1233 int player_res_fire(bool calc_unid, bool temp, bool items)
1234 {
1235     int rf = 0;
1236 
1237     if (items)
1238     {
1239         // rings of fire resistance/fire
1240         rf += you.wearing(EQ_RINGS, RING_PROTECTION_FROM_FIRE, calc_unid);
1241         rf += you.wearing(EQ_RINGS, RING_FIRE, calc_unid);
1242 
1243         // rings of ice
1244         rf -= you.wearing(EQ_RINGS, RING_ICE, calc_unid);
1245 
1246         // Staves
1247         rf += you.wearing(EQ_STAFF, STAFF_FIRE, calc_unid);
1248 
1249         // body armour:
1250         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1251         if (body_armour)
1252             rf += armour_type_prop(body_armour->sub_type, ARMF_RES_FIRE);
1253 
1254         // ego armours
1255         rf += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_FIRE_RESISTANCE);
1256         rf += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_RESISTANCE);
1257 
1258         // randart weapons:
1259         rf += you.scan_artefacts(ARTP_FIRE, calc_unid);
1260 
1261         // dragonskin cloak: 0.5 to draconic resistances
1262         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN)
1263             && coinflip())
1264         {
1265             rf++;
1266         }
1267     }
1268 
1269     // mutations:
1270     rf += you.get_mutation_level(MUT_HEAT_RESISTANCE, temp);
1271     rf -= you.get_mutation_level(MUT_HEAT_VULNERABILITY, temp);
1272     rf -= you.get_mutation_level(MUT_TEMPERATURE_SENSITIVITY, temp);
1273     rf += you.get_mutation_level(MUT_MOLTEN_SCALES, temp) == 3 ? 1 : 0;
1274 
1275     // spells:
1276     if (temp)
1277     {
1278         if (you.duration[DUR_RESISTANCE])
1279             rf++;
1280 
1281         if (you.duration[DUR_QAZLAL_FIRE_RES])
1282             rf++;
1283 
1284         rf += get_form()->res_fire();
1285     }
1286 
1287     if (rf > 3)
1288         rf = 3;
1289     if (temp && you.duration[DUR_FIRE_VULN])
1290         rf--;
1291     if (rf < -3)
1292         rf = -3;
1293 
1294     return rf;
1295 }
1296 
player_res_steam(bool calc_unid,bool temp,bool items)1297 int player_res_steam(bool calc_unid, bool temp, bool items)
1298 {
1299     int res = 0;
1300     const int rf = player_res_fire(calc_unid, temp, items);
1301 
1302     res += you.get_mutation_level(MUT_STEAM_RESISTANCE) * 2;
1303 
1304     if (items)
1305     {
1306         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1307         if (body_armour)
1308             res += armour_type_prop(body_armour->sub_type, ARMF_RES_STEAM) * 2;
1309     }
1310 
1311     res += rf * 2;
1312 
1313     if (res > 2)
1314         res = 2;
1315 
1316     return res;
1317 }
1318 
player_res_cold(bool calc_unid,bool temp,bool items)1319 int player_res_cold(bool calc_unid, bool temp, bool items)
1320 {
1321     int rc = 0;
1322 
1323     if (temp)
1324     {
1325         if (you.duration[DUR_RESISTANCE])
1326             rc++;
1327 
1328         if (you.duration[DUR_QAZLAL_COLD_RES])
1329             rc++;
1330 
1331         rc += get_form()->res_cold();
1332 
1333         // XX temp?
1334         if (you.has_mutation(MUT_VAMPIRISM) && !you.vampire_alive)
1335             rc += 2;
1336     }
1337 
1338     if (items)
1339     {
1340         // rings of cold resistance/ice
1341         rc += you.wearing(EQ_RINGS, RING_PROTECTION_FROM_COLD, calc_unid);
1342         rc += you.wearing(EQ_RINGS, RING_ICE, calc_unid);
1343 
1344         // rings of fire
1345         rc -= you.wearing(EQ_RINGS, RING_FIRE, calc_unid);
1346 
1347         // Staves
1348         rc += you.wearing(EQ_STAFF, STAFF_COLD, calc_unid);
1349 
1350         // body armour:
1351         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1352         if (body_armour)
1353             rc += armour_type_prop(body_armour->sub_type, ARMF_RES_COLD);
1354 
1355         // ego armours
1356         rc += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_COLD_RESISTANCE);
1357         rc += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_RESISTANCE);
1358 
1359         // randart weapons:
1360         rc += you.scan_artefacts(ARTP_COLD, calc_unid);
1361 
1362         // dragonskin cloak: 0.5 to draconic resistances
1363         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1364             rc++;
1365     }
1366 
1367     // mutations:
1368     rc += you.get_mutation_level(MUT_COLD_RESISTANCE, temp);
1369     rc -= you.get_mutation_level(MUT_COLD_VULNERABILITY, temp);
1370     rc -= you.get_mutation_level(MUT_TEMPERATURE_SENSITIVITY, temp);
1371     rc += you.get_mutation_level(MUT_ICY_BLUE_SCALES, temp) == 3 ? 1 : 0;
1372     rc += you.get_mutation_level(MUT_SHAGGY_FUR, temp) == 3 ? 1 : 0;
1373 
1374     if (rc < -3)
1375         rc = -3;
1376     else if (rc > 3)
1377         rc = 3;
1378 
1379     return rc;
1380 }
1381 
res_corr(bool calc_unid,bool temp) const1382 bool player::res_corr(bool calc_unid, bool temp) const
1383 {
1384     if (temp)
1385     {
1386         // dragonskin cloak: 0.5 to draconic resistances
1387         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1388             return true;
1389 
1390         if (get_form()->res_acid())
1391             return true;
1392 
1393         if (you.duration[DUR_RESISTANCE])
1394             return true;
1395     }
1396 
1397     if (have_passive(passive_t::resist_corrosion))
1398         return true;
1399 
1400     if (get_mutation_level(MUT_ACID_RESISTANCE))
1401         return true;
1402 
1403     // TODO: why doesn't this use the usual form suppression mechanism?
1404     if (form_keeps_mutations()
1405         && get_mutation_level(MUT_YELLOW_SCALES) >= 3)
1406     {
1407         return true;
1408     }
1409 
1410     return actor::res_corr(calc_unid, temp);
1411 }
1412 
player_res_acid(bool calc_unid,bool items)1413 int player_res_acid(bool calc_unid, bool items)
1414 {
1415     return you.res_corr(calc_unid, items) ? 1 : 0;
1416 }
1417 
player_res_electricity(bool calc_unid,bool temp,bool items)1418 int player_res_electricity(bool calc_unid, bool temp, bool items)
1419 {
1420     int re = 0;
1421 
1422     if (items)
1423     {
1424         // staff
1425         re += you.wearing(EQ_STAFF, STAFF_AIR, calc_unid);
1426 
1427         // body armour:
1428         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1429         if (body_armour)
1430             re += armour_type_prop(body_armour->sub_type, ARMF_RES_ELEC);
1431 
1432         // randart weapons:
1433         re += you.scan_artefacts(ARTP_ELECTRICITY, calc_unid);
1434 
1435         // dragonskin cloak: 0.5 to draconic resistances
1436         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1437             re++;
1438     }
1439 
1440     // mutations:
1441     re += you.get_mutation_level(MUT_THIN_METALLIC_SCALES, temp) == 3 ? 1 : 0;
1442     re += you.get_mutation_level(MUT_SHOCK_RESISTANCE, temp);
1443     re -= you.get_mutation_level(MUT_SHOCK_VULNERABILITY, temp);
1444 
1445     if (temp)
1446     {
1447         if (you.duration[DUR_RESISTANCE])
1448             re++;
1449 
1450         if (you.duration[DUR_QAZLAL_ELEC_RES])
1451             re++;
1452 
1453         // transformations:
1454         if (get_form()->res_elec())
1455             re++;
1456     }
1457 
1458     if (re > 1)
1459         re = 1;
1460 
1461     return re;
1462 }
1463 
1464 // Kiku protects you from torment to a degree.
player_kiku_res_torment()1465 bool player_kiku_res_torment()
1466 {
1467     // no protection during pain branding weapon
1468     return have_passive(passive_t::resist_torment)
1469            && !(you_worship(GOD_KIKUBAAQUDGHA) && you.gift_timeout);
1470 }
1471 
1472 // If temp is set to false, temporary sources or resistance won't be counted.
player_res_poison(bool calc_unid,bool temp,bool items)1473 int player_res_poison(bool calc_unid, bool temp, bool items)
1474 {
1475     if (you.is_nonliving(temp)
1476         || you.is_lifeless_undead(temp)
1477         || temp && get_form()->res_pois() == 3
1478         || items && player_equip_unrand(UNRAND_OLGREB)
1479         || temp && you.duration[DUR_DIVINE_STAMINA])
1480     {
1481         return 3;
1482     }
1483 
1484     int rp = 0;
1485 
1486     if (items)
1487     {
1488         // rings of poison resistance
1489         rp += you.wearing(EQ_RINGS, RING_POISON_RESISTANCE, calc_unid);
1490 
1491         // Staves
1492         rp += you.wearing(EQ_STAFF, STAFF_POISON, calc_unid);
1493 
1494         // ego armour:
1495         rp += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_POISON_RESISTANCE);
1496 
1497         // body armour:
1498         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1499         if (body_armour)
1500             rp += armour_type_prop(body_armour->sub_type, ARMF_RES_POISON);
1501 
1502         // rPois+ artefacts
1503         rp += you.scan_artefacts(ARTP_POISON, calc_unid);
1504 
1505         // dragonskin cloak: 0.5 to draconic resistances
1506         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1507             rp++;
1508     }
1509 
1510     // mutations:
1511     rp += you.get_mutation_level(MUT_POISON_RESISTANCE, temp);
1512     rp += you.get_mutation_level(MUT_SLIMY_GREEN_SCALES, temp) == 3 ? 1 : 0;
1513 
1514     if (temp)
1515     {
1516         // potions/cards:
1517         if (you.duration[DUR_RESISTANCE])
1518             rp++;
1519 
1520         if (get_form()->res_pois() > 0)
1521             rp++;
1522     }
1523 
1524     // Cap rPois at + before vulnerability effects are applied
1525     // (so carrying multiple rPois effects is never useful)
1526     rp = min(1, rp);
1527 
1528     if (temp)
1529     {
1530         if (get_form()->res_pois() < 0)
1531             rp--;
1532 
1533         if (you.duration[DUR_POISON_VULN])
1534             rp--;
1535     }
1536 
1537     // don't allow rPois--, etc.
1538     rp = max(-1, rp);
1539 
1540     return rp;
1541 }
1542 
player_res_sticky_flame()1543 int player_res_sticky_flame()
1544 {
1545     return get_form()->res_sticky_flame();
1546 }
1547 
player_spec_death()1548 int player_spec_death()
1549 {
1550     int sd = 0;
1551 
1552     // Staves
1553     sd += you.wearing(EQ_STAFF, STAFF_DEATH);
1554 
1555     // species:
1556     sd += you.get_mutation_level(MUT_NECRO_ENHANCER);
1557 
1558     // transformations:
1559     if (you.form == transformation::lich)
1560         sd++;
1561 
1562     return sd;
1563 }
1564 
player_spec_fire()1565 int player_spec_fire()
1566 {
1567     int sf = 0;
1568 
1569     // staves:
1570     sf += you.wearing(EQ_STAFF, STAFF_FIRE);
1571 
1572     // rings of fire:
1573     sf += you.wearing(EQ_RINGS, RING_FIRE);
1574 
1575     if (player_equip_unrand(UNRAND_SALAMANDER))
1576         sf++;
1577 
1578     if (player_equip_unrand(UNRAND_ELEMENTAL_STAFF))
1579         sf++;
1580 
1581     return sf;
1582 }
1583 
player_spec_cold()1584 int player_spec_cold()
1585 {
1586     int sc = 0;
1587 
1588     // staves:
1589     sc += you.wearing(EQ_STAFF, STAFF_COLD);
1590 
1591     // rings of ice:
1592     sc += you.wearing(EQ_RINGS, RING_ICE);
1593 
1594     if (player_equip_unrand(UNRAND_ELEMENTAL_STAFF))
1595         sc++;
1596 
1597     return sc;
1598 }
1599 
player_spec_earth()1600 int player_spec_earth()
1601 {
1602     int se = 0;
1603 
1604     // Staves
1605     se += you.wearing(EQ_STAFF, STAFF_EARTH);
1606 
1607     if (player_equip_unrand(UNRAND_ELEMENTAL_STAFF))
1608         se++;
1609 
1610     return se;
1611 }
1612 
player_spec_air()1613 int player_spec_air()
1614 {
1615     int sa = 0;
1616 
1617     // Staves
1618     sa += you.wearing(EQ_STAFF, STAFF_AIR);
1619 
1620     if (player_equip_unrand(UNRAND_ELEMENTAL_STAFF)
1621         || player_equip_unrand(UNRAND_AIR))
1622     {
1623         sa++;
1624     }
1625 
1626     return sa;
1627 }
1628 
player_spec_conj()1629 int player_spec_conj()
1630 {
1631     int sc = 0;
1632 
1633     // Staves
1634     sc += you.wearing(EQ_STAFF, STAFF_CONJURATION);
1635 
1636     if (player_equip_unrand(UNRAND_BATTLE))
1637         sc++;
1638 
1639     return sc;
1640 }
1641 
player_spec_hex()1642 int player_spec_hex()
1643 {
1644     int sh = 0;
1645 
1646     // Demonspawn mutation
1647     sh += you.get_mutation_level(MUT_HEX_ENHANCER);
1648 
1649     return sh;
1650 }
1651 
player_spec_summ()1652 int player_spec_summ()
1653 {
1654     return 0;
1655 }
1656 
player_spec_poison()1657 int player_spec_poison()
1658 {
1659     int sp = 0;
1660 
1661     // Staves
1662     sp += you.wearing(EQ_STAFF, STAFF_POISON);
1663 
1664     if (player_equip_unrand(UNRAND_OLGREB))
1665         sp++;
1666 
1667     return sp;
1668 }
1669 
1670 // If temp is set to false, temporary sources of resistance won't be
1671 // counted.
player_prot_life(bool calc_unid,bool temp,bool items)1672 int player_prot_life(bool calc_unid, bool temp, bool items)
1673 {
1674     int pl = 0;
1675 
1676     // XX temp?
1677     if (you.has_mutation(MUT_VAMPIRISM) && !you.vampire_alive)
1678         pl = 3;
1679 
1680     // piety-based rN doesn't count as temporary (XX why)
1681     if (you_worship(GOD_SHINING_ONE))
1682     {
1683         if (you.piety >= piety_breakpoint(1))
1684             pl++;
1685         if (you.piety >= piety_breakpoint(3))
1686             pl++;
1687         if (you.piety >= piety_breakpoint(5))
1688             pl++;
1689     }
1690 
1691     if (temp)
1692     {
1693         pl += get_form()->res_neg();
1694 
1695         // completely stoned, unlike statue which has some life force
1696         if (you.petrified())
1697             pl += 3;
1698     }
1699 
1700     if (items)
1701     {
1702         // rings
1703         pl += you.wearing(EQ_RINGS, RING_LIFE_PROTECTION, calc_unid);
1704 
1705         // armour (checks body armour only)
1706         pl += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_POSITIVE_ENERGY);
1707 
1708         // pearl dragon counts
1709         const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
1710         if (body_armour)
1711             pl += armour_type_prop(body_armour->sub_type, ARMF_RES_NEG);
1712 
1713         // randart wpns
1714         pl += you.scan_artefacts(ARTP_NEGATIVE_ENERGY, calc_unid);
1715 
1716         // dragonskin cloak: 0.5 to draconic resistances
1717         if (calc_unid && player_equip_unrand(UNRAND_DRAGONSKIN) && coinflip())
1718             pl++;
1719 
1720         pl += you.wearing(EQ_STAFF, STAFF_DEATH, calc_unid);
1721     }
1722 
1723     // undead/demonic power
1724     pl += you.get_mutation_level(MUT_NEGATIVE_ENERGY_RESISTANCE, temp);
1725 
1726     pl = min(3, pl);
1727 
1728     return pl;
1729 }
1730 
1731 // Even a slight speed advantage is very good... and we certainly don't
1732 // want to go past 6 (see below). -- bwr
player_movement_speed()1733 int player_movement_speed()
1734 {
1735     int mv = you.form == transformation::none
1736         ? 10
1737         : form_base_movespeed(you.form);
1738 
1739     if (feat_is_water(env.grid(you.pos())))
1740     {
1741         if (you.get_mutation_level(MUT_NIMBLE_SWIMMER) >= 2)
1742             mv -= 4;
1743         else if (you.in_water() && !you.can_swim())
1744             mv += 6; // Wading through water is very slow.
1745     }
1746 
1747     // moving on liquefied ground, or while maintaining the
1748     // effect takes longer
1749     if (you.liquefied_ground() || you.duration[DUR_LIQUEFYING])
1750         mv += 3;
1751 
1752     // armour
1753     if (player_equip_unrand(UNRAND_LIGHTNING_SCALES))
1754         mv -= 1;
1755 
1756     mv += you.wearing_ego(EQ_ALL_ARMOUR, SPARM_PONDEROUSNESS);
1757 
1758     // Cheibriados
1759     if (have_passive(passive_t::slowed))
1760         mv += 2 + min(div_rand_round(you.piety, 20), 8);
1761     else if (player_under_penance(GOD_CHEIBRIADOS))
1762         mv += 2 + min(div_rand_round(you.piety_max[GOD_CHEIBRIADOS], 20), 8);
1763 
1764     // Tengu can move slightly faster when flying.
1765     if (you.tengu_flight())
1766         mv--;
1767 
1768     if (you.duration[DUR_FROZEN])
1769         mv += 3;
1770 
1771     // Mutations: -2, -3, -4, unless innate and shapechanged.
1772     if (int fast = you.get_mutation_level(MUT_FAST))
1773         mv -= fast + 1;
1774 
1775     if (int slow = you.get_mutation_level(MUT_SLOW))
1776     {
1777         mv *= 10 + slow * 2;
1778         mv /= 10;
1779     }
1780 
1781     if (you.duration[DUR_SWIFTNESS] > 0 && !you.in_liquid())
1782     {
1783         if (you.attribute[ATTR_SWIFTNESS] > 0)
1784           mv = div_rand_round(3*mv, 4);
1785         else if (mv >= 8)
1786           mv = div_rand_round(3*mv, 2);
1787         else if (mv == 7)
1788           mv = div_rand_round(7*6, 5); // balance for the cap at 6
1789     }
1790 
1791     // We'll use the old value of six as a minimum, with haste this could
1792     // end up as a speed of three, which is about as fast as we want
1793     // the player to be able to go (since 3 is 3.33x as fast and 2 is 5x,
1794     // which is a bit of a jump, and a bit too fast) -- bwr
1795     // Currently Haste takes 6 to 4, which is 2.5x as fast as delay 10
1796     // and still seems plenty fast. -- elliptic
1797     if (mv < FASTEST_PLAYER_MOVE_SPEED)
1798         mv = FASTEST_PLAYER_MOVE_SPEED;
1799 
1800     return mv;
1801 }
1802 
1803 /**
1804  * Multiply the power of some evocation per the player's current evocations
1805  * enhancers.
1806  *
1807  * @param power         The base power of the evocation.
1808  * @param enhancers     Bonus enhancers to evocations (pak device surge).
1809  * @return              A modified power value.
1810  */
player_adjust_evoc_power(const int power,int enhancers)1811 int player_adjust_evoc_power(const int power, int enhancers)
1812 {
1813     const int total_enhancers = you.spec_evoke() + enhancers;
1814     return stepdown_spellpower(100 *apply_enhancement(power, total_enhancers));
1815 }
1816 
1817 // This function differs from the above in that it's used to set the
1818 // initial time_taken value for the turn. Everything else (movement,
1819 // spellcasting, combat) applies a ratio to this value.
player_speed()1820 int player_speed()
1821 {
1822     int ps = 10;
1823 
1824     // When paralysed, speed is irrelevant.
1825     if (you.cannot_act())
1826         return ps;
1827 
1828     if (you.duration[DUR_SLOW] || have_stat_zero())
1829         ps = haste_mul(ps);
1830 
1831     if (you.duration[DUR_BERSERK] && !have_passive(passive_t::no_haste))
1832         ps = berserk_div(ps);
1833     else if (you.duration[DUR_HASTE])
1834         ps = haste_div(ps);
1835 
1836     if (you.form == transformation::statue || you.duration[DUR_PETRIFYING])
1837     {
1838         ps *= 15;
1839         ps /= 10;
1840     }
1841 
1842     return ps;
1843 }
1844 
is_effectively_light_armour(const item_def * item)1845 bool is_effectively_light_armour(const item_def *item)
1846 {
1847     return !item
1848            || (abs(property(*item, PARM_EVASION)) / 10 < 5);
1849 }
1850 
player_effectively_in_light_armour()1851 bool player_effectively_in_light_armour()
1852 {
1853     const item_def *armour = you.slot_item(EQ_BODY_ARMOUR, false);
1854     return is_effectively_light_armour(armour);
1855 }
1856 
1857 // This function returns true if the player has a radically different
1858 // shape... minor changes like blade hands don't count, also note
1859 // that lich transformation doesn't change the character's shape
1860 // (so we end up with Naga-liches, Spriggan-liches, Minotaur-liches)
1861 // it just makes the character undead (with the benefits that implies). - bwr
player_is_shapechanged()1862 bool player_is_shapechanged()
1863 {
1864     if (you.form == transformation::none
1865         || you.form == transformation::blade_hands
1866         || you.form == transformation::lich
1867         || you.form == transformation::shadow
1868         || you.form == transformation::appendage)
1869     {
1870         return false;
1871     }
1872 
1873     return true;
1874 }
1875 
update_acrobat_status()1876 void update_acrobat_status()
1877 {
1878     if (!you.wearing(EQ_AMULET, AMU_ACROBAT))
1879         return;
1880 
1881     // Acrobat duration goes slightly into the next turn, giving the
1882     // player visual feedback of the EV bonus recieved.
1883     // This is assignment and not increment as acrobat duration depends
1884     // on player action.
1885     you.duration[DUR_ACROBAT] = you.time_taken+1;
1886     you.redraw_evasion = true;
1887 }
1888 
1889 // An evasion factor based on the player's body size, smaller == higher
1890 // evasion size factor.
_player_evasion_size_factor(bool base=false)1891 static int _player_evasion_size_factor(bool base = false)
1892 {
1893     // XXX: you.body_size() implementations are incomplete, fix.
1894     const size_type size = you.body_size(PSIZE_BODY, base);
1895     return 2 * (SIZE_MEDIUM - size);
1896 }
1897 
1898 // Determines racial shield penalties (formicids get a bonus compared to
1899 // other medium-sized races)
player_shield_racial_factor()1900 int player_shield_racial_factor()
1901 {
1902     const int ev_factor = you.has_mutation(MUT_QUADRUMANOUS)
1903                                         ? -2 // Same as trolls, etc.
1904                                         : _player_evasion_size_factor(true);
1905     return max(1, 5 + ev_factor);
1906 }
1907 
1908 
1909 // The total EV penalty to the player for all their worn armour items
1910 // with a base EV penalty (i.e. EV penalty as a base armour property,
1911 // not as a randart property).
_player_adjusted_evasion_penalty(const int scale)1912 static int _player_adjusted_evasion_penalty(const int scale)
1913 {
1914     int piece_armour_evasion_penalty = 0;
1915 
1916     // Some lesser armours have small penalties now (barding).
1917     for (int i = EQ_MIN_ARMOUR; i < EQ_MAX_ARMOUR; i++)
1918     {
1919         if (i == EQ_SHIELD || !you.slot_item(static_cast<equipment_type>(i)))
1920             continue;
1921 
1922         // [ds] Evasion modifiers for armour are negatives, change
1923         // those to positive for penalty calc.
1924         const int penalty = (-property(you.inv[you.equip[i]], PARM_EVASION))/3;
1925         if (penalty > 0)
1926             piece_armour_evasion_penalty += penalty;
1927     }
1928 
1929     return piece_armour_evasion_penalty * scale / 10 +
1930            you.adjusted_body_armour_penalty(scale);
1931 }
1932 
1933 // Player EV bonuses for various effects and transformations. This
1934 // does not include tengu/merfolk EV bonuses for flight/swimming.
_player_evasion_bonuses()1935 static int _player_evasion_bonuses()
1936 {
1937     int evbonus = 0;
1938 
1939     if (you.duration[DUR_AGILITY])
1940         evbonus += AGILITY_BONUS;
1941 
1942     evbonus += you.wearing(EQ_RINGS_PLUS, RING_EVASION);
1943 
1944     evbonus += you.scan_artefacts(ARTP_EVASION);
1945 
1946     // mutations
1947     evbonus += you.get_mutation_level(MUT_GELATINOUS_BODY);
1948 
1949     if (you.get_mutation_level(MUT_DISTORTION_FIELD))
1950         evbonus += you.get_mutation_level(MUT_DISTORTION_FIELD) + 1;
1951 
1952     // transformation penalties/bonuses not covered by size alone:
1953     if (you.get_mutation_level(MUT_SLOW_REFLEXES))
1954         evbonus -= you.get_mutation_level(MUT_SLOW_REFLEXES) * 5;
1955 
1956     if (you.props.exists(AIRFORM_POWER_KEY))
1957         evbonus += you.props[AIRFORM_POWER_KEY].get_int() / 10;
1958 
1959     if (you.props.exists(WU_JIAN_HEAVENLY_STORM_KEY))
1960         evbonus += you.props[WU_JIAN_HEAVENLY_STORM_KEY].get_int();
1961 
1962     // If you have an active amulet of the acrobat and just moved or waited,
1963     // get a massive EV bonus.
1964     if (acrobat_boost_active())
1965         evbonus += 15;
1966 
1967     return evbonus;
1968 }
1969 
1970 // Player EV scaling for being flying tengu or swimming merfolk.
_player_scale_evasion(int prescaled_ev,const int scale)1971 static int _player_scale_evasion(int prescaled_ev, const int scale)
1972 {
1973     if (you.duration[DUR_PETRIFYING] || you.caught())
1974         prescaled_ev /= 2;
1975 
1976     // Merfolk get a 25% evasion bonus near water.
1977     if (feat_is_water(env.grid(you.pos()))
1978         && you.get_mutation_level(MUT_NIMBLE_SWIMMER) >= 2)
1979     {
1980         const int ev_bonus = max(2 * scale, prescaled_ev / 4);
1981         return prescaled_ev + ev_bonus;
1982     }
1983 
1984     // Flying Tengu get a 20% evasion bonus.
1985     if (you.tengu_flight())
1986     {
1987         const int ev_bonus = max(1 * scale, prescaled_ev / 5);
1988         return prescaled_ev + ev_bonus;
1989     }
1990 
1991     return prescaled_ev;
1992 }
1993 
1994 /**
1995  * What is the player's bonus to EV from dodging when not paralysed, after
1996  * accounting for size & body armour penalties?
1997  *
1998  * First, calculate base dodge bonus (linear with dodging * dex),
1999  * and armour dodge penalty (base armour evp, increased for small races &
2000  * decreased for large, then with a magic "3" subtracted from it to make the
2001  * penalties not too harsh).
2002  *
2003  * If the player's strength is greater than the armour dodge penalty, return
2004  *      base dodge * (1 - dodge_pen / (str*2)).
2005  * E.g., if str is twice dodge penalty, return 3/4 of base dodge. If
2006  * str = dodge_pen * 4, return 7/8...
2007  *
2008  * If str is less than dodge penalty, return
2009  *      base_dodge * str / (dodge_pen * 2).
2010  * E.g., if str = dodge_pen / 2, return 1/4 of base dodge. if
2011  * str = dodge_pen / 4, return 1/8...
2012  *
2013  * For either equation, if str = dodge_pen, the result is base_dodge/2.
2014  *
2015  * @param scale     A scale to multiply the result by, to avoid precision loss.
2016  * @return          A bonus to EV, multiplied by the scale.
2017  */
_player_armour_adjusted_dodge_bonus(int scale)2018 static int _player_armour_adjusted_dodge_bonus(int scale)
2019 {
2020     const int dodge_bonus =
2021         (800 + you.skill(SK_DODGING, 10) * you.dex() * 8) * scale
2022         / (20 - _player_evasion_size_factor()) / 10 / 10;
2023 
2024     const int armour_dodge_penalty = you.unadjusted_body_armour_penalty() - 3;
2025     if (armour_dodge_penalty <= 0)
2026         return dodge_bonus;
2027 
2028     const int str = max(1, you.strength());
2029     if (armour_dodge_penalty >= str)
2030         return dodge_bonus * str / (armour_dodge_penalty * 2);
2031     return dodge_bonus - dodge_bonus * armour_dodge_penalty / (str * 2);
2032 }
2033 
2034 // Total EV for player using the revised 0.6 evasion model.
_player_evasion(ev_ignore_type evit)2035 static int _player_evasion(ev_ignore_type evit)
2036 {
2037     const int size_factor = _player_evasion_size_factor();
2038     // Size is all that matters when paralysed or at 0 dex.
2039     if ((you.cannot_move() || you.duration[DUR_CLUMSY]
2040             || you.form == transformation::tree)
2041         && !(evit & ev_ignore::helpless))
2042     {
2043         return max(1, 2 + size_factor / 2);
2044     }
2045 
2046     const int scale = 100;
2047     const int size_base_ev = (10 + size_factor) * scale;
2048 
2049     const int vertigo_penalty = you.duration[DUR_VERTIGO] ? 5 * scale : 0;
2050 
2051     const int natural_evasion =
2052         size_base_ev
2053         + _player_armour_adjusted_dodge_bonus(scale)
2054         - _player_adjusted_evasion_penalty(scale)
2055         - you.adjusted_shield_penalty(scale)
2056         - vertigo_penalty;
2057 
2058     const int evasion_bonuses = _player_evasion_bonuses() * scale;
2059 
2060     const int final_evasion =
2061         _player_scale_evasion(natural_evasion, scale) + evasion_bonuses;
2062 
2063     return unscale_round_up(final_evasion, scale);
2064 }
2065 
2066 // Returns the spellcasting penalty (increase in spell failure) for the
2067 // player's worn body armour and shield.
player_armour_shield_spell_penalty()2068 int player_armour_shield_spell_penalty()
2069 {
2070     const int scale = 100;
2071 
2072     const int body_armour_penalty =
2073         max(19 * you.adjusted_body_armour_penalty(scale), 0);
2074 
2075     const int total_penalty = body_armour_penalty
2076                  + 19 * you.adjusted_shield_penalty(scale);
2077 
2078     return max(total_penalty, 0) / scale;
2079 }
2080 
2081 /**
2082  * How many spell-success-chance-boosting ('wizardry') effects can the player
2083  * apply to the given spell?
2084  *
2085  * @param spell     The type of spell being cast.
2086  * @return          The number of relevant wizardry effects.
2087  */
player_wizardry(spell_type)2088 int player_wizardry(spell_type /*spell*/)
2089 {
2090     return you.wearing(EQ_RINGS, RING_WIZARDRY)
2091            + (you.get_mutation_level(MUT_BIG_BRAIN) == 3 ? 1 : 0);
2092 }
2093 
2094 /**
2095  * Calculate the SH value used internally.
2096  *
2097  * Exactly twice the value displayed to players, for legacy reasons.
2098  * @return      The player's current SH value.
2099  */
player_shield_class()2100 int player_shield_class()
2101 {
2102     int shield = 0;
2103 
2104     if (you.incapacitated())
2105         return 0;
2106 
2107     if (you.shield())
2108     {
2109         const item_def& item = you.inv[you.equip[EQ_SHIELD]];
2110         int size_factor = (you.body_size(PSIZE_TORSO) - SIZE_MEDIUM)
2111                         * (item.sub_type - ARM_TOWER_SHIELD);
2112         int base_shield = property(item, PARM_AC) * 2 + size_factor;
2113 
2114         // bonus applied only to base, see above for effect:
2115         shield += base_shield * 50;
2116         shield += base_shield * you.skill(SK_SHIELDS, 5) / 2;
2117 
2118         shield += item.plus * 200;
2119 
2120         shield += you.skill(SK_SHIELDS, 38)
2121                 + min(you.skill(SK_SHIELDS, 38), 3 * 38);
2122 
2123         int stat = 0;
2124         if (item.sub_type == ARM_BUCKLER)
2125             stat = you.dex() * 38;
2126         else if (item.sub_type == ARM_TOWER_SHIELD)
2127             stat = you.dex() * 12 + you.strength() * 26;
2128         else
2129             stat = you.dex() * 19 + you.strength() * 19;
2130         stat = stat * (base_shield + 13) / 26;
2131 
2132         shield += stat;
2133     }
2134 
2135     // mutations
2136     // +4, +6, +8 (displayed values)
2137     shield += (you.get_mutation_level(MUT_LARGE_BONE_PLATES) > 0
2138                ? you.get_mutation_level(MUT_LARGE_BONE_PLATES) * 400 + 400
2139                : 0);
2140 
2141     if (you.get_mutation_level(MUT_CONDENSATION_SHIELD) > 0
2142             && !you.duration[DUR_ICEMAIL_DEPLETED])
2143     {
2144         shield += ICEMAIL_MAX * 100;
2145     }
2146 
2147     shield += qazlal_sh_boost() * 100;
2148     shield += tso_sh_boost() * 100;
2149     shield += you.wearing(EQ_AMULET, AMU_REFLECTION) * AMU_REFLECT_SH * 100;
2150     shield += you.scan_artefacts(ARTP_SHIELDING) * 200;
2151 
2152     return (shield + 50) / 100;
2153 }
2154 
2155 /**
2156  * Calculate the SH value that should be displayed to players.
2157  *
2158  * Exactly half the internal value, for legacy reasons.
2159  * @return      The SH value to be displayed.
2160  */
player_displayed_shield_class()2161 int player_displayed_shield_class()
2162 {
2163     return player_shield_class() / 2;
2164 }
2165 
2166 /**
2167  * Does the player have 'omnireflection' (the ability to reflect piercing
2168  * effects and enchantments)?
2169  *
2170  * @return      Whether the player has the Warlock's Mirror equipped.
2171  */
player_omnireflects()2172 bool player_omnireflects()
2173 {
2174     return player_equip_unrand(UNRAND_WARLOCK_MIRROR);
2175 }
2176 
forget_map(bool rot)2177 void forget_map(bool rot)
2178 {
2179     ASSERT(!crawl_state.game_is_arena());
2180 
2181     // If forgetting was intentional, clear the travel trail.
2182     if (!rot)
2183         clear_travel_trail();
2184 
2185     const bool rot_resist = player_in_branch(BRANCH_ABYSS)
2186                             && have_passive(passive_t::map_rot_res_abyss);
2187     const double geometric_chance = 0.99;
2188     const int radius = (rot_resist ? 200 : 100);
2189 
2190     const int scalar = 0xFF;
2191     for (rectangle_iterator ri(0); ri; ++ri)
2192     {
2193         const coord_def &p = *ri;
2194         if (!env.map_knowledge(p).known() || you.see_cell(p))
2195             continue;
2196 
2197         if (rot)
2198         {
2199             const int dist = grid_distance(you.pos(), p);
2200             int chance = pow(geometric_chance,
2201                              max(1, (dist * dist - radius) / 40)) * scalar;
2202             if (x_chance_in_y(chance, scalar))
2203                 continue;
2204         }
2205 
2206         if (you.see_cell(p))
2207             continue;
2208 
2209         env.map_knowledge(p).clear();
2210         if (env.map_forgotten)
2211             (*env.map_forgotten)(p).clear();
2212         StashTrack.update_stash(p);
2213 #ifdef USE_TILE
2214         tile_forget_map(p);
2215 #endif
2216     }
2217 
2218     ash_detect_portals(is_map_persistent());
2219 #ifdef USE_TILE
2220     tiles.update_minimap_bounds();
2221 #endif
2222 }
2223 
_recover_stat()2224 static void _recover_stat()
2225 {
2226     FixedVector<int, NUM_STATS> recovered_stats(0);
2227 
2228     while (you.attribute[ATTR_STAT_LOSS_XP] <= 0)
2229     {
2230         stat_type stat = random_lost_stat();
2231         ASSERT(stat != NUM_STATS);
2232 
2233         recovered_stats[stat]++;
2234 
2235         // Very heavily drained stats recover faster.
2236         if (you.stat(stat, false) < 0)
2237             recovered_stats[stat] += random2(-you.stat(stat, false) / 2);
2238 
2239         bool still_drained = false;
2240         for (int i = 0; i < NUM_STATS; ++i)
2241             if (you.stat_loss[i] - recovered_stats[i] > 0)
2242                 still_drained = true;
2243 
2244         if (still_drained)
2245             you.attribute[ATTR_STAT_LOSS_XP] += stat_loss_roll();
2246         else
2247             break;
2248     }
2249 
2250     for (int i = 0; i < NUM_STATS; ++i)
2251         if (recovered_stats[i] > 0)
2252             restore_stat((stat_type) i, recovered_stats[i], false, true);
2253 }
2254 
get_exp_progress()2255 int get_exp_progress()
2256 {
2257     if (you.experience_level >= you.get_max_xl())
2258         return 0;
2259 
2260     const int current = exp_needed(you.experience_level);
2261     const int next    = exp_needed(you.experience_level + 1);
2262     if (next == current)
2263         return 0;
2264     return (you.experience - current) * 100 / (next - current);
2265 }
2266 
_recharge_xp_evokers(int exp)2267 static void _recharge_xp_evokers(int exp)
2268 {
2269     FixedVector<item_def*, NUM_MISCELLANY> evokers(nullptr);
2270     list_charging_evokers(evokers);
2271 
2272     int xp_factor = max(min((int)exp_needed(you.experience_level+1, 0) * 2 / 7,
2273                              you.experience_level * 425),
2274                         you.experience_level*4 + 30)
2275                     / (3 + you.skill_rdiv(SK_EVOCATIONS, 2, 13));
2276 
2277     for (int i = 0; i < NUM_MISCELLANY; ++i)
2278     {
2279         item_def* evoker = evokers[i];
2280         if (!evoker)
2281             continue;
2282 
2283         int &debt = evoker_debt(evoker->sub_type);
2284         if (debt == 0)
2285             continue;
2286 
2287         const int old_charges = evoker_charges(i);
2288         debt = max(0, debt - div_rand_round(exp, xp_factor));
2289         const int gained = evoker_charges(i) - old_charges;
2290         if (!gained)
2291             continue;
2292 
2293         if (evoker_max_charges(i) == 1)
2294             mprf("%s has recharged.", evoker->name(DESC_YOUR).c_str());
2295         else
2296         {
2297             mprf("%s has regained %s charge%s.",
2298                  evoker->name(DESC_YOUR).c_str(),
2299                  number_in_words(gained).c_str(), gained > 1 ? "s" : "");
2300         }
2301     }
2302 }
2303 
2304 /// Make progress toward the abyss spawning an exit/stairs.
_reduce_abyss_xp_timer(int exp)2305 static void _reduce_abyss_xp_timer(int exp)
2306 {
2307     if (!player_in_branch(BRANCH_ABYSS))
2308         return;
2309 
2310     const int xp_factor =
2311         max(min((int)exp_needed(you.experience_level+1, 0) / 7,
2312                 you.experience_level * 425),
2313             you.experience_level*2 + 15) / 5;
2314 
2315     if (!you.props.exists(ABYSS_STAIR_XP_KEY))
2316         you.props[ABYSS_STAIR_XP_KEY] = EXIT_XP_COST;
2317     const int reqd_xp = you.props[ABYSS_STAIR_XP_KEY].get_int();
2318     const int new_req = reqd_xp - div_rand_round(exp, xp_factor);
2319     dprf("reducing xp timer from %d to %d (factor = %d)",
2320          reqd_xp, new_req, xp_factor);
2321     you.props[ABYSS_STAIR_XP_KEY].get_int() = new_req;
2322 }
2323 
2324 /// update penance for XP based gods
_handle_xp_penance(int exp)2325 static void _handle_xp_penance(int exp)
2326 {
2327     vector<god_type> xp_gods;
2328     for (god_iterator it; it; ++it)
2329     {
2330         if (xp_penance(*it))
2331             xp_gods.push_back(*it);
2332     }
2333 
2334     if (!xp_gods.empty())
2335     {
2336         god_type god = xp_gods[random2(xp_gods.size())];
2337         reduce_xp_penance(god, exp);
2338     }
2339 }
2340 
2341 /// update temporary mutations
_handle_temp_mutation(int exp)2342 static void _handle_temp_mutation(int exp)
2343 {
2344     if (!(you.attribute[ATTR_TEMP_MUTATIONS] > 0))
2345         return;
2346 
2347     you.attribute[ATTR_TEMP_MUT_XP] -= exp;
2348     if (you.attribute[ATTR_TEMP_MUT_XP] <= 0)
2349         temp_mutation_wanes();
2350 }
2351 
2352 /// update stat loss
_handle_stat_loss(int exp)2353 static void _handle_stat_loss(int exp)
2354 {
2355     if (!(you.attribute[ATTR_STAT_LOSS_XP] > 0))
2356         return;
2357 
2358     int loss = div_rand_round(exp * 3 / 2,
2359                               max(1, calc_skill_cost(you.skill_cost_level) - 3));
2360     you.attribute[ATTR_STAT_LOSS_XP] -= loss;
2361     dprf("Stat loss points: %d", you.attribute[ATTR_STAT_LOSS_XP]);
2362     if (you.attribute[ATTR_STAT_LOSS_XP] <= 0)
2363         _recover_stat();
2364 }
2365 
2366 /// update hp drain
_handle_hp_drain(int exp)2367 static void _handle_hp_drain(int exp)
2368 {
2369     if (!you.hp_max_adj_temp)
2370         return;
2371 
2372     int loss = div_rand_round(exp, 4 * calc_skill_cost(you.skill_cost_level));
2373 
2374     // Make it easier to recover from very heavy levels of draining
2375     // (they're nasty enough as it is)
2376     loss = loss * (1 + (-you.hp_max_adj_temp / 25.0f));
2377 
2378     dprf("Lost %d of %d draining points", loss, -you.hp_max_adj_temp);
2379 
2380     you.hp_max_adj_temp += loss;
2381 
2382     const bool drain_removed = you.hp_max_adj_temp >= 0;
2383     if (drain_removed)
2384         you.hp_max_adj_temp = 0;
2385 
2386     calc_hp();
2387 
2388     if (drain_removed)
2389         mprf(MSGCH_RECOVERY, "Your life force feels restored.");
2390 }
2391 
_handle_god_wrath(int exp)2392 static void _handle_god_wrath(int exp)
2393 {
2394     for (god_iterator it; it; ++it)
2395     {
2396         if (active_penance(*it))
2397         {
2398             you.attribute[ATTR_GOD_WRATH_XP] -= exp;
2399             while (you.attribute[ATTR_GOD_WRATH_XP] < 0)
2400             {
2401                 you.attribute[ATTR_GOD_WRATH_COUNT]++;
2402                 set_penance_xp_timeout();
2403             }
2404             break;
2405         }
2406     }
2407 }
2408 
gain_exp(unsigned int exp_gained)2409 unsigned int gain_exp(unsigned int exp_gained)
2410 {
2411     if (crawl_state.game_is_arena())
2412         return 0;
2413 
2414     you.experience_pool += exp_gained;
2415 
2416     if (player_under_penance(GOD_HEPLIAKLQANA))
2417         return 0; // no XP for you!
2418 
2419     const unsigned int max_gain = (unsigned int)MAX_EXP_TOTAL - you.experience;
2420     if (max_gain < exp_gained)
2421         return max_gain;
2422     return exp_gained;
2423 }
2424 
apply_exp()2425 void apply_exp()
2426 {
2427     const unsigned int exp_gained = you.experience_pool;
2428     if (exp_gained == 0)
2429         return;
2430 
2431     you.experience_pool = 0;
2432 
2433     // xp-gated effects that don't use sprint inflation
2434     _handle_xp_penance(exp_gained);
2435     _handle_god_wrath(exp_gained);
2436 
2437     // evolution mutation timer
2438     you.attribute[ATTR_EVOL_XP] += exp_gained;
2439 
2440     // modified experience due to sprint inflation
2441     unsigned int skill_xp = exp_gained;
2442     if (crawl_state.game_is_sprint())
2443         skill_xp = sprint_modify_exp(skill_xp);
2444 
2445     // xp-gated effects that use sprint inflation
2446     _handle_stat_loss(skill_xp);
2447     _handle_temp_mutation(skill_xp);
2448     _recharge_xp_evokers(skill_xp);
2449     _reduce_abyss_xp_timer(skill_xp);
2450     _handle_hp_drain(skill_xp);
2451 
2452     if (player_under_penance(GOD_HEPLIAKLQANA))
2453         return; // no xp for you!
2454 
2455     // handle actual experience gains,
2456     // i.e. XL and skills
2457 
2458     dprf("gain_exp: %d", exp_gained);
2459 
2460     if (you.experience + exp_gained > (unsigned int)MAX_EXP_TOTAL)
2461         you.experience = MAX_EXP_TOTAL;
2462     else
2463         you.experience += exp_gained;
2464 
2465     you.exp_available += 10 * skill_xp;
2466 
2467     train_skills();
2468     while (check_selected_skills()
2469            && you.exp_available >= calc_skill_cost(you.skill_cost_level))
2470     {
2471         train_skills();
2472     }
2473 
2474     level_change();
2475 }
2476 
will_gain_life(int lev)2477 bool will_gain_life(int lev)
2478 {
2479     if (lev < you.attribute[ATTR_LIFE_GAINED] - 2)
2480         return false;
2481 
2482     return you.lives + you.deaths < (lev - 1) / 3;
2483 }
2484 
_felid_extra_life()2485 static void _felid_extra_life()
2486 {
2487     if (will_gain_life(you.max_level)
2488         && you.lives < 2)
2489     {
2490         you.lives++;
2491         mprf(MSGCH_INTRINSIC_GAIN, "Extra life!");
2492         you.attribute[ATTR_LIFE_GAINED] = you.max_level;
2493         // Should play the 1UP sound from SMB...
2494     }
2495 }
2496 
_gain_and_note_hp_mp()2497 static void _gain_and_note_hp_mp()
2498 {
2499     const int old_mp = you.magic_points;
2500     const int old_maxmp = you.max_magic_points;
2501 
2502     // recalculate for game
2503     calc_hp(true, false);
2504     calc_mp();
2505 
2506     set_mp(old_maxmp > 0 ? old_mp * you.max_magic_points / old_maxmp
2507            : you.max_magic_points);
2508 
2509     // Get "real" values for note-taking, i.e. ignore Berserk,
2510     // transformations or equipped items.
2511     const int note_maxhp = get_real_hp(false, true);
2512     const int note_maxmp = get_real_mp(false);
2513 
2514     char buf[200];
2515     sprintf(buf, "HP: %d/%d MP: %d/%d",
2516             min(you.hp, note_maxhp), note_maxhp,
2517             min(you.magic_points, note_maxmp), note_maxmp);
2518     take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, buf));
2519 }
2520 
2521 /**
2522  * Calculate max HP changes and scale current HP accordingly.
2523  */
calc_hp(bool scale,bool set)2524 void calc_hp(bool scale, bool set)
2525 {
2526     // Rounding must be down or Deep Dwarves would abuse certain values.
2527     // We can reduce errors by a factor of 100 by using partial hp we have.
2528     int oldhp = you.hp;
2529     int old_max = you.hp_max;
2530 
2531     you.hp_max = get_real_hp(true, true);
2532 
2533     if (scale)
2534     {
2535         int hp = you.hp * 100 + you.hit_points_regeneration;
2536         int new_max = you.hp_max;
2537         hp = hp * new_max / old_max;
2538         if (hp < 100)
2539             hp = 100;
2540         set_hp(min(hp / 100, you.hp_max));
2541         you.hit_points_regeneration = hp % 100;
2542     }
2543     if (set)
2544         you.hp = you.hp_max;
2545 
2546     you.hp = min(you.hp, you.hp_max);
2547 
2548     if (oldhp != you.hp || old_max != you.hp_max)
2549     {
2550         dprf("HP changed: %d/%d -> %d/%d", oldhp, old_max, you.hp, you.hp_max);
2551         you.redraw_hit_points = true;
2552     }
2553 }
2554 
xp_to_level_diff(int xp,int scale)2555 int xp_to_level_diff(int xp, int scale)
2556 {
2557     ASSERT(xp >= 0);
2558     const int adjusted_xp = you.experience + xp;
2559     int projected_level = you.experience_level;
2560     while (you.experience >= exp_needed(projected_level + 1))
2561         projected_level++; // handle xl 27 chars
2562     int adjusted_level = projected_level;
2563 
2564     // closest whole number level, rounding down
2565     while (adjusted_xp >= (int) exp_needed(adjusted_level + 1))
2566         adjusted_level++;
2567     if (scale > 1)
2568     {
2569         // TODO: what is up with all the casts here?
2570 
2571         // decimal scaled version of current level including whatever fractional
2572         // part scale can handle
2573         const int cur_level_scaled = projected_level * scale
2574                 + (you.experience - (int) exp_needed(projected_level)) * scale /
2575                     ((int) exp_needed(projected_level + 1)
2576                                     - (int) exp_needed(projected_level));
2577 
2578         // decimal scaled version of what adjusted_xp would get you
2579         const int adjusted_level_scaled = adjusted_level * scale
2580                 + (adjusted_xp - (int) exp_needed(adjusted_level)) * scale /
2581                     ((int) exp_needed(adjusted_level + 1)
2582                                     - (int) exp_needed(adjusted_level));
2583         // TODO: this would be more usable with better rounding behaviour
2584         return adjusted_level_scaled - cur_level_scaled;
2585     }
2586     else
2587         return adjusted_level - projected_level;
2588 }
2589 
_gain_innate_spells()2590 static void _gain_innate_spells()
2591 {
2592     auto &spell_vec = you.props[INNATE_SPELLS_KEY].get_vector();
2593     // Gain spells at every odd XL, starting at XL 3 and continuing to XL 27.
2594     for (int i = 0; i < spell_vec.size() && i < (you.experience_level - 1) / 2;
2595          i++)
2596     {
2597         const spell_type spell = (spell_type)spell_vec[i].get_int();
2598         if (spell == SPELL_NO_SPELL)
2599             continue; // spell lost due to lack of slots
2600         auto spindex = find(begin(you.spells), end(you.spells), spell);
2601         if (spindex != end(you.spells))
2602             continue; // already learned that one
2603 
2604         // XXX: this shouldn't be able to happen, but rare Wanderer starts
2605         // could give out too many spells and hit the cap.
2606         if (you.spell_no >= MAX_KNOWN_SPELLS)
2607         {
2608             for (int j = 0; j < i; j++)
2609             {
2610                 const spell_type oldspell = (spell_type)spell_vec[j].get_int();
2611                 if (oldspell == SPELL_NO_SPELL)
2612                     continue;
2613                 auto oldindex = find(begin(you.spells), end(you.spells),
2614                                      oldspell);
2615                 if (oldindex != end(you.spells))
2616                 {
2617                     mpr("Your capacity for spells is full, and you lose "
2618                         "access to an earlier spell.");
2619                     del_spell_from_memory(oldspell);
2620                     spell_vec[j].get_int() = SPELL_NO_SPELL;
2621                     break;
2622                 }
2623             }
2624             // If somehow a slot can't be freed, give up and lose the spell.
2625             // This extremely shouldn't happen.
2626             if (you.spell_no >= MAX_KNOWN_SPELLS)
2627             {
2628                 spell_vec[i].get_int() = SPELL_NO_SPELL;
2629                 continue;
2630             }
2631         }
2632         mprf("The power to cast %s wells up from within.", spell_title(spell));
2633         add_spell_to_memory(spell);
2634     }
2635 }
2636 
2637 /**
2638  * Handle the effects from a player's change in XL.
2639  * @param aux                     A string describing the cause of the level
2640  *                                change.
2641  * @param skip_attribute_increase If true and XL has increased, don't process
2642  *                                stat gains. Currently only used by wizmode
2643  *                                commands.
2644  */
level_change(bool skip_attribute_increase)2645 void level_change(bool skip_attribute_increase)
2646 {
2647     // necessary for the time being, as level_change() is called
2648     // directly sometimes {dlb}
2649     you.redraw_experience = true;
2650 
2651     while (you.experience < exp_needed(you.experience_level))
2652         lose_level();
2653 
2654     while (you.experience_level < you.get_max_xl()
2655            && you.experience >= exp_needed(you.experience_level + 1))
2656     {
2657         if (!skip_attribute_increase)
2658         {
2659             crawl_state.cancel_cmd_all();
2660 
2661             if (is_processing_macro())
2662                 flush_input_buffer(FLUSH_ABORT_MACRO);
2663         }
2664 
2665         // [ds] Make sure we increment you.experience_level and apply
2666         // any stat/hp increases only after we've cleared all prompts
2667         // for this experience level. If we do part of the work before
2668         // the prompt, and a player on telnet gets disconnected, the
2669         // SIGHUP will save Crawl in the in-between state and rob the
2670         // player of their level-up perks.
2671 
2672         const int new_exp = you.experience_level + 1;
2673         // some species need to do this at a specific time; most just do it at the end
2674         bool updated_maxhp = false;
2675 
2676         if (new_exp <= you.max_level)
2677         {
2678             mprf(MSGCH_INTRINSIC_GAIN,
2679                  "Welcome back to level %d!", new_exp);
2680 
2681             // No more prompts for this XL past this point.
2682 
2683             you.experience_level = new_exp;
2684         }
2685         else  // Character has gained a new level
2686         {
2687             // Don't want to see the dead creature at the prompt.
2688             redraw_screen();
2689             update_screen();
2690 
2691             if (new_exp == 27)
2692                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level 27, the final one!");
2693             else if (new_exp == you.get_max_xl())
2694                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d, the highest you will ever reach!",
2695                         you.get_max_xl());
2696             else
2697             {
2698                 mprf(MSGCH_INTRINSIC_GAIN, "You have reached level %d!",
2699                      new_exp);
2700             }
2701 
2702             const bool manual_stat_level = new_exp % 3 == 0;  // 3,6,9,12...
2703 
2704             // Must do this before actually changing experience_level,
2705             // so we will re-prompt on load if a hup is received.
2706             if (manual_stat_level && !skip_attribute_increase)
2707                 if (!attribute_increase())
2708                     return; // abort level gain, the xp is still there
2709 
2710             // Set this after printing, since a more() might clear it.
2711             you.redraw_experience = true;
2712 
2713             crawl_state.stat_gain_prompt = false;
2714             you.experience_level = new_exp;
2715             you.max_level = you.experience_level;
2716 
2717 #ifdef USE_TILE_LOCAL
2718             // In case of intrinsic ability changes.
2719             tiles.layout_statcol();
2720             redraw_screen();
2721             update_screen();
2722 #endif
2723             if (!skip_attribute_increase)
2724                 species_stat_gain(you.species);
2725 
2726             switch (you.species)
2727             {
2728             case SP_NAGA:
2729                 if (!(you.experience_level % 3))
2730                 {
2731                     mprf(MSGCH_INTRINSIC_GAIN, "Your skin feels tougher.");
2732                     you.redraw_armour_class = true;
2733                 }
2734                 break;
2735 
2736             case SP_BASE_DRACONIAN:
2737                 if (you.experience_level >= 7)
2738                 {
2739                     // XX make seed stable by choosing at birth
2740                     you.species = species::random_draconian_colour();
2741 
2742                     // We just changed our aptitudes, so some skills may now
2743                     // be at the wrong level (with negative progress); if we
2744                     // print anything in this condition, we might trigger a
2745                     // --More--, a redraw, and a crash (#6376 on Mantis).
2746                     //
2747                     // Hence we first fix up our skill levels silently (passing
2748                     // do_level_up = false) but save the old values; then when
2749                     // we want the messages later, we restore the old skill
2750                     // levels and call check_skill_level_change() again, this
2751                     // time passing do_level_up = true.
2752 
2753                     uint8_t saved_skills[NUM_SKILLS];
2754                     for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
2755                     {
2756                         saved_skills[sk] = you.skills[sk];
2757                         check_skill_level_change(sk, false);
2758                     }
2759                     // The player symbol depends on species.
2760                     update_player_symbol();
2761 #ifdef USE_TILE
2762                     init_player_doll();
2763 #endif
2764                     mprf(MSGCH_INTRINSIC_GAIN,
2765                          "Your scales start taking on %s colour.",
2766                          article_a(species::scale_type(you.species)).c_str());
2767 
2768                     // Produce messages about skill increases/decreases. We
2769                     // restore one skill level at a time so that at most the
2770                     // skill being checked is at the wrong level.
2771                     for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
2772                     {
2773                         const int oldapt = species_apt(sk, SP_BASE_DRACONIAN);
2774                         const int newapt = species_apt(sk, you.species);
2775                         if (oldapt != newapt)
2776                         {
2777                             mprf(MSGCH_INTRINSIC_GAIN, "You learn %s %s%s.",
2778                                  skill_name(sk),
2779                                  abs(oldapt - newapt) > 1 ? "much " : "",
2780                                  oldapt > newapt ? "slower" : "quicker");
2781                         }
2782 
2783                         you.skills[sk] = saved_skills[sk];
2784                         check_skill_level_change(sk);
2785                     }
2786 
2787                     // It's possible we passed a training target due to
2788                     // skills being rescaled to new aptitudes. Thus, we must
2789                     // check the training targets.
2790                     check_training_targets();
2791 
2792                     // Tell the player about their new species
2793                     for (auto &mut : species::fake_mutations(you.species, false))
2794                         mprf(MSGCH_INTRINSIC_GAIN, "%s", mut.c_str());
2795 
2796                     // needs to be done early here, so HP doesn't look drained
2797                     // when we redraw the screen
2798                     _gain_and_note_hp_mp();
2799                     updated_maxhp = true;
2800 
2801                     redraw_screen();
2802                     update_screen();
2803                 }
2804                 break;
2805 
2806             case SP_DEMONSPAWN:
2807             {
2808                 bool gave_message = false;
2809                 int level = 0;
2810                 mutation_type first_body_facet = NUM_MUTATIONS;
2811 
2812                 for (const player::demon_trait trait : you.demonic_traits)
2813                 {
2814                     if (is_body_facet(trait.mutation))
2815                     {
2816                         if (first_body_facet < NUM_MUTATIONS
2817                             && trait.mutation != first_body_facet)
2818                         {
2819                             if (you.experience_level == level)
2820                             {
2821                                 mprf(MSGCH_MUTATION, "You feel monstrous as "
2822                                      "your demonic heritage exerts itself.");
2823                                 mark_milestone("monstrous", "discovered their "
2824                                                "monstrous ancestry!");
2825                             }
2826                             break;
2827                         }
2828 
2829                         if (first_body_facet == NUM_MUTATIONS)
2830                         {
2831                             first_body_facet = trait.mutation;
2832                             level = trait.level_gained;
2833                         }
2834                     }
2835                 }
2836 
2837                 for (const player::demon_trait trait : you.demonic_traits)
2838                 {
2839                     if (trait.level_gained == you.experience_level)
2840                     {
2841                         if (!gave_message)
2842                         {
2843                             mprf(MSGCH_INTRINSIC_GAIN,
2844                                  "Your demonic ancestry asserts itself...");
2845 
2846                             gave_message = true;
2847                         }
2848                         perma_mutate(trait.mutation, 1, "demonic ancestry");
2849                     }
2850                 }
2851 
2852                 break;
2853             }
2854 
2855             default:
2856                 break;
2857             }
2858 
2859             if (you.has_mutation(MUT_MULTILIVED))
2860                 _felid_extra_life();
2861 
2862             give_level_mutations(you.species, you.experience_level);
2863 
2864         }
2865 
2866         if (species::is_draconian(you.species) && !(you.experience_level % 3))
2867         {
2868             mprf(MSGCH_INTRINSIC_GAIN, "Your scales feel tougher.");
2869             you.redraw_armour_class = true;
2870         }
2871         if (!updated_maxhp)
2872             _gain_and_note_hp_mp();
2873 
2874         if (you.has_mutation(MUT_INNATE_CASTER))
2875             _gain_innate_spells();
2876 
2877         xom_is_stimulated(12);
2878         if (in_good_standing(GOD_HEPLIAKLQANA))
2879             upgrade_hepliaklqana_ancestor();
2880 
2881         learned_something_new(HINT_NEW_LEVEL);
2882     }
2883 
2884     while (you.experience >= exp_needed(you.max_level + 1))
2885     {
2886         ASSERT(you.experience_level == you.get_max_xl());
2887         ASSERT(you.max_level < 127); // marshalled as an 1-byte value
2888         you.max_level++;
2889         if (you.has_mutation(MUT_MULTILIVED))
2890             _felid_extra_life();
2891     }
2892 
2893     you.redraw_title = true;
2894 
2895 #ifdef DGL_WHEREIS
2896     whereis_record();
2897 #endif
2898 
2899     // Hints mode arbitrarily ends at xp 7.
2900     if (crawl_state.game_is_hints() && you.experience_level >= 7)
2901         hints_finished();
2902 }
2903 
adjust_level(int diff,bool just_xp)2904 void adjust_level(int diff, bool just_xp)
2905 {
2906     ASSERT((uint64_t)you.experience <= (uint64_t)MAX_EXP_TOTAL);
2907     const int max_exp_level = you.get_max_xl();
2908     if (you.experience_level + diff < 1)
2909         you.experience = 0;
2910     else if (you.experience_level + diff >= max_exp_level)
2911     {
2912         const unsigned needed = exp_needed(max_exp_level);
2913         // Level gain when already at max should never reduce player XP;
2914         // but level loss (diff < 0) should.
2915         if (diff < 0 || you.experience < needed)
2916             you.experience = needed;
2917     }
2918     else
2919     {
2920         while (diff < 0 && you.experience >=
2921                 exp_needed(max_exp_level))
2922         {
2923             // Having XP for level 53 and going back to 26 due to a single
2924             // card would mean your felid is not going to get any extra lives
2925             // in foreseable future.
2926             you.experience -= exp_needed(max_exp_level)
2927                     - exp_needed(max_exp_level - 1);
2928             diff++;
2929         }
2930         int old_min = exp_needed(you.experience_level);
2931         int old_max = exp_needed(you.experience_level + 1);
2932         int new_min = exp_needed(you.experience_level + diff);
2933         int new_max = exp_needed(you.experience_level + 1 + diff);
2934         dprf("XP before: %d\n", you.experience);
2935         dprf("%4.2f of %d..%d to %d..%d",
2936              (you.experience - old_min) * 1.0 / (old_max - old_min),
2937              old_min, old_max, new_min, new_max);
2938 
2939         you.experience = ((int64_t)(new_max - new_min))
2940                        * (you.experience - old_min)
2941                        / (old_max - old_min)
2942                        + new_min;
2943         dprf("XP after: %d\n", you.experience);
2944     }
2945 
2946     ASSERT((uint64_t)you.experience <= (uint64_t)MAX_EXP_TOTAL);
2947 
2948     if (!just_xp)
2949         level_change();
2950 }
2951 
2952 /**
2953  * Get the player's current stealth value.
2954  *
2955  * (Keep in mind, while tweaking this function: the order in which stealth
2956  * modifiers are applied is significant!)
2957  *
2958  * @return  The player's current stealth value.
2959  */
player_stealth()2960 int player_stealth()
2961 {
2962     ASSERT(!crawl_state.game_is_arena());
2963     // Extreme stealthiness can be enforced by wizmode stealth setting.
2964     if (crawl_state.disables[DIS_MON_SIGHT])
2965         return 1000;
2966 
2967     // berserking, "clumsy" (0-dex), sacrifice stealth.
2968     if (you.berserk()
2969         || you.duration[DUR_CLUMSY]
2970         || you.get_mutation_level(MUT_NO_STEALTH))
2971     {
2972         return 0;
2973     }
2974 
2975     int stealth = you.dex() * 3;
2976 
2977     stealth += you.skill(SK_STEALTH, 15);
2978 
2979     if (you.confused())
2980         stealth /= 3;
2981 
2982     const item_def *arm = you.slot_item(EQ_BODY_ARMOUR, false);
2983     if (arm)
2984     {
2985         // [ds] New stealth penalty formula from rob: SP = 6 * (EP^2)
2986         // Now 2 * EP^2 / 3 after EP rescaling.
2987         const int evp = you.unadjusted_body_armour_penalty();
2988         const int penalty = evp * evp * 2 / 3;
2989         stealth -= penalty;
2990 
2991         const int pips = armour_type_prop(arm->sub_type, ARMF_STEALTH);
2992         stealth += pips * STEALTH_PIP;
2993     }
2994 
2995     stealth += STEALTH_PIP * you.scan_artefacts(ARTP_STEALTH);
2996     stealth += STEALTH_PIP * you.wearing_ego(EQ_ALL_ARMOUR, SPARM_STEALTH);
2997     stealth += STEALTH_PIP * you.wearing(EQ_RINGS, RING_STEALTH);
2998 
2999     if (you.duration[DUR_STEALTH])
3000         stealth += STEALTH_PIP * 2;
3001 
3002     // Mutations.
3003     stealth += (STEALTH_PIP / 3) * you.get_mutation_level(MUT_NIGHTSTALKER);
3004     stealth += STEALTH_PIP * you.get_mutation_level(MUT_THIN_SKELETAL_STRUCTURE);
3005     stealth += STEALTH_PIP * you.get_mutation_level(MUT_CAMOUFLAGE);
3006     if (you.has_mutation(MUT_TRANSLUCENT_SKIN))
3007         stealth += STEALTH_PIP;
3008 
3009     // Radiating silence is the negative complement of shouting all the
3010     // time... a sudden change from background noise to no noise is going
3011     // to clue anything in to the fact that something is very wrong...
3012     // a personal silence spell would naturally be different, but this
3013     // silence radiates for a distance and prevents monster spellcasting,
3014     // which pretty much gives away the stealth game.
3015     if (you.duration[DUR_SILENCE])
3016         stealth -= STEALTH_PIP;
3017 
3018     // Bloodless vampires are stealthier.
3019     if (you.has_mutation(MUT_VAMPIRISM) && !you.vampire_alive)
3020         stealth += STEALTH_PIP * 2;
3021 
3022     if (feat_is_water(env.grid(you.pos())))
3023     {
3024         if (you.has_mutation(MUT_NIMBLE_SWIMMER))
3025             stealth += STEALTH_PIP;
3026         else if (you.in_water() && !you.can_swim() && !you.extra_balanced())
3027             stealth /= 2;       // splashy-splashy
3028     }
3029 
3030     // If you've been tagged with Corona or are Glowing, the glow
3031     // makes you extremely unstealthy.
3032     if (you.backlit())
3033         stealth = stealth * 2 / 5;
3034 
3035     // On the other hand, shrouding has the reverse effect, if you know
3036     // how to make use of it:
3037     if (you.umbra())
3038     {
3039         int umbra_mul = 1, umbra_div = 1;
3040         if (you.nightvision())
3041         {
3042             umbra_mul = you.piety + MAX_PIETY;
3043             umbra_div = MAX_PIETY;
3044         }
3045         if (player_equip_unrand(UNRAND_SHADOWS)
3046             && 2 * umbra_mul < 3 * umbra_div)
3047         {
3048             umbra_mul = 3;
3049             umbra_div = 2;
3050         }
3051         stealth *= umbra_mul;
3052         stealth /= umbra_div;
3053     }
3054 
3055     if (you.form == transformation::shadow)
3056         stealth *= 2;
3057 
3058     // If you're surrounded by a storm, you're inherently pretty conspicuous.
3059     if (have_passive(passive_t::storm_shield))
3060     {
3061         stealth = stealth
3062                   * (MAX_PIETY - min((int)you.piety, piety_breakpoint(5)))
3063                   / (MAX_PIETY - piety_breakpoint(0));
3064     }
3065     // The shifting glow from the Orb, while too unstable to negate invis
3066     // or affect to-hit, affects stealth even more than regular glow.
3067     if (player_has_orb())
3068         stealth /= 3;
3069 
3070     stealth = max(0, stealth);
3071 
3072     return stealth;
3073 }
3074 
3075 // Is a given duration about to expire?
dur_expiring(duration_type dur)3076 bool dur_expiring(duration_type dur)
3077 {
3078     const int value = you.duration[dur];
3079     if (value <= 0)
3080         return false;
3081 
3082     return value <= duration_expire_point(dur);
3083 }
3084 
_display_char_status(int value,const char * fmt,...)3085 static void _display_char_status(int value, const char *fmt, ...)
3086 {
3087     va_list argp;
3088     va_start(argp, fmt);
3089 
3090     string msg = vmake_stringf(fmt, argp);
3091 
3092     if (you.wizard)
3093         mprf("%s (%d).", msg.c_str(), value);
3094     else
3095         mprf("%s.", msg.c_str());
3096 
3097     va_end(argp);
3098 }
3099 
_display_vampire_status()3100 static void _display_vampire_status()
3101 {
3102     string msg = "At your current blood state you ";
3103     vector<const char *> attrib;
3104 
3105     if (!you.vampire_alive)
3106     {
3107         attrib.push_back("are immune to poison");
3108         attrib.push_back("significantly resist cold");
3109         attrib.push_back("are immune to negative energy");
3110         attrib.push_back("resist torment");
3111         attrib.push_back("do not heal with monsters in sight.");
3112     }
3113     else
3114         attrib.push_back("heal quickly.");
3115 
3116     if (!attrib.empty())
3117     {
3118         msg += comma_separated_line(attrib.begin(), attrib.end());
3119         mpr(msg);
3120     }
3121 }
3122 
_display_movement_speed()3123 static void _display_movement_speed()
3124 {
3125     const int move_cost = (player_speed() * player_movement_speed()) / 10;
3126 
3127     const bool water  = you.in_liquid();
3128     const bool swim   = you.swimming();
3129 
3130     const bool fly    = you.airborne();
3131     const bool swift  = (you.duration[DUR_SWIFTNESS] > 0
3132                          && you.attribute[ATTR_SWIFTNESS] >= 0);
3133     const bool antiswift = (you.duration[DUR_SWIFTNESS] > 0
3134                             && you.attribute[ATTR_SWIFTNESS] < 0);
3135 
3136     _display_char_status(move_cost, "Your %s speed is %s%s%s",
3137           // order is important for these:
3138           (swim)    ? "swimming" :
3139           (water)   ? "wading" :
3140           (fly)     ? "flying"
3141                     : "movement",
3142 
3143           (!water && swift) ? "aided by the wind" :
3144           (!water && antiswift) ? "hindered by the wind" : "",
3145 
3146           (!water && swift) ? ((move_cost >= 10) ? ", but still "
3147                                                  : " and ") :
3148           (!water && antiswift) ? ((move_cost <= 10) ? ", but still "
3149                                                      : " and ")
3150                             : "",
3151 
3152           (move_cost <   8) ? "very quick" :
3153           (move_cost <  10) ? "quick" :
3154           (move_cost == 10) ? "average" :
3155           (move_cost <  13) ? "slow"
3156                             : "very slow");
3157 }
3158 
_display_tohit()3159 static void _display_tohit()
3160 {
3161 #ifdef DEBUG_DIAGNOSTICS
3162     melee_attack attk(&you, nullptr);
3163 
3164     const int to_hit = attk.calc_to_hit(false);
3165 
3166     dprf("To-hit: %d", to_hit);
3167 #endif
3168 }
3169 
3170 /**
3171  * Print a message indicating the player's attack delay with their current
3172  * weapon & its ammo (if applicable).
3173  *
3174  * Assumes the attack speed of a ranged weapon does not depend on what
3175  * ammunition is being used (as long as it is valid).
3176  */
_display_attack_delay()3177 static void _display_attack_delay()
3178 {
3179     const item_def* weapon = you.weapon();
3180     int delay;
3181     if (weapon && is_range_weapon(*weapon))
3182     {
3183         item_def ammo;
3184         ammo.base_type = OBJ_MISSILES;
3185         ammo.sub_type = fires_ammo_type(*weapon);
3186         delay = you.attack_delay(&ammo, false).expected();
3187     }
3188     else
3189         delay = you.attack_delay(nullptr, false).expected();
3190 
3191     const bool at_min_delay = weapon
3192                               && you.skill(item_attack_skill(*weapon))
3193                                  >= weapon_min_delay_skill(*weapon);
3194 
3195     mprf("Your attack delay is about %.1f%s%s.",
3196          delay / 10.0f,
3197          at_min_delay ?
3198             " (and cannot be improved with additional weapon skill)" : "",
3199          you.adjusted_shield_penalty() ?
3200             " (and is slowed by your insufficient shield skill)" : "");
3201 }
3202 
3203 // forward declaration
3204 static string _constriction_description();
3205 
display_char_status()3206 void display_char_status()
3207 {
3208     const int halo_size = you.halo_radius();
3209     if (halo_size >= 0)
3210     {
3211         if (halo_size > 37)
3212             mpr("You are illuminated by a large divine halo.");
3213         else if (halo_size > 10)
3214             mpr("You are illuminated by a divine halo.");
3215         else
3216             mpr("You are illuminated by a small divine halo.");
3217     }
3218     else if (you.haloed())
3219         mpr("An external divine halo illuminates you.");
3220 
3221     if (you.has_mutation(MUT_VAMPIRISM))
3222         _display_vampire_status();
3223 
3224     status_info inf;
3225     for (unsigned i = 0; i <= STATUS_LAST_STATUS; ++i)
3226     {
3227         if (fill_status_info(i, inf) && !inf.long_text.empty())
3228             mpr(inf.long_text);
3229     }
3230     string cinfo = _constriction_description();
3231     if (!cinfo.empty())
3232         mpr(cinfo);
3233 
3234     _display_movement_speed();
3235     _display_tohit();
3236     _display_attack_delay();
3237 
3238     // Display base attributes, if necessary.
3239     if (innate_stat(STAT_STR) != you.strength()
3240         || innate_stat(STAT_INT) != you.intel()
3241         || innate_stat(STAT_DEX) != you.dex())
3242     {
3243         mprf("Your base attributes are Str %d, Int %d, Dex %d.",
3244              innate_stat(STAT_STR),
3245              innate_stat(STAT_INT),
3246              innate_stat(STAT_DEX));
3247     }
3248 }
3249 
clarity(bool calc_unid,bool items) const3250 bool player::clarity(bool calc_unid, bool items) const
3251 {
3252     if (you.get_mutation_level(MUT_CLARITY))
3253         return true;
3254 
3255     if (have_passive(passive_t::clarity))
3256         return true;
3257 
3258     return actor::clarity(calc_unid, items);
3259 }
3260 
3261 /// Does the player have permastasis?
stasis() const3262 bool player::stasis() const
3263 {
3264     return species == SP_FORMICID;
3265 }
3266 
can_burrow() const3267 bool player::can_burrow() const
3268 {
3269     return species == SP_FORMICID;
3270 }
3271 
cloud_immune(bool calc_unid,bool items) const3272 bool player::cloud_immune(bool calc_unid, bool items) const
3273 {
3274     return have_passive(passive_t::cloud_immunity)
3275         || actor::cloud_immune(calc_unid, items);
3276 }
3277 
3278 /**
3279  * How much XP does it take to reach the given XL from 0?
3280  *
3281  *  @param lev          The XL to reach.
3282  *  @param exp_apt      The XP aptitude to use. If -99, use the current species'.
3283  *  @return     The total number of XP points needed to get to the given XL.
3284  */
exp_needed(int lev,int exp_apt)3285 unsigned int exp_needed(int lev, int exp_apt)
3286 {
3287     unsigned int level = 0;
3288 
3289     // Note: For historical reasons, all of the following numbers are for a
3290     // species (like human) with XP aptitude 1, not 0 as one might expect.
3291 
3292     // Basic plan:
3293     // Section 1: levels  1- 5, second derivative goes 10-10-20-30.
3294     // Section 2: levels  6-13, second derivative is exponential/doubling.
3295     // Section 3: levels 14-27, second derivative is constant at 8470.
3296 
3297     // Here's a table:
3298     //
3299     // level      xp      delta   delta2
3300     // =====   =======    =====   ======
3301     //   1           0        0       0
3302     //   2          10       10      10
3303     //   3          30       20      10
3304     //   4          70       40      20
3305     //   5         140       70      30
3306     //   6         270      130      60
3307     //   7         520      250     120
3308     //   8        1010      490     240
3309     //   9        1980      970     480
3310     //  10        3910     1930     960
3311     //  11        7760     3850    1920
3312     //  12       15450     7690    3840
3313     //  13       26895    11445    3755
3314     //  14       45585    18690    7245
3315     //  15       72745    27160    8470
3316     //  16      108375    35630    8470
3317     //  17      152475    44100    8470
3318     //  18      205045    52570    8470
3319     //  19      266085    61040    8470
3320     //  20      335595    69510    8470
3321     //  21      413575    77980    8470
3322     //  22      500025    86450    8470
3323     //  23      594945    94920    8470
3324     //  24      698335    103390   8470
3325     //  25      810195    111860   8470
3326     //  26      930525    120330   8470
3327     //  27     1059325    128800   8470
3328 
3329     // If you've sacrificed experience, XP costs are adjusted as if
3330     // you were still your original (higher) level.
3331     if (exp_apt == -99)
3332         lev += RU_SAC_XP_LEVELS * you.get_mutation_level(MUT_INEXPERIENCED);
3333 
3334     switch (lev)
3335     {
3336     case 1:
3337         level = 1;
3338         break;
3339     case 2:
3340         level = 10;
3341         break;
3342     case 3:
3343         level = 30;
3344         break;
3345     case 4:
3346         level = 70;
3347         break;
3348 
3349     default:
3350         if (lev < 13)
3351         {
3352             lev -= 4;
3353             level = 10 + 10 * lev + (60 << lev);
3354         }
3355         else
3356         {
3357             lev -= 12;
3358             level = 16675 + 5985 * lev + 4235 * lev * lev;
3359         }
3360         break;
3361     }
3362 
3363     if (exp_apt == -99)
3364         exp_apt = species::get_exp_modifier(you.species);
3365 
3366     return (unsigned int) ((level - 1) * apt_to_factor(exp_apt - 1));
3367 }
3368 
3369 // returns bonuses from rings of slaying, etc.
slaying_bonus(bool ranged)3370 int slaying_bonus(bool ranged)
3371 {
3372     int ret = 0;
3373 
3374     ret += you.wearing(EQ_RINGS_PLUS, RING_SLAYING);
3375     ret += you.scan_artefacts(ARTP_SLAYING);
3376     if (you.wearing_ego(EQ_GLOVES, SPARM_ARCHERY) && ranged)
3377         ret += 4;
3378 
3379     ret += 3 * augmentation_amount();
3380     ret += you.get_mutation_level(MUT_SHARP_SCALES);
3381 
3382     if (you.duration[DUR_WEREBLOOD])
3383         ret += you.props[WEREBLOOD_KEY].get_int();
3384 
3385     if (you.duration[DUR_HORROR])
3386         ret -= you.props[HORROR_PENALTY_KEY].get_int();
3387 
3388     if (you.props.exists(WU_JIAN_HEAVENLY_STORM_KEY))
3389         ret += you.props[WU_JIAN_HEAVENLY_STORM_KEY].get_int();
3390 
3391     return ret;
3392 }
3393 
3394 // Checks each equip slot for a randart, and adds up all of those with
3395 // a given property. Slow if any randarts are worn, so avoid where
3396 // possible. If `matches' is non-nullptr, items with nonzero property are
3397 // pushed onto *matches.
scan_artefacts(artefact_prop_type which_property,bool calc_unid,vector<const item_def * > * matches) const3398 int player::scan_artefacts(artefact_prop_type which_property,
3399                            bool calc_unid,
3400                            vector<const item_def *> *matches) const
3401 {
3402     int retval = 0;
3403 
3404     for (int i = EQ_FIRST_EQUIP; i < NUM_EQUIP; ++i)
3405     {
3406         if (melded[i] || equip[i] == -1)
3407             continue;
3408 
3409         const int eq = equip[i];
3410 
3411         const item_def &item = inv[eq];
3412 
3413         // Only weapons give their effects when in our hands.
3414         if (i == EQ_WEAPON && item.base_type != OBJ_WEAPONS)
3415             continue;
3416 
3417         int val = 0;
3418 
3419         // TODO: id check not needed, probably, due to full wear-id?
3420         if (is_artefact(item) && (calc_unid || fully_identified(item)))
3421             val = artefact_property(item, which_property);
3422 
3423         retval += val;
3424 
3425         if (matches && val)
3426             matches->push_back(&item);
3427     }
3428 
3429     return retval;
3430 }
3431 
dec_hp(int hp_loss,bool fatal,const char * aux)3432 void dec_hp(int hp_loss, bool fatal, const char *aux)
3433 {
3434     ASSERT(!crawl_state.game_is_arena());
3435 
3436     if (!fatal && you.hp < 1)
3437         you.hp = 1;
3438 
3439     if (!fatal && hp_loss >= you.hp)
3440         hp_loss = you.hp - 1;
3441 
3442     if (hp_loss < 1)
3443         return;
3444 
3445     // If it's not fatal, use ouch() so that notes can be taken. If it IS
3446     // fatal, somebody else is doing the bookkeeping, and we don't want to mess
3447     // with that.
3448     if (!fatal && aux)
3449         ouch(hp_loss, KILLED_BY_SOMETHING, MID_NOBODY, aux);
3450     else
3451         you.hp -= hp_loss;
3452 
3453     you.redraw_hit_points = true;
3454 }
3455 
calc_mp(bool scale)3456 void calc_mp(bool scale)
3457 {
3458     int old_max = you.max_magic_points;
3459     you.max_magic_points = get_real_mp(true);
3460     if (scale)
3461     {
3462         int mp = you.magic_points * 100 + you.magic_points_regeneration;
3463         int new_max = you.max_magic_points;
3464         if (old_max)
3465             mp = mp * new_max / old_max;
3466         you.magic_points = min(mp / 100, you.max_magic_points);
3467     }
3468     else
3469         you.magic_points = min(you.magic_points, you.max_magic_points);
3470     you.redraw_magic_points = true;
3471 }
3472 
flush_mp()3473 void flush_mp()
3474 {
3475     if (Options.magic_point_warning
3476         && you.magic_points < you.max_magic_points
3477                               * Options.magic_point_warning / 100)
3478     {
3479         mprf(MSGCH_DANGER, "* * * LOW MAGIC WARNING * * *");
3480     }
3481 
3482     take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points));
3483     you.redraw_magic_points = true;
3484 }
3485 
flush_hp()3486 void flush_hp()
3487 {
3488     if (Options.hp_warning
3489         && you.hp <= (you.hp_max * Options.hp_warning) / 100)
3490     {
3491         flash_view_delay(UA_HP, RED, 50);
3492         mprf(MSGCH_DANGER, "* * * LOW HITPOINT WARNING * * *");
3493         dungeon_events.fire_event(DET_HP_WARNING);
3494     }
3495     you.redraw_hit_points = true;
3496 }
3497 
_dec_mp(int mp_loss,bool silent)3498 static void _dec_mp(int mp_loss, bool silent)
3499 {
3500     ASSERT(!crawl_state.game_is_arena());
3501 
3502     if (mp_loss < 1)
3503         return;
3504 
3505     you.magic_points -= mp_loss;
3506 
3507     you.magic_points = max(0, you.magic_points);
3508     if (!silent)
3509         flush_mp();
3510 }
3511 
drain_mp(int mp_loss)3512 void drain_mp(int mp_loss)
3513 {
3514     _dec_mp(mp_loss, false);
3515 }
3516 
pay_hp(int cost)3517 void pay_hp(int cost)
3518 {
3519     you.hp -= cost;
3520     ASSERT(you.hp);
3521 }
3522 
pay_mp(int cost)3523 void pay_mp(int cost)
3524 {
3525     if (you.has_mutation(MUT_HP_CASTING))
3526         you.hp -= cost;
3527     else
3528         _dec_mp(cost, true);
3529 }
3530 
refund_hp(int cost)3531 void refund_hp(int cost)
3532 {
3533     you.hp += cost;
3534 }
3535 
refund_mp(int cost)3536 void refund_mp(int cost)
3537 {
3538     if (you.has_mutation(MUT_HP_CASTING))
3539     {
3540         you.hp += cost;
3541         you.redraw_hit_points = true;
3542     }
3543     else
3544     {
3545         inc_mp(cost, true);
3546         you.redraw_magic_points = true;
3547     }
3548 }
3549 
finalize_mp_cost(bool addl_hp_cost)3550 void finalize_mp_cost(bool addl_hp_cost)
3551 {
3552     if (you.has_mutation(MUT_HP_CASTING) || addl_hp_cost)
3553         flush_hp();
3554     if (!you.has_mutation(MUT_HP_CASTING))
3555         flush_mp();
3556 }
3557 
enough_hp(int minimum,bool suppress_msg,bool abort_macros)3558 bool enough_hp(int minimum, bool suppress_msg, bool abort_macros)
3559 {
3560     ASSERT(!crawl_state.game_is_arena());
3561 
3562     if (you.duration[DUR_DEATHS_DOOR])
3563     {
3564         if (!suppress_msg)
3565             mpr("You cannot pay life while functionally dead.");
3566 
3567         if (abort_macros)
3568         {
3569             crawl_state.cancel_cmd_again();
3570             crawl_state.cancel_cmd_repeat();
3571         }
3572         return false;
3573     }
3574 
3575     // We want to at least keep 1 HP. -- bwr
3576     if (you.hp < minimum + 1)
3577     {
3578         if (!suppress_msg)
3579             mpr("You don't have enough health at the moment.");
3580 
3581         if (abort_macros)
3582         {
3583             crawl_state.cancel_cmd_again();
3584             crawl_state.cancel_cmd_repeat();
3585         }
3586         return false;
3587     }
3588 
3589     return true;
3590 }
3591 
enough_mp(int minimum,bool suppress_msg,bool abort_macros)3592 bool enough_mp(int minimum, bool suppress_msg, bool abort_macros)
3593 {
3594     ASSERT(!crawl_state.game_is_arena());
3595 
3596     if (you.has_mutation(MUT_HP_CASTING))
3597         return enough_hp(minimum, suppress_msg, abort_macros);
3598 
3599     if (you.magic_points < minimum)
3600     {
3601         if (!suppress_msg)
3602         {
3603             if (get_real_mp(true) < minimum)
3604                 mpr("You don't have enough magic capacity.");
3605             else
3606                 mpr("You don't have enough magic at the moment.");
3607         }
3608         if (abort_macros)
3609         {
3610             crawl_state.cancel_cmd_again();
3611             crawl_state.cancel_cmd_repeat();
3612         }
3613         return false;
3614     }
3615 
3616     return true;
3617 }
3618 
_rest_trigger_level(int max)3619 static int _rest_trigger_level(int max)
3620 {
3621     return (max * Options.rest_wait_percent) / 100;
3622 }
3623 
_should_stop_resting(int cur,int max)3624 static bool _should_stop_resting(int cur, int max)
3625 {
3626     return cur == max || cur == _rest_trigger_level(max);
3627 }
3628 
inc_mp(int mp_gain,bool silent)3629 void inc_mp(int mp_gain, bool silent)
3630 {
3631     ASSERT(!crawl_state.game_is_arena());
3632 
3633     if (mp_gain < 1 || you.magic_points >= you.max_magic_points)
3634         return;
3635 
3636     you.magic_points += mp_gain;
3637 
3638     if (you.magic_points > you.max_magic_points)
3639         you.magic_points = you.max_magic_points;
3640 
3641     if (!silent)
3642     {
3643         if (_should_stop_resting(you.magic_points, you.max_magic_points))
3644             interrupt_activity(activity_interrupt::full_mp);
3645         you.redraw_magic_points = true;
3646     }
3647 }
3648 
3649 // Note that "max_too" refers to the base potential, the actual
3650 // resulting max value is subject to penalties, bonuses, and scalings.
3651 // To avoid message spam, don't take notes when HP increases.
inc_hp(int hp_gain,bool silent)3652 void inc_hp(int hp_gain, bool silent)
3653 {
3654     ASSERT(!crawl_state.game_is_arena());
3655 
3656     if (hp_gain < 1 || you.hp >= you.hp_max)
3657         return;
3658 
3659     you.hp += hp_gain;
3660 
3661     if (you.hp > you.hp_max)
3662         you.hp = you.hp_max;
3663 
3664     if (!silent)
3665     {
3666         if (_should_stop_resting(you.hp, you.hp_max))
3667             interrupt_activity(activity_interrupt::full_hp);
3668 
3669         you.redraw_hit_points = true;
3670     }
3671 }
3672 
drain_hp(int hp_loss)3673 void drain_hp(int hp_loss)
3674 {
3675     if (!player_drained() && hp_loss > 0)
3676         you.redraw_magic_points = true;
3677 
3678     const int initial_loss = you.hp_max_adj_temp;
3679     you.hp_max_adj_temp -= hp_loss;
3680     // don't allow more drain than you have normal mhp
3681     you.hp_max_adj_temp = max(-(get_real_hp(false, false) - 1),
3682                               you.hp_max_adj_temp);
3683     if (initial_loss == you.hp_max_adj_temp)
3684         return;
3685 
3686     calc_hp();
3687 
3688     xom_is_stimulated(hp_loss * 25);
3689 
3690     you.redraw_hit_points = true;
3691 }
3692 
undrain_hp(int hp_recovered)3693 int undrain_hp(int hp_recovered)
3694 {
3695     int hp_balance = 0;
3696     if (hp_recovered > -you.hp_max_adj_temp)
3697     {
3698         hp_balance = hp_recovered + you.hp_max_adj_temp;
3699         you.hp_max_adj_temp = 0;
3700     }
3701     else
3702         you.hp_max_adj_temp += hp_recovered;
3703     calc_hp();
3704 
3705     you.redraw_hit_points = true;
3706     if (!player_drained())
3707         you.redraw_magic_points = true;
3708     return hp_balance;
3709 }
3710 
player_drained()3711 int player_drained()
3712 {
3713     return -you.hp_max_adj_temp;
3714 }
3715 
rot_mp(int mp_loss)3716 void rot_mp(int mp_loss)
3717 {
3718     you.mp_max_adj -= mp_loss;
3719     calc_mp();
3720 
3721     you.redraw_magic_points = true;
3722 }
3723 
inc_max_hp(int hp_gain)3724 void inc_max_hp(int hp_gain)
3725 {
3726     you.hp += hp_gain;
3727     you.hp_max_adj_perm += hp_gain;
3728     calc_hp();
3729 
3730     take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max));
3731     you.redraw_hit_points = true;
3732 }
3733 
dec_max_hp(int hp_loss)3734 void dec_max_hp(int hp_loss)
3735 {
3736     you.hp_max_adj_perm -= hp_loss;
3737     calc_hp();
3738 
3739     take_note(Note(NOTE_MAXHP_CHANGE, you.hp_max));
3740     you.redraw_hit_points = true;
3741 }
3742 
set_hp(int new_amount)3743 void set_hp(int new_amount)
3744 {
3745     ASSERT(!crawl_state.game_is_arena());
3746 
3747     you.hp = new_amount;
3748 
3749     if (you.hp > you.hp_max)
3750         you.hp = you.hp_max;
3751 
3752     // Must remain outside conditional, given code usage. {dlb}
3753     you.redraw_hit_points = true;
3754 }
3755 
set_mp(int new_amount)3756 void set_mp(int new_amount)
3757 {
3758     ASSERT(!crawl_state.game_is_arena());
3759 
3760     you.magic_points = new_amount;
3761 
3762     if (you.magic_points > you.max_magic_points)
3763         you.magic_points = you.max_magic_points;
3764 
3765     take_note(Note(NOTE_MP_CHANGE, you.magic_points, you.max_magic_points));
3766 
3767     // Must remain outside conditional, given code usage. {dlb}
3768     you.redraw_magic_points = true;
3769 }
3770 
3771 /**
3772  * Get the player's max HP
3773  * @param trans          Whether to include transformations, berserk,
3774  *                       items etc.
3775  * @param drained        Whether to include the effects of draining.
3776  * @return               The player's calculated max HP.
3777  */
get_real_hp(bool trans,bool drained)3778 int get_real_hp(bool trans, bool drained)
3779 {
3780     int hitp;
3781 
3782     hitp  = you.experience_level * 11 / 2 + 8;
3783     hitp += you.hp_max_adj_perm;
3784     // Important: we shouldn't add Heroism boosts here.
3785     // ^ The above is a 2011 comment from 1kb, in 2021 this isn't
3786     // archaeologied for further explanation, but the below now adds Ash boosts
3787     // to fighting to the HP calculation while preventing it for Heroism
3788     // - eb
3789     hitp += you.experience_level * you.skill(SK_FIGHTING, 5, false, false) / 70
3790           + (you.skill(SK_FIGHTING, 3, false, false) + 1) / 2;
3791 
3792     // Racial modifier.
3793     hitp *= 10 + species::get_hp_modifier(you.species);
3794     hitp /= 10;
3795 
3796     hitp += you.get_mutation_level(MUT_FLAT_HP) * 4;
3797 
3798     const bool hep_frail = have_passive(passive_t::frail)
3799                            || player_under_penance(GOD_HEPLIAKLQANA);
3800 
3801     // Mutations that increase HP by a percentage
3802     hitp *= 100 + (you.get_mutation_level(MUT_ROBUST) * 10)
3803                 + (you.attribute[ATTR_DIVINE_VIGOUR] * 5)
3804                 + (you.get_mutation_level(MUT_RUGGED_BROWN_SCALES) ?
3805                    you.get_mutation_level(MUT_RUGGED_BROWN_SCALES) * 2 + 1 : 0)
3806                 - (you.get_mutation_level(MUT_FRAIL) * 10)
3807                 - (hep_frail ? 10 : 0)
3808                 - (!you.vampire_alive ? 20 : 0);
3809 
3810     hitp /= 100;
3811 
3812     if (drained)
3813         hitp += you.hp_max_adj_temp;
3814 
3815     if (trans)
3816         hitp += you.scan_artefacts(ARTP_HP);
3817 
3818     // Being berserk makes you resistant to damage. I don't know why.
3819     if (trans && you.berserk())
3820         hitp = hitp * 3 / 2;
3821 
3822     // Some transformations give you extra hp.
3823     if (trans)
3824         hitp = hitp * form_hp_mod() / 10;
3825 
3826 #if TAG_MAJOR_VERSION == 34
3827     if (trans && player_equip_unrand(UNRAND_ETERNAL_TORMENT))
3828         hitp = hitp * 4 / 5;
3829 #endif
3830 
3831     return max(1, hitp);
3832 }
3833 
get_real_mp(bool include_items)3834 int get_real_mp(bool include_items)
3835 {
3836     if (you.has_mutation(MUT_HP_CASTING))
3837         return 0;
3838 
3839     const int scale = 100;
3840     int spellcasting = you.skill(SK_SPELLCASTING, 1 * scale, false, false);
3841     int scaled_xl = you.experience_level * scale;
3842 
3843     // the first 4 experience levels give an extra .5 mp up to your spellcasting
3844     // the last 4 give no mp
3845     int enp = min(23 * scale, scaled_xl);
3846 
3847     int spell_extra = spellcasting; // 100%
3848     int invoc_extra = you.skill(SK_INVOCATIONS, 1 * scale, false, false) / 2; // 50%
3849     int highest_skill = max(spell_extra, invoc_extra);
3850     enp += highest_skill + min(8 * scale, min(highest_skill, scaled_xl)) / 2;
3851 
3852     // Analogous to ROBUST/FRAIL
3853     enp *= 100 + (you.get_mutation_level(MUT_HIGH_MAGIC) * 10)
3854                + (you.attribute[ATTR_DIVINE_VIGOUR] * 5)
3855                - (you.get_mutation_level(MUT_LOW_MAGIC) * 10);
3856     enp /= 100 * scale;
3857 //    enp = stepdown_value(enp, 9, 18, 45, 100)
3858     enp += species::get_mp_modifier(you.species);
3859 
3860     // This is our "rotted" base, applied after multipliers
3861     enp += you.mp_max_adj;
3862 
3863     // Now applied after scaling so that power items are more useful -- bwr
3864     if (include_items)
3865     {
3866         enp += 9 * you.wearing(EQ_RINGS, RING_MAGICAL_POWER);
3867         enp +=     you.scan_artefacts(ARTP_MAGICAL_POWER);
3868     }
3869 
3870     if (include_items && you.wearing_ego(EQ_WEAPON, SPWPN_ANTIMAGIC))
3871         enp /= 3;
3872 
3873     enp = max(enp, 0);
3874 
3875     return enp;
3876 }
3877 
3878 /// Does the player currently regenerate hp? Used for resting.
player_regenerates_hp()3879 bool player_regenerates_hp()
3880 {
3881     if (you.has_mutation(MUT_NO_REGENERATION) || regeneration_is_inhibited())
3882         return false;
3883 
3884     return true;
3885 }
3886 
player_regenerates_mp()3887 bool player_regenerates_mp()
3888 {
3889     // Djinn don't do the whole "mp" thing.
3890     if (you.has_mutation(MUT_HP_CASTING))
3891         return false;
3892     // Don't let DD use guardian spirit for free HP, since their
3893     // damage shaving is enough. (due, dpeg)
3894     if (you.spirit_shield() && you.species == SP_DEEP_DWARF)
3895         return false;
3896 #if TAG_MAJOR_VERSION == 34
3897     // Pakellas blocks MP regeneration.
3898     if (have_passive(passive_t::no_mp_regen) || player_under_penance(GOD_PAKELLAS))
3899         return false;
3900 #endif
3901     return true;
3902 }
3903 
get_contamination_level()3904 int get_contamination_level()
3905 {
3906     const int glow = you.magic_contamination;
3907 
3908     if (glow > 60000)
3909         return glow / 20000 + 4;
3910     if (glow > 40000)
3911         return 6;
3912     if (glow > 25000)
3913         return 5;
3914     if (glow > 15000)
3915         return 4;
3916     if (glow > 5000)
3917         return 3;
3918     if (glow > 3500) // An indicator that using another contamination-causing
3919         return 2;    // ability might risk causing yellow glow.
3920     if (glow > 0)
3921         return 1;
3922 
3923     return 0;
3924 }
3925 
player_severe_contamination()3926 bool player_severe_contamination()
3927 {
3928     return get_contamination_level() >= SEVERE_CONTAM_LEVEL;
3929 }
3930 
3931 /**
3932  * Provide a description of the given contamination 'level'.
3933  *
3934  * @param cont  A contamination 'tier', corresponding to a nonlinear
3935  *              contamination value; generated by get_contamination_level().
3936  *
3937  * @return      A string describing the player when in the given contamination
3938  *              level.
3939  */
describe_contamination(int cont)3940 string describe_contamination(int cont)
3941 {
3942     /// Mappings from contamination levels to descriptions.
3943     static const string contam_descriptions[] =
3944     {
3945         "",
3946         "You are very lightly contaminated with residual magic.",
3947         "You are lightly contaminated with residual magic.",
3948         "You are contaminated with residual magic.",
3949         "You are heavily infused with residual magic.",
3950         "You are practically glowing with residual magic!",
3951         "Your entire body has taken on an eerie glow!",
3952         "You are engulfed in a nimbus of crackling magics!",
3953     };
3954 
3955     ASSERT(cont >= 0);
3956     return contam_descriptions[min((size_t) cont,
3957                                    ARRAYSZ(contam_descriptions) - 1)];
3958 }
3959 
3960 // Controlled is true if the player actively did something to cause
3961 // contamination.
contaminate_player(int change,bool controlled,bool msg)3962 void contaminate_player(int change, bool controlled, bool msg)
3963 {
3964     ASSERT(!crawl_state.game_is_arena());
3965 
3966     int old_amount = you.magic_contamination;
3967     int old_level  = get_contamination_level();
3968     bool was_glowing = player_severe_contamination();
3969     int new_level  = 0;
3970 
3971 #if TAG_MAJOR_VERSION == 34
3972     if (change > 0 && player_equip_unrand(UNRAND_ETHERIC_CAGE))
3973         change *= 2;
3974 #endif
3975 
3976     you.magic_contamination = max(0, min(250000,
3977                                          you.magic_contamination + change));
3978 
3979     new_level = get_contamination_level();
3980 
3981     if (you.magic_contamination != old_amount)
3982         dprf("change: %d  radiation: %d", change, you.magic_contamination);
3983 
3984     if (new_level > old_level)
3985     {
3986         if (msg)
3987         {
3988             mprf(player_severe_contamination() ? MSGCH_WARN : MSGCH_PLAIN,
3989                  "%s", describe_contamination(new_level).c_str());
3990         }
3991         if (player_severe_contamination())
3992             xom_is_stimulated(new_level * 25);
3993     }
3994     else if (msg && new_level < old_level)
3995     {
3996         if (old_level == 1 && new_level == 0)
3997             mpr("Your magical contamination has completely faded away.");
3998         else if (player_severe_contamination() || was_glowing)
3999         {
4000             mprf(MSGCH_RECOVERY,
4001                  "You feel less contaminated with magical energies.");
4002         }
4003 
4004         if (!player_severe_contamination() && was_glowing && you.invisible())
4005         {
4006             mpr("You fade completely from view now that you are no longer "
4007                 "glowing from magical contamination.");
4008         }
4009     }
4010 
4011     if (you.magic_contamination > 0)
4012         learned_something_new(HINT_GLOWING);
4013 
4014     // Zin doesn't like mutations or mutagenic radiation.
4015     if (you_worship(GOD_ZIN))
4016     {
4017         // Whenever the glow status is first reached, give a warning message.
4018         if (!was_glowing && player_severe_contamination())
4019             did_god_conduct(DID_CAUSE_GLOWING, 0, false);
4020         // If the player actively did something to increase glowing,
4021         // Zin is displeased.
4022         else if (controlled && change > 0 && was_glowing)
4023             did_god_conduct(DID_CAUSE_GLOWING, 1 + new_level, true);
4024     }
4025 }
4026 
4027 /**
4028  * Increase the player's confusion duration.
4029  *
4030  * @param amount   The number of turns to increase confusion duration by.
4031  * @param quiet    Whether to suppress messaging on success/failure.
4032  * @param force    Whether to ignore resistance (used only for intentional
4033  *                 self-confusion, e.g. via ambrosia).
4034  * @return         Whether confusion was successful.
4035  */
confuse_player(int amount,bool quiet,bool force)4036 bool confuse_player(int amount, bool quiet, bool force)
4037 {
4038     ASSERT(!crawl_state.game_is_arena());
4039 
4040     if (amount <= 0)
4041         return false;
4042 
4043     if (!force && you.clarity())
4044     {
4045         if (!quiet)
4046             mpr("You feel momentarily confused.");
4047         return false;
4048     }
4049 
4050     if (!force && you.duration[DUR_DIVINE_STAMINA] > 0)
4051     {
4052         if (!quiet)
4053             mpr("Your divine stamina protects you from confusion!");
4054         return false;
4055     }
4056 
4057     const int old_value = you.duration[DUR_CONF];
4058     you.increase_duration(DUR_CONF, amount, 40);
4059 
4060     if (you.duration[DUR_CONF] > old_value)
4061     {
4062         you.check_awaken(500);
4063 
4064         if (!quiet)
4065         {
4066             mprf(MSGCH_WARN, "You are %sconfused.",
4067                  old_value > 0 ? "more " : "");
4068         }
4069 
4070         learned_something_new(HINT_YOU_ENCHANTED);
4071 
4072         xom_is_stimulated((you.duration[DUR_CONF] - old_value)
4073                            / BASELINE_DELAY);
4074     }
4075 
4076     return true;
4077 }
4078 
paralyse_player(string source,int amount)4079 void paralyse_player(string source, int amount)
4080 {
4081     if (!amount)
4082         amount = 2 + random2(6 + you.duration[DUR_PARALYSIS] / BASELINE_DELAY);
4083 
4084     you.paralyse(nullptr, amount, source);
4085 }
4086 
poison_player(int amount,string source,string source_aux,bool force)4087 bool poison_player(int amount, string source, string source_aux, bool force)
4088 {
4089     ASSERT(!crawl_state.game_is_arena());
4090 
4091     if (crawl_state.disables[DIS_AFFLICTIONS])
4092         return false;
4093 
4094     if (you.duration[DUR_DIVINE_STAMINA] > 0)
4095     {
4096         mpr("Your divine stamina protects you from poison!");
4097         return false;
4098     }
4099 
4100     if (player_res_poison() >= 3)
4101     {
4102         dprf("Cannot poison, you are immune!");
4103         return false;
4104     }
4105     else if (!force && player_res_poison() > 0 && !one_chance_in(3))
4106         return false;
4107 
4108     const int old_value = you.duration[DUR_POISONING];
4109     const bool was_fatal = poison_is_lethal();
4110 
4111     if (player_res_poison() < 0)
4112         amount *= 2;
4113 
4114     you.duration[DUR_POISONING] += amount * 1000;
4115 
4116     if (you.duration[DUR_POISONING] > old_value)
4117     {
4118         if (poison_is_lethal() && !was_fatal)
4119             mprf(MSGCH_DANGER, "You are lethally poisoned!");
4120         else
4121         {
4122             mprf(MSGCH_WARN, "You are %spoisoned.",
4123                 old_value > 0 ? "more " : "");
4124         }
4125 
4126         learned_something_new(HINT_YOU_POISON);
4127     }
4128 
4129     you.props["poisoner"] = source;
4130     you.props["poison_aux"] = source_aux;
4131 
4132     // Display the poisoned segment of our health, in case we take no damage
4133     you.redraw_hit_points = true;
4134 
4135     return amount;
4136 }
4137 
get_player_poisoning()4138 int get_player_poisoning()
4139 {
4140     if (player_res_poison() < 3)
4141     {
4142         // Approximate the effect of damage shaving by giving the first
4143         // 25 points of poison damage for 'free'
4144         if (you.species == SP_DEEP_DWARF)
4145             return max(0, (you.duration[DUR_POISONING] / 1000) - 25);
4146         else
4147             return you.duration[DUR_POISONING] / 1000;
4148     }
4149     else
4150         return 0;
4151 }
4152 
4153 // Fraction of current poison removed every 10 aut.
4154 const double poison_denom = 5.0;
4155 
4156 // these values are stored relative to dur's scaling, which is
4157 // poison_points * 1000;
4158 // 0.1 HP/aut
4159 const double poison_min_hp_aut  = 100.0;
4160 // 5.0 HP/aut
4161 const double poison_max_hp_aut  = 5000.0;
4162 
4163 // The amount of aut needed for poison to end if
4164 // you.duration[DUR_POISONING] == dur, assuming no Chei/DD shenanigans.
4165 // This function gives the following behaviour:
4166 // * 1/poison_denominator of current poison is removed every 10 aut normally
4167 // * but speed of poison is capped between the two parameters
_poison_dur_to_aut(double dur)4168 static double _poison_dur_to_aut(double dur)
4169 {
4170     const double min_speed_dur = poison_denom * poison_min_hp_aut * 10.0;
4171     const double decay = log(poison_denom / (poison_denom - 1.0));
4172     // Poison already at minimum speed.
4173     if (dur < min_speed_dur)
4174         return dur / poison_min_hp_aut;
4175     // Poison is not at maximum speed.
4176     if (dur < poison_denom * poison_max_hp_aut * 10.0)
4177         return 10.0 * (poison_denom + log(dur / min_speed_dur) / decay);
4178     return 10.0 * (poison_denom + log(poison_max_hp_aut / poison_min_hp_aut) / decay)
4179          + (dur - poison_denom * poison_max_hp_aut * 10.0) / poison_max_hp_aut;
4180 }
4181 
4182 // The inverse of the above function, i.e. the amount of poison needed
4183 // to last for aut time.
_poison_aut_to_dur(double aut)4184 static double _poison_aut_to_dur(double aut)
4185 {
4186     // Amount of time that poison lasts at minimum speed.
4187     if (aut < poison_denom * 10.0)
4188         return aut * poison_min_hp_aut;
4189     const double decay = log(poison_denom / (poison_denom - 1.0));
4190     // Amount of time that poison exactly at the maximum speed lasts.
4191     const double aut_from_max_speed = 10.0 * (poison_denom
4192         + log(poison_max_hp_aut / poison_min_hp_aut) / decay);
4193     if (aut < aut_from_max_speed)
4194     {
4195         return 10.0 * poison_denom * poison_min_hp_aut
4196             * exp(decay / 10.0 * (aut - poison_denom * 10.0));
4197     }
4198     return poison_denom * 10.0 * poison_max_hp_aut
4199          + poison_max_hp_aut * (aut - aut_from_max_speed);
4200 }
4201 
handle_player_poison(int delay)4202 void handle_player_poison(int delay)
4203 {
4204     const double cur_dur = you.duration[DUR_POISONING];
4205     const double cur_aut = _poison_dur_to_aut(cur_dur);
4206 
4207     // If Cheibriados has slowed your life processes, poison affects you less
4208     // quickly (you take the same total damage, but spread out over a longer
4209     // period of time).
4210     const double delay_scaling = have_passive(passive_t::slow_poison)
4211                                ? 2.0 / 3.0 : 1.0;
4212 
4213     const double new_aut = cur_aut - ((double) delay) * delay_scaling;
4214     const double new_dur = _poison_aut_to_dur(new_aut);
4215 
4216     const int decrease = you.duration[DUR_POISONING] - (int) new_dur;
4217 
4218     // Transforming into a form with no metabolism merely suspends the poison
4219     // but doesn't let your body get rid of it.
4220     if (you.is_nonliving() || you.is_lifeless_undead())
4221         return;
4222 
4223     // Other sources of immunity (Zin, staff of Olgreb) let poison dissipate.
4224     bool do_dmg = (player_res_poison() >= 3 ? false : true);
4225 
4226     int dmg = (you.duration[DUR_POISONING] / 1000)
4227                - ((you.duration[DUR_POISONING] - decrease) / 1000);
4228 
4229     // Approximate old damage shaving by giving immunity to small amounts
4230     // of poison. Stronger poison will do the same damage as for non-DD
4231     // until it goes below the threshold, which is a bit weird, but
4232     // so is damage shaving.
4233     if (you.species == SP_DEEP_DWARF && you.duration[DUR_POISONING] - decrease < 25000)
4234     {
4235         dmg = (you.duration[DUR_POISONING] / 1000)
4236             - (25000 / 1000);
4237         if (dmg < 0)
4238             dmg = 0;
4239     }
4240 
4241     msg_channel_type channel = MSGCH_PLAIN;
4242     const char *adj = "";
4243 
4244     if (dmg > 6)
4245     {
4246         channel = MSGCH_DANGER;
4247         adj = "extremely ";
4248     }
4249     else if (dmg > 2)
4250     {
4251         channel = MSGCH_WARN;
4252         adj = "very ";
4253     }
4254 
4255     if (do_dmg && dmg > 0)
4256     {
4257         int oldhp = you.hp;
4258         ouch(dmg, KILLED_BY_POISON);
4259         if (you.hp < oldhp)
4260             mprf(channel, "You feel %ssick.", adj);
4261     }
4262 
4263     // Now decrease the poison in our system
4264     reduce_player_poison(decrease);
4265 }
4266 
reduce_player_poison(int amount)4267 void reduce_player_poison(int amount)
4268 {
4269     if (amount <= 0)
4270         return;
4271 
4272     you.duration[DUR_POISONING] -= amount;
4273 
4274     // Less than 1 point of damage remaining, so just end the poison
4275     if (you.duration[DUR_POISONING] < 1000)
4276         you.duration[DUR_POISONING] = 0;
4277 
4278     if (you.duration[DUR_POISONING] <= 0)
4279     {
4280         you.duration[DUR_POISONING] = 0;
4281         you.props.erase("poisoner");
4282         you.props.erase("poison_aux");
4283         mprf(MSGCH_RECOVERY, "You are no longer poisoned.");
4284     }
4285 
4286     you.redraw_hit_points = true;
4287 }
4288 
4289 // Takes *current* regeneration rate into account. Might sometimes be
4290 // incorrect, but hopefully if so then the player is surviving with 1 HP.
poison_is_lethal()4291 bool poison_is_lethal()
4292 {
4293     if (you.hp <= 0)
4294         return get_player_poisoning();
4295     if (get_player_poisoning() < you.hp)
4296         return false;
4297     return poison_survival() <= 0;
4298 }
4299 
4300 // Try to predict the minimum value of the player's health in the coming
4301 // turns given the current poison amount and regen rate.
poison_survival()4302 int poison_survival()
4303 {
4304     if (!get_player_poisoning())
4305         return you.hp;
4306     const int rr = player_regen();
4307     const bool chei = have_passive(passive_t::slow_poison);
4308     const bool dd = (you.species == SP_DEEP_DWARF);
4309     const int amount = you.duration[DUR_POISONING];
4310     const double full_aut = _poison_dur_to_aut(amount);
4311     // Calculate the poison amount at which regen starts to beat poison.
4312     double min_poison_rate = poison_min_hp_aut;
4313     if (dd)
4314         min_poison_rate = 25.0/poison_denom;
4315     if (chei)
4316         min_poison_rate /= 1.5;
4317     int regen_beats_poison;
4318     if (rr <= (int) min_poison_rate)
4319         regen_beats_poison = dd ? 25000 : 0;
4320     else
4321     {
4322         regen_beats_poison = poison_denom * 10.0 * rr;
4323         if (chei)
4324             regen_beats_poison = 3 * regen_beats_poison / 2;
4325     }
4326 
4327     if (rr == 0)
4328         return min(you.hp, you.hp - amount / 1000 + regen_beats_poison / 1000);
4329 
4330     // Calculate the amount of time until regen starts to beat poison.
4331     double poison_duration = full_aut - _poison_dur_to_aut(regen_beats_poison);
4332 
4333     if (poison_duration < 0)
4334         poison_duration = 0;
4335     if (chei)
4336         poison_duration *= 1.5;
4337 
4338     // Worst case scenario is right before natural regen gives you a point of
4339     // HP, so consider the nearest two such points.
4340     const int predicted_regen = (int) ((((double) you.hit_points_regeneration) + rr * poison_duration / 10.0) / 100.0);
4341     double test_aut1 = (100.0 * predicted_regen - 1.0 - ((double) you.hit_points_regeneration)) / (rr / 10.0);
4342     double test_aut2 = (100.0 * predicted_regen + 99.0 - ((double) you.hit_points_regeneration)) / (rr / 10.0);
4343 
4344     if (chei)
4345     {
4346         test_aut1 /= 1.5;
4347         test_aut2 /= 1.5;
4348     }
4349 
4350     // Don't do any correction if there's not much poison left
4351     const int test_amount1 = test_aut1 < full_aut ?
4352                              _poison_aut_to_dur(full_aut - test_aut1) : 0;
4353     const int test_amount2 = test_aut2 < full_aut ?
4354                              _poison_aut_to_dur(full_aut - test_aut2) : 0;
4355 
4356     int prediction1 = you.hp;
4357     int prediction2 = you.hp;
4358 
4359     // Don't look backwards in time.
4360     if (test_aut1 > 0.0)
4361         prediction1 -= (amount / 1000 - test_amount1 / 1000 - (predicted_regen - 1));
4362     prediction2 -= (amount / 1000 - test_amount2 / 1000 - predicted_regen);
4363 
4364     return min(prediction1, prediction2);
4365 }
4366 
miasma_player(actor * who,string source_aux)4367 bool miasma_player(actor *who, string source_aux)
4368 {
4369     ASSERT(!crawl_state.game_is_arena());
4370 
4371     if (you.res_miasma() || you.duration[DUR_DEATHS_DOOR])
4372         return false;
4373 
4374     if (you.duration[DUR_DIVINE_STAMINA] > 0)
4375     {
4376         mpr("Your divine stamina protects you from the miasma!");
4377         return false;
4378     }
4379 
4380     bool success = poison_player(5 + roll_dice(3, 12),
4381                                  who ? who->name(DESC_A) : "",
4382                                  source_aux);
4383 
4384     if (one_chance_in(3))
4385     {
4386         slow_player(10 + random2(5));
4387         success = true;
4388     }
4389 
4390     return success;
4391 }
4392 
napalm_player(int amount,string source,string source_aux)4393 bool napalm_player(int amount, string source, string source_aux)
4394 {
4395     ASSERT(!crawl_state.game_is_arena());
4396 
4397     if (player_res_sticky_flame() || amount <= 0 || you.duration[DUR_WATER_HOLD] || feat_is_watery(env.grid(you.pos())))
4398         return false;
4399 
4400     const int old_value = you.duration[DUR_LIQUID_FLAMES];
4401     you.increase_duration(DUR_LIQUID_FLAMES, amount, 100);
4402 
4403     if (you.duration[DUR_LIQUID_FLAMES] > old_value)
4404         mprf(MSGCH_WARN, "You are covered in liquid flames!");
4405 
4406     you.props["sticky_flame_source"] = source;
4407     you.props["sticky_flame_aux"] = source_aux;
4408 
4409     return true;
4410 }
4411 
dec_napalm_player(int delay)4412 void dec_napalm_player(int delay)
4413 {
4414     delay = min(delay, you.duration[DUR_LIQUID_FLAMES]);
4415 
4416     if (feat_is_watery(env.grid(you.pos())))
4417     {
4418         if (you.ground_level())
4419             mprf(MSGCH_WARN, "The flames go out!");
4420         else
4421             mprf(MSGCH_WARN, "You dip into the water, and the flames go out!");
4422         you.duration[DUR_LIQUID_FLAMES] = 0;
4423         you.props.erase("sticky_flame_source");
4424         you.props.erase("sticky_flame_aux");
4425         return;
4426     }
4427 
4428     mprf(MSGCH_WARN, "You are covered in liquid flames!");
4429 
4430     const int hurted = resist_adjust_damage(&you, BEAM_FIRE,
4431                                             random2avg(9, 2) + 1);
4432 
4433     you.expose_to_element(BEAM_STICKY_FLAME, 2);
4434     maybe_melt_player_enchantments(BEAM_STICKY_FLAME, hurted * delay / BASELINE_DELAY);
4435 
4436     ouch(hurted * delay / BASELINE_DELAY, KILLED_BY_BURNING);
4437 
4438     you.duration[DUR_LIQUID_FLAMES] =
4439         max(0, you.duration[DUR_LIQUID_FLAMES] - delay);
4440 }
4441 
slow_player(int turns)4442 bool slow_player(int turns)
4443 {
4444     ASSERT(!crawl_state.game_is_arena());
4445 
4446     if (turns <= 0)
4447         return false;
4448 
4449     if (you.stasis())
4450     {
4451         mpr("Your stasis prevents you from being slowed.");
4452         return false;
4453     }
4454 
4455     // Multiplying these values because moving while slowed takes longer than
4456     // the usual delay.
4457     turns = haste_mul(turns);
4458     int threshold = haste_mul(100);
4459 
4460     if (you.duration[DUR_SLOW] >= threshold * BASELINE_DELAY)
4461         mpr("You already are as slow as you could be.");
4462     else
4463     {
4464         if (you.duration[DUR_SLOW] == 0)
4465             mpr("You feel yourself slow down.");
4466         else
4467             mpr("You feel as though you will be slow longer.");
4468 
4469         you.increase_duration(DUR_SLOW, turns, threshold);
4470         learned_something_new(HINT_YOU_ENCHANTED);
4471     }
4472 
4473     return true;
4474 }
4475 
dec_slow_player(int delay)4476 void dec_slow_player(int delay)
4477 {
4478     if (!you.duration[DUR_SLOW])
4479         return;
4480 
4481     if (you.duration[DUR_SLOW] > BASELINE_DELAY)
4482     {
4483         // Make slowing and hasting effects last as long.
4484         you.duration[DUR_SLOW] -= you.duration[DUR_HASTE]
4485             ? haste_mul(delay) : delay;
4486     }
4487 
4488     if (you.torpor_slowed())
4489     {
4490         you.duration[DUR_SLOW] = 1;
4491         return;
4492     }
4493     if (you.props.exists(TORPOR_SLOWED_KEY))
4494         you.props.erase(TORPOR_SLOWED_KEY);
4495 
4496     if (you.duration[DUR_SLOW] <= BASELINE_DELAY)
4497     {
4498         you.duration[DUR_SLOW] = 0;
4499         if (!have_stat_zero())
4500             mprf(MSGCH_DURATION, "You feel yourself speed up.");
4501     }
4502 }
4503 
dec_berserk_recovery_player(int delay)4504 void dec_berserk_recovery_player(int delay)
4505 {
4506     if (!you.duration[DUR_BERSERK_COOLDOWN])
4507         return;
4508 
4509     if (you.duration[DUR_BERSERK_COOLDOWN] > BASELINE_DELAY)
4510     {
4511         you.duration[DUR_BERSERK_COOLDOWN] -= you.duration[DUR_HASTE]
4512                                               ? haste_mul(delay) : delay;
4513     }
4514 
4515     if (you.duration[DUR_BERSERK_COOLDOWN] <= BASELINE_DELAY)
4516     {
4517         mprf(MSGCH_DURATION, "You recover from your berserk rage.");
4518         you.duration[DUR_BERSERK_COOLDOWN] = 0;
4519     }
4520 }
4521 
haste_player(int turns,bool rageext)4522 bool haste_player(int turns, bool rageext)
4523 {
4524     ASSERT(!crawl_state.game_is_arena());
4525 
4526     if (turns <= 0)
4527         return false;
4528 
4529     if (you.stasis())
4530     {
4531         mpr("Your stasis prevents you from being hasted.");
4532         return false;
4533     }
4534 
4535     // Cutting the nominal turns in half since hasted actions take half the
4536     // usual delay.
4537     turns = haste_div(turns);
4538     const int threshold = 40;
4539 
4540     if (!you.duration[DUR_HASTE])
4541         mpr("You feel yourself speed up.");
4542     else if (you.duration[DUR_HASTE] > threshold * BASELINE_DELAY)
4543         mpr("You already have as much speed as you can handle.");
4544     else if (!rageext)
4545         mpr("You feel as though your hastened speed will last longer.");
4546 
4547     you.increase_duration(DUR_HASTE, turns, threshold);
4548 
4549     return true;
4550 }
4551 
dec_haste_player(int delay)4552 void dec_haste_player(int delay)
4553 {
4554     if (!you.duration[DUR_HASTE])
4555         return;
4556 
4557     if (you.duration[DUR_HASTE] > BASELINE_DELAY)
4558     {
4559         int old_dur = you.duration[DUR_HASTE];
4560 
4561         you.duration[DUR_HASTE] -= delay;
4562 
4563         int threshold = 6 * BASELINE_DELAY;
4564         // message if we cross the threshold
4565         if (old_dur > threshold && you.duration[DUR_HASTE] <= threshold)
4566         {
4567             mprf(MSGCH_DURATION, "Your extra speed is starting to run out.");
4568             if (coinflip())
4569                 you.duration[DUR_HASTE] -= BASELINE_DELAY;
4570         }
4571     }
4572     else if (you.duration[DUR_HASTE] <= BASELINE_DELAY)
4573     {
4574         if (!you.duration[DUR_BERSERK])
4575             mprf(MSGCH_DURATION, "You feel yourself slow down.");
4576         you.duration[DUR_HASTE] = 0;
4577     }
4578 }
4579 
dec_elixir_player(int delay)4580 void dec_elixir_player(int delay)
4581 {
4582     if (!you.duration[DUR_ELIXIR])
4583         return;
4584 
4585     you.duration[DUR_ELIXIR] -= delay;
4586     if (you.duration[DUR_ELIXIR] < 0)
4587         you.duration[DUR_ELIXIR] = 0;
4588 
4589     const int hp = (delay * you.hp_max / 10) / BASELINE_DELAY;
4590     if (!you.duration[DUR_DEATHS_DOOR])
4591         inc_hp(hp);
4592 
4593     const int mp = (delay * you.max_magic_points / 10) / BASELINE_DELAY;
4594     inc_mp(mp);
4595 }
4596 
dec_ambrosia_player(int delay)4597 void dec_ambrosia_player(int delay)
4598 {
4599     if (!you.duration[DUR_AMBROSIA])
4600         return;
4601 
4602     // ambrosia ends when confusion does.
4603     if (!you.confused())
4604         you.duration[DUR_AMBROSIA] = 0;
4605 
4606     you.duration[DUR_AMBROSIA] = max(0, you.duration[DUR_AMBROSIA] - delay);
4607 
4608     // 3-5 per turn, 9-50 over (3-10) turns
4609     const int hp_restoration = div_rand_round(delay*(3 + random2(3)), BASELINE_DELAY);
4610     const int mp_restoration = div_rand_round(delay*(3 + random2(3)), BASELINE_DELAY);
4611 
4612     if (!you.duration[DUR_DEATHS_DOOR])
4613         inc_hp(you.scale_potion_healing(hp_restoration));
4614 
4615     inc_mp(mp_restoration);
4616 
4617     if (!you.duration[DUR_AMBROSIA])
4618         mpr("You feel less invigorated.");
4619 }
4620 
dec_channel_player(int delay)4621 void dec_channel_player(int delay)
4622 {
4623     if (!you.duration[DUR_CHANNEL_ENERGY])
4624         return;
4625 
4626     you.duration[DUR_CHANNEL_ENERGY] =
4627         max(0, you.duration[DUR_CHANNEL_ENERGY] - delay);
4628 
4629     // 3-5 per turn, 9-50 over (3-10) turns
4630     const int mp_restoration = div_rand_round(delay*(3 + random2(3)),
4631                                               BASELINE_DELAY);
4632     inc_mp(mp_restoration);
4633 
4634     if (!you.duration[DUR_CHANNEL_ENERGY])
4635         mpr("You feel less invigorated.");
4636 }
4637 
dec_frozen_ramparts(int delay)4638 void dec_frozen_ramparts(int delay)
4639 {
4640     if (!you.duration[DUR_FROZEN_RAMPARTS])
4641         return;
4642 
4643     you.duration[DUR_FROZEN_RAMPARTS] =
4644         max(0, you.duration[DUR_FROZEN_RAMPARTS] - delay);
4645 
4646     if (!you.duration[DUR_FROZEN_RAMPARTS])
4647     {
4648         end_frozen_ramparts();
4649         mpr("The frozen ramparts melt away.");
4650     }
4651 }
4652 
invis_allowed(bool quiet,string * fail_reason)4653 bool invis_allowed(bool quiet, string *fail_reason)
4654 {
4655     string msg;
4656     bool success = true;
4657 
4658     if (you.haloed() && you.halo_radius() != -1)
4659     {
4660         bool divine = you.props.exists(WU_JIAN_HEAVENLY_STORM_KEY)
4661                       || you.religion == GOD_SHINING_ONE;
4662         bool weapon = player_equip_unrand(UNRAND_EOS);
4663         string reason;
4664 
4665         if (divine && weapon)
4666             reason = "Your weapon and divine halo glow too brightly";
4667         else if (divine)
4668             reason = "Your divine halo glows too radiantly";
4669         else if (weapon)
4670             reason = "Your weapon shines too brightly";
4671         else
4672             die("haloed by an unknown source");
4673 
4674         msg = reason + " to become invisible.";
4675         success = false;
4676     }
4677     else if (you.backlit())
4678     {
4679         msg = "Invisibility will do you no good right now";
4680         if (quiet)
4681             success = false;
4682         else if (!quiet && !yesno((msg + "; use anyway?").c_str(), false, 'n'))
4683         {
4684             canned_msg(MSG_OK);
4685             success = false;
4686             quiet = true; // since we just said something
4687         }
4688         msg += ".";
4689     }
4690 
4691     if (!success)
4692     {
4693         if (fail_reason)
4694             *fail_reason = msg;
4695         if (!quiet)
4696             mpr(msg);
4697     }
4698     return success;
4699 }
4700 
flight_allowed(bool quiet,string * fail_reason)4701 bool flight_allowed(bool quiet, string *fail_reason)
4702 {
4703     string msg;
4704     bool success = true;
4705 
4706     if (get_form()->forbids_flight())
4707     {
4708         msg = you.form == transformation::tree ? "Your roots keep you in place."
4709             : "You can't fly in this form.";
4710         success = false;
4711     }
4712 
4713     if (!success)
4714     {
4715         if (fail_reason)
4716             *fail_reason = msg;
4717         if (!quiet)
4718             mpr(msg);
4719     }
4720     return success;
4721 }
4722 
float_player()4723 void float_player()
4724 {
4725     if (you.fishtail)
4726     {
4727         mpr("Your tail turns into legs as you fly out of the water.");
4728         merfolk_stop_swimming();
4729     }
4730     else if (you.tengu_flight())
4731         mpr("You swoop lightly up into the air.");
4732     else
4733         mpr("You fly up into the air.");
4734 
4735     if (you.has_mutation(MUT_TENGU_FLIGHT))
4736         you.redraw_evasion = true;
4737 }
4738 
fly_player(int pow,bool already_flying)4739 void fly_player(int pow, bool already_flying)
4740 {
4741     if (!flight_allowed())
4742         return;
4743 
4744     bool standing = !you.airborne() && !already_flying;
4745     if (!already_flying)
4746         mprf(MSGCH_DURATION, "You feel %s buoyant.", standing ? "very" : "more");
4747 
4748     you.increase_duration(DUR_FLIGHT, 25 + random2(pow), 100);
4749 
4750     if (standing)
4751         float_player();
4752 }
4753 
enable_emergency_flight()4754 void enable_emergency_flight()
4755 {
4756     mprf("You can't survive in this terrain! You fly above the %s, but the "
4757          "process is draining.",
4758          (env.grid(you.pos()) == DNGN_LAVA)       ? "lava" :
4759          (env.grid(you.pos()) == DNGN_DEEP_WATER) ? "water"
4760                                              : "buggy terrain");
4761 
4762     you.props[EMERGENCY_FLIGHT_KEY] = true;
4763 }
4764 
4765 /**
4766  * Handle the player's flight ending. Apply emergency flight if needed.
4767  *
4768  * @param quiet         Should we notify the player flight is ending?
4769  * @return              If flight was ended.
4770  */
land_player(bool quiet)4771 bool land_player(bool quiet)
4772 {
4773     // re-update the equipment cache: any sources of flight from equipment?
4774     you.attribute[ATTR_PERM_FLIGHT] = you.equip_flight() ? 1 : 0;
4775 
4776     // there was another source keeping you aloft
4777     if (you.airborne())
4778         return false;
4779 
4780     // Handle landing on (formerly) instakill terrain
4781     if (is_feat_dangerous(env.grid(you.pos())))
4782     {
4783         fall_into_a_pool(env.grid(you.pos()));
4784         return false;
4785     }
4786 
4787     if (!quiet)
4788         mpr("You float gracefully downwards.");
4789     if (you.has_mutation(MUT_TENGU_FLIGHT))
4790         you.redraw_evasion = true;
4791 
4792     // Re-enter the terrain.
4793     move_player_to_grid(you.pos(), false);
4794     return true;
4795 }
4796 
_end_water_hold()4797 static void _end_water_hold()
4798 {
4799     you.duration[DUR_WATER_HOLD] = 0;
4800     you.props.erase("water_holder");
4801     you.props.erase("water_hold_substance");
4802 }
4803 
clear_far_engulf(bool force)4804 bool player::clear_far_engulf(bool force)
4805 {
4806     if (!you.duration[DUR_WATER_HOLD])
4807         return false;
4808 
4809     monster * const mons = monster_by_mid(you.props["water_holder"].get_int());
4810     if (force || !mons || !mons->alive() || !adjacent(mons->pos(), you.pos()))
4811     {
4812         if (you.res_water_drowning())
4813             mprf("The %s engulfing you falls away.", water_hold_substance().c_str());
4814         else
4815             mpr("You gasp with relief as air once again reaches your lungs.");
4816 
4817         _end_water_hold();
4818         return true;
4819     }
4820     return false;
4821 }
4822 
handle_player_drowning(int delay)4823 void handle_player_drowning(int delay)
4824 {
4825     if (you.clear_far_engulf())
4826         return;
4827     if (you.res_water_drowning())
4828     {
4829         // Reset so damage doesn't ramp up while able to breathe
4830         you.duration[DUR_WATER_HOLD] = 10;
4831     }
4832     else
4833     {
4834         you.duration[DUR_WATER_HOLD] += delay;
4835         int dam =
4836             div_rand_round((28 + stepdown((float)you.duration[DUR_WATER_HOLD], 28.0))
4837                             * delay,
4838                             BASELINE_DELAY * 10);
4839         ouch(dam, KILLED_BY_WATER, you.props["water_holder"].get_int());
4840         mprf(MSGCH_WARN, "Your lungs strain for air!");
4841     }
4842 }
4843 
count_worn_ego(int which_ego)4844 int count_worn_ego(int which_ego)
4845 {
4846     int result = 0;
4847     for (int slot = EQ_MIN_ARMOUR; slot <= EQ_MAX_ARMOUR; ++slot)
4848     {
4849         if (you.equip[slot] != -1 && !you.melded[slot]
4850             && get_armour_ego_type(you.inv[you.equip[slot]]) == which_ego)
4851         {
4852             result++;
4853         }
4854     }
4855 
4856     return result;
4857 }
4858 
player()4859 player::player()
4860 {
4861     // warning: this constructor is called for `you` in an indeterminate order
4862     // with respect to other globals, and so anything that depends on a global
4863     // you should not do here. This includes things like `branches`, as well as
4864     // any const static string prop name -- any object that needs to call a
4865     // constructor is risky, and may or may not have called it yet. E.g. strings
4866     // could be empty, branches could have every branch set as the dungeon, etc.
4867     // One candidate location is startup.cc:_initialize, which is nearly the
4868     // first things called in the launch game loop.
4869 
4870     chr_god_name.clear();
4871     chr_species_name.clear();
4872     chr_class_name.clear();
4873     // Permanent data:
4874     your_name.clear();
4875     species          = SP_UNKNOWN;
4876     char_class       = JOB_UNKNOWN;
4877     type             = MONS_PLAYER;
4878     mid              = MID_PLAYER;
4879     position.reset();
4880 
4881 #ifdef WIZARD
4882     wizard = Options.wiz_mode == WIZ_YES;
4883     explore = Options.explore_mode == WIZ_YES;
4884 #else
4885     wizard = false;
4886     explore = false;
4887 #endif
4888     suppress_wizard = false;
4889 
4890     birth_time       = time(0);
4891 
4892     // Long-term state:
4893     elapsed_time     = 0;
4894     elapsed_time_at_last_input = 0;
4895 
4896     hp               = 0;
4897     hp_max           = 0;
4898     hp_max_adj_temp  = 0;
4899     hp_max_adj_perm  = 0;
4900 
4901     magic_points     = 0;
4902     max_magic_points = 0;
4903     mp_max_adj       = 0;
4904 
4905     stat_loss.init(0);
4906     base_stats.init(0);
4907 
4908     max_level       = 1;
4909     hit_points_regeneration   = 0;
4910     magic_points_regeneration = 0;
4911     experience       = 0;
4912     total_experience = 0;
4913     experience_level = 1;
4914     experience_pool  = 0;
4915     gold             = 0;
4916     zigs_completed   = 0;
4917     zig_max          = 0;
4918 
4919     equip.init(-1);
4920     melded.reset();
4921     unrand_reacts.reset();
4922     activated.reset();
4923     last_unequip = -1;
4924 
4925     symbol          = MONS_PLAYER;
4926     form            = transformation::none;
4927 
4928     for (auto &item : inv)
4929         item.clear();
4930     runes.reset();
4931     obtainable_runes = 15;
4932 
4933     spell_library.reset();
4934     spells.init(SPELL_NO_SPELL);
4935     old_vehumet_gifts.clear();
4936     spell_no        = 0;
4937     vehumet_gifts.clear();
4938     chapter  = CHAPTER_ORB_HUNTING;
4939     royal_jelly_dead = false;
4940     transform_uncancellable = false;
4941     fishtail = false;
4942     vampire_alive = true;
4943 
4944     pet_target      = MHITNOT;
4945 
4946     duration.init(0);
4947     apply_berserk_penalty = false;
4948     berserk_penalty = 0;
4949     attribute.init(0);
4950     quiver.init(ENDOFPACK);
4951 
4952     last_timer_effect.init(0);
4953     next_timer_effect.init(20 * BASELINE_DELAY);
4954 
4955     pending_revival = false;
4956     lives = 0;
4957     deaths = 0;
4958 
4959     wizard_vision = false;
4960 
4961     init_skills();
4962 
4963     skill_menu_do = SKM_NONE;
4964     skill_menu_view = SKM_NONE;
4965 
4966     skill_cost_level = 1;
4967     exp_available = 0;
4968 
4969     item_description.init(255);
4970     unique_items.init(UNIQ_NOT_EXISTS);
4971     unique_creatures.reset();
4972     force_autopickup.init(0);
4973 
4974     kills = KillMaster();
4975 
4976     where_are_you    = BRANCH_DUNGEON;
4977     depth            = 1;
4978 
4979     religion         = GOD_NO_GOD;
4980     jiyva_second_name.clear();
4981     piety            = 0;
4982     piety_hysteresis = 0;
4983     gift_timeout     = 0;
4984     saved_good_god_piety = 0;
4985     previous_good_god = GOD_NO_GOD;
4986     penance.init(0);
4987     worshipped.init(0);
4988     num_current_gifts.init(0);
4989     num_total_gifts.init(0);
4990     one_time_ability_used.reset();
4991     piety_max.init(0);
4992     exp_docked.init(0);
4993     exp_docked_total.init(0);
4994 
4995     mutation.init(0);
4996     innate_mutation.init(0);
4997     temp_mutation.init(0);
4998     demonic_traits.clear();
4999     sacrifices.init(0);
5000 
5001     magic_contamination = 0;
5002 
5003     seen_weapon.init(0);
5004     seen_armour.init(0);
5005     seen_misc.reset();
5006 
5007     octopus_king_rings = 0x00;
5008 
5009     normal_vision    = LOS_DEFAULT_RANGE;
5010     current_vision   = LOS_DEFAULT_RANGE;
5011 
5012     real_time_ms     = chrono::milliseconds::zero();
5013     real_time_delta  = chrono::milliseconds::zero();
5014     num_turns        = 0;
5015     exploration      = 0;
5016 
5017     trapped            = false;
5018     triggered_spectral = false;
5019 
5020     last_view_update = 0;
5021 
5022     spell_letter_table.init(-1);
5023     ability_letter_table.init(ABIL_NON_ABILITY);
5024 
5025     uniq_map_tags.clear();
5026     uniq_map_names.clear();
5027     uniq_map_tags_abyss.clear();
5028     uniq_map_names_abyss.clear();
5029     vault_list.clear();
5030 
5031     global_info = PlaceInfo();
5032     global_info.assert_validity();
5033 
5034     m_quiver_history = quiver::ammo_history();
5035     quiver_action = quiver::action_cycler();
5036     launcher_action = quiver::launcher_action_cycler();
5037 
5038     props.clear();
5039 
5040     beholders.clear();
5041     fearmongers.clear();
5042     dactions.clear();
5043     level_stack.clear();
5044     type_ids.init(false);
5045 
5046     banished_by.clear();
5047     banished_power = 0;
5048 
5049     last_mid = 0;
5050     last_cast_spell = SPELL_NO_SPELL;
5051 
5052     // Non-saved UI state:
5053     prev_targ        = MHITNOT;
5054     prev_grd_targ.reset();
5055     divine_exegesis  = false;
5056 
5057     travel_x         = 0;
5058     travel_y         = 0;
5059     travel_z         = level_id();
5060 
5061     running.clear();
5062     travel_ally_pace = false;
5063     received_weapon_warning = false;
5064     received_noskill_warning = false;
5065     wizmode_teleported_into_rock = false;
5066     skill_boost.clear();
5067     digging = false;
5068 
5069     delay_queue.clear();
5070 
5071     last_keypress_time = chrono::system_clock::now();
5072 
5073     action_count.clear();
5074 
5075     branches_left.reset();
5076 
5077     // Volatile (same-turn) state:
5078     turn_is_over     = false;
5079     banished         = false;
5080 
5081     wield_change         = false;
5082     gear_change          = false;
5083     redraw_noise         = false;
5084     redraw_quiver        = false;
5085     redraw_status_lights = false;
5086     redraw_hit_points    = false;
5087     redraw_magic_points  = false;
5088     redraw_stats.init(false);
5089     redraw_experience    = false;
5090     redraw_armour_class  = false;
5091     redraw_evasion       = false;
5092     redraw_title         = false;
5093 
5094     flash_colour        = BLACK;
5095     flash_where         = nullptr;
5096 
5097     time_taken          = 0;
5098     shield_blocks       = 0;
5099 
5100     abyss_speed         = 0;
5101     game_seed           = 0;
5102     fully_seeded        = true;
5103     deterministic_levelgen = true;
5104 
5105     los_noise_level     = 0;        ///< temporary slot for loud noise levels
5106     los_noise_last_turn = 0;
5107     ///< loudest noise heard on the last turn, for HUD display
5108 
5109     transit_stair       = DNGN_UNSEEN;
5110     entering_level      = false;
5111 
5112     reset_escaped_death();
5113     on_current_level    = true;
5114     seen_portals        = 0;
5115     frame_no            = 0;
5116 
5117     save                = nullptr;
5118     prev_save_version.clear();
5119 
5120     clear_constricted();
5121     constricting = 0;
5122 
5123     // Protected fields:
5124     clear_place_info();
5125 }
5126 
init_skills()5127 void player::init_skills()
5128 {
5129     auto_training = !(Options.default_manual_training);
5130     skills.init(0);
5131     train.init(TRAINING_DISABLED);
5132     train_alt.init(TRAINING_DISABLED);
5133     training.init(0);
5134     can_currently_train.reset();
5135     skill_points.init(0);
5136     ct_skill_points.init(0);
5137     skill_order.init(MAX_SKILL_ORDER);
5138     skill_manual_points.init(0);
5139     training_targets.init(0);
5140     exercises.clear();
5141     exercises_all.clear();
5142 }
5143 
operator =(const player & rhs)5144 player_save_info& player_save_info::operator=(const player& rhs)
5145 {
5146     // TODO: maybe seed, version?
5147     name             = rhs.your_name;
5148     experience       = rhs.experience;
5149     experience_level = rhs.experience_level;
5150     wizard           = rhs.wizard || rhs.suppress_wizard;
5151     species          = rhs.species;
5152     species_name     = rhs.chr_species_name;
5153     class_name       = rhs.chr_class_name;
5154     religion         = rhs.religion;
5155     god_name         = rhs.chr_god_name;
5156     jiyva_second_name= rhs.jiyva_second_name;
5157 
5158     // [ds] Perhaps we should move game type to player?
5159     saved_game_type  = crawl_state.type;
5160 
5161     return *this;
5162 }
5163 
operator <(const player_save_info & rhs) const5164 bool player_save_info::operator<(const player_save_info& rhs) const
5165 {
5166     return experience_level > rhs.experience_level
5167            || (experience_level == rhs.experience_level && name < rhs.name);
5168 }
5169 
really_short_desc() const5170 string player_save_info::really_short_desc() const
5171 {
5172     ostringstream desc;
5173     desc << name << " the " << species_name << ' ' << class_name;
5174 
5175     return desc.str();
5176 }
5177 
short_desc(bool use_qualifier) const5178 string player_save_info::short_desc(bool use_qualifier) const
5179 {
5180     ostringstream desc;
5181 
5182     const string qualifier = use_qualifier
5183                     ? game_state::game_type_name_for(saved_game_type)
5184                     : "";
5185     if (!qualifier.empty())
5186         desc << "[" << qualifier << "] ";
5187 
5188     desc << name << ", a level " << experience_level << ' '
5189          << species_name << ' ' << class_name;
5190 
5191     if (religion == GOD_JIYVA)
5192         desc << " of " << god_name << " " << jiyva_second_name;
5193     else if (religion != GOD_NO_GOD)
5194         desc << " of " << god_name;
5195 
5196 #ifdef WIZARD
5197     if (wizard)
5198         desc << " (WIZ)";
5199 #endif
5200 
5201     return desc.str();
5202 }
5203 
~player()5204 player::~player()
5205 {
5206     if (CrawlIsCrashing && save)
5207     {
5208         save->abort();
5209         delete save;
5210         save = nullptr;
5211     }
5212     ASSERT(!save); // the save file should be closed or deleted
5213 }
5214 
airborne() const5215 bool player::airborne() const
5216 {
5217     if (get_form()->forbids_flight())
5218         return false;
5219 
5220     return you.duration[DUR_FLIGHT]   // potions, polar vortex
5221         || you.props[EMERGENCY_FLIGHT_KEY].get_bool()
5222         || permanent_flight(true);
5223 }
5224 
is_banished() const5225 bool player::is_banished() const
5226 {
5227     return banished;
5228 }
5229 
is_sufficiently_rested() const5230 bool player::is_sufficiently_rested() const
5231 {
5232     // Only return false if resting will actually help.
5233     return (!player_regenerates_hp() || hp >= _rest_trigger_level(hp_max))
5234             && (magic_points >= _rest_trigger_level(max_magic_points)
5235                 || !player_regenerates_mp())
5236             && !you.duration[DUR_BARBS];
5237 }
5238 
in_water() const5239 bool player::in_water() const
5240 {
5241     return ground_level() && !you.can_water_walk() && feat_is_water(env.grid(pos()));
5242 }
5243 
in_liquid() const5244 bool player::in_liquid() const
5245 {
5246     return in_water() || liquefied_ground();
5247 }
5248 
can_swim(bool permanently) const5249 bool player::can_swim(bool permanently) const
5250 {
5251     return (species::can_swim(species)
5252             || body_size(PSIZE_BODY) >= SIZE_GIANT
5253             || !permanently)
5254                 && form_can_swim();
5255 }
5256 
5257 /// Can the player do a passing imitation of a notorious Palestinian?
can_water_walk() const5258 bool player::can_water_walk() const
5259 {
5260     return have_passive(passive_t::water_walk)
5261            || you.props.exists(TEMP_WATERWALK_KEY);
5262 }
5263 
visible_igrd(const coord_def & where) const5264 int player::visible_igrd(const coord_def &where) const
5265 {
5266     if (feat_eliminates_items(env.grid(where)))
5267         return NON_ITEM;
5268 
5269     return env.igrid(where);
5270 }
5271 
has_spell(spell_type spell) const5272 bool player::has_spell(spell_type spell) const
5273 {
5274     return find(begin(spells), end(spells), spell) != end(spells);
5275 }
5276 
cannot_speak() const5277 bool player::cannot_speak() const
5278 {
5279     if (silenced(pos()))
5280         return true;
5281 
5282     if (cannot_move()) // we allow talking during sleep ;)
5283         return true;
5284 
5285     // No transform that prevents the player from speaking yet.
5286     // ... yet setting this would prevent saccing junk and similar activities
5287     // for no good reason.
5288     return false;
5289 }
5290 
5291 /**
5292  * What verb should be used to describe the player's shouting?
5293  *
5294  * @param directed      Whether you're shouting at anyone in particular.
5295  * @return              A shouty kind of verb.
5296  */
shout_verb(bool directed) const5297 string player::shout_verb(bool directed) const
5298 {
5299     if (!get_form()->shout_verb.empty())
5300         return get_form()->shout_verb;
5301 
5302     // Overrides species, but gets overridden in turn by other forms.
5303     if (you.duration[DUR_WEREBLOOD])
5304         return "howl";
5305 
5306     const int screaminess = get_mutation_level(MUT_SCREAM);
5307     return species::shout_verb(you.species, screaminess, directed);
5308 }
5309 
5310 /**
5311  * How loud are the player's shouts?
5312  *
5313  * @return The noise produced by a single player shout.
5314  */
shout_volume() const5315 int player::shout_volume() const
5316 {
5317     const int base_noise = 12 + get_form()->shout_volume_modifier;
5318 
5319     return base_noise + 2 * (get_mutation_level(MUT_SCREAM));
5320 }
5321 
god_conduct(conduct_type thing_done,int level)5322 void player::god_conduct(conduct_type thing_done, int level)
5323 {
5324     ::did_god_conduct(thing_done, level);
5325 }
5326 
banish(const actor *,const string & who,const int power,bool force)5327 void player::banish(const actor* /*agent*/, const string &who, const int power,
5328                     bool force)
5329 {
5330     ASSERT(!crawl_state.game_is_arena());
5331     if (brdepth[BRANCH_ABYSS] == -1)
5332         return;
5333 
5334     if (elapsed_time <= attribute[ATTR_BANISHMENT_IMMUNITY])
5335     {
5336         mpr("You resist the pull of the Abyss.");
5337         return;
5338     }
5339 
5340     if (!force && player_in_branch(BRANCH_ABYSS)
5341         && x_chance_in_y(you.depth, brdepth[BRANCH_ABYSS]))
5342     {
5343         mpr("You wobble for a moment.");
5344         return;
5345     }
5346 
5347     banished    = true;
5348     banished_by = who;
5349     banished_power = power;
5350 }
5351 
5352 /*
5353  * Approximate the loudest noise the player heard in the last
5354  * turn, possibly rescaling. This gets updated every
5355  * `world_reacts`. If `adjusted` is set to true, this rescales
5356  * noise on a 0-1000 scale according to some breakpoints that
5357  * I have hand-calibrated. Otherwise, it returns the raw noise
5358  * value (approximately from 0 to 40). The breakpoints aim to
5359  * approximate 1x los radius, 2x los radius, and 3x los radius
5360  * relative to an open area.
5361  *
5362  * @param adjusted      Whether to rescale the noise level.
5363  *
5364  * @return The (scaled or unscaled) noise level heard by the player.
5365  */
get_noise_perception(bool adjusted) const5366 int player::get_noise_perception(bool adjusted) const
5367 {
5368     // los_noise_last_turn is already normalized for the branch's ambient
5369     // noise.
5370     const int level = los_noise_last_turn;
5371     static const int BAR_MAX = 1000; // TODO: export to output.cc & webtiles
5372     if (!adjusted)
5373          return (level + 500) / BAR_MAX;
5374 
5375     static const vector<int> NOISE_BREAKPOINTS = { 0, 6000, 13000, 29000 };
5376     const int BAR_FRAC = BAR_MAX / (NOISE_BREAKPOINTS.size() - 1);
5377     for (size_t i = 1; i < NOISE_BREAKPOINTS.size(); ++i)
5378     {
5379         const int breakpoint = NOISE_BREAKPOINTS[i];
5380         if (level > breakpoint)
5381             continue;
5382         // what fragment of this breakpoint does the noise fill up?
5383         const int prev_break = NOISE_BREAKPOINTS[i-1];
5384         const int break_width = breakpoint - prev_break;
5385         const int in_segment = (level - prev_break) * BAR_FRAC / break_width;
5386         // that fragment + previous breakpoints passed is our total noise.
5387         return in_segment + (i - 1) * BAR_FRAC;
5388         // example: 10k noise. that's 4k past the 6k breakpoint
5389         // ((10k-6k) * 333 / (13k - 6k)) + 333, or a bit more than half the bar
5390     }
5391 
5392     return BAR_MAX;
5393 }
5394 
paralysed() const5395 bool player::paralysed() const
5396 {
5397     return duration[DUR_PARALYSIS];
5398 }
5399 
cannot_move() const5400 bool player::cannot_move() const
5401 {
5402     return paralysed() || petrified();
5403 }
5404 
confused() const5405 bool player::confused() const
5406 {
5407     return duration[DUR_CONF];
5408 }
5409 
caught() const5410 bool player::caught() const
5411 {
5412     return attribute[ATTR_HELD];
5413 }
5414 
petrifying() const5415 bool player::petrifying() const
5416 {
5417     return duration[DUR_PETRIFYING];
5418 }
5419 
petrified() const5420 bool player::petrified() const
5421 {
5422     return duration[DUR_PETRIFIED];
5423 }
5424 
liquefied_ground() const5425 bool player::liquefied_ground() const
5426 {
5427     return liquefied(pos())
5428            && ground_level() && !is_insubstantial();
5429 }
5430 
shield_block_penalty() const5431 int player::shield_block_penalty() const
5432 {
5433     return 5 * shield_blocks * shield_blocks;
5434 }
5435 
5436 /**
5437  * Returns whether the player currently has any kind of shield.
5438  *
5439  * XXX: why does this function exist?
5440  */
shielded() const5441 bool player::shielded() const
5442 {
5443     return shield()
5444            || duration[DUR_DIVINE_SHIELD]
5445            || get_mutation_level(MUT_LARGE_BONE_PLATES) > 0
5446            || qazlal_sh_boost() > 0
5447            || you.wearing(EQ_AMULET, AMU_REFLECTION)
5448            || you.scan_artefacts(ARTP_SHIELDING)
5449            || (get_mutation_level(MUT_CONDENSATION_SHIELD)
5450                 && !you.duration[DUR_ICEMAIL_DEPLETED]);
5451 }
5452 
shield_bonus() const5453 int player::shield_bonus() const
5454 {
5455     const int shield_class = player_shield_class();
5456     if (shield_class <= 0)
5457         return -100;
5458 
5459     return random2avg(shield_class * 2, 2) / 3 - 1;
5460 }
5461 
shield_bypass_ability(int tohit) const5462 int player::shield_bypass_ability(int tohit) const
5463 {
5464     return 15 + tohit / 2;
5465 }
5466 
shield_block_succeeded()5467 void player::shield_block_succeeded()
5468 {
5469     actor::shield_block_succeeded();
5470 
5471     shield_blocks++;
5472     practise_shield_block();
5473     if (shield())
5474         count_action(CACT_BLOCK, shield()->sub_type);
5475     else
5476         count_action(CACT_BLOCK, -1, BLOCK_OTHER); // non-shield block
5477 }
5478 
missile_repulsion() const5479 bool player::missile_repulsion() const
5480 {
5481     return get_mutation_level(MUT_DISTORTION_FIELD) == 3
5482         || you.wearing_ego(EQ_ALL_ARMOUR, SPARM_REPULSION)
5483         || scan_artefacts(ARTP_RMSL, true)
5484         || have_passive(passive_t::upgraded_storm_shield);
5485 }
5486 
5487 /**
5488  * What's the base value of the penalties the player receives from their
5489  * body armour?
5490  *
5491  * Used as the base for adjusted armour penalty calculations, as well as for
5492  * stealth penalty calculations.
5493  *
5494  * @return  The player's body armour's PARM_EVASION, if any, taking into account
5495  *          the sturdy frame mutation that reduces encumbrance.
5496  */
unadjusted_body_armour_penalty() const5497 int player::unadjusted_body_armour_penalty() const
5498 {
5499     const item_def *body_armour = slot_item(EQ_BODY_ARMOUR, false);
5500     if (!body_armour)
5501         return 0;
5502 
5503     // PARM_EVASION is always less than or equal to 0
5504     return max(0, -property(*body_armour, PARM_EVASION) / 10
5505                   - get_mutation_level(MUT_STURDY_FRAME) * 2);
5506 }
5507 
5508 /**
5509  * The encumbrance penalty to the player for their worn body armour.
5510  *
5511  * @param scale     A scale to multiply the result by, to avoid precision loss.
5512  * @return          A penalty to EV based quadratically on body armour
5513  *                  encumbrance.
5514  */
adjusted_body_armour_penalty(int scale) const5515 int player::adjusted_body_armour_penalty(int scale) const
5516 {
5517     const int base_ev_penalty = unadjusted_body_armour_penalty();
5518 
5519     // New formula for effect of str on aevp: (2/5) * evp^2 / (str+3)
5520     return 2 * base_ev_penalty * base_ev_penalty * (450 - skill(SK_ARMOUR, 10))
5521            * scale / (5 * (strength() + 3)) / 450;
5522 }
5523 
5524 /**
5525  * The encumbrance penalty to the player for their worn shield.
5526  *
5527  * @param scale     A scale to multiply the result by, to avoid precision loss.
5528  * @return          A penalty to EV based on shield weight.
5529  */
adjusted_shield_penalty(int scale) const5530 int player::adjusted_shield_penalty(int scale) const
5531 {
5532     const item_def *shield_l = slot_item(EQ_SHIELD, false);
5533     if (!shield_l)
5534         return 0;
5535 
5536     const int base_shield_penalty = -property(*shield_l, PARM_EVASION);
5537     return max(0, ((base_shield_penalty * scale) - skill(SK_SHIELDS, scale)
5538                   / player_shield_racial_factor() * 10) / 10);
5539 }
5540 
get_shield_skill_to_offset_penalty(const item_def & item)5541 float player::get_shield_skill_to_offset_penalty(const item_def &item)
5542 {
5543     int evp = property(item, PARM_EVASION);
5544     return -1 * evp * player_shield_racial_factor() / 10.0;
5545 }
5546 
armour_tohit_penalty(bool random_factor,int scale) const5547 int player::armour_tohit_penalty(bool random_factor, int scale) const
5548 {
5549     return maybe_roll_dice(1, adjusted_body_armour_penalty(scale), random_factor);
5550 }
5551 
shield_tohit_penalty(bool random_factor,int scale) const5552 int player::shield_tohit_penalty(bool random_factor, int scale) const
5553 {
5554     return maybe_roll_dice(1, adjusted_shield_penalty(scale), random_factor);
5555 }
5556 
5557 /**
5558  * Get the player's skill level for sk.
5559  *
5560  * @param scale a scale factor to multiply by.
5561  * @param real whether to return the real value, or modified value.
5562  * @param temp whether to include modification by other temporary factors (e.g. heroism)
5563  */
skill(skill_type sk,int scale,bool real,bool temp) const5564 int player::skill(skill_type sk, int scale, bool real, bool temp) const
5565 {
5566     // If you add another enhancement/reduction, be sure to change
5567     // SkillMenuSwitch::get_help() to reflect that
5568 
5569     // wizard racechange, or upgraded old save
5570     if (is_useless_skill(sk))
5571         return 0;
5572 
5573     // skills[sk] might not be updated yet if this is in the middle of
5574     // skill training, so make sure to use the correct value.
5575     int actual_skill = skills[sk];
5576     unsigned int effective_points = skill_points[sk];
5577     if (!real)
5578         effective_points += get_crosstrain_points(sk);
5579     effective_points = min(effective_points, skill_exp_needed(MAX_SKILL_LEVEL, sk));
5580     actual_skill = calc_skill_level_change(sk, actual_skill, effective_points);
5581 
5582     int level = actual_skill * scale
5583       + get_skill_progress(sk, actual_skill, effective_points, scale);
5584     if (real)
5585         return level;
5586 
5587     if (penance[GOD_ASHENZARI])
5588     {
5589         if (temp)
5590             level = max(level - 4 * scale, level / 2);
5591     }
5592     else if (ash_has_skill_boost(sk))
5593             level = ash_skill_boost(sk, scale);
5594 
5595     if (temp && duration[DUR_HEROISM] && sk <= SK_LAST_MUNDANE)
5596         level = min(level + 5 * scale, MAX_SKILL_LEVEL * scale);
5597     return level;
5598 }
5599 
player_icemail_armour_class()5600 int player_icemail_armour_class()
5601 {
5602     if (!you.has_mutation(MUT_ICEMAIL))
5603         return 0;
5604 
5605     return you.duration[DUR_ICEMAIL_DEPLETED] ? 0
5606             : you.get_mutation_level(MUT_ICEMAIL) * ICEMAIL_MAX / 2;
5607 }
5608 
player_condensation_shield_class()5609 int player_condensation_shield_class()
5610 {
5611     if (!you.has_mutation(MUT_CONDENSATION_SHIELD))
5612         return 0;
5613 
5614     return you.duration[DUR_ICEMAIL_DEPLETED] ? 0 : ICEMAIL_MAX / 2;
5615 }
5616 
5617 /**
5618  * How many points of AC does the player get from their sanguine armour, if
5619  * they have any?
5620  *
5621  * @return      The AC bonus * 100. (For scaling.)
5622  */
sanguine_armour_bonus()5623 int sanguine_armour_bonus()
5624 {
5625     if (!you.duration[DUR_SANGUINE_ARMOUR])
5626         return 0;
5627 
5628     const int mut_lev = you.get_mutation_level(MUT_SANGUINE_ARMOUR);
5629     // like iridescent, but somewhat moreso (when active)
5630     return 300 + mut_lev * 300;
5631 }
5632 
5633 /**
5634  * How much AC does the player get from an unenchanted version of the given
5635  * armour?
5636  *
5637  * @param armour    The armour in question.
5638  * @param scale     A value to multiply the result by. (Used to avoid integer
5639  *                  rounding.)
5640  * @return          The AC from that armour, including armour skill, mutations
5641  *                  & divine blessings, but not enchantments or egos.
5642  */
base_ac_from(const item_def & armour,int scale) const5643 int player::base_ac_from(const item_def &armour, int scale) const
5644 {
5645     const int base = property(armour, PARM_AC) * scale;
5646 
5647     // [ds] effectively: ac_value * (22 + Arm) / 22, where Arm = Armour Skill.
5648     const int AC = base * (440 + skill(SK_ARMOUR, 20)) / 440;
5649 
5650     // The deformed don't fit into body armour very well.
5651     // (This includes nagas and palentongas.)
5652     if (get_armour_slot(armour) == EQ_BODY_ARMOUR
5653             && (get_mutation_level(MUT_DEFORMED)
5654                 || get_mutation_level(MUT_PSEUDOPODS)))
5655     {
5656         return AC - base / 2;
5657     }
5658 
5659     return AC;
5660 }
5661 
5662 /**
5663  * What bonus AC are you getting from your species?
5664  *
5665  * Does not account for any real mutations, such as scales or thick skin, that
5666  * you may have as a result of your species.
5667  * @param temp Whether to account for transformations.
5668  * @returns how much AC you are getting from your species "fake mutations" * 100
5669  */
racial_ac(bool temp) const5670 int player::racial_ac(bool temp) const
5671 {
5672     // drac scales suppressed in all serious forms, except dragon
5673     if (species::is_draconian(species)
5674         && (!player_is_shapechanged() || form == transformation::dragon
5675             || !temp))
5676     {
5677         int AC = 400 + 100 * (experience_level / 3);  // max 13
5678         if (species == SP_GREY_DRACONIAN) // no breath
5679             AC += 500;
5680         return AC;
5681     }
5682 
5683     if (!(player_is_shapechanged() && temp))
5684     {
5685         if (species == SP_NAGA)
5686             return 100 * experience_level / 3;              // max 9
5687         else if (species == SP_GARGOYLE)
5688         {
5689             return 200 + 100 * experience_level * 2 / 5     // max 20
5690                        + 100 * max(0, experience_level - 7) * 2 / 5;
5691         }
5692     }
5693 
5694     return 0;
5695 }
5696 
5697 // Each instance of this class stores a mutation which might change a
5698 // player's AC and how much their AC should change if the player has
5699 // said mutation.
5700 class mutation_ac_changes{
5701     public:
5702         /**
5703          * The AC a player gains from a given mutation. If the player
5704          * lacks said mutation, return 0.
5705          *
5706          * @return How much AC to give the player for the handled
5707          *         mutation.
5708          */
get_ac_change_for_mutation()5709         int get_ac_change_for_mutation(){
5710             int ac_change = 0;
5711 
5712             int mutation_level = you.get_mutation_level(mut, mutation_activation_threshold);
5713 
5714             switch (mutation_level){
5715                 case 0:
5716                     ac_change = 0;
5717                     break;
5718                 case 1:
5719                 case 2:
5720                 case 3:
5721                     ac_change = ac_changes[mutation_level - 1];
5722                     break;
5723             }
5724 
5725             // The output for this function is scaled differently than the UI.
5726             return ac_change * 100;
5727         }
5728 
mutation_ac_changes(mutation_type mut_aug,mutation_activity_type mutation_activation_threshold_aug,vector<int> ac_changes_aug)5729         mutation_ac_changes(mutation_type mut_aug,
5730                             mutation_activity_type mutation_activation_threshold_aug,
5731                             vector<int> ac_changes_aug)
5732         : mut (mut_aug),
5733           mutation_activation_threshold (mutation_activation_threshold_aug),
5734           ac_changes (ac_changes_aug)
5735         {
5736         }
5737 
5738     private:
5739         mutation_type mut;
5740         mutation_activity_type mutation_activation_threshold;
5741         vector<int> ac_changes;
5742 };
5743 
5744 // Constant vectors for the most common mutation ac results used in
5745 // all_mutation_ac_changes
5746 const vector<int> ONE_TWO_THREE  = {1,2,3};
5747 const vector<int> TWO_THREE_FOUR = {2,3,4};
5748 
5749 vector<mutation_ac_changes> all_mutation_ac_changes = {
5750      mutation_ac_changes(MUT_GELATINOUS_BODY,        mutation_activity_type::PARTIAL, ONE_TWO_THREE)
5751     ,mutation_ac_changes(MUT_TOUGH_SKIN,             mutation_activity_type::PARTIAL, ONE_TWO_THREE)
5752     ,mutation_ac_changes(MUT_SHAGGY_FUR,             mutation_activity_type::PARTIAL, ONE_TWO_THREE)
5753     ,mutation_ac_changes(MUT_PHYSICAL_VULNERABILITY, mutation_activity_type::PARTIAL, {-5,-10,-15})
5754     // Scale mutations are more easily disabled (forms etc.). This appears to be for flavour reasons.
5755     // Preserved behaviour from before mutation ac was turned to data.
5756     ,mutation_ac_changes(MUT_IRIDESCENT_SCALES,      mutation_activity_type::FULL,    {2, 4, 6})
5757     ,mutation_ac_changes(MUT_RUGGED_BROWN_SCALES,    mutation_activity_type::FULL,    ONE_TWO_THREE)
5758     ,mutation_ac_changes(MUT_ICY_BLUE_SCALES,        mutation_activity_type::FULL,    TWO_THREE_FOUR)
5759     ,mutation_ac_changes(MUT_MOLTEN_SCALES,          mutation_activity_type::FULL,    TWO_THREE_FOUR)
5760     ,mutation_ac_changes(MUT_SLIMY_GREEN_SCALES,     mutation_activity_type::FULL,    TWO_THREE_FOUR)
5761     ,mutation_ac_changes(MUT_THIN_METALLIC_SCALES,   mutation_activity_type::FULL,    TWO_THREE_FOUR)
5762     ,mutation_ac_changes(MUT_YELLOW_SCALES,          mutation_activity_type::FULL,    TWO_THREE_FOUR)
5763     ,mutation_ac_changes(MUT_SHARP_SCALES,           mutation_activity_type::FULL,    ONE_TWO_THREE)
5764 };
5765 
5766 /**
5767  * The AC changes the player has from mutations.
5768  *
5769  * Mostly additions from things like scales, but the physical vulnerability
5770  * mutation is also accounted for.
5771  *
5772  * @return  The player's AC gain from mutation, with 100 scaling (i.e,
5773  *          the returned result 100 times the UI shows as of Jan 2020)
5774 */
ac_changes_from_mutations() const5775 int player::ac_changes_from_mutations() const
5776 {
5777 
5778     int AC = 0;
5779 
5780     for (vector<mutation_ac_changes>::iterator it =
5781             all_mutation_ac_changes.begin();
5782             it != all_mutation_ac_changes.end(); ++it)
5783     {
5784         AC += it->get_ac_change_for_mutation();
5785     }
5786 
5787     return AC;
5788 }
5789 
5790 /**
5791  * Get a vector with the items of armour the player is wearing.
5792  *
5793  * @return  A vector of non-null pointers to all armour the player has equipped.
5794  */
get_armour_items() const5795 vector<const item_def *> player::get_armour_items() const
5796 {
5797     vector<const item_def *> armour_items;
5798 
5799     for (int eq = EQ_MIN_ARMOUR; eq <= EQ_MAX_ARMOUR; ++eq)
5800     {
5801         if (!slot_item(static_cast<equipment_type>(eq)))
5802             continue;
5803 
5804         armour_items.push_back(&inv[equip[eq]]);
5805 
5806     }
5807 
5808     return armour_items;
5809 }
5810 
5811 /**
5812  * Get a vector with the items of armour the player would be wearing
5813  * if they put on a specific piece of armour
5814  *
5815  * @param   The item which the player would be wearing in this theoretical
5816  *          situation.
5817  * @return  A vector of non-null pointers to all armour the player would have
5818  *          equipped.
5819  */
get_armour_items_one_sub(const item_def & sub) const5820 vector<const item_def *> player::get_armour_items_one_sub(const item_def& sub) const
5821 {
5822     vector<const item_def *> armour_items = get_armour_items_one_removal(sub);
5823 
5824     armour_items.push_back(&sub);
5825 
5826     return armour_items;
5827 }
5828 
5829 /**
5830  * Get a vector with the items of armour the player would be wearing
5831  * if they removed a specific piece of armour
5832  *
5833  * @param   The item which the player would be remove in this theoretical
5834  *          situation.
5835  * @return  A vector of non-null pointers to all armour the player would have
5836  *          equipped after removing the item passed in.
5837  */
get_armour_items_one_removal(const item_def & remove) const5838 vector<const item_def *> player::get_armour_items_one_removal(const item_def& remove) const
5839 {
5840     vector<const item_def *> armour_items;
5841 
5842     for (int eq = EQ_MIN_ARMOUR; eq <= EQ_MAX_ARMOUR; ++eq)
5843     {
5844         if (get_armour_slot(remove) == eq)
5845             continue;
5846 
5847         if (!slot_item(static_cast<equipment_type>(eq)))
5848             continue;
5849 
5850         armour_items.push_back(&inv[equip[eq]]);
5851 
5852     }
5853 
5854     return armour_items;
5855 }
5856 
5857 /**
5858  * Get the players "base" ac, assuming they are wearing a particular set of
5859  * armour items (which isn't necessarily the set of armour items they are
5860  * currently wearing.)
5861  *
5862  * @param   A scale by which the player's base AC is multiplied.
5863  * @param   A list of items to assume the player is wearing.
5864  * @return  The player's AC, multiplied by the given scale.
5865  */
base_ac_with_specific_items(int scale,vector<const item_def * > armour_items) const5866 int player::base_ac_with_specific_items(int scale,
5867                             vector<const item_def *> armour_items) const
5868 {
5869     int AC = 0;
5870 
5871     for (auto item : armour_items)
5872     {
5873         // Shields give SH instead of AC
5874         if (get_armour_slot(*item) != EQ_SHIELD)
5875         {
5876             AC += base_ac_from(*item, 100);
5877             AC += item->plus * 100;
5878         }
5879 
5880         if (get_armour_ego_type(*item) == SPARM_PROTECTION)
5881             AC += 300;
5882     }
5883 
5884     AC += wearing(EQ_RINGS_PLUS, RING_PROTECTION) * 100;
5885 
5886     //XXX: This doesn't take into account armour_items, so an unrand shield
5887     //     with +AC would have a buggy display.
5888     AC += scan_artefacts(ARTP_AC) * 100;
5889 
5890     AC += get_form()->get_ac_bonus();
5891 
5892     AC += racial_ac(true);
5893 
5894     AC += ac_changes_from_mutations();
5895 
5896     return AC * scale / 100;
5897 }
5898 /**
5899  * The player's "base" armour class, before transitory buffs are applied.
5900  *
5901  * (This is somewhat arbitrarily defined - forms, for example, are considered
5902  * to be long-lived for these purposes.)
5903  *
5904  * @param   A scale by which the player's base AC is multiplied.
5905  * @return  The player's AC, multiplied by the given scale.
5906  */
base_ac(int scale) const5907 int player::base_ac(int scale) const
5908 {
5909     vector<const item_def *> armour_items = get_armour_items();
5910 
5911     return base_ac_with_specific_items(scale, armour_items);
5912 }
5913 
armour_class(bool) const5914 int player::armour_class(bool /*calc_unid*/) const
5915 {
5916     return armour_class_with_specific_items(get_armour_items());
5917 }
5918 
armour_class_with_one_sub(item_def sub) const5919 int player::armour_class_with_one_sub(item_def sub) const
5920 {
5921     return armour_class_with_specific_items(
5922                             get_armour_items_one_sub(sub));
5923 }
5924 
armour_class_with_one_removal(item_def removed) const5925 int player::armour_class_with_one_removal(item_def removed) const
5926 {
5927     return armour_class_with_specific_items(
5928                             get_armour_items_one_removal(removed));
5929 }
5930 
armour_class_with_specific_items(vector<const item_def * > items) const5931 int player::armour_class_with_specific_items(vector<const item_def *> items) const
5932 {
5933     const int scale = 100;
5934     int AC = base_ac_with_specific_items(scale, items);
5935 
5936     if (duration[DUR_ICY_ARMOUR])
5937     {
5938         AC += max(0, 500 + you.props[ICY_ARMOUR_KEY].get_int() * 8
5939                      - unadjusted_body_armour_penalty() * 50);
5940     }
5941 
5942     if (has_mutation(MUT_ICEMAIL))
5943         AC += 100 * player_icemail_armour_class();
5944 
5945     if (duration[DUR_QAZLAL_AC])
5946         AC += 300;
5947 
5948     if (duration[DUR_SPWPN_PROTECTION])
5949         AC += 700;
5950 
5951     if (duration[DUR_CORROSION])
5952         AC -= 400 * you.props["corrosion_amount"].get_int();
5953 
5954     AC += sanguine_armour_bonus();
5955 
5956     if (you.has_mutation(MUT_CURL)
5957         && you.props[PALENTONGA_CURL_KEY].get_bool())
5958     {
5959         AC += 7 * scale;
5960     }
5961 
5962     return AC / scale;
5963 }
5964 
5965  /**
5966   * Guaranteed damage reduction.
5967   *
5968   * The percentage of the damage received that is guaranteed to be reduced
5969   * by the armour. As the AC roll is done before GDR is applied, GDR is only
5970   * useful when the AC roll is inferior to it. Therefore a higher GDR means
5971   * more damage reduced, but also more often.
5972   *
5973   * \f[ GDR = 16 \times (AC)^\frac{1}{4} \f]
5974   *
5975   * \return GDR as a percentage.
5976   **/
gdr_perc() const5977 int player::gdr_perc() const
5978 {
5979     return max(0, (int)(16 * sqrt(sqrt(you.armour_class()))));
5980 }
5981 
5982 /**
5983  * What is the player's actual, current EV, possibly relative to an attacker,
5984  * including various temporary penalties?
5985  *
5986  * @param evit     Penalty types which should be excluded from the calculation.
5987  * @param act      The creature that the player is attempting to evade, if any.
5988  *                 May be null.
5989  * @return         The player's relevant EV.
5990  */
evasion(ev_ignore_type evit,const actor * act) const5991 int player::evasion(ev_ignore_type evit, const actor* act) const
5992 {
5993     const int base_evasion = _player_evasion(evit);
5994 
5995     const int constrict_penalty = is_constricted() ? 3 : 0;
5996 
5997     const bool attacker_invis = act && !act->visible_to(this);
5998     const int invis_penalty
5999         = attacker_invis && !testbits(evit, ev_ignore::helpless) ? 10 : 0;
6000 
6001     return base_evasion - constrict_penalty - invis_penalty;
6002 }
6003 
heal(int amount)6004 bool player::heal(int amount)
6005 {
6006     ::inc_hp(amount);
6007     return true; /* TODO Check whether the player was healed. */
6008 }
6009 
6010 /**
6011  * What is the player's (current) mon_holy_type category?
6012  * Stays up to date with god for evil/unholy
6013  * Nonliving (statues, etc), undead, or alive.
6014  *
6015  * @param temp      Whether to consider temporary effects: forms,
6016  *                  petrification...
6017  * @return          The player's holiness category.
6018  */
holiness(bool temp) const6019 mon_holy_type player::holiness(bool temp) const
6020 {
6021     mon_holy_type holi;
6022 
6023     // Lich form takes precedence over a species' base holiness
6024     // Alive Vampires are MH_NATURAL
6025     if (is_lifeless_undead(temp))
6026         holi = MH_UNDEAD;
6027     else if (species::is_nonliving(you.species))
6028         holi = MH_NONLIVING;
6029     else
6030         holi = MH_NATURAL;
6031 
6032     // Petrification takes precedence over base holiness and lich form
6033     if (temp && (form == transformation::statue
6034                  || form == transformation::wisp
6035                  || form == transformation::storm
6036                  || petrified()))
6037     {
6038         holi = MH_NONLIVING;
6039     }
6040 
6041     if (is_good_god(religion))
6042         holi |= MH_HOLY;
6043 
6044     if (is_evil_god(religion)
6045         || species == SP_DEMONSPAWN || you.has_mutation(MUT_VAMPIRISM))
6046     {
6047         holi |= MH_EVIL;
6048     }
6049 
6050     // possible XXX: Monsters get evil/unholy bits set on spell selection
6051     //  should players?
6052     return holi;
6053 }
6054 
6055 // With temp (default true), report temporary effects such as lichform.
undead_or_demonic(bool temp) const6056 bool player::undead_or_demonic(bool temp) const
6057 {
6058     // This is only for TSO-related stuff, so demonspawn are included.
6059     return undead_state(temp) || species == SP_DEMONSPAWN;
6060 }
6061 
is_holy() const6062 bool player::is_holy() const
6063 {
6064     return bool(holiness() & MH_HOLY);
6065 }
6066 
is_nonliving(bool temp) const6067 bool player::is_nonliving(bool temp) const
6068 {
6069     return bool(holiness(temp) & MH_NONLIVING);
6070 }
6071 
6072 // This is a stub. Check is used only for silver damage. Worship of chaotic
6073 // gods should probably be checked in the non-existing player::is_unclean,
6074 // which could be used for something Zin-related (such as a priestly monster).
how_chaotic(bool) const6075 int player::how_chaotic(bool /*check_spells_god*/) const
6076 {
6077     return 0;
6078 }
6079 
6080 /**
6081  * Does the player need to breathe?
6082  *
6083  * Pretty much only matters for confusing spores and drowning damage.
6084  *
6085  * @return  Whether the player has no need to breathe.
6086  */
is_unbreathing() const6087 bool player::is_unbreathing() const
6088 {
6089     return is_nonliving() || is_lifeless_undead()
6090            || form == transformation::tree;
6091 }
6092 
is_insubstantial() const6093 bool player::is_insubstantial() const
6094 {
6095     return form == transformation::wisp
6096         || form == transformation::storm;
6097 }
6098 
res_acid(bool calc_unid) const6099 int player::res_acid(bool calc_unid) const
6100 {
6101     return player_res_acid(calc_unid);
6102 }
6103 
res_fire() const6104 int player::res_fire() const
6105 {
6106     return player_res_fire();
6107 }
6108 
res_steam() const6109 int player::res_steam() const
6110 {
6111     return player_res_steam();
6112 }
6113 
res_cold() const6114 int player::res_cold() const
6115 {
6116     return player_res_cold();
6117 }
6118 
res_elec() const6119 int player::res_elec() const
6120 {
6121     return player_res_electricity();
6122 }
6123 
res_water_drowning() const6124 int player::res_water_drowning() const
6125 {
6126     int rw = 0;
6127 
6128     if (is_unbreathing()
6129         || species::can_swim(species) && !form_changed_physiology()
6130         || form == transformation::ice_beast)
6131     {
6132         rw++;
6133     }
6134 
6135     return rw;
6136 }
6137 
res_poison(bool temp) const6138 int player::res_poison(bool temp) const
6139 {
6140     return player_res_poison(true, temp);
6141 }
6142 
res_miasma(bool temp) const6143 bool player::res_miasma(bool temp) const
6144 {
6145     if (has_mutation(MUT_FOUL_STENCH)
6146         || is_nonliving(temp)
6147         || temp && get_form()->res_miasma())
6148     {
6149         return true;
6150     }
6151 
6152     const item_def *armour = slot_item(EQ_BODY_ARMOUR);
6153     if (armour && is_unrandom_artefact(*armour, UNRAND_EMBRACE))
6154         return true;
6155 
6156     return is_lifeless_undead();
6157 }
6158 
6159 
res_sticky_flame() const6160 bool player::res_sticky_flame() const
6161 {
6162     return player_res_sticky_flame();
6163 }
6164 
res_holy_energy() const6165 int player::res_holy_energy() const
6166 {
6167     if (undead_or_demonic())
6168         return -1;
6169 
6170     if (is_holy())
6171         return 3;
6172 
6173     return 0;
6174 }
6175 
res_negative_energy(bool intrinsic_only) const6176 int player::res_negative_energy(bool intrinsic_only) const
6177 {
6178     return player_prot_life(!intrinsic_only, true, !intrinsic_only);
6179 }
6180 
res_torment() const6181 bool player::res_torment() const
6182 {
6183     if (you.get_mutation_level(MUT_TORMENT_RESISTANCE) >= 2)
6184         return true;
6185 
6186     return get_form()->res_neg() == 3
6187            || you.has_mutation(MUT_VAMPIRISM) && !you.vampire_alive
6188            || you.petrified()
6189 #if TAG_MAJOR_VERSION == 34
6190            || player_equip_unrand(UNRAND_ETERNAL_TORMENT)
6191 #endif
6192            ;
6193 }
6194 
res_polar_vortex() const6195 bool player::res_polar_vortex() const
6196 {
6197     // Full control of the winds around you can negate a hostile polar vortex.
6198     return duration[DUR_VORTEX] ? 1 : 0;
6199 }
6200 
res_petrify(bool temp) const6201 bool player::res_petrify(bool temp) const
6202 {
6203     return get_mutation_level(MUT_PETRIFICATION_RESISTANCE)
6204            || temp && get_form()->res_petrify();
6205 }
6206 
res_constrict() const6207 int player::res_constrict() const
6208 {
6209     if (is_insubstantial())
6210         return 3;
6211 
6212     if (get_mutation_level(MUT_SPINY))
6213         return 3;
6214 
6215     return 0;
6216 }
6217 
willpower(bool) const6218 int player::willpower(bool /*calc_unid*/) const
6219 {
6220     return player_willpower();
6221 }
6222 
player_willpower(bool calc_unid,bool temp)6223 int player_willpower(bool calc_unid, bool temp)
6224 {
6225     if (temp && you.form == transformation::shadow)
6226         return WILL_INVULN;
6227 
6228     if (player_equip_unrand(UNRAND_FOLLY))
6229         return 0;
6230 
6231     int rm = you.experience_level * species::get_wl_modifier(you.species);
6232 
6233     // randarts
6234     rm += WL_PIP * you.scan_artefacts(ARTP_WILLPOWER, calc_unid);
6235 
6236     // body armour
6237     const item_def *body_armour = you.slot_item(EQ_BODY_ARMOUR);
6238     if (body_armour)
6239         rm += armour_type_prop(body_armour->sub_type, ARMF_WILLPOWER) * WL_PIP;
6240 
6241     // ego armours
6242     rm += WL_PIP * you.wearing_ego(EQ_ALL_ARMOUR, SPARM_WILLPOWER,
6243                                    calc_unid);
6244 
6245     // rings of willpower
6246     rm += WL_PIP * you.wearing(EQ_RINGS, RING_WILLPOWER, calc_unid);
6247 
6248     // Mutations
6249     rm += WL_PIP * you.get_mutation_level(MUT_STRONG_WILLED);
6250     rm += WL_PIP * you.get_mutation_level(MUT_DEMONIC_WILL);
6251     rm -= WL_PIP * you.get_mutation_level(MUT_WEAK_WILLED);
6252 
6253     // transformations
6254     if (you.form == transformation::lich && temp)
6255         rm += WL_PIP;
6256 
6257     // Trog's Hand
6258     if (you.duration[DUR_TROGS_HAND] && temp)
6259         rm += WL_PIP * 2;
6260 
6261     // Enchantment effect
6262     if (you.duration[DUR_LOWERED_WL] && temp)
6263         rm /= 2;
6264 
6265     if (rm < 0)
6266         rm = 0;
6267 
6268     return rm;
6269 }
6270 
6271 /**
6272  * Is the player prevented from teleporting? If so, why?
6273  *
6274  * @param calc_unid     Whether to identify unknown items that prevent tele
6275  *                      (probably obsolete)
6276  * @param blinking      Are you blinking or teleporting?
6277  * @return              Why the player is prevented from teleporting, if they
6278  *                      are; else, the empty string.
6279  */
no_tele_reason(bool calc_unid,bool blinking) const6280 string player::no_tele_reason(bool calc_unid, bool blinking) const
6281 {
6282     if (!blinking)
6283     {
6284         if (crawl_state.game_is_sprint())
6285             return "Long-range teleportation is disallowed in Dungeon Sprint.";
6286         else if (player_in_branch(BRANCH_GAUNTLET))
6287         {
6288             return "A magic seal in the Gauntlet prevents long-range "
6289                 "teleports.";
6290         }
6291     }
6292 
6293     if (stasis())
6294         return "Your stasis prevents you from teleporting.";
6295 
6296     vector<string> problems;
6297 
6298     if (duration[DUR_DIMENSION_ANCHOR])
6299         problems.emplace_back("locked down by a dimension anchor");
6300 
6301     if (duration[DUR_LOCKED_DOWN])
6302         problems.emplace_back("magically locked down");
6303 
6304     if (form == transformation::tree)
6305         problems.emplace_back("held in place by your roots");
6306 
6307     vector<const item_def *> notele_items;
6308     if (has_notele_item(calc_unid, &notele_items))
6309     {
6310         vector<string> worn_notele;
6311         bool found_nonartefact = false;
6312 
6313         for (const auto item : notele_items)
6314         {
6315             if (item->base_type == OBJ_WEAPONS)
6316             {
6317                 problems.push_back(make_stringf("wielding %s",
6318                                                 item->name(DESC_A).c_str()));
6319             }
6320             else
6321                 worn_notele.push_back(item->name(DESC_A));
6322         }
6323 
6324         if (worn_notele.size() > (problems.empty() ? 3 : 1))
6325         {
6326             problems.push_back(
6327                 make_stringf("wearing %s %s preventing teleportation",
6328                              number_in_words(worn_notele.size()).c_str(),
6329                              found_nonartefact ? "items": "artefacts"));
6330         }
6331         else if (!worn_notele.empty())
6332         {
6333             problems.push_back(
6334                 make_stringf("wearing %s",
6335                              comma_separated_line(worn_notele.begin(),
6336                                                   worn_notele.end()).c_str()));
6337         }
6338     }
6339 
6340     if (problems.empty())
6341         return ""; // no problem
6342 
6343     return make_stringf("You cannot %s because you are %s.",
6344                         blinking ? "blink" : "teleport",
6345                         comma_separated_line(problems.begin(),
6346                                              problems.end()).c_str());
6347 }
6348 
6349 /**
6350  * Is the player prevented from teleporting/blinking right now? If so,
6351  * print why.
6352  *
6353  * @param calc_unid     Whether to identify unknown items that prevent tele
6354  *                      (probably obsolete)
6355  * @param blinking      Are you blinking or teleporting?
6356  * @return              Whether the player is prevented from teleportation.
6357  */
no_tele_print_reason(bool calc_unid,bool blinking) const6358 bool player::no_tele_print_reason(bool calc_unid, bool blinking) const
6359 {
6360     const string reason = no_tele_reason(calc_unid, blinking);
6361     if (reason.empty())
6362         return false;
6363 
6364     mpr(reason);
6365     return true;
6366 }
6367 
6368 /**
6369  * Is the player prevented from teleporting/blinking right now?
6370  *
6371  * @param calc_unid     Whether to identify unknown items that prevent tele
6372  *                      (probably obsolete)
6373  * @param permit_id     Unused for players.
6374  * @param blinking      Are you blinking or teleporting?
6375  * @return              Whether the player is prevented from teleportation.
6376  */
no_tele(bool calc_unid,bool,bool blinking) const6377 bool player::no_tele(bool calc_unid, bool /*permit_id*/, bool blinking) const
6378 {
6379     return !no_tele_reason(calc_unid, blinking).empty();
6380 }
6381 
fights_well_unarmed(int heavy_armour_penalty)6382 bool player::fights_well_unarmed(int heavy_armour_penalty)
6383 {
6384     return x_chance_in_y(skill(SK_UNARMED_COMBAT, 10), 200)
6385         && x_chance_in_y(2, 1 + heavy_armour_penalty);
6386 }
6387 
racial_permanent_flight() const6388 bool player::racial_permanent_flight() const
6389 {
6390     return get_mutation_level(MUT_TENGU_FLIGHT)
6391         || get_mutation_level(MUT_BIG_WINGS)
6392         || has_mutation(MUT_FLOAT);
6393 }
6394 
6395 /**
6396  * Check for sources of flight from species, forms, and (optionally) equipment.
6397  */
permanent_flight(bool include_equip) const6398 bool player::permanent_flight(bool include_equip) const
6399 {
6400     if (get_form()->forbids_flight())
6401         return false;
6402 
6403     return include_equip && attribute[ATTR_PERM_FLIGHT] // equipment
6404         || racial_permanent_flight()                    // species muts
6405         || get_form()->enables_flight();
6406 }
6407 
6408 /**
6409  * Does the player get the tengu flight perks?
6410  */
tengu_flight() const6411 bool player::tengu_flight() const
6412 {
6413     // XX could tengu just get MUT_FLOAT?
6414     return you.has_mutation(MUT_TENGU_FLIGHT) && airborne();
6415 }
6416 
6417 /**
6418  * Returns true if player spellcasting is considered unholy.
6419  *
6420  * Checks to see if the player is wielding the Majin-Bo.
6421  *
6422  * @return          Whether player spellcasting is an unholy act.
6423  */
spellcasting_unholy() const6424 bool player::spellcasting_unholy() const
6425 {
6426     return player_equip_unrand(UNRAND_MAJIN);
6427 }
6428 
6429 /**
6430  * What is the player's (current) place on the Undead Spectrum?
6431  * (alive, semi-undead (vampire), or very dead (ghoul, mummy, lich)
6432  *
6433  * @param temp  Whether to consider temporary effects (lichform)
6434  * @return      The player's undead state.
6435  */
undead_state(bool temp) const6436 undead_state_type player::undead_state(bool temp) const
6437 {
6438     if (temp && form == transformation::lich)
6439         return US_UNDEAD;
6440     return species::undead_type(species);
6441 }
6442 
nightvision() const6443 bool player::nightvision() const
6444 {
6445     return have_passive(passive_t::nightvision);
6446 }
6447 
reach_range() const6448 reach_type player::reach_range() const
6449 {
6450     const item_def *wpn = weapon();
6451     if (wpn)
6452         return weapon_reach(*wpn);
6453     return REACH_NONE;
6454 }
6455 
mons_species(bool) const6456 monster_type player::mons_species(bool /*zombie_base*/) const
6457 {
6458     return species::to_mons_species(species);
6459 }
6460 
poison(actor * agent,int amount,bool force)6461 bool player::poison(actor *agent, int amount, bool force)
6462 {
6463     return ::poison_player(amount, agent? agent->name(DESC_A, true) : "", "",
6464                            force);
6465 }
6466 
expose_to_element(beam_type element,int _strength,bool slow_cold_blood)6467 void player::expose_to_element(beam_type element, int _strength,
6468                                bool slow_cold_blood)
6469 {
6470     ::expose_player_to_element(element, _strength, slow_cold_blood);
6471 }
6472 
blink()6473 void player::blink()
6474 {
6475     uncontrolled_blink();
6476 }
6477 
teleport(bool now,bool wizard_tele)6478 void player::teleport(bool now, bool wizard_tele)
6479 {
6480     ASSERT(!crawl_state.game_is_arena());
6481 
6482     if (now)
6483         you_teleport_now(wizard_tele);
6484     else
6485         you_teleport();
6486 }
6487 
hurt(const actor * agent,int amount,beam_type flavour,kill_method_type kill_type,string source,string aux,bool,bool)6488 int player::hurt(const actor *agent, int amount, beam_type flavour,
6489                  kill_method_type kill_type, string source, string aux,
6490                  bool /*cleanup_dead*/, bool /*attacker_effects*/)
6491 {
6492     // We ignore cleanup_dead here.
6493     if (!agent)
6494     {
6495         // FIXME: This can happen if a deferred_damage_fineff does damage
6496         // to a player from a dead monster. We should probably not do that,
6497         // but it could be tricky to fix, so for now let's at least avoid
6498         // a crash even if it does mean funny death messages.
6499         ouch(amount, kill_type, MID_NOBODY, aux.c_str(), false, source.c_str());
6500     }
6501     else
6502     {
6503         ouch(amount, kill_type, agent->mid, aux.c_str(),
6504              agent->visible_to(this), source.c_str());
6505     }
6506 
6507     if ((flavour == BEAM_DEVASTATION || flavour == BEAM_MINDBURST)
6508         && can_bleed())
6509     {
6510         blood_spray(pos(), type, amount / 5);
6511     }
6512 
6513     return amount;
6514 }
6515 
drain_stat(stat_type s,int amount)6516 void player::drain_stat(stat_type s, int amount)
6517 {
6518     lose_stat(s, amount);
6519 }
6520 
corrode_equipment(const char * corrosion_source,int degree)6521 bool player::corrode_equipment(const char* corrosion_source, int degree)
6522 {
6523     // rCorr protects against 50% of corrosion.
6524     if (res_corr())
6525     {
6526         degree = binomial(degree, 50);
6527         if (!degree)
6528         {
6529             dprf("rCorr protects.");
6530             return false;
6531         }
6532     }
6533     // always increase duration, but...
6534     increase_duration(DUR_CORROSION, 10 + roll_dice(2, 4), 50,
6535                       make_stringf("%s corrodes you!",
6536                                    corrosion_source).c_str());
6537 
6538     // the more corrosion you already have, the lower the odds of more
6539     int prev_corr = props["corrosion_amount"].get_int();
6540     bool did_corrode = false;
6541     for (int i = 0; i < degree; i++)
6542         if (!x_chance_in_y(prev_corr, prev_corr + 7))
6543         {
6544             props["corrosion_amount"].get_int()++;
6545             prev_corr++;
6546             did_corrode = true;
6547         }
6548 
6549     if (did_corrode)
6550     {
6551         redraw_armour_class = true;
6552         wield_change = true;
6553     }
6554     return true;
6555 }
6556 
6557 /**
6558  * Attempts to apply corrosion to the player and deals acid damage.
6559  *
6560  * @param evildoer the cause of this acid splash.
6561  * @param acid_strength The strength of the acid.
6562  * @param allow_corrosion Whether to try and apply the corrosion debuff.
6563  * @param hurt_msg A message to display when dealing damage.
6564  */
splash_with_acid(const actor * evildoer,int acid_strength,bool allow_corrosion,const char *)6565 void player::splash_with_acid(const actor* evildoer, int acid_strength,
6566                               bool allow_corrosion, const char* /*hurt_msg*/)
6567 {
6568     if (allow_corrosion && binomial(3, acid_strength + 1, 30))
6569         corrode_equipment();
6570 
6571     const int dam = roll_dice(4, acid_strength);
6572     const int post_res_dam = resist_adjust_damage(&you, BEAM_ACID, dam);
6573 
6574     mprf("You are splashed with acid%s%s",
6575          post_res_dam > 0 ? "" : " but take no damage",
6576          attack_strength_punctuation(post_res_dam).c_str());
6577     if (post_res_dam > 0)
6578     {
6579         if (post_res_dam < dam)
6580             canned_msg(MSG_YOU_RESIST);
6581 
6582         ouch(post_res_dam, KILLED_BY_ACID,
6583              evildoer ? evildoer->mid : MID_NOBODY);
6584     }
6585 }
6586 
drain(const actor *,bool quiet,int pow)6587 bool player::drain(const actor */*who*/, bool quiet, int pow)
6588 {
6589     return drain_player(pow, !quiet);
6590 }
6591 
confuse(actor *,int str)6592 void player::confuse(actor */*who*/, int str)
6593 {
6594     confuse_player(str);
6595 }
6596 
6597 /**
6598  * Paralyse the player for str turns.
6599  *
6600  *  Duration is capped at 13.
6601  *
6602  * @param who Pointer to the actor who paralysed the player.
6603  * @param str The number of turns the paralysis will last.
6604  * @param source Description of the source of the paralysis.
6605  */
paralyse(const actor * who,int str,string source)6606 void player::paralyse(const actor *who, int str, string source)
6607 {
6608     ASSERT(!crawl_state.game_is_arena());
6609 
6610     if (stasis())
6611     {
6612         mpr("Your stasis prevents you from being paralysed.");
6613         return;
6614     }
6615 
6616     // The who check has an effect in a few cases, most notably making
6617     // Death's Door + Borg's paralysis unblockable.
6618     if (who && (duration[DUR_PARALYSIS] || duration[DUR_PARALYSIS_IMMUNITY]))
6619     {
6620         mpr("You shrug off the repeated paralysis!");
6621         return;
6622     }
6623 
6624     int &paralysis(duration[DUR_PARALYSIS]);
6625 
6626     const bool use_actor_name = source.empty() && who != nullptr;
6627     if (use_actor_name)
6628         source = who->name(DESC_A);
6629 
6630     if (!paralysis && !source.empty())
6631     {
6632         take_note(Note(NOTE_PARALYSIS, str, 0, source));
6633         // use the real name here even for invisible monsters
6634         props[PARALYSED_BY_KEY] = use_actor_name ? who->name(DESC_A, true)
6635                                                : source;
6636     }
6637 
6638     if (asleep())
6639         you.awaken();
6640 
6641     mpr("You suddenly lose the ability to move!");
6642 
6643     paralysis = min(str, 13) * BASELINE_DELAY;
6644 
6645     stop_directly_constricting_all(false);
6646     end_wait_spells();
6647 }
6648 
petrify(const actor * who,bool force)6649 void player::petrify(const actor *who, bool force)
6650 {
6651     ASSERT(!crawl_state.game_is_arena());
6652 
6653     if (res_petrify() && !force)
6654     {
6655         canned_msg(MSG_YOU_UNAFFECTED);
6656         return;
6657     }
6658 
6659     if (duration[DUR_DIVINE_STAMINA] > 0)
6660     {
6661         mpr("Your divine stamina protects you from petrification!");
6662         return;
6663     }
6664 
6665     // Petrification always wakes you up
6666     if (asleep())
6667         you.awaken();
6668 
6669     if (petrifying())
6670     {
6671         mpr("Your limbs have turned to stone.");
6672         duration[DUR_PETRIFYING] = 1;
6673         return;
6674     }
6675 
6676     if (petrified())
6677         return;
6678 
6679     duration[DUR_PETRIFYING] = 3 * BASELINE_DELAY;
6680 
6681     if (who)
6682         props[PETRIFIED_BY_KEY] = who->name(DESC_A, true);
6683 
6684     redraw_evasion = true;
6685     mprf(MSGCH_WARN, "You are slowing down.");
6686 }
6687 
fully_petrify(bool)6688 bool player::fully_petrify(bool /*quiet*/)
6689 {
6690     duration[DUR_PETRIFIED] = 6 * BASELINE_DELAY
6691                         + random2(4 * BASELINE_DELAY);
6692     redraw_evasion = true;
6693     mpr("You have turned to stone.");
6694 
6695     end_wait_spells();
6696 
6697     return true;
6698 }
6699 
slow_down(actor *,int str)6700 void player::slow_down(actor */*foe*/, int str)
6701 {
6702     ::slow_player(str);
6703 }
6704 
6705 
has_claws(bool allow_tran) const6706 int player::has_claws(bool allow_tran) const
6707 {
6708     if (allow_tran)
6709     {
6710         // these transformations bring claws with them
6711         if (form == transformation::dragon)
6712             return 3;
6713 
6714         // blade hands override claws
6715         if (form == transformation::blade_hands)
6716             return 0;
6717     }
6718 
6719     return get_mutation_level(MUT_CLAWS, allow_tran);
6720 }
6721 
has_usable_claws(bool allow_tran) const6722 bool player::has_usable_claws(bool allow_tran) const
6723 {
6724     return !slot_item(EQ_GLOVES) && has_claws(allow_tran);
6725 }
6726 
has_talons(bool allow_tran) const6727 int player::has_talons(bool allow_tran) const
6728 {
6729     // XXX: Do merfolk in water belong under allow_tran?
6730     if (fishtail)
6731         return 0;
6732 
6733     return get_mutation_level(MUT_TALONS, allow_tran);
6734 }
6735 
has_usable_talons(bool allow_tran) const6736 bool player::has_usable_talons(bool allow_tran) const
6737 {
6738     return !slot_item(EQ_BOOTS) && has_talons(allow_tran);
6739 }
6740 
has_hooves(bool allow_tran) const6741 int player::has_hooves(bool allow_tran) const
6742 {
6743     // XXX: Do merfolk in water belong under allow_tran?
6744     if (fishtail)
6745         return 0;
6746 
6747     return get_mutation_level(MUT_HOOVES, allow_tran);
6748 }
6749 
has_usable_hooves(bool allow_tran) const6750 bool player::has_usable_hooves(bool allow_tran) const
6751 {
6752     return has_hooves(allow_tran)
6753            && (!slot_item(EQ_BOOTS)
6754                || wearing(EQ_BOOTS, ARM_BARDING, true));
6755 }
6756 
has_fangs(bool allow_tran) const6757 int player::has_fangs(bool allow_tran) const
6758 {
6759     if (allow_tran)
6760     {
6761         // these transformations bring fangs with them
6762         if (form == transformation::dragon)
6763             return 3;
6764     }
6765 
6766     return get_mutation_level(MUT_FANGS, allow_tran);
6767 }
6768 
has_usable_fangs(bool allow_tran) const6769 int player::has_usable_fangs(bool allow_tran) const
6770 {
6771     return has_fangs(allow_tran);
6772 }
6773 
has_tail(bool allow_tran) const6774 bool player::has_tail(bool allow_tran) const
6775 {
6776     if (allow_tran)
6777     {
6778         // these transformations bring a tail with them
6779         if (form == transformation::dragon)
6780             return 1;
6781 
6782         // Most transformations suppress a tail.
6783         if (!form_keeps_mutations())
6784             return 0;
6785     }
6786 
6787     // XXX: Do merfolk in water belong under allow_tran?
6788     if (species::is_draconian(species)
6789         || has_mutation(MUT_CONSTRICTING_TAIL, allow_tran)
6790         || fishtail // XX respect allow_tran
6791         || get_mutation_level(MUT_ARMOURED_TAIL, allow_tran)
6792         || get_mutation_level(MUT_STINGER, allow_tran))
6793     {
6794         return 1;
6795     }
6796 
6797     return 0;
6798 }
6799 
6800 // Whether the player has a usable offhand for the
6801 // purpose of punching.
has_usable_offhand() const6802 bool player::has_usable_offhand() const
6803 {
6804     if (get_mutation_level(MUT_MISSING_HAND))
6805         return false;
6806     if (shield())
6807         return false;
6808 
6809     const item_def* wp = slot_item(EQ_WEAPON);
6810     return !wp || hands_reqd(*wp) != HANDS_TWO;
6811 }
6812 
has_usable_tentacle() const6813 bool player::has_usable_tentacle() const
6814 {
6815     return usable_tentacles();
6816 }
6817 
usable_tentacles() const6818 int player::usable_tentacles() const
6819 {
6820     int numtentacle = has_usable_tentacles();
6821 
6822     if (numtentacle == 0)
6823         return false;
6824 
6825     int free_tentacles = numtentacle - num_constricting();
6826 
6827     if (shield())
6828         free_tentacles -= 2;
6829 
6830     const item_def* wp = slot_item(EQ_WEAPON);
6831     if (wp)
6832     {
6833         hands_reqd_type hands_req = hands_reqd(*wp);
6834         free_tentacles -= 2 * hands_req + 2;
6835     }
6836 
6837     return free_tentacles;
6838 }
6839 
has_pseudopods(bool allow_tran) const6840 int player::has_pseudopods(bool allow_tran) const
6841 {
6842     return get_mutation_level(MUT_PSEUDOPODS, allow_tran);
6843 }
6844 
has_usable_pseudopods(bool allow_tran) const6845 int player::has_usable_pseudopods(bool allow_tran) const
6846 {
6847     return has_pseudopods(allow_tran);
6848 }
6849 
arm_count() const6850 int player::arm_count() const
6851 {
6852     // XX transformations? arm count per se isn't used by much though.
6853 
6854     return species::arm_count(species)
6855                     - get_mutation_level(MUT_MISSING_HAND);
6856 }
6857 
has_tentacles(bool allow_tran) const6858 int player::has_tentacles(bool allow_tran) const
6859 {
6860     // tentacles count as a mutation for these purposes. (TODO: realmut?)
6861     if (you.has_mutation(MUT_TENTACLE_ARMS, allow_tran))
6862         return arm_count();
6863 
6864     return 0;
6865 }
6866 
has_usable_tentacles(bool allow_tran) const6867 int player::has_usable_tentacles(bool allow_tran) const
6868 {
6869     return has_tentacles(allow_tran);
6870 }
6871 
sicken(int amount)6872 bool player::sicken(int amount)
6873 {
6874     ASSERT(!crawl_state.game_is_arena());
6875 
6876     if (res_miasma() || amount <= 0)
6877         return false;
6878 
6879     if (duration[DUR_DIVINE_STAMINA] > 0)
6880     {
6881         mpr("Your divine stamina protects you from disease!");
6882         return false;
6883     }
6884 
6885     mpr("You feel ill.");
6886     increase_duration(DUR_SICKNESS, amount, 210);
6887 
6888     return true;
6889 }
6890 
6891 /// Can the player see invisible things?
can_see_invisible(bool calc_unid) const6892 bool player::can_see_invisible(bool calc_unid) const
6893 {
6894     if (crawl_state.game_is_arena())
6895         return true;
6896 
6897     if (wearing(EQ_RINGS, RING_SEE_INVISIBLE, calc_unid)
6898         // armour: (checks head armour only)
6899         || wearing_ego(EQ_HELMET, SPARM_SEE_INVISIBLE)
6900         // randart gear
6901         || scan_artefacts(ARTP_SEE_INVISIBLE, calc_unid) > 0)
6902     {
6903         return true;
6904     }
6905 
6906     return innate_sinv();
6907 }
6908 
6909 /// Can the player see invisible things without needing items' help?
innate_sinv() const6910 bool player::innate_sinv() const
6911 {
6912     if (has_mutation(MUT_ACUTE_VISION))
6913         return true;
6914 
6915     // antennae give sInvis at 3
6916     if (get_mutation_level(MUT_ANTENNAE) == 3)
6917         return true;
6918 
6919     if (get_mutation_level(MUT_EYEBALLS) == 3)
6920         return true;
6921 
6922     if (have_passive(passive_t::sinv))
6923         return true;
6924 
6925     return false;
6926 }
6927 
invisible() const6928 bool player::invisible() const
6929 {
6930     return (duration[DUR_INVIS] || form == transformation::shadow)
6931            && !backlit();
6932 }
6933 
visible_to(const actor * looker) const6934 bool player::visible_to(const actor *looker) const
6935 {
6936     if (crawl_state.game_is_arena())
6937         return false;
6938 
6939     const bool invis_to = invisible() && !looker->can_see_invisible()
6940                           && !in_water();
6941     if (this == looker)
6942         return !invis_to;
6943 
6944     const monster* mon = looker->as_monster();
6945     return mon->friendly()
6946         || (!mon->has_ench(ENCH_BLIND) && !invis_to);
6947 }
6948 
6949 /**
6950  * Is the player backlit?
6951  *
6952  * @param self_halo If true, ignore the player's self-halo.
6953  * @returns True if the player is backlit.
6954 */
backlit(bool self_halo) const6955 bool player::backlit(bool self_halo) const
6956 {
6957     return player_severe_contamination()
6958            || duration[DUR_CORONA]
6959            || duration[DUR_LIQUID_FLAMES]
6960            || duration[DUR_QUAD_DAMAGE]
6961            || !umbraed() && haloed() && (self_halo || halo_radius() == -1);
6962 }
6963 
umbra() const6964 bool player::umbra() const
6965 {
6966     return !backlit() && umbraed() && !haloed();
6967 }
6968 
6969 // This is the imperative version.
backlight()6970 void player::backlight()
6971 {
6972     if (!duration[DUR_INVIS] && form != transformation::shadow)
6973     {
6974         if (duration[DUR_CORONA])
6975             mpr("You glow brighter.");
6976         else
6977             mpr("You are outlined in light.");
6978         increase_duration(DUR_CORONA, random_range(15, 35), 250);
6979     }
6980     else
6981     {
6982         mpr("You feel strangely conspicuous.");
6983         increase_duration(DUR_CORONA, random_range(3, 5), 250);
6984     }
6985 }
6986 
can_mutate() const6987 bool player::can_mutate() const
6988 {
6989     return true;
6990 }
6991 
6992 /**
6993  * Can the player be mutated without stat drain instead?
6994  *
6995  * @param temp      Whether to consider temporary modifiers (lichform)
6996  * @return Whether the player will mutate when mutated, instead of draining
6997  *         stats.
6998  */
can_safely_mutate(bool temp) const6999 bool player::can_safely_mutate(bool temp) const
7000 {
7001     if (!can_mutate())
7002         return false;
7003 
7004     return undead_state(temp) == US_ALIVE
7005            || undead_state(temp) == US_SEMI_UNDEAD;
7006 }
7007 
7008 // Is the player too undead to bleed, rage, or polymorph?
is_lifeless_undead(bool temp) const7009 bool player::is_lifeless_undead(bool temp) const
7010 {
7011     if (temp && undead_state() == US_SEMI_UNDEAD)
7012         return !you.vampire_alive;
7013     else
7014         return undead_state(temp) == US_UNDEAD;
7015 }
7016 
can_polymorph() const7017 bool player::can_polymorph() const
7018 {
7019     return !(transform_uncancellable || is_lifeless_undead());
7020 }
7021 
can_bleed(bool temp) const7022 bool player::can_bleed(bool temp) const
7023 {
7024     if (temp && !form_can_bleed(form))
7025         return false;
7026 
7027     return !is_lifeless_undead(temp) && !is_nonliving(temp);
7028 }
7029 
can_drink(bool temp) const7030 bool player::can_drink(bool temp) const
7031 {
7032     if (temp && (you.form == transformation::lich
7033                     || you.duration[DUR_NO_POTIONS]))
7034     {
7035         return false;
7036     }
7037     return !you.has_mutation(MUT_NO_DRINK);
7038 
7039 }
7040 
is_stationary() const7041 bool player::is_stationary() const
7042 {
7043     return form == transformation::tree
7044         || you.duration[DUR_LOCKED_DOWN];
7045 }
7046 
malmutate(const string & reason)7047 bool player::malmutate(const string &reason)
7048 {
7049     ASSERT(!crawl_state.game_is_arena());
7050 
7051     if (!can_mutate())
7052         return false;
7053 
7054     const mutation_type mut_quality = one_chance_in(5) ? RANDOM_MUTATION
7055                                                        : RANDOM_BAD_MUTATION;
7056     if (mutate(mut_quality, reason))
7057     {
7058         learned_something_new(HINT_YOU_MUTATED);
7059         return true;
7060     }
7061     return false;
7062 }
7063 
polymorph(int pow,bool allow_immobile)7064 bool player::polymorph(int pow, bool allow_immobile)
7065 {
7066     ASSERT(!crawl_state.game_is_arena());
7067 
7068     if (!can_polymorph())
7069         return false;
7070 
7071     transformation f = transformation::none;
7072 
7073     vector<transformation> forms = {
7074         transformation::bat,
7075         transformation::wisp,
7076         transformation::pig,
7077     };
7078     if (allow_immobile)
7079     {
7080         forms.emplace_back(transformation::tree);
7081         forms.emplace_back(transformation::fungus);
7082     }
7083 
7084     for (int tries = 0; tries < 3; tries++)
7085     {
7086         f = forms[random2(forms.size())];
7087 
7088         // need to do a dry run first, as Zin's protection has a random factor
7089         if (transform(pow, f, true, true))
7090             break;
7091 
7092         f = transformation::none;
7093     }
7094 
7095     if (f != transformation::none && transform(pow, f))
7096     {
7097         transform_uncancellable = true;
7098         return true;
7099     }
7100     return false;
7101 }
7102 
is_icy() const7103 bool player::is_icy() const
7104 {
7105     return form == transformation::ice_beast;
7106 }
7107 
is_fiery() const7108 bool player::is_fiery() const
7109 {
7110     return false;
7111 }
7112 
is_skeletal() const7113 bool player::is_skeletal() const
7114 {
7115     return false;
7116 }
7117 
shiftto(const coord_def & c)7118 void player::shiftto(const coord_def &c)
7119 {
7120     crawl_view.shift_player_to(c);
7121     set_position(c);
7122     clear_invalid_constrictions();
7123 }
7124 
asleep() const7125 bool player::asleep() const
7126 {
7127     return duration[DUR_SLEEP];
7128 }
7129 
cannot_act() const7130 bool player::cannot_act() const
7131 {
7132     return asleep() || cannot_move();
7133 }
7134 
7135 
can_feel_fear(bool include_unknown) const7136 bool player::can_feel_fear(bool include_unknown) const
7137 {
7138     // XXX: monsters are immune to fear when berserking.
7139     // should players also be?
7140     return you.holiness() & MH_NATURAL && (!include_unknown || !you.clarity());
7141 }
7142 
can_throw_large_rocks() const7143 bool player::can_throw_large_rocks() const
7144 {
7145     return species::can_throw_large_rocks(species);
7146 }
7147 
can_smell() const7148 bool player::can_smell() const
7149 {
7150     return !you.is_lifeless_undead(true);
7151 }
7152 
can_sleep(bool holi_only) const7153 bool player::can_sleep(bool holi_only) const
7154 {
7155     return !you.duration[DUR_SLEEP_IMMUNITY] && actor::can_sleep(holi_only);
7156 }
7157 
7158 /**
7159  * Attempts to put the player to sleep.
7160  *
7161  * @param power     The power of the effect putting the player to sleep.
7162  * @param hibernate Whether the player is being put to sleep by 'ensorcelled
7163  *                  hibernation' (doesn't affect characters with rC, ignores
7164  *                  power), or by a normal sleep effect.
7165  */
put_to_sleep(actor *,int power,bool hibernate)7166 void player::put_to_sleep(actor*, int power, bool hibernate)
7167 {
7168     ASSERT(!crawl_state.game_is_arena());
7169 
7170     const bool valid_target = hibernate ? can_hibernate() : can_sleep();
7171     if (!valid_target)
7172     {
7173         canned_msg(MSG_YOU_UNAFFECTED);
7174         return;
7175     }
7176 
7177     if (duration[DUR_SLEEP_IMMUNITY])
7178     {
7179         mpr("You can't fall asleep again this soon!");
7180         return;
7181     }
7182 
7183     if (duration[DUR_PARALYSIS]
7184         || duration[DUR_PETRIFIED]
7185         || duration[DUR_PETRIFYING])
7186     {
7187         mpr("You can't fall asleep in your current state!");
7188         return;
7189     }
7190 
7191     mpr("You fall asleep.");
7192 
7193     stop_directly_constricting_all(false);
7194     end_wait_spells();
7195     stop_delay();
7196     flash_view(UA_MONSTER, DARKGREY);
7197 
7198     // As above, do this after redraw.
7199     const int dur = hibernate ? 3 + random2avg(5, 2) :
7200                                 5 + random2avg(power/10, 5);
7201     set_duration(DUR_SLEEP, dur);
7202 }
7203 
awaken()7204 void player::awaken()
7205 {
7206     ASSERT(!crawl_state.game_is_arena());
7207 
7208     duration[DUR_SLEEP] = 0;
7209     set_duration(DUR_SLEEP_IMMUNITY, random_range(3, 5));
7210     mpr("You wake up.");
7211     flash_view(UA_MONSTER, BLACK);
7212 }
7213 
check_awaken(int disturbance)7214 void player::check_awaken(int disturbance)
7215 {
7216     if (asleep() && x_chance_in_y(disturbance + 1, 50))
7217     {
7218         awaken();
7219         dprf("Disturbance of intensity %d awoke player", disturbance);
7220     }
7221 }
7222 
beam_resists(bolt & beam,int hurted,bool doEffects,string source)7223 int player::beam_resists(bolt &beam, int hurted, bool doEffects, string source)
7224 {
7225     return check_your_resists(hurted, beam.flavour, source, &beam, doEffects);
7226 }
7227 
7228 // Used for falling into traps and other bad effects, but is a slightly
7229 // different effect from the player invokable ability.
do_shaft()7230 bool player::do_shaft()
7231 {
7232     if (!is_valid_shaft_level()
7233         || !feat_is_shaftable(env.grid(pos()))
7234         || duration[DUR_SHAFT_IMMUNITY])
7235     {
7236         return false;
7237     }
7238 
7239     // Ensure altars, items, and shops discovered at the moment
7240     // the player gets shafted are correctly registered.
7241     maybe_update_stashes();
7242 
7243     duration[DUR_SHAFT_IMMUNITY] = 1;
7244     down_stairs(DNGN_TRAP_SHAFT);
7245 
7246     return true;
7247 }
7248 
can_do_shaft_ability(bool quiet) const7249 bool player::can_do_shaft_ability(bool quiet) const
7250 {
7251     if (attribute[ATTR_HELD])
7252     {
7253         if (!quiet)
7254             mprf("You can't shaft yourself while %s.", held_status());
7255         return false;
7256     }
7257 
7258     if (feat_is_shaftable(env.grid(pos())))
7259     {
7260         if (!is_valid_shaft_level())
7261         {
7262             if (!quiet)
7263                 mpr("You can't shaft yourself on this level.");
7264             return false;
7265         }
7266     }
7267     else
7268     {
7269         if (!quiet)
7270             mpr("You can't shaft yourself on this terrain.");
7271         return false;
7272     }
7273 
7274     return true;
7275 }
7276 
7277 // Like do_shaft, but forced by the player.
7278 // It has a slightly different set of rules.
do_shaft_ability()7279 bool player::do_shaft_ability()
7280 {
7281     if (can_do_shaft_ability(true))
7282     {
7283         mpr("A shaft appears beneath you!");
7284         down_stairs(DNGN_TRAP_SHAFT, true);
7285         return true;
7286     }
7287     else
7288     {
7289         canned_msg(MSG_NOTHING_HAPPENS);
7290         redraw_screen();
7291         update_screen();
7292         return false;
7293     }
7294 }
7295 
did_escape_death() const7296 bool player::did_escape_death() const
7297 {
7298     return escaped_death_cause != NUM_KILLBY;
7299 }
7300 
reset_escaped_death()7301 void player::reset_escaped_death()
7302 {
7303     escaped_death_cause = NUM_KILLBY;
7304     escaped_death_aux   = "";
7305 }
7306 
add_gold(int delta)7307 void player::add_gold(int delta)
7308 {
7309     set_gold(gold + delta);
7310 }
7311 
del_gold(int delta)7312 void player::del_gold(int delta)
7313 {
7314     set_gold(gold - delta);
7315 }
7316 
set_gold(int amount)7317 void player::set_gold(int amount)
7318 {
7319     ASSERT(amount >= 0);
7320 
7321     if (amount != gold)
7322     {
7323         const int old_gold = gold;
7324         gold = amount;
7325         shopping_list.gold_changed(old_gold, gold);
7326 
7327         // XXX: this might benefit from being in its own function
7328         if (you_worship(GOD_GOZAG))
7329         {
7330             for (const auto& power : get_god_powers(you.religion))
7331             {
7332                 const int cost = get_gold_cost(power.abil);
7333                 if (gold >= cost && old_gold < cost)
7334                     power.display(true, "You now have enough gold to %s.");
7335                 else if (old_gold >= cost && gold < cost)
7336                     power.display(false, "You no longer have enough gold to %s.");
7337             }
7338             you.redraw_title = true;
7339         }
7340     }
7341 }
7342 
increase_duration(duration_type dur,int turns,int cap,const char * msg)7343 void player::increase_duration(duration_type dur, int turns, int cap,
7344                                const char* msg)
7345 {
7346     if (msg)
7347         mpr(msg);
7348     cap *= BASELINE_DELAY;
7349 
7350     duration[dur] += turns * BASELINE_DELAY;
7351     if (cap && duration[dur] > cap)
7352         duration[dur] = cap;
7353 }
7354 
set_duration(duration_type dur,int turns,int cap,const char * msg)7355 void player::set_duration(duration_type dur, int turns,
7356                           int cap, const char * msg)
7357 {
7358     duration[dur] = 0;
7359     increase_duration(dur, turns, cap, msg);
7360 }
7361 
goto_place(const level_id & lid)7362 void player::goto_place(const level_id &lid)
7363 {
7364     where_are_you = static_cast<branch_type>(lid.branch);
7365     depth = lid.depth;
7366     ASSERT_RANGE(depth, 1, brdepth[where_are_you] + 1);
7367 }
7368 
attempt_escape(int attempts)7369 bool player::attempt_escape(int attempts)
7370 {
7371     monster *themonst;
7372 
7373     if (!is_constricted())
7374         return true;
7375 
7376     themonst = monster_by_mid(constricted_by);
7377     ASSERT(themonst);
7378     escape_attempts += attempts;
7379 
7380     const bool direct = is_directly_constricted();
7381     const string object = direct ? themonst->name(DESC_ITS, true)
7382                                  : "the roots'";
7383     // player breaks free if (4+n)d13 >= 5d(8+HD/4)
7384     const int escape_score = roll_dice(4 + escape_attempts, 13);
7385     if (escape_score
7386         >= roll_dice(5, 8 + div_rand_round(themonst->get_hit_dice(), 4)))
7387     {
7388         mprf("You escape %s grasp.", object.c_str());
7389 
7390         // Stun the monster to prevent it from constricting again right away.
7391         if (direct)
7392             themonst->speed_increment -= 5;
7393 
7394         stop_being_constricted(true);
7395 
7396         return true;
7397     }
7398     else
7399     {
7400         mprf("%s grasp on you weakens, but your attempt to escape fails.",
7401              object.c_str());
7402         turn_is_over = true;
7403         return false;
7404     }
7405 }
7406 
sentinel_mark(bool trap)7407 void player::sentinel_mark(bool trap)
7408 {
7409     if (duration[DUR_SENTINEL_MARK])
7410     {
7411         mpr("The mark upon you grows brighter.");
7412         increase_duration(DUR_SENTINEL_MARK, random_range(20, 40), 180);
7413     }
7414     else
7415     {
7416         mprf(MSGCH_WARN, "A sentinel's mark forms upon you.");
7417         increase_duration(DUR_SENTINEL_MARK, trap ? random_range(25, 40)
7418                                                   : random_range(35, 60),
7419                           250);
7420     }
7421 }
7422 
7423 /*
7424  * Is the player too terrified to move (because of fungusform)?
7425  *
7426  * @return true iff there is an alarming monster anywhere near a fungusform player.
7427  */
is_nervous()7428 bool player::is_nervous()
7429 {
7430     if (form != transformation::fungus)
7431         return false;
7432     for (monster_near_iterator mi(&you); mi; ++mi)
7433     {
7434         if (made_nervous_by(*mi))
7435             return true;
7436     }
7437     return false;
7438 }
7439 
7440 /*
7441  * Does monster `mons` make the player nervous (in fungusform)?
7442  *
7443  * @param mons  the monster to check
7444  * @return      true iff mons is non-null, player is fungal, and `mons` is a threatening monster.
7445  */
made_nervous_by(const monster * mons)7446 bool player::made_nervous_by(const monster *mons)
7447 {
7448     if (form != transformation::fungus)
7449         return false;
7450     if (!mons)
7451         return false;
7452     if (!mons_is_wandering(*mons)
7453         && !mons->asleep()
7454         && !mons->confused()
7455         && !mons->cannot_act()
7456         && mons_is_threatening(*mons)
7457         && !mons->wont_attack()
7458         && !mons->neutral())
7459     {
7460         return true;
7461     }
7462     return false;
7463 }
7464 
weaken(actor *,int pow)7465 void player::weaken(actor */*attacker*/, int pow)
7466 {
7467     if (!duration[DUR_WEAK])
7468         mprf(MSGCH_WARN, "You feel your attacks grow feeble.");
7469     else
7470         mprf(MSGCH_WARN, "You feel as though you will be weak longer.");
7471 
7472     increase_duration(DUR_WEAK, pow + random2(pow + 3), 50);
7473 }
7474 
7475 /**
7476  * Check if the player is about to die from flight/form expiration.
7477  *
7478  * Check whether the player is on a cell which would be deadly if not for some
7479  * temporary condition, and if such condition is expiring. In that case, we
7480  * give a strong warning to the player. The actual message printing is done
7481  * by the caller.
7482  *
7483  * @param dur the duration to check for dangerous expiration.
7484  * @param p the coordinates of the cell to check. Defaults to player position.
7485  * @return whether the player is in immediate danger.
7486  */
need_expiration_warning(duration_type dur,dungeon_feature_type feat)7487 bool need_expiration_warning(duration_type dur, dungeon_feature_type feat)
7488 {
7489     if (!is_feat_dangerous(feat, true) || !dur_expiring(dur))
7490         return false;
7491 
7492     if (dur == DUR_FLIGHT)
7493         return true;
7494     else if (dur == DUR_TRANSFORMATION
7495              && (form_can_swim()) || form_can_fly())
7496     {
7497         return true;
7498     }
7499     return false;
7500 }
7501 
need_expiration_warning(duration_type dur,coord_def p)7502 bool need_expiration_warning(duration_type dur, coord_def p)
7503 {
7504     return need_expiration_warning(dur, env.grid(p));
7505 }
7506 
need_expiration_warning(dungeon_feature_type feat)7507 bool need_expiration_warning(dungeon_feature_type feat)
7508 {
7509     return need_expiration_warning(DUR_FLIGHT, feat)
7510            || need_expiration_warning(DUR_TRANSFORMATION, feat);
7511 }
7512 
need_expiration_warning(coord_def p)7513 bool need_expiration_warning(coord_def p)
7514 {
7515     return need_expiration_warning(env.grid(p));
7516 }
7517 
_constriction_description()7518 static string _constriction_description()
7519 {
7520     string cinfo = "";
7521     vector<string> c_name;
7522 
7523     const int num_free_tentacles = you.usable_tentacles();
7524     if (num_free_tentacles)
7525     {
7526         cinfo += make_stringf("You have %d tentacle%s available for constriction.",
7527                               num_free_tentacles,
7528                               num_free_tentacles > 1 ? "s" : "");
7529     }
7530 
7531     if (you.is_directly_constricted())
7532     {
7533         const monster * const constrictor = monster_by_mid(you.constricted_by);
7534         ASSERT(constrictor);
7535 
7536         if (!cinfo.empty())
7537             cinfo += "\n";
7538 
7539         cinfo += make_stringf("You are being %s by %s.",
7540                               constrictor->constriction_does_damage(true) ?
7541                                   "held" : "constricted",
7542                               constrictor->name(DESC_A).c_str());
7543     }
7544 
7545     if (you.is_constricting())
7546     {
7547         for (const auto &entry : *you.constricting)
7548         {
7549             monster *whom = monster_by_mid(entry.first);
7550             ASSERT(whom);
7551 
7552             if (!whom->is_directly_constricted())
7553                 continue;
7554 
7555             c_name.push_back(whom->name(DESC_A));
7556         }
7557 
7558         if (!c_name.empty())
7559         {
7560             if (!cinfo.empty())
7561                 cinfo += "\n";
7562 
7563             cinfo += "You are constricting ";
7564             cinfo += comma_separated_line(c_name.begin(), c_name.end());
7565             cinfo += ".";
7566         }
7567     }
7568 
7569     return cinfo;
7570 }
7571 
7572 /**
7573  *   The player's radius of monster detection.
7574  *   @return   the radius in which a player can detect monsters.
7575 **/
player_monster_detect_radius()7576 int player_monster_detect_radius()
7577 {
7578     int radius = you.get_mutation_level(MUT_ANTENNAE) * 2;
7579 
7580     if (player_equip_unrand(UNRAND_HOOD_ASSASSIN))
7581         radius = max(radius, 4);
7582     if (have_passive(passive_t::detect_montier))
7583         radius = max(radius, you.piety / 20);
7584     return min(radius, LOS_MAX_RANGE);
7585 }
7586 
7587 /**
7588  * Return true if the player has angered Pandemonium by picking up or moving
7589  * the Orb of Zot.
7590  */
player_on_orb_run()7591 bool player_on_orb_run()
7592 {
7593     return you.chapter == CHAPTER_ESCAPING
7594            || you.chapter == CHAPTER_ANGERED_PANDEMONIUM;
7595 }
7596 
7597 /**
7598  * Return true if the player has the Orb of Zot.
7599  * @return  True if the player has the Orb, false otherwise.
7600  */
player_has_orb()7601 bool player_has_orb()
7602 {
7603     return you.chapter == CHAPTER_ESCAPING;
7604 }
7605 
form_uses_xl() const7606 bool player::form_uses_xl() const
7607 {
7608     // No body parts that translate in any way to something fisticuffs could
7609     // matter to, the attack mode is different. Plus, it's weird to have
7610     // users of one particular [non-]weapon be effective for this
7611     // unintentional form while others can just run or die. I believe this
7612     // should apply to more forms, too.  [1KB]
7613     return form == transformation::wisp || form == transformation::fungus
7614         || form == transformation::pig
7615         || form == transformation::bat
7616                         && you.get_mutation_level(MUT_VAMPIRISM) < 2;
7617 }
7618 
wear_barding() const7619 bool player::wear_barding() const
7620 {
7621     return species::wears_barding(species);
7622 }
7623 
_get_potion_heal_factor()7624 static int _get_potion_heal_factor()
7625 {
7626     // healing factor is expressed in halves, so default is 2/2 -- 100%.
7627     int factor = 2;
7628 
7629     // start with penalties
7630     factor -= player_equip_unrand(UNRAND_VINES) ? 2 : 0;
7631     factor -= you.mutation[MUT_NO_POTION_HEAL];
7632 
7633     // then apply bonuses - Kryia's doubles potion healing
7634     factor *= player_equip_unrand(UNRAND_KRYIAS) ? 2 : 1;
7635 
7636     // make sure we don't turn healing negative.
7637     return max(0, factor);
7638 }
7639 
print_potion_heal_message()7640 void print_potion_heal_message()
7641 {
7642     // Don't give multiple messages in weird cases with both enhanced
7643     // and reduced healing.
7644     if (_get_potion_heal_factor() > 2)
7645     {
7646         if (player_equip_unrand(UNRAND_KRYIAS))
7647         {
7648             item_def* item = you.slot_item(EQ_BODY_ARMOUR);
7649             mprf("%s enhances the healing.",
7650             item->name(DESC_THE, false, false, false).c_str());
7651         }
7652         else
7653             mpr("The healing is enhanced."); // bad message, but this should
7654                                              // never be possible anyway
7655     }
7656     else if (_get_potion_heal_factor() == 0)
7657         mpr("Your system rejects the healing.");
7658     else if (_get_potion_heal_factor() < 2)
7659         mpr("Your system partially rejects the healing.");
7660 }
7661 
7662 
can_potion_heal()7663 bool player::can_potion_heal()
7664 {
7665     return _get_potion_heal_factor() > 0;
7666 }
7667 
scale_potion_healing(int healing_amount)7668 int player::scale_potion_healing(int healing_amount)
7669 {
7670     return div_rand_round(healing_amount * _get_potion_heal_factor(), 2);
7671 }
7672 
player_open_door(coord_def doorpos)7673 void player_open_door(coord_def doorpos)
7674 {
7675     // Finally, open the closed door!
7676     set<coord_def> all_door;
7677     find_connected_identical(doorpos, all_door);
7678     const char *adj, *noun;
7679     get_door_description(all_door.size(), &adj, &noun);
7680 
7681     const string door_desc_adj  =
7682         env.markers.property_at(doorpos, MAT_ANY, "door_description_adjective");
7683     const string door_desc_noun =
7684         env.markers.property_at(doorpos, MAT_ANY, "door_description_noun");
7685     if (!door_desc_adj.empty())
7686         adj = door_desc_adj.c_str();
7687     if (!door_desc_noun.empty())
7688         noun = door_desc_noun.c_str();
7689 
7690     if (!you.confused())
7691     {
7692         string door_open_prompt =
7693             env.markers.property_at(doorpos, MAT_ANY, "door_open_prompt");
7694 
7695         bool ignore_exclude = false;
7696 
7697         if (!door_open_prompt.empty())
7698         {
7699             door_open_prompt += " (y/N)";
7700             if (!yesno(door_open_prompt.c_str(), true, 'n', true, false))
7701             {
7702                 if (is_exclude_root(doorpos))
7703                     canned_msg(MSG_OK);
7704                 else
7705                 {
7706                     if (yesno("Put travel exclusion on door? (Y/n)",
7707                               true, 'y'))
7708                     {
7709                         // Zero radius exclusion right on top of door.
7710                         set_exclude(doorpos, 0);
7711                     }
7712                 }
7713                 interrupt_activity(activity_interrupt::force);
7714                 return;
7715             }
7716             ignore_exclude = true;
7717         }
7718 
7719         if (!ignore_exclude && is_exclude_root(doorpos))
7720         {
7721             string prompt = make_stringf("This %s%s is marked as excluded! "
7722                                          "Open it anyway?", adj, noun);
7723 
7724             if (!yesno(prompt.c_str(), true, 'n', true, false))
7725             {
7726                 canned_msg(MSG_OK);
7727                 interrupt_activity(activity_interrupt::force);
7728                 return;
7729             }
7730         }
7731     }
7732 
7733     const int skill = 8 + you.skill_rdiv(SK_STEALTH, 4, 3);
7734 
7735     string berserk_open = env.markers.property_at(doorpos, MAT_ANY,
7736                                                   "door_berserk_verb_open");
7737     string berserk_adjective = env.markers.property_at(doorpos, MAT_ANY,
7738                                                        "door_berserk_adjective");
7739     string door_open_creak = env.markers.property_at(doorpos, MAT_ANY,
7740                                                      "door_noisy_verb_open");
7741     string door_airborne = env.markers.property_at(doorpos, MAT_ANY,
7742                                                    "door_airborne_verb_open");
7743     string door_open_verb = env.markers.property_at(doorpos, MAT_ANY,
7744                                                     "door_verb_open");
7745 
7746     if (you.berserk())
7747     {
7748         // XXX: Better flavour for larger doors?
7749         if (silenced(you.pos()))
7750         {
7751             if (!berserk_open.empty())
7752             {
7753                 berserk_open += ".";
7754                 mprf(berserk_open.c_str(), adj, noun);
7755             }
7756             else
7757                 mprf("The %s%s flies open!", adj, noun);
7758         }
7759         else
7760         {
7761             if (!berserk_open.empty())
7762             {
7763                 if (!berserk_adjective.empty())
7764                     berserk_open += " " + berserk_adjective;
7765                 else
7766                     berserk_open += ".";
7767                 mprf(MSGCH_SOUND, berserk_open.c_str(), adj, noun);
7768             }
7769             else
7770                 mprf(MSGCH_SOUND, "The %s%s flies open with a bang!", adj, noun);
7771             noisy(15, you.pos());
7772         }
7773     }
7774     else if (one_chance_in(skill) && !silenced(you.pos()))
7775     {
7776         if (!door_open_creak.empty())
7777             mprf(MSGCH_SOUND, door_open_creak.c_str(), adj, noun);
7778         else
7779         {
7780             mprf(MSGCH_SOUND, "As you open the %s%s, it creaks loudly!",
7781                  adj, noun);
7782         }
7783         noisy(10, you.pos());
7784     }
7785     else
7786     {
7787         const char* verb;
7788         if (you.airborne())
7789         {
7790             if (!door_airborne.empty())
7791                 verb = door_airborne.c_str();
7792             else
7793                 verb = "You reach down and open the %s%s.";
7794         }
7795         else
7796         {
7797             if (!door_open_verb.empty())
7798                verb = door_open_verb.c_str();
7799             else
7800                verb = "You open the %s%s.";
7801         }
7802 
7803         mprf(verb, adj, noun);
7804     }
7805 
7806     vector<coord_def> excludes;
7807     for (const auto &dc : all_door)
7808     {
7809         if (cell_is_runed(dc))
7810             explored_tracked_feature(env.grid(dc));
7811         dgn_open_door(dc);
7812         set_terrain_changed(dc);
7813         dungeon_events.fire_position_event(DET_DOOR_OPENED, dc);
7814 
7815         // Even if some of the door is out of LOS, we want the entire
7816         // door to be updated. Hitting this case requires a really big
7817         // door!
7818         if (env.map_knowledge(dc).seen())
7819         {
7820             env.map_knowledge(dc).set_feature(env.grid(dc));
7821 #ifdef USE_TILE
7822             tile_env.bk_bg(dc) = tileidx_feature_base(env.grid(dc));
7823 #endif
7824         }
7825 
7826         if (is_excluded(dc))
7827             excludes.push_back(dc);
7828     }
7829 
7830     update_exclusion_los(excludes);
7831     viewwindow();
7832     update_screen();
7833     you.turn_is_over = true;
7834 }
7835 
player_close_door(coord_def doorpos)7836 void player_close_door(coord_def doorpos)
7837 {
7838     // Finally, close the opened door!
7839     string berserk_close = env.markers.property_at(doorpos, MAT_ANY,
7840                                                 "door_berserk_verb_close");
7841     const string berserk_adjective = env.markers.property_at(doorpos, MAT_ANY,
7842                                                 "door_berserk_adjective");
7843     const string door_close_creak = env.markers.property_at(doorpos, MAT_ANY,
7844                                                 "door_noisy_verb_close");
7845     const string door_airborne = env.markers.property_at(doorpos, MAT_ANY,
7846                                                 "door_airborne_verb_close");
7847     const string door_close_verb = env.markers.property_at(doorpos, MAT_ANY,
7848                                                 "door_verb_close");
7849     const string door_desc_adj  = env.markers.property_at(doorpos, MAT_ANY,
7850                                                 "door_description_adjective");
7851     const string door_desc_noun = env.markers.property_at(doorpos, MAT_ANY,
7852                                                 "door_description_noun");
7853     set<coord_def> all_door;
7854     find_connected_identical(doorpos, all_door);
7855     const auto door_vec = vector<coord_def>(all_door.begin(), all_door.end());
7856 
7857     const char *adj, *noun;
7858     get_door_description(all_door.size(), &adj, &noun);
7859     const string waynoun_str = make_stringf("%sway", noun);
7860     const char *waynoun = waynoun_str.c_str();
7861 
7862     if (!door_desc_adj.empty())
7863         adj = door_desc_adj.c_str();
7864     if (!door_desc_noun.empty())
7865     {
7866         noun = door_desc_noun.c_str();
7867         waynoun = noun;
7868     }
7869 
7870     for (const coord_def& dc : all_door)
7871     {
7872         if (monster* mon = monster_at(dc))
7873         {
7874             const bool mons_unseen = !you.can_see(*mon);
7875             if (mons_unseen || mons_is_object(mon->type))
7876             {
7877                 mprf("Something is blocking the %s!", waynoun);
7878                 // No free detection!
7879                 if (mons_unseen)
7880                     you.turn_is_over = true;
7881             }
7882             else
7883                 mprf("There's a creature in the %s!", waynoun);
7884             return;
7885         }
7886 
7887         if (env.igrid(dc) != NON_ITEM)
7888         {
7889             if (!has_push_spaces(dc, false, &door_vec))
7890             {
7891                 mprf("There's something jamming the %s.", waynoun);
7892                 return;
7893             }
7894         }
7895 
7896         // messaging with gateways will be inconsistent if this isn't last
7897         if (you.pos() == dc)
7898         {
7899             mprf("There's a thick-headed creature in the %s!", waynoun);
7900             return;
7901         }
7902     }
7903     const int you_old_top_item = env.igrid(you.pos());
7904 
7905     bool items_moved = false;
7906     for (const coord_def& dc : all_door)
7907         items_moved |= push_items_from(dc, &door_vec);
7908 
7909     // TODO: if only one thing moved, use that item's name
7910     // TODO: handle des-derived strings.  (Better yet, find a way to not have
7911     // format strings in des...)
7912     const char *items_msg = items_moved ? ", pushing everything out of the way"
7913                                         : "";
7914 
7915     const int skill = 8 + you.skill_rdiv(SK_STEALTH, 4, 3);
7916 
7917     if (you.berserk())
7918     {
7919         if (silenced(you.pos()))
7920         {
7921             if (!berserk_close.empty())
7922             {
7923                 berserk_close += ".";
7924                 mprf(berserk_close.c_str(), adj, noun);
7925             }
7926             else
7927                 mprf("You slam the %s%s shut%s!", adj, noun, items_msg);
7928         }
7929         else
7930         {
7931             if (!berserk_close.empty())
7932             {
7933                 if (!berserk_adjective.empty())
7934                     berserk_close += " " + berserk_adjective;
7935                 else
7936                     berserk_close += ".";
7937                 mprf(MSGCH_SOUND, berserk_close.c_str(), adj, noun);
7938             }
7939             else
7940             {
7941                 mprf(MSGCH_SOUND, "You slam the %s%s shut with a bang%s!",
7942                                   adj, noun, items_msg);
7943             }
7944 
7945             noisy(15, you.pos());
7946         }
7947     }
7948     else if (one_chance_in(skill) && !silenced(you.pos()))
7949     {
7950         if (!door_close_creak.empty())
7951             mprf(MSGCH_SOUND, door_close_creak.c_str(), adj, noun);
7952         else
7953         {
7954             mprf(MSGCH_SOUND, "As you close the %s%s%s, it creaks loudly!",
7955                               adj, noun, items_msg);
7956         }
7957 
7958         noisy(10, you.pos());
7959     }
7960     else
7961     {
7962         if (you.airborne())
7963         {
7964             if (!door_airborne.empty())
7965                 mprf(door_airborne.c_str(), adj, noun);
7966             else
7967                 mprf("You reach down and close the %s%s%s.", adj, noun, items_msg);
7968         }
7969         else
7970         {
7971             if (!door_close_verb.empty())
7972                 mprf(door_close_verb.c_str(), adj, noun);
7973             else
7974                 mprf("You close the %s%s%s.", adj, noun, items_msg);
7975         }
7976     }
7977 
7978     vector<coord_def> excludes;
7979     for (const coord_def& dc : all_door)
7980     {
7981         // Once opened, formerly runed doors become normal doors.
7982         dgn_close_door(dc);
7983         set_terrain_changed(dc);
7984         dungeon_events.fire_position_event(DET_DOOR_CLOSED, dc);
7985 
7986         // Even if some of the door is out of LOS once it's closed
7987         // (or even if some of it is out of LOS when it's open), we
7988         // want the entire door to be updated.
7989         if (env.map_knowledge(dc).seen())
7990         {
7991             env.map_knowledge(dc).set_feature(env.grid(dc));
7992 #ifdef USE_TILE
7993             tile_env.bk_bg(dc) = tileidx_feature_base(env.grid(dc));
7994 #endif
7995         }
7996 
7997         if (is_excluded(dc))
7998             excludes.push_back(dc);
7999     }
8000 
8001     update_exclusion_los(excludes);
8002 
8003     // item pushing may have moved items under the player
8004     if (env.igrid(you.pos()) != you_old_top_item)
8005         item_check();
8006     you.turn_is_over = true;
8007 }
8008 
8009 /**
8010  * Return a string describing the player's hand(s) taking a given verb.
8011  *
8012  * @param plural_verb    A plural-agreeing verb. ("Smoulders", "are", etc.)
8013  * @return               A string describing the action.
8014  *                       E.g. "tentacles smoulder", "paw is", etc.
8015  */
hands_verb(const string & plural_verb) const8016 string player::hands_verb(const string &plural_verb) const
8017 {
8018     bool plural;
8019     const string hand = hand_name(true, &plural);
8020     return hand + " " + conjugate_verb(plural_verb, plural);
8021 }
8022 
8023 // Is this a character that would not normally have a preceding space when
8024 // it follows a word?
_is_end_punct(char c)8025 static bool _is_end_punct(char c)
8026 {
8027     switch (c)
8028     {
8029     case ' ': case '.': case '!': case '?':
8030     case ',': case ':': case ';': case ')':
8031         return true;
8032     }
8033     return false;
8034 }
8035 
8036 /**
8037  * Return a string describing the player's hand(s) (or equivalent) taking the
8038  * given action (verb).
8039  *
8040  * @param plural_verb   The plural-agreeing verb corresponding to the action to
8041  *                      take. E.g., "smoulder", "glow", "gain", etc.
8042  * @param object        The object or predicate complement of the action,
8043  *                      including any sentence-final punctuation. E.g. ".",
8044  *                      "new energy.", etc.
8045  * @return              A string describing the player's hands taking the
8046  *                      given action. E.g. "Your tentacle gains new energy."
8047  */
hands_act(const string & plural_verb,const string & object) const8048 string player::hands_act(const string &plural_verb,
8049                          const string &object) const
8050 {
8051     const bool space = !object.empty() && !_is_end_punct(object[0]);
8052     return "Your " + hands_verb(plural_verb) + (space ? " " : "") + object;
8053 }
8054 
inaccuracy() const8055 int player::inaccuracy() const
8056 {
8057     int degree = 0;
8058     if (player_equip_unrand(UNRAND_AIR))
8059         degree++;
8060     if (get_mutation_level(MUT_MISSING_EYE))
8061         degree++;
8062     return degree;
8063 }
8064 
8065 /**
8066  * Handle effects that occur after the player character stops berserking.
8067  */
player_end_berserk()8068 void player_end_berserk()
8069 {
8070     if (!you.duration[DUR_PARALYSIS] && !you.petrified())
8071         mprf(MSGCH_WARN, "You are exhausted.");
8072 
8073     you.berserk_penalty = 0;
8074 
8075     const int dur = 12 + roll_dice(2, 12);
8076     // Slow durations are multiplied by haste_mul (3/2), exhaustion lasts
8077     // slightly longer.
8078     you.increase_duration(DUR_BERSERK_COOLDOWN, dur * 2);
8079 
8080     // Don't trigger too many hints mode messages.
8081     const bool hints_slow = Hints.hints_events[HINT_YOU_ENCHANTED];
8082     Hints.hints_events[HINT_YOU_ENCHANTED] = false;
8083 
8084     slow_player(dur);
8085 
8086     //Un-apply Berserk's +50% Current/Max HP
8087     calc_hp(true, false);
8088 
8089     learned_something_new(HINT_POSTBERSERK);
8090     Hints.hints_events[HINT_YOU_ENCHANTED] = hints_slow;
8091     quiver::set_needs_redraw();
8092 }
8093 
8094 /**
8095  * Does the player have the Sanguine Armour mutation (not suppressed by a form)
8096  * while being at a low enough HP (<67%) for its benefits to trigger?
8097  *
8098  * @return Whether Sanguine Armour should be active.
8099  */
sanguine_armour_valid()8100 bool sanguine_armour_valid()
8101 {
8102     // why does this need to specify the activity type explicitly?
8103     return you.hp <= you.hp_max * 2 / 3
8104            && you.get_mutation_level(MUT_SANGUINE_ARMOUR, mutation_activity_type::FULL);
8105 }
8106 
8107 /// Trigger sanguine armour, updating the duration & messaging as appropriate.
activate_sanguine_armour()8108 void activate_sanguine_armour()
8109 {
8110     const bool was_active = you.duration[DUR_SANGUINE_ARMOUR];
8111     you.duration[DUR_SANGUINE_ARMOUR] = random_range(60, 100);
8112     if (!was_active)
8113     {
8114         mpr("Your blood congeals into armour.");
8115         you.redraw_armour_class = true;
8116     }
8117 }
8118 
8119 /**
8120  * Refreshes the protective aura around the player after striking with
8121  * a weapon of protection. The duration is very short.
8122  */
refresh_weapon_protection()8123 void refresh_weapon_protection()
8124 {
8125     if (!you.duration[DUR_SPWPN_PROTECTION])
8126         mpr("Your weapon exudes an aura of protection.");
8127 
8128     you.increase_duration(DUR_SPWPN_PROTECTION, 3 + random2(2), 5);
8129     you.redraw_armour_class = true;
8130 }
8131 
8132 // Is the player immune to a particular hex because of their
8133 // intrinsic properties?
immune_to_hex(const spell_type hex) const8134 bool player::immune_to_hex(const spell_type hex) const
8135 {
8136     switch (hex)
8137     {
8138     case SPELL_PARALYSIS_GAZE:
8139     case SPELL_PARALYSE:
8140     case SPELL_SLOW:
8141         return stasis();
8142     case SPELL_CONFUSE:
8143     case SPELL_CONFUSION_GAZE:
8144     case SPELL_MASS_CONFUSION:
8145         return clarity() || you.duration[DUR_DIVINE_STAMINA] > 0;
8146     case SPELL_TELEPORT_OTHER:
8147     case SPELL_BLINK_OTHER:
8148     case SPELL_BLINK_OTHER_CLOSE:
8149         return no_tele();
8150     case SPELL_MESMERISE:
8151     case SPELL_AVATAR_SONG:
8152     case SPELL_SIREN_SONG:
8153         return clarity() || berserk();
8154     case SPELL_CAUSE_FEAR:
8155         return clarity() || !(holiness() & MH_NATURAL) || berserk();
8156     case SPELL_PETRIFY:
8157         return res_petrify();
8158     case SPELL_PORKALATOR:
8159         return is_lifeless_undead();
8160     case SPELL_VIRULENCE:
8161         return res_poison() == 3;
8162     // don't include the hidden "sleep immunity" duration
8163     case SPELL_SLEEP:
8164     case SPELL_DREAM_DUST:
8165         return !actor::can_sleep();
8166     case SPELL_HIBERNATION:
8167         return !can_hibernate();
8168     default:
8169         return false;
8170     }
8171 }
8172 
8173 // Activate DUR_AGILE.
be_agile(int pow)8174 void player::be_agile(int pow)
8175 {
8176     const bool were_agile = you.duration[DUR_AGILITY] > 0;
8177     mprf(MSGCH_DURATION, "You feel %sagile all of a sudden.",
8178          were_agile ? "more " : "");
8179 
8180     you.increase_duration(DUR_AGILITY, 35 + random2(pow), 80);
8181     if (!were_agile)
8182         you.redraw_evasion = true;
8183 }
8184