Use cell_is_solid where appropriate.
[crawl:crawl.git] / crawl-ref / source / target.cc
1 #include "AppHdr.h"
2
3 #include "target.h"
4
5 #include "beam.h"
6 #include "coord.h"
7 #include "coordit.h"
8 #include "env.h"
9 #include "fight.h"
10 #include "godabil.h"
11 #include "itemprop.h"
12 #include "libutil.h"
13 #include "losglobal.h"
14 #include "player.h"
15 #include "spl-damage.h"
16 #include "terrain.h"
17
18 #define notify_fail(x) (why_not = (x), false)
19
20 static string _wallmsg(coord_def c)
21 {
22     ASSERT(map_bounds(c)); // there'd be an information leak
23     const char *wall = feat_type_name(grd(c));
24     return "There is " + article_a(wall) + " there.";
25 }
26
27 bool targetter::set_aim(coord_def a)
28 {
29     // This matches a condition in direction_chooser::move_is_ok().
30     if (agent && !cell_see_cell(agent->pos(), a, LOS_NO_TRANS))
31         return false;
32
33     aim = a;
34     return true;
35 }
36
37 bool targetter::can_affect_outside_range()
38 {
39     return false;
40 }
41
42 bool targetter::can_affect_walls()
43 {
44     return false;
45 }
46
47 bool targetter::anyone_there(coord_def loc)
48 {
49     if (!map_bounds(loc))
50         return false;
51     if (agent && agent->is_player())
52         return env.map_knowledge(loc).monsterinfo();
53     return actor_at(loc);
54 }
55
56 bool targetter::has_additional_sites(coord_def loc)
57 {
58     return false;
59 }
60
61 targetter_beam::targetter_beam(const actor *act, int range, zap_type zap,
62                                int pow, int min_ex_rad, int max_ex_rad) :
63                                min_expl_rad(min_ex_rad),
64                                max_expl_rad(max_ex_rad)
65 {
66     ASSERT(act);
67     ASSERT(min_ex_rad >= 0);
68     ASSERT(max_ex_rad >= 0);
69     ASSERT(max_ex_rad >= min_ex_rad);
70     agent = act;
71     beam.set_agent(const_cast<actor *>(act));
72     origin = aim = act->pos();
73     beam.attitude = ATT_FRIENDLY;
74     zappy(zap, pow, beam);
75     beam.is_tracer = true;
76     beam.is_targetting = true;
77     beam.range = range;
78     beam.source = origin;
79     beam.target = aim;
80     beam.dont_stop_player = true;
81     beam.friend_info.dont_stop = true;
82     beam.foe_info.dont_stop = true;
83     beam.ex_size = min_ex_rad;
84     beam.aimed_at_spot = true;
85
86     penetrates_targets = beam.is_beam;
87     range2 = dist_range(range);
88 }
89
90 bool targetter_beam::set_aim(coord_def a)
91 {
92     if (!targetter::set_aim(a))
93         return false;
94
95     bolt tempbeam = beam;
96
97     tempbeam.target = aim;
98     tempbeam.path_taken.clear();
99     tempbeam.fire();
100     path_taken = tempbeam.path_taken;
101
102     if (max_expl_rad > 0)
103     {
104         bolt tempbeam2 = beam;
105         tempbeam2.target = origin;
106         for (vector<coord_def>::const_iterator i = path_taken.begin();
107              i != path_taken.end(); ++i)
108         {
109             if (cell_is_solid(*i)
110                 && tempbeam.affects_wall(grd(*i)) != MB_TRUE)
111                 break;
112             tempbeam2.target = *i;
113             if (anyone_there(*i)
114                 && !tempbeam.ignores_monster(monster_at(*i)))
115             {
116                 break;
117             }
118         }
119         tempbeam2.use_target_as_pos = true;
120         exp_map_min.init(INT_MAX);
121         tempbeam2.determine_affected_cells(exp_map_min, coord_def(), 0,
122                                            min_expl_rad, true, true);
123         exp_map_max.init(INT_MAX);
124         tempbeam2.determine_affected_cells(exp_map_max, coord_def(), 0,
125                                            max_expl_rad, true, true);
126     }
127     return true;
128 }
129
130 bool targetter_beam::valid_aim(coord_def a)
131 {
132     if (a != origin && !cell_see_cell(origin, a, LOS_NO_TRANS))
133     {
134         if (agent->see_cell(a))
135             return notify_fail("There's something in the way.");
136         return notify_fail("You cannot see that place.");
137     }
138     if ((origin - a).abs() > range2)
139         return notify_fail("Out of range.");
140     return true;
141 }
142
143 bool targetter_beam::can_affect_outside_range()
144 {
145     // XXX is this everything?
146     return max_expl_rad > 0;
147 }
148
149 aff_type targetter_beam::is_affected(coord_def loc)
150 {
151     bool on_path = false;
152     coord_def c;
153     aff_type current = AFF_YES;
154     for (vector<coord_def>::const_iterator i = path_taken.begin();
155          i != path_taken.end(); ++i)
156     {
157         if (cell_is_solid(*i)
158             && beam.affects_wall(grd(*i)) != MB_TRUE
159             && max_expl_rad > 0)
160             break;
161
162         c = *i;
163         if (c == loc)
164         {
165             if (max_expl_rad > 0)
166                 on_path = true;
167             else if (cell_is_solid(*i))
168             {
169                 maybe_bool res = beam.affects_wall(grd(*i));
170                 if (res == MB_TRUE)
171                     return current;
172                 else if (res == MB_MAYBE)
173                     return AFF_MAYBE;
174                 else
175                     return AFF_NO;
176
177             }
178             else
179                 return current;
180         }
181         if (anyone_there(*i)
182             && !penetrates_targets
183             && !beam.ignores_monster(monster_at(*i)))
184         {
185             // We assume an exploding spell will always stop here.
186             if (max_expl_rad > 0)
187                 break;
188             current = AFF_MAYBE;
189         }
190     }
191     if (max_expl_rad > 0 && (loc - c).rdist() <= 9)
192     {
193         maybe_bool aff_wall = beam.affects_wall(grd(loc));
194         if (!cell_is_solid(loc) || aff_wall != MB_FALSE)
195         {
196             coord_def centre(9,9);
197             if (exp_map_min(loc - c + centre) < INT_MAX)
198                 return (!cell_is_solid(loc) || aff_wall == MB_TRUE)
199                        ? AFF_YES : AFF_MAYBE;
200             if (exp_map_max(loc - c + centre) < INT_MAX)
201                 return AFF_MAYBE;
202         }
203     }
204     return on_path ? AFF_TRACER : AFF_NO;
205 }
206
207 targetter_imb::targetter_imb(const actor *act, int pow, int range) :
208                targetter_beam(act, range, ZAP_ISKENDERUNS_MYSTIC_BLAST, pow, 0, 0)
209 {
210 }
211
212 bool targetter_imb::set_aim(coord_def a)
213 {
214     if (!targetter_beam::set_aim(a))
215         return false;
216
217     vector<coord_def> cur_path;
218
219     splash.clear();
220     splash2.clear();
221
222     coord_def end = path_taken[path_taken.size() - 1];
223
224     // IMB never splashes if you self-target.
225     if (end == origin)
226         return true;
227
228     coord_def c;
229     bool first = true;
230
231     for (vector<coord_def>::iterator i = path_taken.begin();
232          i != path_taken.end(); i++)
233     {
234         c = *i;
235         cur_path.push_back(c);
236         if (!(anyone_there(c)
237               && !beam.ignores_monster((monster_at(c))))
238             && c != end)
239             continue;
240
241         vector<coord_def> *which_splash = (first) ? &splash : &splash2;
242
243         for (adjacent_iterator ai(c); ai; ++ai)
244         {
245             if (!imb_can_splash(origin, c, cur_path, *ai))
246                 continue;
247
248             which_splash->push_back(*ai);
249             if (!cell_is_solid(*ai)
250                 && !(anyone_there(*ai)
251                      && !beam.ignores_monster(monster_at(*ai))))
252             {
253                 which_splash->push_back(c + (*ai - c) * 2);
254             }
255         }
256
257         first = false;
258     }
259
260     return true;
261 }
262
263 aff_type targetter_imb::is_affected(coord_def loc)
264 {
265     aff_type from_path = targetter_beam::is_affected(loc);
266     if (from_path != AFF_NO)
267         return from_path;
268
269     for (vector<coord_def>::const_iterator i = splash.begin();
270          i != splash.end(); ++i)
271     {
272         if (*i == loc)
273             return cell_is_solid(*i) ? AFF_NO : AFF_MAYBE;
274     }
275     for (vector<coord_def>::const_iterator i = splash2.begin();
276          i != splash2.end(); ++i)
277     {
278         if (*i == loc)
279             return cell_is_solid(*i) ? AFF_NO : AFF_TRACER;
280     }
281     return AFF_NO;
282 }
283
284 targetter_view::targetter_view()
285 {
286     origin = aim = you.pos();
287 }
288
289 bool targetter_view::valid_aim(coord_def a)
290 {
291     return true; // don't reveal map bounds
292 }
293
294 aff_type targetter_view::is_affected(coord_def loc)
295 {
296     if (loc == aim)
297         return AFF_YES;
298
299     return AFF_NO;
300 }
301
302
303 targetter_smite::targetter_smite(const actor* act, int ran,
304                                  int exp_min, int exp_max, bool wall_ok,
305                                  bool (*affects_pos_func)(const coord_def &)):
306     exp_range_min(exp_min), exp_range_max(exp_max), affects_walls(wall_ok),
307     affects_pos(affects_pos_func)
308 {
309     ASSERT(act);
310     ASSERT(exp_min >= 0);
311     ASSERT(exp_max >= 0);
312     ASSERT(exp_min <= exp_max);
313     agent = act;
314     origin = aim = act->pos();
315     range2 = dist_range(ran);
316 }
317
318 bool targetter_smite::valid_aim(coord_def a)
319 {
320     if (a != origin && !cell_see_cell(origin, a, LOS_NO_TRANS))
321     {
322         // Scrying/glass/tree/grate.
323         if (agent && agent->see_cell(a))
324             return notify_fail("There's something in the way.");
325         return notify_fail("You cannot see that place.");
326     }
327     if ((origin - a).abs() > range2)
328         return notify_fail("Out of range.");
329     if (!affects_walls && cell_is_solid(a))
330         return notify_fail(_wallmsg(a));
331     return true;
332 }
333
334 bool targetter_smite::set_aim(coord_def a)
335 {
336     if (!targetter::set_aim(a))
337         return false;
338
339     if (exp_range_max > 0)
340     {
341         coord_def centre(9,9);
342         bolt beam;
343         beam.target = a;
344         beam.use_target_as_pos = true;
345         exp_map_min.init(INT_MAX);
346         beam.determine_affected_cells(exp_map_min, coord_def(), 0,
347                                       exp_range_min, true, true);
348         exp_map_max.init(INT_MAX);
349         beam.determine_affected_cells(exp_map_max, coord_def(), 0,
350                                       exp_range_max, true, true);
351     }
352     return true;
353 }
354
355 bool targetter_smite::can_affect_outside_range()
356 {
357     // XXX is this everything?
358     return exp_range_max > 0;
359 }
360
361 aff_type targetter_smite::is_affected(coord_def loc)
362 {
363     if (!valid_aim(aim))
364         return AFF_NO;
365
366     if (affects_pos && !affects_pos(loc))
367         return AFF_NO;
368
369     if (loc == aim)
370         return AFF_YES;
371
372     if (exp_range_max <= 0)
373         return AFF_NO;
374
375     if ((loc - aim).rdist() > 9)
376         return AFF_NO;
377     coord_def centre(9,9);
378     if (exp_map_min(loc - aim + centre) < INT_MAX)
379         return AFF_YES;
380     if (exp_map_max(loc - aim + centre) < INT_MAX)
381         return AFF_MAYBE;
382
383     return AFF_NO;
384 }
385
386 targetter_fragment::targetter_fragment(const actor* act, int power, int ran) :
387     targetter_smite(act, ran, 1, 1, true, NULL),
388     pow(power)
389 {
390 }
391
392 bool targetter_fragment::valid_aim(coord_def a)
393 {
394     if (!targetter_smite::valid_aim(a))
395         return false;
396
397     bolt tempbeam;
398     bool temp;
399     if (!setup_fragmentation_beam(tempbeam, pow, agent, a, false,
400                                   true, true, NULL, temp, temp))
401     {
402         return notify_fail("You cannot affect that.");
403     }
404     return true;
405 }
406
407 bool targetter_fragment::set_aim(coord_def a)
408 {
409     if (!targetter::set_aim(a))
410         return false;
411
412     bolt tempbeam;
413     bool temp;
414
415     if (setup_fragmentation_beam(tempbeam, pow, agent, a, false,
416                                  false, true, NULL, temp, temp))
417     {
418         exp_range_min = tempbeam.ex_size;
419         setup_fragmentation_beam(tempbeam, pow, agent, a, false,
420                                  true, true, NULL, temp, temp);
421         exp_range_max = tempbeam.ex_size;
422     }
423     else
424     {
425         exp_range_min = exp_range_max = 0;
426         return false;
427     }
428
429     coord_def centre(9,9);
430     bolt beam;
431     beam.target = a;
432     beam.use_target_as_pos = true;
433     exp_map_min.init(INT_MAX);
434     beam.determine_affected_cells(exp_map_min, coord_def(), 0,
435                                   exp_range_min, false, false);
436     exp_map_max.init(INT_MAX);
437     beam.determine_affected_cells(exp_map_max, coord_def(), 0,
438                                   exp_range_max, false, false);
439
440     return true;
441 }
442
443 bool targetter_fragment::can_affect_walls()
444 {
445     return true;
446 }
447
448 targetter_reach::targetter_reach(const actor* act, reach_type ran) :
449     range(ran)
450 {
451     ASSERT(act);
452     agent = act;
453     origin = aim = act->pos();
454 }
455
456 bool targetter_reach::valid_aim(coord_def a)
457 {
458     if (!cell_see_cell(origin, a, LOS_DEFAULT))
459         return notify_fail("You cannot see that place.");
460     if (!agent->see_cell_no_trans(a))
461         return notify_fail("You can't get through.");
462
463     int dist = (origin - a).abs();
464
465     if (dist > range)
466         return notify_fail("You can't reach that far!");
467
468     return true;
469 }
470
471 aff_type targetter_reach::is_affected(coord_def loc)
472 {
473     if (!valid_aim(loc))
474         return AFF_NO;
475
476     if (loc == aim)
477         return AFF_YES;
478
479     if (((loc - origin) * 2 - (aim - origin)).abs() <= 1
480         && feat_is_reachable_past(grd(loc)))
481     {
482         return AFF_TRACER;
483     }
484
485     return AFF_NO;
486 }
487
488 targetter_cleave::targetter_cleave(const actor* act, coord_def target)
489 {
490     ASSERT(act);
491     agent = act;
492     origin = act->pos();
493     aim = target;
494     list<actor*> act_targets;
495     get_all_cleave_targets(act, target, act_targets);
496     while (!act_targets.empty())
497     {
498         targets.insert(act_targets.front()->pos());
499         act_targets.pop_front();
500     }
501 }
502
503 aff_type targetter_cleave::is_affected(coord_def loc)
504 {
505     return targets.count(loc) ? AFF_YES : AFF_NO;
506 }
507
508 targetter_cloud::targetter_cloud(const actor* act, int range,
509                                  int count_min, int count_max) :
510     cnt_min(count_min), cnt_max(count_max)
511 {
512     ASSERT(cnt_min > 0);
513     ASSERT(cnt_max > 0);
514     ASSERT(cnt_min <= cnt_max);
515     if (agent = act)
516         origin = aim = act->pos();
517     range2 = dist_range(range);
518 }
519
520 static bool _cloudable(coord_def loc)
521 {
522     return in_bounds(loc)
523            && !cell_is_solid(loc)
524            && env.cgrid(loc) == EMPTY_CLOUD;
525 }
526
527 bool targetter_cloud::valid_aim(coord_def a)
528 {
529     if (agent && (origin - a).abs() > range2)
530         return notify_fail("Out of range.");
531     if (!map_bounds(a)
532         || agent
533            && origin != a
534            && !cell_see_cell(origin, a, LOS_NO_TRANS))
535     {
536         // Scrying/glass/tree/grate.
537         if (agent && agent->see_cell(a))
538             return notify_fail("There's something in the way.");
539         return notify_fail("You cannot see that place.");
540     }
541     if (cell_is_solid(a))
542         return notify_fail(_wallmsg(a));
543     if (agent)
544     {
545         if (env.cgrid(a) != EMPTY_CLOUD)
546             return notify_fail("There's already a cloud there.");
547         ASSERT(_cloudable(a));
548     }
549     return true;
550 }
551
552 bool targetter_cloud::set_aim(coord_def a)
553 {
554     if (!targetter::set_aim(a))
555         return false;
556
557     seen.clear();
558     queue.clear();
559     queue.push_back(vector<coord_def>());
560
561     int placed = 0;
562     queue[0].push_back(a);
563
564     for (unsigned int d1 = 0; d1 < queue.size() && placed < cnt_max; d1++)
565     {
566         unsigned int to_place = queue[d1].size();
567         placed += to_place;
568
569         for (unsigned int i = 0; i < to_place; i++)
570         {
571             coord_def c = queue[d1][i];
572             for (adjacent_iterator ai(c); ai; ++ai)
573                 if (_cloudable(*ai) && seen.find(*ai) == seen.end())
574                 {
575                     unsigned int d2 = d1 + ((*ai - c).abs() == 1 ? 5 : 7);
576                     if (d2 >= queue.size())
577                         queue.resize(d2 + 1);
578                     queue[d2].push_back(*ai);
579                     seen[*ai] = AFF_TRACER;
580                 }
581
582             seen[c] = placed <= cnt_min ? AFF_YES : AFF_MAYBE;
583         }
584     }
585
586     return true;
587 }
588
589 bool targetter_cloud::can_affect_outside_range()
590 {
591     return true;
592 }
593
594 aff_type targetter_cloud::is_affected(coord_def loc)
595 {
596     if (!valid_aim(aim))
597         return AFF_NO;
598
599     map<coord_def, aff_type>::const_iterator it = seen.find(loc);
600     if (it == seen.end() || it->second <= 0) // AFF_TRACER is used privately
601         return AFF_NO;
602
603     return it->second;
604 }
605
606 targetter_splash::targetter_splash(const actor* act)
607 {
608     ASSERT(act);
609     agent = act;
610     origin = aim = act->pos();
611 }
612
613 bool targetter_splash::valid_aim(coord_def a)
614 {
615     if (agent && grid_distance(origin, a) > 1)
616         return notify_fail("Out of range.");
617     return true;
618 }
619
620 aff_type targetter_splash::is_affected(coord_def loc)
621 {
622     if (!valid_aim(aim) || !valid_aim(loc))
623         return AFF_NO;
624
625     if (loc == aim)
626         return AFF_YES;
627
628     // self-spit currently doesn't splash
629     if (aim == origin)
630         return AFF_NO;
631
632     // it splashes around only upon hitting someone
633     if (!anyone_there(aim))
634         return AFF_NO;
635
636     if (grid_distance(loc, aim) > 1)
637         return AFF_NO;
638
639     // you're safe from being splashed by own spit
640     if (loc == origin)
641         return AFF_NO;
642
643     return anyone_there(loc) ? AFF_YES : AFF_MAYBE;
644 }
645
646
647 targetter_los::targetter_los(const actor *act, los_type _los,
648                              int range, int range_max)
649 {
650     ASSERT(act);
651     agent = act;
652     origin = aim = act->pos();
653     los = _los;
654     range2 = range * range + 1;
655     if (!range_max)
656         range_max = range;
657     ASSERT(range_max >= range);
658     range_max2 = range_max * range_max + 1;
659 }
660
661 bool targetter_los::valid_aim(coord_def a)
662 {
663     if ((a - origin).abs() > range_max2)
664         return notify_fail("Out of range.");
665     // If this message ever becomes used, please improve it.  I did not
666     // bother adding complexity just for monsters and "hit allies" prompts
667     // which don't need it.
668     if (!is_affected(a))
669         return notify_fail("The effect is blocked.");
670     return true;
671 }
672
673 aff_type targetter_los::is_affected(coord_def loc)
674 {
675     if (loc == aim)
676         return AFF_YES;
677
678     if ((loc - origin).abs() > range_max2)
679         return AFF_NO;
680
681     if (!cell_see_cell(loc, origin, los))
682         return AFF_NO;
683
684     return (loc - origin).abs() > range_max2 ? AFF_MAYBE : AFF_YES;
685 }
686
687
688 targetter_thunderbolt::targetter_thunderbolt(const actor *act, int r,
689                                              coord_def _prev)
690 {
691     ASSERT(act);
692     agent = act;
693     origin = act->pos();
694     prev = _prev;
695     aim = prev.origin() ? origin : prev;
696     ASSERT_RANGE(r, 1 + 1, you.current_vision + 1);
697     range2 = sqr(r) + 1;
698 }
699
700 bool targetter_thunderbolt::valid_aim(coord_def a)
701 {
702     if (a != origin && !cell_see_cell(origin, a, LOS_NO_TRANS))
703     {
704         // Scrying/glass/tree/grate.
705         if (agent->see_cell(a))
706             return notify_fail("There's something in the way.");
707         return notify_fail("You cannot see that place.");
708     }
709     if ((origin - a).abs() > range2)
710         return notify_fail("Out of range.");
711     return true;
712 }
713
714 static void _make_ray(ray_def &ray, coord_def a, coord_def b)
715 {
716     // Like beams, we need to allow picking the "better" ray if one is blocked
717     // by a wall.
718     if (!find_ray(a, b, ray, opc_solid_see))
719         ray = ray_def(geom::ray(a.x + 0.5, a.y + 0.5, b.x + 0.5, b.y + 0.5));
720 }
721
722 static bool left_of(coord_def a, coord_def b)
723 {
724     return a.x * b.y > a.y * b.x;
725 }
726
727 bool targetter_thunderbolt::set_aim(coord_def a)
728 {
729     aim = a;
730     zapped.clear();
731
732     if (a == origin)
733         return false;
734
735     arc_length.init(0);
736
737     ray_def ray;
738     coord_def p; // ray.pos() does lots of processing, cache it
739
740     // For consistency with beams, we need to
741     _make_ray(ray, origin, aim);
742     bool hit = true;
743     while ((origin - (p = ray.pos())).abs() <= range2)
744     {
745         if (!map_bounds(p) || opc_solid_see(p) >= OPC_OPAQUE)
746             hit = false;
747         if (hit && p != origin && zapped[p] <= 0)
748         {
749             zapped[p] = AFF_YES;
750             arc_length[origin.range(p)]++;
751         }
752         ray.advance();
753     }
754
755     if (prev.origin())
756         return true;
757
758     _make_ray(ray, origin, prev);
759     hit = true;
760     while ((origin - (p = ray.pos())).abs() <= range2)
761     {
762         if (!map_bounds(p) || opc_solid_see(p) >= OPC_OPAQUE)
763             hit = false;
764         if (hit && p != origin && zapped[p] <= 0)
765         {
766             zapped[p] = AFF_MAYBE; // fully affected, we just want to highlight cur
767             arc_length[origin.range(p)]++;
768         }
769         ray.advance();
770     }
771
772     coord_def a1 = prev - origin;
773     coord_def a2 = aim - origin;
774     if (left_of(a2, a1))
775         swapv(a1, a2);
776
777     for (int x = -LOS_RADIUS; x <= LOS_RADIUS; ++x)
778         for (int y = -LOS_RADIUS; y <= LOS_RADIUS; ++y)
779         {
780             if (sqr(x) + sqr(y) > range2)
781                 continue;
782             coord_def r(x, y);
783             if (left_of(a1, r) && left_of(r, a2))
784             {
785                 (p = r) += origin;
786                 if (zapped.find(p) == zapped.end())
787                     arc_length[r.range()]++;
788                 if (zapped[p] <= 0 && cell_see_cell(origin, p, LOS_NO_TRANS))
789                     zapped[p] = AFF_MAYBE;
790             }
791         }
792
793     zapped[origin] = AFF_NO;
794
795     return true;
796 }
797
798 aff_type targetter_thunderbolt::is_affected(coord_def loc)
799 {
800     if (loc == aim)
801         return zapped[loc] ? AFF_YES : AFF_TRACER;
802
803     if ((loc - origin).abs() > range2)
804         return AFF_NO;
805
806     return zapped[loc];
807 }
808
809 targetter_spray::targetter_spray(const actor* act, int range, zap_type zap)
810 {
811     ASSERT(act);
812     agent = act;
813     origin = aim = act->pos();
814     _range = range;
815     range2 = dist_range(range);
816 }
817
818 bool targetter_spray::valid_aim(coord_def a)
819 {
820     if (a != origin && !cell_see_cell(origin, a, LOS_NO_TRANS))
821     {
822         if (agent->see_cell(a))
823             return notify_fail("There's something in the way.");
824         return notify_fail("You cannot see that place.");
825     }
826     if ((origin - a).abs() > range2)
827         return notify_fail("Out of range.");
828     return true;
829 }
830
831 bool targetter_spray::set_aim(coord_def a)
832 {
833     if (!targetter::set_aim(a))
834         return false;
835
836     if (a == origin)
837         return false;
838
839     beams = get_spray_rays(agent, aim, _range, 3);
840
841     paths_taken.clear();
842     for (unsigned int i = 0; i < beams.size(); ++i)
843         paths_taken.push_back(beams[i].path_taken);
844
845     return true;
846 }
847
848 aff_type targetter_spray::is_affected(coord_def loc)
849 {
850     coord_def c;
851     aff_type affected = AFF_NO;
852
853     for (unsigned int n = 0; n < paths_taken.size(); ++n)
854     {
855         aff_type beam_affect = AFF_YES;
856         bool beam_reached = false;
857         for (vector<coord_def>::const_iterator i = paths_taken[n].begin();
858          i != paths_taken[n].end(); ++i)
859         {
860             c = *i;
861             if (c == loc)
862             {
863                 if (cell_is_solid(*i))
864                     beam_affect = AFF_NO;
865                 else if (beam_affect != AFF_MAYBE)
866                     beam_affect = AFF_YES;
867
868                 beam_reached = true;
869                 break;
870             }
871             else if (anyone_there(*i)
872                 && !beams[n].ignores_monster(monster_at(*i)))
873             {
874                 beam_affect = AFF_MAYBE;
875             }
876         }
877
878         if (beam_reached && beam_affect > affected)
879             affected = beam_affect;
880     }
881
882     return affected;
883 }
884
885 targetter_jump::targetter_jump(const actor* act, int range)
886 {
887     ASSERT(act);
888     agent = act;
889     origin = act->pos();
890     range2 = dist_range(range);
891     jump_is_blocked = false;
892 }
893
894 bool targetter_jump::valid_aim(coord_def a)
895 {
896     coord_def c, jump_pos;
897     ray_def ray;
898
899     if (origin == a)
900         return notify_fail("You cannot jump-attack yourself.");
901     else if ((origin - a).abs() > range2)
902         return notify_fail("Out of range.");
903     else if (!cell_see_cell(origin, a, LOS_NO_TRANS))
904     {
905         if (agent->see_cell(a))
906             return notify_fail("There's something in the way.");
907         else
908             return notify_fail("You cannot see that place.");
909     }
910     else if (cell_is_solid(a))
911         return notify_fail("There's something in the way.");
912     else if (!find_ray(agent->pos(), a, ray, opc_solid_see))
913         return notify_fail("There's something in the way.");
914     else if (!has_additional_sites(a))
915     {
916         switch (no_landing_reason)
917         {
918         case BLOCKED_FLYING:
919             return notify_fail("A flying creature is in the way.");
920         case BLOCKED_GIANT:
921             return notify_fail("A giant creature is in the way.");
922         case BLOCKED_MOVE:
923         case BLOCKED_OCCUPIED:
924             return notify_fail("There is no safe place to jump near that"
925                                " location.");
926         case BLOCKED_PATH:
927             return notify_fail("There's something in the way.");
928         case BLOCKED_NONE:
929             die("buggy no_landing_reason");
930         }
931     }
932     return true;
933 }
934
935 bool targetter_jump::valid_landing(coord_def a, bool check_invis)
936 {
937     actor *act;
938     ray_def ray;
939
940     if (grd(a) == DNGN_OPEN_SEA || grd(a) == DNGN_LAVA_SEA
941         || !agent->is_habitable(a))
942     {
943         blocked_landing_reason = BLOCKED_MOVE;
944         return false;
945     }
946     if (agent->is_player())
947     {
948         monster* beholder = you.get_beholder(a);
949         if (beholder)
950         {
951             return false;
952             blocked_landing_reason = BLOCKED_MOVE;
953         }
954
955         monster* fearmonger = you.get_fearmonger(a);
956         if (fearmonger)
957         {
958             blocked_landing_reason = BLOCKED_MOVE;
959             return false;
960         }
961     }
962     if (!find_ray(agent->pos(), a, ray, opc_solid_see))
963     {
964         blocked_landing_reason = BLOCKED_PATH;
965         return false;
966     }
967     // Check if a landing site is invalid due to a visible monster obstructing
968     // the path.
969     ray.advance();
970     while (map_bounds(ray.pos()))
971     {
972         act = actor_at(ray.pos());
973         if (ray.pos() == a)
974         {
975             if (act && (!check_invis || agent->can_see(act)))
976             {
977                 blocked_landing_reason = BLOCKED_OCCUPIED;
978                 return false;
979             }
980             break;
981         }
982         const dungeon_feature_type grid = grd(ray.pos());
983         if (act && (!check_invis || agent->can_see(act)))
984         {
985
986             // Can't jump over airborn enemies nor giant enemies not in deep
987             // water or lava.
988             if (act->airborne())
989             {
990                 blocked_landing_reason = BLOCKED_FLYING;
991                 return false;
992             }
993             else if (act->body_size() == SIZE_GIANT
994                      && grid != DNGN_DEEP_WATER && grid != DNGN_LAVA)
995             {
996                 blocked_landing_reason = BLOCKED_GIANT;
997                 return false;
998             }
999         }
1000         ray.advance();
1001     }
1002     return true;
1003 }
1004
1005 aff_type targetter_jump::is_affected(coord_def loc)
1006 {
1007     aff_type aff = AFF_NO;
1008
1009     if (loc == aim)
1010         aff = AFF_YES;
1011     else if (additional_sites.count(loc))
1012         aff = AFF_LANDING;
1013     return aff;
1014 }
1015
1016 // If something unseen either occupies the aim position or blocks the jump path,
1017 // indicate that with jump_is_blocked, but still return true so long there is at
1018 // least one valid landing position from the player's perspective.
1019 bool targetter_jump::set_aim(coord_def a)
1020 {
1021     set<coord_def>::const_iterator site;
1022
1023     if (a == origin)
1024         return false;
1025     if (!targetter::set_aim(a))
1026         return false;
1027
1028     jump_is_blocked = false;
1029
1030     // Find our set of landing sites, choose one at random to be the destination
1031     // and see if it's actually blocked.
1032     set_additional_sites(aim);
1033     if (additional_sites.size())
1034     {
1035         int site_ind = random2(additional_sites.size());
1036         for (site = additional_sites.begin(); site_ind > 0; site++)
1037             site_ind--;
1038         landing_site = *site;
1039         if (!valid_landing(landing_site, false))
1040             jump_is_blocked = true;
1041         return true;
1042     }
1043     return false;
1044 }
1045
1046 // Determine the set of valid landing sites
1047 void targetter_jump::set_additional_sites(coord_def a)
1048 {
1049      get_additional_sites(a);
1050      additional_sites = temp_sites;
1051 }
1052
1053 // Determine the set of valid landing sites for the target, putting the results
1054 // in the private set variable temp_sites.  This uses valid_aim(), so it looks
1055 // for uninhabited squares that are habitable by the player, but doesn't check
1056 // against e.g. harmful clouds
1057 void targetter_jump::get_additional_sites(coord_def a)
1058 {
1059     bool agent_adjacent = a.distance_from(agent->pos()) == 1;
1060     temp_sites.clear();
1061
1062     no_landing_reason = BLOCKED_NONE;
1063     for (adjacent_iterator ai(a, false); ai; ++ai)
1064     {
1065         // See if site is valid, record a putative reason for why no sites were
1066         // found.  A flying or giant monster blocking the landing site gets
1067         // priority as an reason, since it's very jump-specific.
1068         if (!agent_adjacent || agent->pos().distance_from(*ai) > 1)
1069         {
1070             if (valid_landing(*ai))
1071             {
1072                 temp_sites.insert(*ai);
1073                 no_landing_reason = BLOCKED_NONE;
1074             }
1075             else if (no_landing_reason != BLOCKED_FLYING
1076                      && no_landing_reason != BLOCKED_GIANT)
1077             {
1078                 no_landing_reason = blocked_landing_reason;
1079             }
1080         }
1081     }
1082 }
1083
1084 // See if we can find at least one valid landing position for the given monster.
1085 bool targetter_jump::has_additional_sites(coord_def a)
1086 {
1087     get_additional_sites(a);
1088     return temp_sites.size();
1089 }