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, ¬ele_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 ¶lysis(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