1 /**
2  * @file
3  * @brief Monster tentacle-related code.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-tentacle.h"
9 
10 #include <functional>
11 
12 #include "act-iter.h"
13 #include "coordit.h"
14 #include "delay.h"
15 #include "env.h"
16 #include "fprop.h"
17 #include "libutil.h" // map_find
18 #include "losglobal.h"
19 #include "mgen-data.h"
20 #include "mon-death.h"
21 #include "mon-place.h"
22 #include "nearby-danger.h"
23 #include "terrain.h"
24 
25 const int MAX_KRAKEN_TENTACLE_DIST = 12;
26 const int MAX_ACTIVE_KRAKEN_TENTACLES = 4;
27 const int MAX_ACTIVE_STARSPAWN_TENTACLES = 2;
28 
29 static monster_type _head_child_segment[][3] =
30 {
31     { MONS_KRAKEN, MONS_KRAKEN_TENTACLE,
32         MONS_KRAKEN_TENTACLE_SEGMENT },
33     { MONS_TENTACLED_STARSPAWN, MONS_STARSPAWN_TENTACLE,
34         MONS_STARSPAWN_TENTACLE_SEGMENT },
35 };
36 
37 static monster_type _solo_tentacle_to_segment[][2] =
38 {
39     { MONS_ELDRITCH_TENTACLE, MONS_ELDRITCH_TENTACLE_SEGMENT },
40     { MONS_SNAPLASHER_VINE,   MONS_SNAPLASHER_VINE_SEGMENT },
41 };
42 
_segment_data(const monster & head,coord_def pos,monster_type type)43 static mgen_data _segment_data(const monster& head, coord_def pos,
44                                monster_type type)
45 {
46     mgen_data mg(type, SAME_ATTITUDE((&head)), pos, head.foe, MG_FORCE_PLACE);
47     if (mons_is_zombified(head))
48         mg.base_type = head.type;
49     mg.set_summoned(&head, 0, 0, head.god)
50       .set_col(head.colour);
51     return mg;
52 }
53 
mons_is_tentacle_head(monster_type mc)54 bool mons_is_tentacle_head(monster_type mc)
55 {
56     for (const monster_type (&m)[3] : _head_child_segment)
57         if (mc == m[0])
58             return true;
59 
60     return false;
61 }
62 
mons_is_child_tentacle(monster_type mc)63 bool mons_is_child_tentacle(monster_type mc)
64 {
65     for (const monster_type (&m)[3] : _head_child_segment)
66         if (mc == m[1])
67             return true;
68 
69     return false;
70 }
71 
mons_is_child_tentacle_segment(monster_type mc)72 bool mons_is_child_tentacle_segment(monster_type mc)
73 {
74     for (const monster_type (&m)[3] : _head_child_segment)
75         if (mc == m[2])
76             return true;
77 
78     return false;
79 }
80 
mons_is_solo_tentacle(monster_type mc)81 bool mons_is_solo_tentacle(monster_type mc)
82 {
83     for (const monster_type (&m)[2] : _solo_tentacle_to_segment)
84         if (mc == m[0])
85             return true;
86 
87     return false;
88 }
89 
mons_is_tentacle(monster_type mc)90 bool mons_is_tentacle(monster_type mc)
91 {
92     return mons_is_child_tentacle(mc) || mons_is_solo_tentacle(mc);
93 }
94 
mons_is_tentacle_segment(monster_type mc)95 bool mons_is_tentacle_segment(monster_type mc)
96 {
97     for (const monster_type (&m)[2] : _solo_tentacle_to_segment)
98         if (mc == m[1])
99             return true;
100 
101     return mons_is_child_tentacle_segment(mc);
102 }
103 
mons_is_tentacle_or_tentacle_segment(monster_type mc)104 bool mons_is_tentacle_or_tentacle_segment(monster_type mc)
105 {
106     return mons_is_tentacle(mc) || mons_is_tentacle_segment(mc);
107 }
108 
mons_tentacle_parent_type(const monster * mons)109 monster_type mons_tentacle_parent_type(const monster* mons)
110 {
111     const monster_type mc = mons_base_type(*mons);
112 
113     for (const monster_type (&m)[3] : _head_child_segment)
114         if (mc == m[1])
115             return m[0];
116 
117     for (const monster_type (&m)[3] : _head_child_segment)
118         if (mc == m[2])
119             return m[1];
120 
121     for (const monster_type (&m)[2] : _solo_tentacle_to_segment)
122         if (mc == m[1])
123             return m[0];
124 
125     return MONS_PROGRAM_BUG;
126 }
127 
mons_tentacle_child_type(const monster * mons)128 monster_type mons_tentacle_child_type(const monster* mons)
129 {
130     const monster_type mc = mons_base_type(*mons);
131 
132     for (const monster_type (&m)[3] : _head_child_segment)
133         if (mc == m[0])
134             return m[1];
135 
136     for (const monster_type (&m)[3] : _head_child_segment)
137         if (mc == m[1])
138             return m[2];
139 
140     for (const monster_type (&m)[2] : _solo_tentacle_to_segment)
141         if (mc == m[0])
142             return m[1];
143 
144     return MONS_PROGRAM_BUG;
145 }
146 
is_child_tentacle() const147 bool monster::is_child_tentacle() const
148 {
149     return mons_is_child_tentacle(mons_base_type(*this));
150 }
151 
is_child_tentacle_segment() const152 bool monster::is_child_tentacle_segment() const
153 {
154     return mons_is_child_tentacle_segment(mons_base_type(*this));
155 }
156 
is_child_monster() const157 bool monster::is_child_monster() const
158 {
159     return is_child_tentacle() || is_child_tentacle_segment();
160 }
161 
is_child_tentacle_of(const monster * mons) const162 bool monster::is_child_tentacle_of(const monster* mons) const
163 {
164     return mons_base_type(*mons) == mons_tentacle_parent_type(this)
165            && tentacle_connect == mons->mid;
166 }
167 
is_parent_monster_of(const monster * mons) const168 bool monster::is_parent_monster_of(const monster* mons) const
169 {
170     return mons_base_type(*this) == mons_tentacle_parent_type(mons)
171            && mons->tentacle_connect == mid;
172 }
173 
174 //Returns whether a given monster is a tentacle segment immediately attached
175 //to the parent monster
mons_tentacle_adjacent(const monster * parent,const monster * child)176 bool mons_tentacle_adjacent(const monster* parent, const monster* child)
177 {
178     return mons_is_tentacle_head(mons_base_type(*parent))
179            && mons_is_tentacle_segment(child->type)
180            && child->props.exists("inwards")
181            && child->props["inwards"].get_int() == (int) parent->mid;
182 }
183 
get_tentacle_head(const monster & mon)184 const monster& get_tentacle_head(const monster& mon)
185 {
186     const monster* m = &mon;
187     // For tentacle segments, find the associated tentacle.
188     if (m->is_child_tentacle_segment())
189     {
190         monster* tentacle = monster_by_mid(m->tentacle_connect);
191         if (!tentacle)
192             return *m;
193 
194         m = tentacle;
195     }
196 
197     // For tentacles, find the associated head.
198     if (m->is_child_tentacle())
199     {
200         monster* head = monster_by_mid(m->tentacle_connect);
201         if (!head)
202             return *m;
203 
204         m = head;
205     }
206 
207     return *m;
208 }
209 
_establish_connection(monster * tentacle,monster * head,set<position_node>::iterator path,monster_type connector_type)210 static void _establish_connection(monster* tentacle,
211                                   monster* head,
212                                   set<position_node>::iterator path,
213                                   monster_type connector_type)
214 {
215     const position_node * last = &(*path);
216     const position_node * current = last->last;
217 
218     // Tentacle is adjacent to the end position, not much to do.
219     if (!current)
220     {
221         // This is a little awkward now. Oh well. -cao
222         if (tentacle != head)
223             tentacle->props["inwards"].get_int() = head->mid;
224         else
225             tentacle->props["inwards"].get_int() = MID_NOBODY;
226 
227         return;
228     }
229 
230     // No base monster case (demonic tentacles)
231     if (!monster_at(last->pos))
232     {
233         mgen_data mg = _segment_data(*head, last->pos, connector_type);
234         mg.props[MGEN_TENTACLE_CONNECT] = int(tentacle->mid);
235         if (monster *connect = create_monster(mg))
236         {
237             connect->props["inwards"].get_int() = MID_NOBODY;
238             connect->props["outwards"].get_int() = MID_NOBODY;
239 
240             if (head->holiness() & MH_UNDEAD)
241                 connect->flags |= MF_FAKE_UNDEAD;
242 
243             connect->max_hit_points = tentacle->max_hit_points;
244             connect->hit_points = tentacle->hit_points;
245         }
246         else
247         {
248             // Big failure mode.
249             return;
250         }
251     }
252 
253     while (current)
254     {
255         // Last monster we visited or placed
256         monster* last_mon = monster_at(last->pos);
257         if (!last_mon)
258         {
259             // Should be something there, what to do if there isn't?
260             mpr("Error! failed to place monster in tentacle connect change");
261             break;
262         }
263 
264         // Monster at the current square, should be the end of the line if there
265         monster* current_mons = monster_at(current->pos);
266         if (current_mons)
267         {
268             // Todo verify current monster type
269             current_mons->props["inwards"].get_int() = last_mon->mid;
270             last_mon->props["outwards"].get_int() = current_mons->mid;
271             break;
272         }
273 
274          // place a connector
275         mgen_data mg = _segment_data(*head, current->pos, connector_type);
276         mg.props[MGEN_TENTACLE_CONNECT] = int(tentacle->mid);
277         if (monster *connect = create_monster(mg))
278         {
279             connect->max_hit_points = tentacle->max_hit_points;
280             connect->hit_points = tentacle->hit_points;
281 
282             connect->props["inwards"].get_int() = last_mon->mid;
283             connect->props["outwards"].get_int() = MID_NOBODY;
284 
285             if (last_mon->type == connector_type)
286                 last_mon->props["outwards"].get_int() = connect->mid;
287 
288             if (head->holiness() & MH_UNDEAD)
289                 connect->flags |= MF_FAKE_UNDEAD;
290 
291             if (monster_can_submerge(connect, env.grid(connect->pos())))
292                 connect->add_ench(ENCH_SUBMERGED);
293         }
294         else
295         {
296             // connector placement failed, what to do?
297             mprf("connector placement failed at %d %d", current->pos.x, current->pos.y);
298         }
299 
300         last = current;
301         current = current->last;
302     }
303 }
304 
305 struct tentacle_attack_constraints
306 {
307     vector<coord_def> * target_positions;
308 
309     map<coord_def, set<int> > * connection_constraints;
310     monster *base_monster;
311     int max_string_distance;
312     int connect_idx[8];
313 
tentacle_attack_constraintstentacle_attack_constraints314     tentacle_attack_constraints()
315     {
316         for (int i=0; i<8; i++)
317             connect_idx[i] = i;
318     }
319 
min_disttentacle_attack_constraints320     int min_dist(const coord_def & pos)
321     {
322         int min = INT_MAX;
323         for (coord_def tpos : *target_positions)
324             min = std::min(min, grid_distance(pos, tpos));
325         return min;
326     }
327 
operator ()tentacle_attack_constraints328     void operator()(const position_node & node,
329                     vector<position_node> & expansion)
330     {
331         shuffle_array(connect_idx);
332 
333 //        mprf("expanding %d %d, string dist %d", node.pos.x, node.pos.y, node.string_distance);
334         for (int idx : connect_idx)
335         {
336             position_node temp;
337 
338             temp.pos = node.pos + Compass[idx];
339             temp.string_distance = node.string_distance;
340             temp.departure = node.departure;
341             temp.connect_level = node.connect_level;
342             temp.path_distance = node.path_distance;
343             temp.estimate = 0;
344 
345             if (!in_bounds(temp.pos) || is_sanctuary(temp.pos))
346                 continue;
347 
348             if (!base_monster->is_habitable(temp.pos))
349                 temp.path_distance = DISCONNECT_DIST;
350             else
351             {
352                 actor * act_at = actor_at(temp.pos);
353                 monster* mons_at = monster_at(temp.pos);
354 
355                 if (!act_at)
356                     temp.path_distance += 1;
357                 // Can still search through a firewood monster, just at a higher
358                 // path cost.
359                 else if (mons_at && mons_is_firewood(*mons_at)
360                     && !mons_aligned(base_monster, mons_at))
361                 {
362                     temp.path_distance += 10;
363                 }
364                 // An actor we can't path through is there
365                 else
366                     temp.path_distance = DISCONNECT_DIST;
367 
368             }
369 
370             int connect_level = temp.connect_level;
371             int base_connect_level = connect_level;
372 
373             if (auto constraint = map_find(*connection_constraints, temp.pos))
374             {
375                 int max_val = constraint->empty()
376                             ? INT_MAX : *constraint->rbegin();
377 
378                 if (max_val < connect_level)
379                     temp.departure = true;
380 
381                 // If we can still feasibly retract (haven't left connect range)
382                 if (!temp.departure)
383                 {
384                     if (constraint->count(connect_level))
385                     {
386                         while (constraint->count(connect_level + 1))
387                             connect_level++;
388                     }
389 
390                     int delta = connect_level - base_connect_level;
391                     temp.connect_level = connect_level;
392                     if (delta)
393                         temp.string_distance -= delta;
394                 }
395 
396                 if (connect_level < max_val)
397                    temp.path_distance = DISCONNECT_DIST;
398             }
399             else
400             {
401                 // We stopped retracting
402                 temp.departure = true;
403             }
404 
405             if (temp.departure)
406                 temp.string_distance++;
407 
408 //            if (temp.string_distance > MAX_KRAKEN_TENTACLE_DIST)
409             if (temp.string_distance > max_string_distance)
410                 temp.path_distance = DISCONNECT_DIST;
411 
412             if (temp.path_distance != DISCONNECT_DIST)
413                 temp.estimate = min_dist(temp.pos);
414 
415             expansion.push_back(temp);
416         }
417     }
418 };
419 
420 struct tentacle_connect_constraints
421 {
422     map<coord_def, set<int> > * connection_constraints;
423 
424     monster* base_monster;
425 
tentacle_connect_constraintstentacle_connect_constraints426     tentacle_connect_constraints()
427     {
428         for (int i=0; i<8; i++)
429             connect_idx[i] = i;
430     }
431 
432     int connect_idx[8];
operator ()tentacle_connect_constraints433     void operator()(const position_node & node,
434                     vector<position_node> & expansion)
435     {
436         shuffle_array(connect_idx);
437 
438         for (int idx : connect_idx)
439         {
440             position_node temp;
441 
442             temp.pos = node.pos + Compass[idx];
443 
444             if (!in_bounds(temp.pos))
445                 continue;
446 
447             auto constraint = map_find(*connection_constraints, temp.pos);
448 
449             if (!constraint || !constraint->count(node.connect_level))
450                 continue;
451 
452             if (!base_monster->is_habitable(temp.pos) || actor_at(temp.pos))
453                 temp.path_distance = DISCONNECT_DIST;
454             else
455                 temp.path_distance = 1 + node.path_distance;
456 
457             //temp.estimate = grid_distance(temp.pos, kraken->pos());
458             // Don't bother with an estimate, the search is highly constrained
459             // so it's not really going to help
460             temp.estimate = 0;
461             int test_level = node.connect_level;
462 
463             while (constraint->count(test_level + 1))
464                 test_level++;
465 
466             int max = constraint->empty() ? INT_MAX : *constraint->rbegin();
467 
468             if (test_level < max)
469                 continue;
470 
471             temp.connect_level = test_level;
472 
473             expansion.push_back(temp);
474         }
475     }
476 };
477 
478 struct target_position
479 {
480     coord_def target;
operator ()target_position481     bool operator() (const coord_def & pos)
482     {
483         return pos == target;
484     }
485 };
486 
487 /*struct target_monster
488 {
489     int target_mindex;
490 
491     bool operator() (const coord_def & pos)
492     {
493         monster* temp = monster_at(pos);
494         if (!temp || temp->mindex() != target_mindex)
495             return false;
496         return true;
497 
498     }
499 };*/
500 
501 struct multi_target
502 {
503     vector<coord_def> * targets;
504 
operator ()multi_target505     bool operator() (const coord_def & pos)
506     {
507         return find(begin(*targets), end(*targets), pos) != end(*targets);
508     }
509 };
510 
511 // returns pathfinding success/failure
_tentacle_pathfind(monster * tentacle,tentacle_attack_constraints & attack_constraints,coord_def & new_position,vector<coord_def> & target_positions,int total_length)512 static bool _tentacle_pathfind(monster* tentacle,
513                        tentacle_attack_constraints & attack_constraints,
514                        coord_def & new_position,
515                        vector<coord_def> & target_positions,
516                        int total_length)
517 {
518     multi_target foe_check { &target_positions };
519 
520     vector<set<position_node>::iterator > tentacle_path;
521 
522     set<position_node> visited;
523     visited.clear();
524 
525     position_node temp;
526     temp.pos = tentacle->pos();
527 
528     auto constraint = map_find(*attack_constraints.connection_constraints,
529                                temp.pos);
530     ASSERT(constraint);
531     temp.connect_level = 0;
532     while (constraint->count(temp.connect_level + 1))
533         temp.connect_level++;
534 
535     temp.departure = false;
536     temp.string_distance = total_length;
537 
538     search_astar(temp,
539                  foe_check, attack_constraints,
540                  visited, tentacle_path);
541 
542     bool path_found = false;
543     // Did we find a path?
544     if (!tentacle_path.empty())
545     {
546         // The end position is the enemy or target square, we need
547         // to rewind the found path to find the next move
548 
549         const position_node * current = &(*tentacle_path[0]);
550         const position_node * last;
551 
552         // The last position in the chain is the base position,
553         // so we want to stop at the one before the last.
554         while (current && current->last)
555         {
556             last = current;
557             current = current->last;
558             new_position = last->pos;
559             path_found = true;
560         }
561     }
562 
563     return path_found;
564 }
565 
_try_tentacle_connect(const coord_def & new_pos,const coord_def & base_position,monster * tentacle,monster * head,tentacle_connect_constraints & connect_costs,monster_type connect_type)566 static bool _try_tentacle_connect(const coord_def & new_pos,
567                                   const coord_def & base_position,
568                                   monster* tentacle,
569                                   monster* head,
570                                   tentacle_connect_constraints & connect_costs,
571                                   monster_type connect_type)
572 {
573     // Nothing to do here.
574     // Except fix the tentacle end's pointer, idiot.
575     if (base_position == new_pos)
576     {
577         if (tentacle == head)
578             tentacle->props["inwards"].get_int() = MID_NOBODY;
579         else
580             tentacle->props["inwards"].get_int() = head->mid;
581         return true;
582     }
583 
584     int start_level = 0;
585     // This condition should never miss
586     if (auto constraint = map_find(*connect_costs.connection_constraints,
587                           new_pos))
588     {
589         while (constraint->count(start_level + 1))
590             start_level++;
591     }
592 
593     // Find the tentacle -> head path
594     target_position current_target;
595     current_target.target = base_position;
596 /*  target_monster current_target;
597     current_target.target_mindex = headnum;
598 */
599 
600     set<position_node> visited;
601     vector<set<position_node>::iterator> candidates;
602 
603     position_node temp;
604     temp.pos = new_pos;
605     temp.connect_level = start_level;
606 
607     search_astar(temp,
608                  current_target, connect_costs,
609                  visited, candidates);
610 
611     if (candidates.empty())
612         return false;
613 
614     _establish_connection(tentacle, head, candidates[0], connect_type);
615 
616     return true;
617 }
618 
_collect_tentacles(monster * mons,vector<monster_iterator> & tentacles)619 static void _collect_tentacles(monster* mons,
620                                vector<monster_iterator> & tentacles)
621 {
622     // TODO: reorder tentacles based on distance to head or something.
623     for (monster_iterator mi; mi; ++mi)
624         if (mons->is_parent_monster_of(*mi))
625             tentacles.push_back(mi);
626 }
627 
_purge_connectors(monster * tentacle)628 static void _purge_connectors(monster* tentacle)
629 {
630     for (monster_iterator mi; mi; ++mi)
631     {
632         if (mi->is_child_tentacle_of(tentacle))
633         {
634             int hp = mi->hit_points;
635             if (hp > 0 && hp < tentacle->hit_points)
636                 tentacle->hit_points = hp;
637 
638             monster_die(**mi, KILL_MISC, NON_MONSTER, true);
639         }
640     }
641     ASSERT(tentacle->alive());
642 }
643 
_collect_foe_positions(monster * mons,vector<coord_def> & foe_positions,function<bool (const actor *)> sight_check)644 static void _collect_foe_positions(monster *mons,
645                                    vector<coord_def> &foe_positions,
646                                    function<bool(const actor *)> sight_check)
647 {
648     coord_def foe_pos(-1, -1);
649     actor * foe = mons->get_foe();
650     if (foe && sight_check(foe))
651     {
652         foe_positions.push_back(mons->get_foe()->pos());
653         foe_pos = foe_positions.back();
654     }
655 
656     for (monster_iterator mi; mi; ++mi)
657     {
658         const monster * const test = *mi;
659         if (!mons_is_firewood(*test)
660             && !mons_aligned(test, mons)
661             && test->pos() != foe_pos
662             && sight_check(test))
663         {
664             foe_positions.push_back(test->pos());
665         }
666     }
667 }
668 
669 // Return value is a retract position for the tentacle or -1, -1 if no
670 // retract position exists.
671 //
672 // move_kraken_tentacle should check retract pos, it could potentially
673 // give the kraken head's position as a retract pos.
_collect_connection_data(monster * start_monster,map<coord_def,set<int>> & connection_data,coord_def & retract_pos)674 static int _collect_connection_data(monster* start_monster,
675                map<coord_def, set<int> > & connection_data,
676                coord_def & retract_pos)
677 {
678     int current_count = 0;
679     monster* current_mon = start_monster;
680     retract_pos.x = -1;
681     retract_pos.y = -1;
682     bool retract_found = false;
683 
684     while (current_mon)
685     {
686         for (adjacent_iterator adj_it(current_mon->pos(), false);
687              adj_it; ++adj_it)
688         {
689             connection_data[*adj_it].insert(current_count);
690         }
691 
692         bool basis = current_mon->props.exists("inwards");
693         monster* next = basis ? monster_by_mid(current_mon->props["inwards"].get_int()) : nullptr;
694 
695         if (next && next->is_child_tentacle_of(start_monster))
696         {
697             current_mon = next;
698             if (current_mon->tentacle_connect != start_monster->mid)
699                 mpr("link information corruption!!! tentacle in chain doesn't match mindex");
700             if (!retract_found)
701             {
702                 retract_pos = current_mon->pos();
703                 retract_found = true;
704             }
705         }
706         else
707         {
708             current_mon = nullptr;
709 //            mprf("null at count %d", current_count);
710         }
711         current_count++;
712     }
713 
714 //    mprf("returned count %d", current_count);
715     return current_count;
716 }
717 
move_solo_tentacle(monster * tentacle)718 void move_solo_tentacle(monster* tentacle)
719 {
720     if (!tentacle || !mons_is_solo_tentacle(mons_base_type(*tentacle)))
721         return;
722 
723     vector<coord_def> foe_positions;
724 
725     bool attack_foe = false;
726     bool severed = tentacle->has_ench(ENCH_SEVERED);
727 
728     coord_def base_position;
729     if (!tentacle->props.exists("base_position"))
730         tentacle->props["base_position"].get_coord() = tentacle->pos();
731 
732     base_position = tentacle->props["base_position"].get_coord();
733 
734     if (!severed)
735     {
736         _collect_foe_positions(tentacle, foe_positions,
737                 [tentacle, base_position](const actor *test) -> bool
738                 {
739                     return test->visible_to(tentacle)
740                         && cell_see_cell(base_position, test->pos(),
741                                          LOS_SOLID_SEE);
742                 });
743         attack_foe = !foe_positions.empty();
744     }
745 
746     coord_def retract_pos;
747     map<coord_def, set<int> > connection_data;
748 
749     int visited_count = _collect_connection_data(tentacle,
750                                                  connection_data,
751                                                  retract_pos);
752 
753     bool retract_found = retract_pos.x != -1 && retract_pos.y != -1;
754 
755     _purge_connectors(tentacle);
756 
757     if (severed)
758     {
759         for (fair_adjacent_iterator ai(base_position); ai; ++ai)
760         {
761             if (!actor_at(*ai) && tentacle->is_habitable(*ai))
762             {
763                 tentacle->props["base_position"].get_coord() = *ai;
764                 base_position = *ai;
765                 break;
766             }
767         }
768     }
769 
770     coord_def new_pos = tentacle->pos();
771     coord_def old_pos = tentacle->pos();
772 
773     int demonic_max_dist = (tentacle->type == MONS_ELDRITCH_TENTACLE ? 5 : 8);
774     tentacle_attack_constraints attack_constraints;
775     attack_constraints.base_monster = tentacle;
776     attack_constraints.max_string_distance = demonic_max_dist;
777     attack_constraints.connection_constraints = &connection_data;
778     attack_constraints.target_positions = &foe_positions;
779 
780     bool path_found = false;
781     if (attack_foe)
782     {
783         path_found = _tentacle_pathfind(tentacle, attack_constraints,
784                                         new_pos, foe_positions, visited_count);
785     }
786 
787     //If this tentacle is constricting a creature, attempt to pull it back
788     //towards the head.
789     bool pull_constrictee = false;
790     bool shift_constrictee = false;
791     coord_def shift_pos;
792     actor* constrictee = nullptr;
793     if (tentacle->is_constricting())
794     {
795         constrictee = actor_by_mid(tentacle->constricting->begin()->first);
796 
797         // Don't drag things that cannot move
798         if (!constrictee->is_stationary())
799         {
800             if (retract_found)
801             {
802                 if (constrictee->is_habitable(old_pos))
803                 {
804                     pull_constrictee = true;
805                     shift_pos = old_pos;
806                 }
807             }
808             else if (tentacle->type == MONS_SNAPLASHER_VINE)
809             {
810                 // Don't shift our victim if they're already next to a tree
811                 // (To avoid shaking players back and forth constantly)
812                 bool near_tree = false;
813                 for (adjacent_iterator ai(constrictee->pos()); ai; ++ai)
814                 {
815                     if (feat_is_tree(env.grid(*ai)))
816                     {
817                         near_tree = true;
818                         break;
819                     }
820                 }
821 
822                 if (!near_tree)
823                 {
824                     for (adjacent_iterator ai(tentacle->pos()); ai; ++ai)
825                     {
826                         if (adjacent(*ai, constrictee->pos())
827                             && constrictee->is_habitable(*ai)
828                             && !actor_at(*ai))
829                         {
830                             for (adjacent_iterator ai2(*ai); ai2; ++ai2)
831                             {
832                                 if (feat_is_tree(env.grid(*ai2)))
833                                 {
834                                     pull_constrictee = true;
835                                     shift_constrictee = true;
836                                     shift_pos = *ai;
837                                     break;
838                                 }
839                             }
840                         }
841                     }
842                 }
843             }
844         }
845     }
846 
847     if (!attack_foe || !path_found)
848     {
849         // todo: set a random position?
850 
851         dprf("pathing failed, target %d %d", new_pos.x, new_pos.y);
852         for (fair_adjacent_iterator ai(old_pos); ai; ++ai)
853         {
854             if (!in_bounds(*ai) || is_sanctuary(*ai) || actor_at(*ai))
855                 continue;
856 
857             int escalated = 0;
858             auto constraint = map_find(connection_data, *ai);
859             ASSERT(constraint);
860 
861             while (constraint->count(escalated + 1))
862                 escalated++;
863 
864             if (!severed
865                 && tentacle->is_habitable(*ai)
866                 && escalated == *constraint->rbegin()
867                 && (visited_count < demonic_max_dist
868                     || constraint->size() > 1))
869             {
870                 new_pos = *ai;
871                 break;
872             }
873             else if (tentacle->is_habitable(*ai)
874                      && visited_count > 1
875                      && escalated == *constraint->rbegin()
876                      && constraint->size() > 1)
877             {
878                 new_pos = *ai;
879                 break;
880             }
881         }
882     }
883     else if (pull_constrictee && !shift_constrictee)
884         new_pos = retract_pos;
885 
886     if (new_pos != old_pos)
887     {
888         // Did we path into a target?
889         if (actor* blocking_actor = actor_at(new_pos))
890         {
891             tentacle->target = new_pos;
892             tentacle->foe = blocking_actor->mindex();
893             new_pos = old_pos;
894         }
895     }
896 
897     // Why do I have to do this move? I don't get it.
898     // specifically, if tentacle isn't registered at its new position on env.mgrid
899     // the search fails (sometimes), Don't know why. -cao
900     tentacle->move_to_pos(new_pos);
901 
902     if (pull_constrictee)
903     {
904         if (you.can_see(*tentacle))
905         {
906             mprf("The vine drags %s backwards!",
907                     constrictee->name(DESC_THE).c_str());
908         }
909 
910         if (constrictee->as_player())
911             move_player_to_grid(shift_pos, false);
912         else
913             constrictee->move_to_pos(shift_pos);
914 
915         // Interrupt stair travel and passwall.
916         if (constrictee->is_player())
917             stop_delay(true);
918     }
919     tentacle->clear_invalid_constrictions();
920 
921     tentacle_connect_constraints connect_costs;
922     connect_costs.connection_constraints = &connection_data;
923     connect_costs.base_monster = tentacle;
924 
925     bool connected = _try_tentacle_connect(new_pos, base_position,
926                                            tentacle, tentacle,
927                                            connect_costs,
928                                            mons_tentacle_child_type(tentacle));
929 
930     if (!connected)
931     {
932         // This should really never fail for demonic tentacles (they don't
933         // have the whole shifting base problem). -cao
934         mprf("tentacle connect failed! What the heck!  severed status %d",
935              tentacle->has_ench(ENCH_SEVERED));
936         mprf("pathed to %d %d from %d %d mid %d count %d", new_pos.x, new_pos.y,
937              old_pos.x, old_pos.y, tentacle->mid, visited_count);
938 
939         // Is it ok to purge the tentacle here?
940         monster_die(*tentacle, KILL_MISC, NON_MONSTER, true);
941         return;
942     }
943 
944 //  mprf("mindex %d vsisted %d", tentacle_idx, visited_count);
945     tentacle->check_redraw(old_pos);
946     tentacle->apply_location_effects(old_pos);
947 }
948 
move_child_tentacles(monster * mons)949 void move_child_tentacles(monster* mons)
950 {
951     ASSERT(mons);
952 
953     if (!mons_is_tentacle_head(mons_base_type(*mons))
954         || mons->asleep())
955     {
956         return;
957     }
958 
959     bool no_foe = false;
960 
961     vector<coord_def> foe_positions;
962     _collect_foe_positions(mons, foe_positions,
963                            [mons](const actor *test) -> bool
964                            {
965                                return mons->can_see(*test);
966                            });
967 
968     //if (!kraken->near_foe())
969     if (foe_positions.empty()
970         || mons->behaviour == BEH_FLEE
971         || mons->behaviour == BEH_WANDER)
972     {
973         no_foe = true;
974     }
975     vector<monster_iterator> tentacles;
976     _collect_tentacles(mons, tentacles);
977 
978     // Move each tentacle in turn
979     for (monster_iterator &tent_it : tentacles)
980     {
981         // XXX: Why not just use *tent_it ?
982         monster* tentacle = monster_at(tent_it->pos());
983 
984         if (!tentacle)
985         {
986             dprf("Missing tentacle in path.");
987             continue;
988         }
989 
990         tentacle_connect_constraints connect_costs;
991         map<coord_def, set<int> > connection_data;
992 
993         monster* current_mon = tentacle;
994         int current_count = 0;
995         bool retract_found = false;
996         coord_def retract_pos(-1, -1);
997 
998         while (current_mon)
999         {
1000             for (adjacent_iterator adj_it(current_mon->pos(), false);
1001                  adj_it; ++adj_it)
1002             {
1003                 connection_data[*adj_it].insert(current_count);
1004             }
1005 
1006             bool basis = current_mon->props.exists("inwards");
1007             monster* inward = basis ? monster_by_mid(current_mon->props["inwards"].get_int()) : nullptr;
1008 
1009             if (inward
1010                 && (inward->is_child_tentacle_of(tentacle)
1011                     || inward->is_parent_monster_of(tentacle)))
1012             {
1013                 current_mon = inward;
1014                 if (!retract_found
1015                     && current_mon->is_child_tentacle_of(tentacle))
1016                 {
1017                     retract_pos = current_mon->pos();
1018                     retract_found = true;
1019                 }
1020             }
1021             else
1022                 current_mon = nullptr;
1023             current_count++;
1024         }
1025 
1026         _purge_connectors(tentacle);
1027 
1028         if (no_foe
1029             && grid_distance(tentacle->pos(), mons->pos()) == 1)
1030         {
1031             // Drop the tentacle if no enemies are in sight and it is
1032             // adjacent to the main body. This is to prevent players from
1033             // just sniping tentacles while outside the kraken's fov.
1034             monster_die(*tentacle, KILL_MISC, NON_MONSTER, true);
1035             continue;
1036         }
1037 
1038         coord_def new_pos = tentacle->pos();
1039         coord_def old_pos = tentacle->pos();
1040         bool path_found = false;
1041 
1042         tentacle_attack_constraints attack_constraints;
1043         attack_constraints.base_monster = tentacle;
1044         attack_constraints.max_string_distance = MAX_KRAKEN_TENTACLE_DIST;
1045         attack_constraints.connection_constraints = &connection_data;
1046         attack_constraints.target_positions = &foe_positions;
1047 
1048         //If this tentacle is constricting a creature, attempt to pull it back
1049         //towards the head.
1050         bool pull_constrictee = false;
1051         actor* constrictee = nullptr;
1052         if (tentacle->is_constricting() && retract_found)
1053         {
1054             constrictee = actor_by_mid(tentacle->constricting->begin()->first);
1055             if (feat_has_solid_floor(env.grid(old_pos))
1056                 && constrictee->is_habitable(old_pos))
1057             {
1058                 pull_constrictee = true;
1059             }
1060         }
1061 
1062         if (!no_foe && !pull_constrictee)
1063         {
1064             path_found = _tentacle_pathfind(
1065                     tentacle,
1066                     attack_constraints,
1067                     new_pos,
1068                     foe_positions,
1069                     current_count);
1070         }
1071 
1072         if (no_foe || !path_found || pull_constrictee)
1073         {
1074             if (retract_found)
1075                 new_pos = retract_pos;
1076             else
1077             {
1078                 // What happened here? Usually retract found should be true
1079                 // or we should get pruned (due to being adjacent to the
1080                 // head), in any case just stay here.
1081             }
1082         }
1083 
1084         // Did we path into a target?
1085         if (actor* blocking_actor = actor_at(new_pos))
1086         {
1087             tentacle->target = new_pos;
1088             tentacle->foe = blocking_actor->mindex();
1089             new_pos = old_pos;
1090         }
1091 
1092         // Why do I have to do this move? I don't get it.
1093         // specifically, if tentacle isn't registered at its new position on
1094         // env.mgrid the search fails (sometimes), Don't know why. -cao
1095         tentacle->move_to_pos(new_pos);
1096 
1097         if (pull_constrictee)
1098         {
1099             if (you.can_see(*tentacle))
1100             {
1101                 mprf("The tentacle pulls %s backwards!",
1102                      constrictee->name(DESC_THE).c_str());
1103             }
1104 
1105             if (constrictee->as_player())
1106                 move_player_to_grid(old_pos, false);
1107             else
1108                 constrictee->move_to_pos(old_pos);
1109 
1110             // Interrupt stair travel and passwall.
1111             if (constrictee->is_player())
1112                 stop_delay(true);
1113         }
1114         tentacle->clear_invalid_constrictions();
1115 
1116         connect_costs.connection_constraints = &connection_data;
1117         connect_costs.base_monster = tentacle;
1118         bool connected = _try_tentacle_connect(new_pos, mons->pos(),
1119                                 tentacle, mons,
1120                                 connect_costs,
1121                                 mons_tentacle_child_type(tentacle));
1122 
1123         // Can't connect, usually the head moved and invalidated our position
1124         // in some way. Should look into this more at some point -cao
1125         if (!connected)
1126         {
1127             env.mgrid(tentacle->pos()) = tentacle->mindex();
1128             monster_die(*tentacle, KILL_MISC, NON_MONSTER, true);
1129 
1130             continue;
1131         }
1132 
1133         tentacle->check_redraw(old_pos);
1134         tentacle->apply_location_effects(old_pos);
1135     }
1136 }
1137 
_mons_get_parent_monster(monster * mons)1138 static monster* _mons_get_parent_monster(monster* mons)
1139 {
1140     for (monster_iterator mi; mi; ++mi)
1141     {
1142         if (mi->is_parent_monster_of(mons))
1143             return *mi;
1144     }
1145 
1146     return nullptr;
1147 }
1148 
1149 // When given either a tentacle end or segment, kills the end and all segments
1150 // of that tentacle.
destroy_tentacle(monster * mons)1151 bool destroy_tentacle(monster* mons)
1152 {
1153     bool any = false;
1154 
1155     monster* head = mons_is_tentacle_segment(mons->type)
1156             ? _mons_get_parent_monster(mons) : mons;
1157 
1158     //If we tried to find the head, but failed (probably because it is already
1159     //dead), cancel trying to kill this tentacle
1160     if (head == nullptr)
1161         return false;
1162 
1163     // Some issue with using monster_die leading to DEAD_MONSTER
1164     // or w/e. Using hurt seems to cause more problems though.
1165     for (monster_iterator mi; mi; ++mi)
1166     {
1167         if (mi->is_child_tentacle_of(head))
1168         {
1169             any = true;
1170             //mi->hurt(*mi, INSTANT_DEATH);
1171             monster_die(**mi, KILL_MISC, NON_MONSTER, true);
1172         }
1173     }
1174 
1175     if (mons != head)
1176     {
1177         any = true;
1178         monster_die(*head, KILL_MISC, NON_MONSTER, true);
1179     }
1180 
1181     return any;
1182 }
1183 
destroy_tentacles(monster * head)1184 bool destroy_tentacles(monster* head)
1185 {
1186     bool any = false;
1187     for (monster_iterator mi; mi; ++mi)
1188     {
1189         if (mi->is_child_tentacle_of(head))
1190         {
1191             any |= destroy_tentacle(*mi);
1192             if (!mi->is_child_tentacle_segment())
1193             {
1194                 monster_die(*mi->as_monster(), KILL_MISC, NON_MONSTER, true);
1195                 any = true;
1196             }
1197         }
1198     }
1199     return any;
1200 }
1201 
_max_tentacles(const monster * mon)1202 static int _max_tentacles(const monster* mon)
1203 {
1204     if (mons_base_type(*mon) == MONS_KRAKEN)
1205         return MAX_ACTIVE_KRAKEN_TENTACLES;
1206     else if (mon->type == MONS_TENTACLED_STARSPAWN)
1207         return MAX_ACTIVE_STARSPAWN_TENTACLES;
1208     else
1209         return 0;
1210 }
1211 
mons_available_tentacles(monster * head)1212 int mons_available_tentacles(monster* head)
1213 {
1214     int tentacle_count = 0;
1215 
1216     for (monster_iterator mi; mi; ++mi)
1217     {
1218         if (mi->is_child_tentacle_of(head))
1219             tentacle_count++;
1220     }
1221 
1222     return _max_tentacles(head) - tentacle_count;
1223 }
1224 
mons_create_tentacles(monster * head)1225 void mons_create_tentacles(monster* head)
1226 {
1227     int possible_count = mons_available_tentacles(head);
1228 
1229     if (possible_count <= 0)
1230         return;
1231 
1232     monster_type tent_type = mons_tentacle_child_type(head);
1233 
1234     vector<coord_def> adj_squares;
1235 
1236     // Collect open adjacent squares. Candidate squares must be
1237     // unoccupied.
1238     for (adjacent_iterator adj_it(head->pos()); adj_it; ++adj_it)
1239     {
1240         if (monster_habitable_grid(tent_type, env.grid(*adj_it))
1241             && !actor_at(*adj_it))
1242         {
1243             adj_squares.push_back(*adj_it);
1244         }
1245     }
1246 
1247     if (unsigned(possible_count) > adj_squares.size())
1248         possible_count = adj_squares.size();
1249     else if (adj_squares.size() > unsigned(possible_count))
1250         shuffle_array(adj_squares);
1251 
1252     int visible_count = 0;
1253 
1254     for (int i = 0 ; i < possible_count; ++i)
1255     {
1256         mgen_data mg = _segment_data(*head, adj_squares[i], tent_type);
1257         mg.props[MGEN_TENTACLE_CONNECT] = int(head->mid);
1258         if (monster *tentacle = create_monster(mg))
1259         {
1260             if (you.can_see(*tentacle))
1261                 visible_count++;
1262 
1263             tentacle->props["inwards"].get_int() = head->mid;
1264 
1265             if (head->holiness() & MH_UNDEAD)
1266                 tentacle->flags |= MF_FAKE_UNDEAD;
1267         }
1268     }
1269 
1270     if (mons_base_type(*head) == MONS_KRAKEN)
1271     {
1272         if (visible_count == 1)
1273             mpr("A tentacle rises from the water!");
1274         else if (visible_count > 1)
1275             mpr("Tentacles burst out of the water!");
1276     }
1277     else if (head->type == MONS_TENTACLED_STARSPAWN)
1278     {
1279         if (visible_count == 1)
1280             mpr("A tentacle flies out from the starspawn's body!");
1281         else if (visible_count > 1)
1282             mpr("Tentacles burst from the starspawn's body!");
1283     }
1284     return;
1285 }
1286