1 /**
2  * @file
3  * @brief Tracks monsters that are in suspended animation between levels.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-transit.h"
9 
10 #include <algorithm>
11 
12 #include "artefact.h"
13 #include "coordit.h"
14 #include "dactions.h"
15 #include "dungeon.h"
16 #include "god-companions.h"
17 #include "god-passive.h" // passive_t::convert_orcs
18 #include "items.h"
19 #include "libutil.h" // map_find
20 #include "mon-place.h"
21 #include "mpr.h"
22 #include "religion.h"
23 #include "tag-version.h"
24 #include "timed-effects.h"
25 
26 #define MAX_LOST 100
27 
28 monsters_in_transit the_lost_ones;
29 
_cull_lost_mons(m_transit_list & mlist,int how_many)30 static void _cull_lost_mons(m_transit_list &mlist, int how_many)
31 {
32     // First pass, drop non-uniques.
33     for (auto i = mlist.begin(); i != mlist.end();)
34     {
35         auto finger = i++;
36         if (!mons_is_unique(finger->mons.type))
37         {
38             mlist.erase(finger);
39 
40             if (--how_many <= MAX_LOST)
41                 return;
42         }
43     }
44 
45     // If we're still over the limit (unlikely), just lose
46     // the old ones.
47     while (how_many-- > MAX_LOST && !mlist.empty())
48         mlist.erase(mlist.begin());
49 }
50 
51 /**
52  * Get the monster transit list for the given level.
53  * @param lid    The level.
54  * @returns      The monster transit list.
55  **/
get_transit_list(const level_id & lid)56 m_transit_list *get_transit_list(const level_id &lid)
57 {
58     return map_find(the_lost_ones, lid);
59 }
60 
61 /**
62  * Add a monster to a level's transit list.
63  * @param lid    The level.
64  * @param m      The monster to add.
65  **/
add_monster_to_transit(const level_id & lid,const monster & m)66 void add_monster_to_transit(const level_id &lid, const monster& m)
67 {
68     ASSERT(m.alive());
69 
70     m_transit_list &mlist = the_lost_ones[lid];
71     mlist.emplace_back(m);
72     mlist.back().transit_start_time = you.elapsed_time;
73 
74     dprf("Monster in transit to %s: %s", lid.describe().c_str(),
75          m.name(DESC_PLAIN, true).c_str());
76 
77     if (m.is_divine_companion())
78         move_companion_to(&m, lid);
79 
80     const int how_many = mlist.size();
81     if (how_many > MAX_LOST)
82         _cull_lost_mons(mlist, how_many);
83 }
84 
85 /**
86  * Remove a monster from a level's transit list.
87  * @param lid    The level.
88  * @param mid    The mid_t of the monster to remove.
89  **/
remove_monster_from_transit(const level_id & lid,mid_t mid)90 void remove_monster_from_transit(const level_id &lid, mid_t mid)
91 {
92     m_transit_list &mlist = the_lost_ones[lid];
93 
94     for (auto i = mlist.begin(); i != mlist.end(); ++i)
95     {
96         if (i->mons.mid == mid)
97         {
98             mlist.erase(i);
99             return;
100         }
101     }
102 }
103 
_level_place_followers(m_transit_list & m)104 static void _level_place_followers(m_transit_list &m)
105 {
106     for (auto i = m.begin(); i != m.end();)
107     {
108         auto mon = i++;
109         if ((mon->mons.flags & MF_TAKING_STAIRS) && mon->place(true))
110         {
111             if (mon->mons.is_divine_companion())
112             {
113                 move_companion_to(monster_by_mid(mon->mons.mid),
114                                                  level_id::current());
115             }
116 
117             // Now that the monster is onlevel, we can safely apply traps to it.
118             if (monster* new_mon = monster_by_mid(mon->mons.mid))
119                 // old loc isn't really meaningful
120                 new_mon->apply_location_effects(new_mon->pos());
121             m.erase(mon);
122         }
123     }
124 }
125 
_place_lost_ones(void (* placefn)(m_transit_list & ml))126 static void _place_lost_ones(void (*placefn)(m_transit_list &ml))
127 {
128     level_id c = level_id::current();
129 
130     monsters_in_transit::iterator i = the_lost_ones.find(c);
131     if (i == the_lost_ones.end())
132         return;
133     placefn(i->second);
134     if (i->second.empty())
135         the_lost_ones.erase(i);
136 }
137 
138 /**
139  * Place any followers transiting to this level.
140  **/
place_followers()141 void place_followers()
142 {
143     _place_lost_ones(_level_place_followers);
144 }
145 
_place_lost_monster(follower & f)146 static monster* _place_lost_monster(follower &f)
147 {
148     dprf("Placing lost one: %s", f.mons.name(DESC_PLAIN, true).c_str());
149     if (monster* mons = f.place(false))
150     {
151         // Figure out how many turns we need to update the monster
152         int turns = (you.elapsed_time - f.transit_start_time)/10;
153 
154         //Unflag as summoned or else monster will be ignored in update_monster
155         mons->flags &= ~MF_JUST_SUMMONED;
156         return update_monster(*mons, turns);
157     }
158     else
159         return nullptr;
160 }
161 
_level_place_lost_monsters(m_transit_list & m)162 static void _level_place_lost_monsters(m_transit_list &m)
163 {
164     for (auto i = m.begin(); i != m.end(); )
165     {
166         auto mon = i++;
167 
168         // Monsters transiting to the Abyss have a 50% chance of being
169         // placed, otherwise a 100% chance.
170         if (player_in_branch(BRANCH_ABYSS) && coinflip())
171             continue;
172 
173         if (monster* new_mon =_place_lost_monster(*mon))
174         {
175             // Now that the monster is on the level, we can safely apply traps
176             // to it.
177             new_mon->apply_location_effects(new_mon->pos());
178             m.erase(mon);
179         }
180     }
181 }
182 
183 /**
184  * Place any monsters in transit to this level.
185  **/
place_transiting_monsters()186 void place_transiting_monsters()
187 {
188     _place_lost_ones(_level_place_lost_monsters);
189 }
190 
apply_daction_to_transit(daction_type act)191 void apply_daction_to_transit(daction_type act)
192 {
193     for (auto &entry : the_lost_ones)
194     {
195         m_transit_list* m = &entry.second;
196         for (auto j = m->begin(); j != m->end(); ++j)
197         {
198             monster* mon = &j->mons;
199             if (mons_matches_daction(mon, act))
200                 apply_daction_to_mons(mon, act, false, true);
201 
202             // If that killed the monster, remove it from transit.
203             // Removing this monster invalidates the iterator that
204             // points to it, so decrement the iterator first.
205             if (!mon->alive())
206                 m->erase(j--);
207         }
208     }
209 }
210 
count_daction_in_transit(daction_type act)211 int count_daction_in_transit(daction_type act)
212 {
213     int count = 0;
214     for (const auto &entry : the_lost_ones)
215     {
216         for (const auto &follower : entry.second)
217             if (mons_matches_daction(&follower.mons, act))
218                 count++;
219     }
220 
221     return count;
222 }
223 
224 //////////////////////////////////////////////////////////////////////////
225 // follower
226 
follower(const monster & m)227 follower::follower(const monster& m) : mons(m), items()
228 {
229     ASSERT(m.alive());
230     load_mons_items();
231 }
232 
load_mons_items()233 void follower::load_mons_items()
234 {
235     for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
236         if (mons.inv[i] != NON_ITEM)
237             items[i] = env.item[ mons.inv[i] ];
238         else
239             items[i].clear();
240 }
241 
place(bool near_player)242 monster* follower::place(bool near_player)
243 {
244     ASSERT(mons.alive());
245 
246     monster *m = get_free_monster();
247     if (!m)
248         return nullptr;
249 
250     // Copy the saved data.
251     *m = mons;
252 
253     // Shafts no longer retain the position, if anything else would
254     // want to request a specific one, it should do so here if !near_player
255 
256     if (m->find_place_to_live(near_player))
257     {
258 #if TAG_MAJOR_VERSION == 34
259         // fix up some potential cloned monsters for beogh chars.
260         // see comments on maybe_bad_priest monster and in tags.cc for details
261         monster *dup_m = monster_by_mid(m->mid);
262         if (dup_m && maybe_bad_priest_monster(*dup_m))
263             fixup_bad_priest_monster(*dup_m);
264         // any clones not covered under maybe_bad_priest_monster will result
265         // in duplicate mid errors.
266 #endif
267         dprf("Placed follower: %s", m->name(DESC_PLAIN, true).c_str());
268         m->target.reset();
269 
270         m->flags &= ~MF_TAKING_STAIRS & ~MF_BANISHED;
271         m->flags |= MF_JUST_SUMMONED;
272         restore_mons_items(*m);
273         env.mid_cache[m->mid] = m->mindex();
274         return m;
275     }
276 
277     m->reset();
278     return nullptr;
279 }
280 
restore_mons_items(monster & m)281 void follower::restore_mons_items(monster& m)
282 {
283     for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
284     {
285         if (items[i].base_type == OBJ_UNASSIGNED)
286             m.inv[i] = NON_ITEM;
287         else
288         {
289             const int islot = get_mitm_slot(0);
290             m.inv[i] = islot;
291             if (islot == NON_ITEM)
292                 continue;
293 
294             item_def &it = env.item[islot];
295             it = items[i];
296             it.set_holding_monster(m);
297         }
298     }
299 }
300 
_is_religious_follower(const monster & mon)301 static bool _is_religious_follower(const monster &mon)
302 {
303     return (you_worship(GOD_YREDELEMNUL)
304             || will_have_passive(passive_t::convert_orcs)
305             || you_worship(GOD_FEDHAS))
306                 && is_follower(mon);
307 }
308 
_mons_can_follow_player_from(const monster & mons,const coord_def from,bool within_level=false)309 static bool _mons_can_follow_player_from(const monster &mons,
310                                          const coord_def from,
311                                          bool within_level = false)
312 {
313     if (!mons.alive()
314         || mons.speed_increment < 50
315         || mons.incapacitated()
316         || mons.is_stationary())
317     {
318         return false;
319     }
320 
321     if (!monster_habitable_grid(&mons, DNGN_FLOOR))
322         return false;
323 
324     // Only non-wandering friendly monsters or those actively
325     // seeking the player will follow up/down stairs.
326     if (!mons.friendly()
327           && (!mons_is_seeking(mons) || mons.foe != MHITYOU)
328         || mons.foe == MHITNOT)
329     {
330         return false;
331     }
332 
333     // Unfriendly monsters must be directly adjacent to follow.
334     if (!mons.friendly() && (mons.pos() - from).rdist() > 1)
335         return false;
336 
337     // Monsters that can't use stairs can still be marked as followers
338     // (though they'll be ignored for transit), so any adjacent real
339     // follower can follow through. (jpeg)
340     if (within_level && !mons_class_can_use_transporter(mons.type)
341         || !within_level && !mons_can_use_stairs(mons, env.grid(from)))
342     {
343         if (_is_religious_follower(mons))
344             return true;
345 
346         return false;
347     }
348     return true;
349 }
350 
351 // Tag any monster following the player
_tag_follower_at(const coord_def & pos,const coord_def & from,bool & real_follower)352 static bool _tag_follower_at(const coord_def &pos, const coord_def &from,
353                              bool &real_follower)
354 {
355     if (!in_bounds(pos) || pos == from)
356         return false;
357 
358     monster* fol = monster_at(pos);
359     if (fol == nullptr)
360         return false;
361 
362     if (!_mons_can_follow_player_from(*fol, from))
363         return false;
364 
365     real_follower = true;
366     fol->flags |= MF_TAKING_STAIRS;
367 
368     // Clear patrolling/travel markers.
369     fol->patrol_point.reset();
370     fol->travel_path.clear();
371     fol->travel_target = MTRAV_NONE;
372 
373     dprf("%s is marked for following.", fol->name(DESC_THE, true).c_str());
374     return true;
375 }
376 
_follower_tag_radius(const coord_def & from)377 static int _follower_tag_radius(const coord_def &from)
378 {
379     // If only friendlies are adjacent, we set a max radius of 5, otherwise
380     // only adjacent friendlies may follow.
381     for (adjacent_iterator ai(from); ai; ++ai)
382     {
383         if (const monster* mon = monster_at(*ai))
384             if (!mon->friendly())
385                 return 1;
386     }
387 
388     return 5;
389 }
390 
391 /**
392  * Handle movement of adjacent player followers from a given location. This is
393  * used when traveling through stairs or a transporter.
394  *
395  * @param from       The location from which the player moved.
396  * @param handler    A handler function that does movement of the actor to the
397  *                   destination, returning true if the actor was friendly. The
398  *                   `real` argument tracks whether the actor was an actual
399  *                   follower that counts towards the follower limit.
400  **/
handle_followers(const coord_def & from,bool (* handler)(const coord_def & pos,const coord_def & from,bool & real))401 void handle_followers(const coord_def &from,
402                       bool (*handler)(const coord_def &pos,
403                                       const coord_def &from, bool &real))
404 {
405     const int radius = _follower_tag_radius(from);
406     int n_followers = 18;
407 
408     vector<coord_def> places[2];
409     int place_set = 0;
410 
411     bool visited[GXM][GYM];
412     memset(&visited, 0, sizeof(visited));
413 
414     places[place_set].push_back(from);
415     while (!places[place_set].empty())
416     {
417         for (const coord_def &p : places[place_set])
418         {
419             for (adjacent_iterator ai(p); ai; ++ai)
420             {
421                 if ((*ai - from).rdist() > radius
422                     || visited[ai->x][ai->y])
423                 {
424                     continue;
425                 }
426                 visited[ai->x][ai->y] = true;
427 
428                 bool real_follower = false;
429                 if (handler(*ai, from, real_follower))
430                 {
431                     // If we've run out of our follower allowance, bail.
432                     if (real_follower && --n_followers <= 0)
433                         return;
434                     places[!place_set].push_back(*ai);
435                 }
436             }
437         }
438         places[place_set].clear();
439         place_set = !place_set;
440     }
441 }
442 
443 /**
444  * Tag all followers at your position for level transit through stairs.
445  **/
tag_followers()446 void tag_followers()
447 {
448     handle_followers(you.pos(), _tag_follower_at);
449 }
450 
451 /**
452  * Untag all followers at your position for level transit through stairs.
453  **/
untag_followers()454 void untag_followers()
455 {
456     for (auto &mons : menv_real)
457         mons.flags &= ~MF_TAKING_STAIRS;
458 }
459 
_transport_follower_at(const coord_def & pos,const coord_def & from,bool & real_follower)460 static bool _transport_follower_at(const coord_def &pos, const coord_def &from,
461                                    bool &real_follower)
462 {
463     if (!in_bounds(pos) || pos == from)
464         return false;
465 
466     monster* fol = monster_at(pos);
467     if (fol == nullptr)
468         return false;
469 
470     if (!_mons_can_follow_player_from(*fol, from, true))
471         return false;
472 
473     if (fol->find_place_to_live(true))
474     {
475         real_follower = true;
476         env.map_knowledge(pos).clear_monster();
477         dprf("%s is transported.", fol->name(DESC_THE, true).c_str());
478     }
479 
480     return true;
481 }
482 
483 /**
484  * Transport all followers from a position to a position near your current one.
485  *
486  * @param from    The position from where you transported.
487  **/
transport_followers_from(const coord_def & from)488 void transport_followers_from(const coord_def &from)
489 {
490     handle_followers(from, _transport_follower_at);
491 }
492