1 /**
2 * @file
3 * @brief Functions for using some of the wackier inventory items.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "evoke.h"
9
10 #include <algorithm>
11 #include <cmath>
12 #include <cstdlib>
13 #include <cstring>
14
15 #include "act-iter.h"
16 #include "areas.h"
17 #include "artefact.h"
18 #include "branch.h"
19 #include "chardump.h"
20 #include "cloud.h"
21 #include "coordit.h"
22 #include "delay.h"
23 #include "directn.h"
24 #include "dungeon.h"
25 #include "english.h"
26 #include "env.h"
27 #include "exercise.h"
28 #include "fight.h"
29 #include "god-abil.h"
30 #include "god-conduct.h"
31 #include "god-passive.h"
32 #include "invent.h"
33 #include "item-prop.h"
34 #include "items.h"
35 #include "level-state-type.h"
36 #include "libutil.h"
37 #include "losglobal.h"
38 #include "message.h"
39 #include "mgen-data.h"
40 #include "misc.h"
41 #include "mon-behv.h"
42 #include "mon-clone.h"
43 #include "mon-pick.h"
44 #include "mon-place.h"
45 #include "mutant-beast.h"
46 #include "place.h"
47 #include "player.h"
48 #include "player-stats.h"
49 #include "prompt.h"
50 #include "religion.h"
51 #include "shout.h"
52 #include "skills.h"
53 #include "spl-book.h"
54 #include "spl-cast.h"
55 #include "spl-clouds.h"
56 #include "spl-damage.h"
57 #include "spl-util.h"
58 #include "state.h"
59 #include "stepdown.h"
60 #include "stringutil.h"
61 #include "tag-version.h"
62 #include "target.h"
63 #include "terrain.h"
64 #include "throw.h"
65 #ifdef USE_TILE
66 #include "tilepick.h"
67 #endif
68 #include "transform.h"
69 #include "traps.h"
70 #include "unicode.h"
71 #include "view.h"
72
_evoke_horn_of_geryon()73 static bool _evoke_horn_of_geryon()
74 {
75 bool created = false;
76
77 if (silenced(you.pos()))
78 {
79 mpr("You can't produce a sound!");
80 return false;
81 }
82
83 mprf(MSGCH_SOUND, "You produce a hideous howling noise!");
84 did_god_conduct(DID_EVIL, 3);
85 int num = 1;
86 const int adjusted_power =
87 player_adjust_evoc_power(you.skill(SK_EVOCATIONS, 10));
88 if (adjusted_power + random2(90) > 130)
89 ++num;
90 if (adjusted_power + random2(90) > 180)
91 ++num;
92 if (adjusted_power + random2(90) > 230)
93 ++num;
94 for (int n = 0; n < num; ++n)
95 {
96 monster* mon;
97 beh_type beh = BEH_HOSTILE;
98
99 if (random2(adjusted_power) > 7)
100 beh = BEH_FRIENDLY;
101 mgen_data mg(MONS_HELL_BEAST, beh, you.pos(), MHITYOU, MG_FORCE_BEH);
102 mg.set_summoned(&you, 3, SPELL_NO_SPELL);
103 mg.set_prox(PROX_CLOSE_TO_PLAYER);
104 mon = create_monster(mg);
105 if (mon)
106 created = true;
107 }
108 if (!created)
109 mpr("Nothing answers your call.");
110 return true;
111 }
112
113 /**
114 * Spray lightning in all directions. (Randomly: shock, lightning bolt, OoE.)
115 *
116 * @param range The range of the beams. (As with all beams, eventually
117 * capped at LOS.)
118 * @param power The power of the beams. (Affects damage.)
119 */
_spray_lightning(int range,int power)120 static void _spray_lightning(int range, int power)
121 {
122 const zap_type which_zap = random_choose(ZAP_SHOCK,
123 ZAP_LIGHTNING_BOLT,
124 ZAP_ORB_OF_ELECTRICITY);
125
126 bolt beam;
127 // range has no tracer, so randomness is ok
128 beam.range = range;
129 beam.source = you.pos();
130 beam.target = you.pos();
131 beam.target.x += random2(13) - 6;
132 beam.target.y += random2(13) - 6;
133 // Non-controlleable, so no player tracer.
134 zapping(which_zap, power, beam);
135 }
136
137 /**
138 * Evoke a lightning rod, creating an arc of lightning that can be sustained
139 * by continuing to evoke.
140 *
141 * @return Whether anything happened.
142 */
_lightning_rod(dist * preselect)143 static bool _lightning_rod(dist *preselect)
144 {
145 if (you.confused())
146 {
147 canned_msg(MSG_TOO_CONFUSED);
148 return false;
149 }
150
151 const int power =
152 player_adjust_evoc_power(5 + you.skill(SK_EVOCATIONS, 3));
153
154 const spret ret = your_spells(SPELL_THUNDERBOLT, power, false,
155 nullptr, preselect);
156
157 if (ret == spret::abort)
158 return false;
159
160 return true;
161 }
162
163 /**
164 * Spray lightning in all directions around the player.
165 *
166 * Quantity, range & power increase with level.
167 */
black_drac_breath()168 void black_drac_breath()
169 {
170 const int num_shots = roll_dice(2, 1 + you.experience_level / 7);
171 const int range = you.experience_level / 3 + 5; // 5-14
172 const int power = 25 + (you.form == transformation::dragon
173 ? 2 * you.experience_level : you.experience_level);
174 for (int i = 0; i < num_shots; ++i)
175 _spray_lightning(range, power);
176 }
177
178 /**
179 * Returns the MP cost of zapping a wand, depending on the player's MP-powered wands
180 * level and their available MP (or HP, if they're a djinn).
181 */
wand_mp_cost()182 int wand_mp_cost()
183 {
184 const int cost = you.get_mutation_level(MUT_MP_WANDS) * 3;
185 if (you.has_mutation(MUT_HP_CASTING))
186 return min(you.hp - 1, cost);
187 // Update mutation-data.h when updating this value.
188 return min(you.magic_points, cost);
189 }
190
wand_power()191 int wand_power()
192 {
193 const int mp_cost = wand_mp_cost();
194 return (15 + you.skill(SK_EVOCATIONS, 7) / 2) * (mp_cost + 9) / 9;
195 }
196
zap_wand(int slot,dist * _target)197 void zap_wand(int slot, dist *_target)
198 {
199 if (inv_count() < 1)
200 {
201 canned_msg(MSG_NOTHING_CARRIED); // why is this handled here??
202 return;
203 }
204
205 if (!evoke_check(slot))
206 return;
207
208 int item_slot;
209 if (slot != -1)
210 item_slot = slot;
211 else
212 {
213 item_slot = prompt_invent_item("Zap which item?",
214 menu_type::invlist,
215 OBJ_WANDS,
216 OPER_ZAP);
217 }
218
219 if (prompt_failed(item_slot))
220 return;
221
222 item_def& wand = you.inv[item_slot];
223 if (wand.base_type != OBJ_WANDS)
224 {
225 mpr("You can't zap that!");
226 return;
227 }
228
229 if (!evoke_check(slot))
230 return;
231
232 // If you happen to be wielding the wand, its display might change.
233 if (you.equip[EQ_WEAPON] == item_slot)
234 you.wield_change = true;
235
236 const int mp_cost = wand_mp_cost();
237 const int power = wand_power();
238 pay_mp(mp_cost);
239
240 const spell_type spell =
241 spell_in_wand(static_cast<wand_type>(wand.sub_type));
242
243 spret ret = your_spells(spell, power, false, &wand, _target);
244
245 if (ret == spret::abort)
246 {
247 refund_mp(mp_cost);
248 return;
249 }
250 else if (ret == spret::fail)
251 {
252 refund_mp(mp_cost);
253 canned_msg(MSG_NOTHING_HAPPENS);
254 you.turn_is_over = true;
255 return;
256 }
257
258 // Spend MP.
259 if (mp_cost)
260 finalize_mp_cost();
261
262 // Take off a charge.
263 wand.charges--;
264
265 if (wand.charges == 0)
266 {
267 ASSERT(in_inventory(wand));
268
269 mpr("The now-empty wand crumbles to dust.");
270 dec_inv_item_quantity(wand.link, 1);
271 }
272
273 practise_evoking(1);
274 count_action(CACT_EVOKE, EVOC_WAND);
275 alert_nearby_monsters();
276
277 you.turn_is_over = true;
278 }
279
manual_skill_names(bool short_text)280 string manual_skill_names(bool short_text)
281 {
282 skill_set skills;
283 for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; sk++)
284 if (you.skill_manual_points[sk])
285 skills.insert(sk);
286
287 if (short_text && skills.size() > 1)
288 {
289 char buf[40];
290 sprintf(buf, "%lu skills", (unsigned long) skills.size());
291 return string(buf);
292 }
293 else
294 return skill_names(skills);
295 }
296
_box_of_beasts()297 static bool _box_of_beasts()
298 {
299 mpr("You open the lid...");
300
301 // two rolls to reduce std deviation - +-6 so can get < max even at 27 sk
302 int rnd_factor = random2(7);
303 rnd_factor -= random2(7);
304 const int hd_min = min(27,
305 player_adjust_evoc_power(you.skill(SK_EVOCATIONS)
306 + rnd_factor));
307 const int tier = mutant_beast_tier(hd_min);
308 ASSERT(tier < NUM_BEAST_TIERS);
309
310 mgen_data mg(MONS_MUTANT_BEAST, BEH_FRIENDLY, you.pos(), MHITYOU,
311 MG_AUTOFOE);
312 mg.set_summoned(&you, 3 + random2(3), 0);
313
314 mg.hd = beast_tiers[tier];
315 dprf("hd %d (min %d, tier %d)", mg.hd, hd_min, tier);
316 const monster* mons = create_monster(mg);
317
318 if (!mons)
319 {
320 // Failed to create monster for some reason
321 mpr("...but nothing happens.");
322 return false;
323 }
324
325 mprf("...and %s %s out!",
326 mons->name(DESC_A).c_str(), mons->airborne() ? "flies" : "leaps");
327 did_god_conduct(DID_CHAOS, random_range(5,10));
328
329 return true;
330 }
331
_make_zig(item_def & zig)332 static bool _make_zig(item_def &zig)
333 {
334 if (feat_is_critical(env.grid(you.pos())))
335 {
336 mpr("You can't place a gateway to a ziggurat here.");
337 return false;
338 }
339 for (int lev = 1; lev <= brdepth[BRANCH_ZIGGURAT]; lev++)
340 {
341 if (is_level_on_stack(level_id(BRANCH_ZIGGURAT, lev))
342 || you.where_are_you == BRANCH_ZIGGURAT)
343 {
344 mpr("Finish your current ziggurat first!");
345 return false;
346 }
347 }
348
349 ASSERT(in_inventory(zig));
350 dec_inv_item_quantity(zig.link, 1);
351 dungeon_terrain_changed(you.pos(), DNGN_ENTER_ZIGGURAT);
352 mpr("You set the figurine down, and a mystic portal to a ziggurat forms.");
353 return true;
354 }
355
_gale_push_dist(const actor * agent,const actor * victim,int pow)356 static int _gale_push_dist(const actor* agent, const actor* victim, int pow)
357 {
358 int dist = 1 + random2(pow / 20);
359
360 if (victim->body_size(PSIZE_BODY) < SIZE_MEDIUM)
361 dist++;
362 else if (victim->body_size(PSIZE_BODY) > SIZE_BIG)
363 dist /= 2;
364 else if (victim->body_size(PSIZE_BODY) > SIZE_MEDIUM)
365 dist -= 1;
366
367 int range = victim->pos().distance_from(agent->pos());
368 if (range > 5)
369 dist -= 2;
370 else if (range > 2)
371 dist--;
372
373 if (dist < 0)
374 return 0;
375 else
376 return dist;
377 }
378
_angle_between(coord_def origin,coord_def p1,coord_def p2)379 static double _angle_between(coord_def origin, coord_def p1, coord_def p2)
380 {
381 double ang0 = atan2(p1.x - origin.x, p1.y - origin.y);
382 double ang = atan2(p2.x - origin.x, p2.y - origin.y);
383 return min(fabs(ang - ang0), fabs(ang - ang0 + 2 * PI));
384 }
385
wind_blast(actor * agent,int pow,coord_def target,bool card)386 void wind_blast(actor* agent, int pow, coord_def target, bool card)
387 {
388 vector<actor *> act_list;
389
390 int radius = min(5, 4 + div_rand_round(pow, 60));
391
392 for (actor_near_iterator ai(agent->pos(), LOS_SOLID); ai; ++ai)
393 {
394 if (ai->is_stationary()
395 || ai->pos().distance_from(agent->pos()) > radius
396 || ai->pos() == agent->pos() // so it's never aimed_at_feet
397 || !target.origin()
398 && _angle_between(agent->pos(), target, ai->pos()) > PI/4.0)
399 {
400 continue;
401 }
402
403 act_list.push_back(*ai);
404 }
405
406 far_to_near_sorter sorter = {agent->pos()};
407 sort(act_list.begin(), act_list.end(), sorter);
408
409 bolt wind_beam;
410 wind_beam.hit = AUTOMATIC_HIT;
411 wind_beam.pierce = true;
412 wind_beam.affects_nothing = true;
413 wind_beam.source = agent->pos();
414 wind_beam.range = LOS_RADIUS;
415 wind_beam.is_tracer = true;
416
417 map<actor *, coord_def> collisions;
418
419 bool player_affected = false;
420 vector<monster *> affected_monsters;
421
422 for (actor *act : act_list)
423 {
424 wind_beam.target = act->pos();
425 wind_beam.fire();
426
427 int push = _gale_push_dist(agent, act, pow);
428 bool pushed = false;
429
430 for (unsigned int j = 0; j < wind_beam.path_taken.size() - 1 && push;
431 ++j)
432 {
433 if (wind_beam.path_taken[j] == act->pos())
434 {
435 coord_def newpos = wind_beam.path_taken[j+1];
436 if (!actor_at(newpos) && !cell_is_solid(newpos)
437 && act->can_pass_through(newpos)
438 && act->is_habitable(newpos))
439 {
440 act->move_to_pos(newpos);
441 if (act->is_player())
442 stop_delay(true);
443 --push;
444 pushed = true;
445 }
446 else //Try to find an alternate route to push
447 {
448 bool success = false;
449 for (adjacent_iterator di(newpos); di; ++di)
450 {
451 if (adjacent(*di, act->pos())
452 && di->distance_from(agent->pos())
453 == newpos.distance_from(agent->pos())
454 && !actor_at(*di) && !cell_is_solid(*di)
455 && act->can_pass_through(*di)
456 && act->is_habitable(*di))
457 {
458 act->move_to_pos(*di);
459 if (act->is_player())
460 stop_delay(true);
461
462 --push;
463 pushed = true;
464
465 // Adjust wind path for moved monster
466 wind_beam.target = *di;
467 wind_beam.fire();
468 success = true;
469 break;
470 }
471 }
472
473 // If no luck, they slam into something.
474 if (!success)
475 collisions.insert(make_pair(act, newpos));
476 }
477 }
478 }
479
480 if (pushed)
481 {
482 if (act->is_monster())
483 {
484 act->as_monster()->speed_increment -= random2(6) + 4;
485 if (you.can_see(*act))
486 affected_monsters.push_back(act->as_monster());
487 }
488 else
489 player_affected = true;
490 }
491 }
492
493 // Now move clouds
494 vector<coord_def> cloud_list;
495 for (distance_iterator di(agent->pos(), true, false, radius + 2); di; ++di)
496 {
497 if (cloud_at(*di)
498 && cell_see_cell(agent->pos(), *di, LOS_SOLID)
499 && (target.origin()
500 || _angle_between(agent->pos(), target, *di) <= PI/4.0))
501 {
502 cloud_list.push_back(*di);
503 }
504 }
505
506 for (int i = cloud_list.size() - 1; i >= 0; --i)
507 {
508 wind_beam.target = cloud_list[i];
509 wind_beam.fire();
510
511 int dist = cloud_list[i].distance_from(agent->pos());
512 int push = (dist > 5 ? 2 : dist > 2 ? 3 : 4);
513
514 if (dist == 0 && agent->is_player())
515 {
516 delete_cloud(agent->pos());
517 break;
518 }
519
520 for (unsigned int j = 0;
521 j < wind_beam.path_taken.size() - 1 && push;
522 ++j)
523 {
524 if (wind_beam.path_taken[j] == cloud_list[i])
525 {
526 coord_def newpos = wind_beam.path_taken[j+1];
527 if (!cell_is_solid(newpos)
528 && !cloud_at(newpos))
529 {
530 swap_clouds(newpos, wind_beam.path_taken[j]);
531 --push;
532 }
533 else //Try to find an alternate route to push
534 {
535 for (distance_iterator di(wind_beam.path_taken[j],
536 false, true, 1); di; ++di)
537 {
538 if (di->distance_from(agent->pos())
539 == newpos.distance_from(agent->pos())
540 && *di != agent->pos() // never aimed_at_feet
541 && !cell_is_solid(*di)
542 && !cloud_at(*di))
543 {
544 swap_clouds(*di, wind_beam.path_taken[j]);
545 --push;
546 wind_beam.target = *di;
547 wind_beam.fire();
548 j--;
549 break;
550 }
551 }
552 }
553 }
554 }
555 }
556
557 if (agent->is_player())
558 {
559 const string source = card ? "card" : "fan";
560
561 if (pow > 120)
562 mprf("A mighty gale blasts forth from the %s!", source.c_str());
563 else
564 mprf("A fierce wind blows from the %s.", source.c_str());
565 }
566
567 noisy(8, agent->pos());
568
569 if (player_affected)
570 mpr("You are blown backwards!");
571
572 if (!affected_monsters.empty())
573 {
574 counted_monster_list affected = counted_monster_list(affected_monsters);
575 const string message =
576 make_stringf("%s %s blown away by the wind.",
577 affected.describe().c_str(),
578 conjugate_verb("be", affected.count() > 1).c_str());
579 if (strwidth(message) < get_number_of_cols() - 2)
580 mpr(message);
581 else
582 mpr("The monsters around you are blown away!");
583 }
584
585 for (auto it : collisions)
586 if (it.first->alive())
587 it.first->collide(it.second, agent, pow);
588
589 bool did_disperse = false;
590 // Handle trap triggering, finally. A dispersal before we finish
591 // would lead to weird crashes and behavior in the preceeding.
592 for (auto m : affected_monsters)
593 {
594 if (!m->alive())
595 continue;
596 coord_def landing = m->pos();
597
598 // XXX: this doesn't properly fire lua position triggers
599 m->apply_location_effects(landing);
600
601 // Dispersal will fire the location effects for everything dispersed;
602 // it's still possible for something to get blown somewhere it needs
603 // a location effect and not subsequently dispersed but handling that
604 // is more trouble than this headache already is.
605 if (m->pos() != landing)
606 {
607 did_disperse = true;
608 break;
609 }
610 }
611
612 if (player_affected && !did_disperse)
613 you.apply_location_effects(you.pos());
614 }
615
_phial_of_floods(dist * target)616 static bool _phial_of_floods(dist *target)
617 {
618 // TODO: code duplication with your_spells
619 if (you.confused())
620 {
621 canned_msg(MSG_TOO_CONFUSED);
622 return false;
623 }
624
625 dist target_local;
626 if (!target)
627 target = &target_local;
628 bolt beam;
629
630 if (you.confused())
631 {
632 canned_msg(MSG_TOO_CONFUSED);
633 return false;
634 }
635
636 const int base_pow = 10 + you.skill(SK_EVOCATIONS, 4); // placeholder?
637 zappy(ZAP_PRIMAL_WAVE, base_pow, false, beam);
638 beam.range = LOS_RADIUS;
639 beam.aimed_at_spot = true;
640
641 // TODO: this needs a custom targeter
642 direction_chooser_args args;
643 args.mode = TARG_HOSTILE;
644 args.top_prompt = "Aim the phial where?";
645
646 if (spell_direction(*target, beam, &args)
647 && player_tracer(ZAP_PRIMAL_WAVE, base_pow, beam))
648 {
649 const int power = player_adjust_evoc_power(base_pow);
650 // use real power to recalc hit/dam
651 zappy(ZAP_PRIMAL_WAVE, power, false, beam);
652 beam.fire();
653
654 return true;
655 }
656
657 return false;
658 }
659
_phantom_mirror(dist * target)660 static spret _phantom_mirror(dist *target)
661 {
662 bolt beam;
663 monster* victim = nullptr;
664 dist target_local;
665 if (!target)
666 target = &target_local;
667
668 targeter_smite tgt(&you, LOS_RADIUS, 0, 0);
669
670 direction_chooser_args args;
671 args.restricts = DIR_TARGET;
672 args.needs_path = false;
673 args.self = confirm_prompt_type::cancel;
674 args.top_prompt = "Aiming: <white>Phantom Mirror</white>";
675 args.hitfunc = &tgt;
676 if (!spell_direction(*target, beam, &args))
677 return spret::abort;
678 victim = monster_at(beam.target);
679 if (!victim || !you.can_see(*victim))
680 {
681 if (beam.target == you.pos())
682 mpr("You can't use the mirror on yourself.");
683 else
684 mpr("You can't see anything there to clone.");
685 return spret::abort;
686 }
687
688 // Mirrored monsters (including by Mara, rakshasas) can still be
689 // re-reflected.
690 if (!actor_is_illusion_cloneable(victim)
691 && !victim->has_ench(ENCH_PHANTOM_MIRROR))
692 {
693 mpr("The mirror can't reflect that.");
694 return spret::abort;
695 }
696
697 monster* mon = clone_mons(victim, true, nullptr, ATT_FRIENDLY);
698 if (!mon)
699 {
700 canned_msg(MSG_NOTHING_HAPPENS);
701 return spret::fail;
702 }
703 const int power = player_adjust_evoc_power(5 + you.skill(SK_EVOCATIONS, 3));
704 int dur = min(6, max(1,
705 player_adjust_evoc_power(
706 you.skill(SK_EVOCATIONS, 1) / 4 + 1)
707 * (100 - victim->check_willpower(power)) / 100));
708
709 mon->mark_summoned(dur, true, SPELL_PHANTOM_MIRROR);
710
711 mon->summoner = MID_PLAYER;
712 mons_add_blame(mon, "mirrored by the player character");
713 mon->add_ench(ENCH_PHANTOM_MIRROR);
714 mon->add_ench(mon_enchant(ENCH_DRAINED,
715 div_rand_round(mon->get_experience_level(), 3),
716 &you, INFINITE_DURATION));
717
718 mon->behaviour = BEH_SEEK;
719 set_nearest_monster_foe(mon);
720
721 mprf("You reflect %s with the mirror!",
722 victim->name(DESC_THE).c_str());
723
724 return spret::success;
725 }
726
_valid_tremorstone_target(const monster & m)727 static bool _valid_tremorstone_target(const monster &m)
728 {
729 return !mons_is_firewood(m)
730 && !god_protects(&m)
731 && !always_shoot_through_monster(&you, m);
732 }
733
734 /**
735 * Find the cell at range 3 closest to the center of mass of monsters in range,
736 * or a random range 3 cell if there are none.
737 *
738 * @param see_targets a boolean parameter indicating if the user can see any of
739 * the targets
740 * @return The cell in question.
741 */
_find_tremorstone_target(bool & see_targets)742 static coord_def _find_tremorstone_target(bool& see_targets)
743 {
744 coord_def com = {0, 0};
745 see_targets = false;
746 int num = 0;
747
748 for (radius_iterator ri(you.pos(), LOS_NO_TRANS); ri; ++ri)
749 {
750 if (monster_at(*ri) && _valid_tremorstone_target(*monster_at(*ri)))
751 {
752 com += *ri;
753 see_targets = see_targets || you.can_see(*monster_at(*ri));
754 ++num;
755 }
756 }
757
758 coord_def target = {0, 0};
759 int distance = LOS_RADIUS * num;
760 int ties = 0;
761
762 for (radius_iterator ri(you.pos(), 3, C_SQUARE, LOS_NO_TRANS, true); ri; ++ri)
763 {
764 if (ri->distance_from(you.pos()) != 3 || cell_is_solid(*ri))
765 continue;
766
767 if (num > 0)
768 {
769 if (com.distance_from((*ri) * num) < distance)
770 {
771 ties = 1;
772 target = *ri;
773 distance = com.distance_from((*ri) * num);
774 }
775 else if (com.distance_from((*ri) * num) == distance
776 && one_chance_in(++ties))
777 {
778 target = *ri;
779 }
780 }
781 else if (one_chance_in(++ties))
782 target = *ri;
783 }
784
785 return target;
786 }
787
788 /**
789 * Find an adjacent tile for a tremorstone explosion to go off in.
790 *
791 * @param center The original target of the stone.
792 * @return The new, final origin of the stone's explosion.
793 */
_fuzz_tremorstone_target(coord_def center)794 static coord_def _fuzz_tremorstone_target(coord_def center)
795 {
796 coord_def chosen = center;
797 int seen = 1;
798 for (adjacent_iterator ai(center); ai; ++ai)
799 if (!cell_is_solid(*ai) && one_chance_in(++seen))
800 chosen = *ai;
801 return chosen;
802 }
803
804 /**
805 * Number of explosions, scales up from 1 at 0 evo to 6 at 27 evo,
806 * via a stepdown.
807 *
808 * Currently pow is just evo + 15, but the abstraction is kept around in
809 * case an evocable enhancer returns to the game so that 0 evo with enhancer
810 * gets some amount of enhancement.
811 */
_tremorstone_count(int pow)812 static int _tremorstone_count(int pow)
813 {
814 return 1 + stepdown((pow - 15) / 3, 2, ROUND_CLOSE);
815 }
816
817 /**
818 * Evokes a tremorstone, blasting something in the general area of a
819 * chosen target.
820 *
821 * @return spret::abort if the player cancels, spret::fail if they
822 * try to evoke but fail, and spret::success otherwise.
823 */
_tremorstone()824 static spret _tremorstone()
825 {
826 if (you.confused())
827 {
828 canned_msg(MSG_TOO_CONFUSED);
829 return spret::abort;
830 }
831
832 bool see_target;
833 bolt beam;
834
835 static const int RADIUS = 2;
836 static const int SPREAD = 1;
837 static const int RANGE = RADIUS + SPREAD;
838 const int pow = 15 + you.skill(SK_EVOCATIONS);
839 const int adjust_pow = player_adjust_evoc_power(pow);
840 const int num_explosions = _tremorstone_count(adjust_pow);
841
842 beam.source_id = MID_PLAYER;
843 beam.thrower = KILL_YOU;
844 zappy(ZAP_TREMORSTONE, pow, false, beam);
845 beam.range = RANGE;
846 beam.ex_size = RADIUS;
847 beam.target = _find_tremorstone_target(see_target);
848
849 targeter_radius hitfunc(&you, LOS_NO_TRANS);
850 auto vulnerable = [](const actor *act) -> bool
851 {
852 return act && _valid_tremorstone_target(*act->as_monster());
853 };
854 if ((!see_target
855 && !yesno("You can't see anything, release a tremorstone anyway?",
856 true, 'n'))
857 || stop_attack_prompt(hitfunc, "release a tremorstone", vulnerable))
858 {
859 return spret::abort;
860 }
861
862 mpr("The tremorstone explodes into fragments!");
863 const coord_def center = beam.target;
864
865 for (int i = 0; i < num_explosions; i++)
866 {
867 bolt explosion = beam;
868 explosion.target = _fuzz_tremorstone_target(center);
869 explosion.explode(i == num_explosions - 1);
870 }
871
872 return spret::success;
873 }
874
875 static const vector<random_pick_entry<cloud_type>> condenser_clouds =
876 {
877 { 0, 50, 200, FALL, CLOUD_MEPHITIC },
878 { 0, 100, 125, PEAK, CLOUD_FIRE },
879 { 0, 100, 125, PEAK, CLOUD_COLD },
880 { 0, 100, 125, PEAK, CLOUD_POISON },
881 { 0, 110, 50, RISE, CLOUD_NEGATIVE_ENERGY },
882 { 0, 110, 50, RISE, CLOUD_STORM },
883 { 0, 110, 50, RISE, CLOUD_ACID },
884 };
885
_condenser()886 static spret _condenser()
887 {
888 if (you.confused())
889 {
890 canned_msg(MSG_TOO_CONFUSED);
891 return spret::abort;
892 }
893
894 if (env.level_state & LSTATE_STILL_WINDS)
895 {
896 mpr("The air is too still to form clouds.");
897 return spret::abort;
898 }
899
900 const int pow = 15 + you.skill(SK_EVOCATIONS, 7) / 2;
901 const int adjust_pow = min(110,player_adjust_evoc_power(pow));
902
903 random_picker<cloud_type, NUM_CLOUD_TYPES> cloud_picker;
904 cloud_type cloud = cloud_picker.pick(condenser_clouds, adjust_pow, CLOUD_NONE);
905
906 vector<coord_def> target_cells;
907 bool see_targets = false;
908
909 for (radius_iterator di(you.pos(), LOS_NO_TRANS); di; ++di)
910 {
911 monster *mons = monster_at(*di);
912
913 if (!mons || mons->wont_attack() || !mons_is_threatening(*mons))
914 continue;
915
916 if (you.can_see(*mons))
917 see_targets = true;
918
919 for (adjacent_iterator ai(mons->pos(), false); ai; ++ai)
920 {
921 actor * act = actor_at(*ai);
922 if (!cell_is_solid(*ai) && you.see_cell(*ai) && !cloud_at(*ai)
923 && !(act && act->wont_attack()))
924 {
925 target_cells.push_back(*ai);
926 }
927 }
928 }
929
930 if (!see_targets
931 && !yesno("You can't see anything. Try to condense clouds anyway?",
932 true, 'n'))
933 {
934 canned_msg(MSG_OK);
935 return spret::abort;
936 }
937
938 if (target_cells.empty())
939 {
940 canned_msg(MSG_NOTHING_HAPPENS);
941 return spret::fail;
942 }
943
944 for (auto p : target_cells)
945 {
946 const int cloud_power = 5
947 + random2avg(12 + div_rand_round(adjust_pow * 3, 4), 3);
948 place_cloud(cloud, p, cloud_power, &you);
949 }
950
951 mprf("Clouds of %s condense around you!", cloud_type_name(cloud).c_str());
952
953 return spret::success;
954 }
955
_xoms_chessboard()956 static bool _xoms_chessboard()
957 {
958 if (you.confused())
959 {
960 canned_msg(MSG_TOO_CONFUSED);
961 return false;
962 }
963
964 vector<monster *> targets;
965 bool see_target = false;
966
967 for (monster_near_iterator mi(&you, LOS_NO_TRANS); mi; ++mi)
968 {
969 if (mi->friendly() || mi->neutral())
970 continue;
971 if (mons_is_firewood(**mi))
972 continue;
973 if (you.can_see(**mi))
974 see_target = true;
975
976 targets.emplace_back(*mi);
977 }
978
979 if (!see_target
980 && !yesno("You can't see anything. Try to make a move anyway?",
981 true, 'n'))
982 {
983 canned_msg(MSG_OK);
984 return false;
985 }
986
987 const int power =
988 player_adjust_evoc_power(15 + you.skill(SK_EVOCATIONS, 7) / 2);
989
990 mpr("You make a move on Xom's chessboard...");
991
992 if (targets.empty())
993 {
994 canned_msg(MSG_NOTHING_HAPPENS);
995 return true;
996 }
997
998 bolt beam;
999 const monster * target = *random_iterator(targets);
1000 beam.source = target->pos();
1001 beam.target = target->pos();
1002
1003 // List of possible effects. Mostly debuffs, a few buffs to keep it
1004 // exciting
1005 zap_type zap = random_choose_weighted(5, ZAP_HASTE,
1006 5, ZAP_INVISIBILITY,
1007 5, ZAP_MIGHT,
1008 10, ZAP_CORONA,
1009 15, ZAP_SLOW,
1010 15, ZAP_MALMUTATE,
1011 15, ZAP_PETRIFY,
1012 10, ZAP_PARALYSE,
1013 10, ZAP_CONFUSE,
1014 10, ZAP_SLEEP);
1015 beam.origin_spell = SPELL_NO_SPELL; // let zapping reset this
1016
1017 return zapping(zap, power, beam, false) == spret::success;
1018 }
1019
1020
1021 // Is there anything that would prevent a player from evoking?
1022 // If slot == -1, it asks this question in general.
1023 // If slot is a particular item, it asks this question for that item. This
1024 // wierdly does not check whether an item is actually evokable! (TODO)
evoke_check(int slot,bool quiet)1025 bool evoke_check(int slot, bool quiet)
1026 {
1027 item_def *i = nullptr;
1028 if (slot >= 0 && slot < ENDOFPACK && you.inv[slot].defined())
1029 i = &you.inv[slot];
1030
1031 if (i && item_type_removed(i->base_type, i->sub_type))
1032 {
1033 if (!quiet)
1034 mpr("Sorry, this item was removed!");
1035 return false;
1036 }
1037
1038 // TODO: are these reaching checks necessary any more?
1039 // is slot a wielded reaching weapon, or if no slot, is the player wielding
1040 // a reaching weapon?
1041 const bool wielded = you.weapon()
1042 && (slot != -1 && slot == you.equip[EQ_WEAPON]
1043 || slot == -1 && you.equip[EQ_WEAPON] >= 0);
1044 const bool reaching = wielded
1045 && weapon_reach(*you.weapon()) > REACH_NONE;
1046
1047 // ammo checks are done below, this is the precondition for messaging
1048 // about ranged failures
1049 const bool ranged = wielded && is_range_weapon(*you.weapon());
1050
1051 if ((reaching || ranged) && you.melded[EQ_WEAPON])
1052 {
1053 if (!quiet)
1054 canned_msg(MSG_PRESENT_FORM);
1055 return false;
1056 }
1057
1058 if (you.berserk() && !reaching)
1059 {
1060 if (!quiet)
1061 canned_msg(MSG_TOO_BERSERK);
1062 return false;
1063 }
1064 if (you.confused() && !ranged) // attack is ok under confusion, but not reaching
1065 {
1066 if (!quiet)
1067 canned_msg(MSG_TOO_CONFUSED);
1068 return false;
1069 }
1070 if (ranged && i && (you.launcher_action.is_empty()
1071 || !you.launcher_action.get()->is_valid()))
1072 {
1073 if (!quiet)
1074 {
1075 // XX messaging should be unified with actual launching code
1076 mprf("You do not have any ammo quivered for %s.",
1077 you.weapon()->name(DESC_YOUR).c_str());
1078 }
1079 return false;
1080 }
1081 if (reaching || ranged)
1082 return true;
1083
1084 // is this supposed to be allowed under confusion?
1085 if (i && i->base_type == OBJ_MISCELLANY && i->sub_type == MISC_ZIGGURAT)
1086 return true;
1087
1088 if (!i)
1089 {
1090 // does the player have a zigfig? overrides sac artiface
1091 // this is ugly...this thing should probably be goldified
1092 for (const item_def &s : you.inv)
1093 if (s.defined() && s.base_type == OBJ_MISCELLANY
1094 && s.sub_type == MISC_ZIGGURAT)
1095 {
1096 return true;
1097 }
1098 }
1099
1100 #if TAG_MAJOR_VERSION == 34
1101 if (player_under_penance(GOD_PAKELLAS))
1102 {
1103 if (!quiet)
1104 {
1105 simple_god_message("'s wrath prevents you from evoking devices!",
1106 GOD_PAKELLAS);
1107 }
1108 return false;
1109 }
1110 #endif
1111
1112 if (you.get_mutation_level(MUT_NO_ARTIFICE))
1113 {
1114 if (!quiet)
1115 mpr("You cannot evoke magical items.");
1116 return false;
1117 }
1118
1119 if (i && i->base_type == OBJ_WANDS && i->charges <= 0)
1120 {
1121 // I think this case should be obsolete? Maybe still could happen with
1122 // an upgrade
1123 if (!quiet)
1124 mpr("This wand has no charges.");
1125 return false;
1126 }
1127
1128 if (i && is_xp_evoker(*i) && evoker_charges(i->sub_type) <= 0)
1129 {
1130 // DESC_THE prints "The tin of tremorstones (inert) is presently inert."
1131 if (!quiet)
1132 mprf("The %s is presently inert.", i->name(DESC_DBNAME).c_str());
1133 return false;
1134 }
1135
1136 return true;
1137 }
1138
evoke_item(int slot,dist * preselect)1139 bool evoke_item(int slot, dist *preselect)
1140 {
1141 if (!evoke_check(slot))
1142 return false;
1143
1144 if (slot == -1)
1145 {
1146 slot = prompt_invent_item("Evoke which item? (* to show all)",
1147 menu_type::invlist,
1148 OSEL_EVOKABLE, OPER_EVOKE);
1149
1150 if (prompt_failed(slot))
1151 return false;
1152 }
1153 else if (!check_warning_inscriptions(you.inv[slot], OPER_EVOKE))
1154 return false;
1155
1156 ASSERT(slot >= 0);
1157
1158 #ifdef ASSERTS // Used only by an assert
1159 const bool wielded = (you.equip[EQ_WEAPON] == slot);
1160 #endif /* DEBUG */
1161
1162 item_def& item = you.inv[slot];
1163 // Also handles messages.
1164 if (!item_is_evokable(item, true, false, true) || !evoke_check(slot))
1165 return false;
1166
1167 bool did_work = false; // "Nothing happens" message
1168 bool unevokable = false;
1169
1170 const unrandart_entry *entry = is_unrandom_artefact(item)
1171 ? get_unrand_entry(item.unrand_idx) : nullptr;
1172
1173 if (entry && (entry->evoke_func || entry->targeted_evoke_func))
1174 {
1175 ASSERT(item_is_equipped(item));
1176
1177 bool qret;
1178 // only use one of these, prioritizing the targeted version. In
1179 // principle we could call them both?
1180 ASSERT(!(entry->evoke_func && entry->targeted_evoke_func)); // probably should be in art-data.pl
1181 if (entry->targeted_evoke_func)
1182 qret = entry->targeted_evoke_func(&item, &did_work, &unevokable, preselect);
1183 else
1184 qret = entry->evoke_func(&item, &did_work, &unevokable);
1185
1186 if (!unevokable)
1187 count_action(CACT_EVOKE, item.unrand_idx);
1188
1189 // what even _is_ this return value?
1190 if (qret)
1191 return did_work;
1192 }
1193 else switch (item.base_type)
1194 {
1195 case OBJ_WANDS:
1196 zap_wand(slot, preselect);
1197 return true;
1198
1199 case OBJ_WEAPONS:
1200 {
1201 ASSERT(wielded);
1202 dist targ_local;
1203 if (!preselect)
1204 preselect = &targ_local;
1205
1206 quiver::get_primary_action()->trigger(*preselect);
1207 return you.turn_is_over;
1208 }
1209
1210 case OBJ_MISCELLANY:
1211 did_work = true; // easier to do it this way for misc items
1212
1213 switch (item.sub_type)
1214 {
1215 #if TAG_MAJOR_VERSION == 34
1216 case MISC_BOTTLED_EFREET:
1217 canned_msg(MSG_NOTHING_HAPPENS);
1218 return false;
1219
1220 case MISC_FAN_OF_GALES:
1221 canned_msg(MSG_NOTHING_HAPPENS);
1222 return false;
1223
1224 case MISC_LAMP_OF_FIRE:
1225 canned_msg(MSG_NOTHING_HAPPENS);
1226 return false;
1227
1228 case MISC_STONE_OF_TREMORS:
1229 canned_msg(MSG_NOTHING_HAPPENS);
1230 return false;
1231 #endif
1232
1233 case MISC_PHIAL_OF_FLOODS:
1234 if (_phial_of_floods(preselect))
1235 {
1236 expend_xp_evoker(item.sub_type);
1237 practise_evoking(3);
1238 }
1239 else
1240 return false;
1241 break;
1242
1243 case MISC_HORN_OF_GERYON:
1244 if (_evoke_horn_of_geryon())
1245 {
1246 expend_xp_evoker(item.sub_type);
1247 practise_evoking(3);
1248 }
1249 else
1250 return false;
1251 break;
1252
1253 case MISC_BOX_OF_BEASTS:
1254 if (_box_of_beasts())
1255 {
1256 expend_xp_evoker(item.sub_type);
1257 if (!evoker_charges(item.sub_type))
1258 mpr("The box is emptied!");
1259 practise_evoking(1);
1260 }
1261 break;
1262
1263 #if TAG_MAJOR_VERSION == 34
1264 case MISC_SACK_OF_SPIDERS:
1265 canned_msg(MSG_NOTHING_HAPPENS);
1266 return false;
1267
1268 case MISC_CRYSTAL_BALL_OF_ENERGY:
1269 canned_msg(MSG_NOTHING_HAPPENS);
1270 return false;
1271 #endif
1272
1273 case MISC_LIGHTNING_ROD:
1274 if (_lightning_rod(preselect))
1275 {
1276 practise_evoking(1);
1277 expend_xp_evoker(item.sub_type);
1278 if (!evoker_charges(item.sub_type))
1279 mpr("The lightning rod overheats!");
1280 }
1281 else
1282 return false;
1283 break;
1284
1285 case MISC_QUAD_DAMAGE:
1286 mpr("QUAD DAMAGE!");
1287 you.duration[DUR_QUAD_DAMAGE] = 30 * BASELINE_DELAY;
1288 ASSERT(in_inventory(item));
1289 dec_inv_item_quantity(item.link, 1);
1290 invalidate_agrid(true);
1291 break;
1292
1293 case MISC_PHANTOM_MIRROR:
1294 switch (_phantom_mirror(preselect))
1295 {
1296 default:
1297 case spret::abort:
1298 return false;
1299
1300 case spret::success:
1301 expend_xp_evoker(item.sub_type);
1302 if (!evoker_charges(item.sub_type))
1303 mpr("The mirror clouds!");
1304 // deliberate fall-through
1305 case spret::fail:
1306 practise_evoking(1);
1307 break;
1308 }
1309 break;
1310
1311 case MISC_ZIGGURAT:
1312 // Don't set did_work to false, _make_zig handles the message.
1313 unevokable = !_make_zig(item);
1314 break;
1315
1316 case MISC_TIN_OF_TREMORSTONES:
1317 switch (_tremorstone())
1318 {
1319 default:
1320 case spret::abort:
1321 return false;
1322
1323 case spret::success:
1324 expend_xp_evoker(item.sub_type);
1325 if (!evoker_charges(item.sub_type))
1326 mpr("The tin is emptied!");
1327 case spret::fail:
1328 practise_evoking(1);
1329 break;
1330 }
1331 break;
1332
1333 case MISC_CONDENSER_VANE:
1334 switch (_condenser())
1335 {
1336 default:
1337 case spret::abort:
1338 return false;
1339
1340 case spret::success:
1341 expend_xp_evoker(item.sub_type);
1342 if (!evoker_charges(item.sub_type))
1343 mpr("The condenser dries out!");
1344 case spret::fail:
1345 practise_evoking(1);
1346 break;
1347 }
1348 break;
1349
1350 case MISC_XOMS_CHESSBOARD:
1351 if (_xoms_chessboard())
1352 {
1353 expend_xp_evoker(item.sub_type);
1354 if (!evoker_charges(item.sub_type))
1355 mpr("The chess piece greys!");
1356 practise_evoking(1);
1357 }
1358 else
1359 return false;
1360 break;
1361
1362 default:
1363 did_work = false;
1364 unevokable = true;
1365 break;
1366 }
1367 if (did_work && !unevokable)
1368 count_action(CACT_EVOKE, item.sub_type, OBJ_MISCELLANY);
1369 break;
1370
1371 default:
1372 unevokable = true;
1373 break;
1374 }
1375
1376 if (!did_work)
1377 canned_msg(MSG_NOTHING_HAPPENS);
1378
1379 if (!unevokable)
1380 you.turn_is_over = true;
1381 else
1382 crawl_state.zero_turns_taken();
1383
1384 return did_work;
1385 }
1386