Revert "Track who destroys an item; incur Nemelex penance for deck destruction."
[crawl.git] / crawl-ref / source / mon-transit.cc
1 /**
2  * @file
3  * @brief Tracks monsters that are in suspended animation between levels.
4 **/
5
6 #include "AppHdr.h"
7
8 #include <algorithm>
9
10 #include "mon-transit.h"
11
12 #include "artefact.h"
13 #include "coord.h"
14 #include "coordit.h"
15 #include "dactions.h"
16 #include "dungeon.h"
17 #include "env.h"
18 #include "godcompanions.h"
19 #include "items.h"
20 #include "mon-place.h"
21 #include "mon-util.h"
22 #include "random.h"
23 #include "religion.h"
24 #include "travel.h"
25
26 #define MAX_LOST 100
27
28 monsters_in_transit the_lost_ones;
29 items_in_transit    transiting_items;
30
31 void transit_lists_clear()
32 {
33     the_lost_ones.clear();
34     transiting_items.clear();
35 }
36
37 static void level_place_lost_monsters(m_transit_list &m);
38 static void level_place_followers(m_transit_list &m);
39
40 static void cull_lost_mons(m_transit_list &mlist, int how_many)
41 {
42     // First pass, drop non-uniques.
43     for (m_transit_list::iterator i = mlist.begin(); i != mlist.end();)
44     {
45         m_transit_list::iterator finger = i++;
46         if (!mons_is_unique(finger->mons.type))
47         {
48             mlist.erase(finger);
49
50             if (--how_many <= MAX_LOST)
51                 return;
52         }
53     }
54
55     // If we're still over the limit (unlikely), just lose
56     // the old ones.
57     while (how_many-- > MAX_LOST && !mlist.empty())
58         mlist.erase(mlist.begin());
59 }
60
61 static void cull_lost_items(i_transit_list &ilist, int how_many)
62 {
63     // First pass, drop non-artefacts.
64     for (i_transit_list::iterator i = ilist.begin(); i != ilist.end();)
65     {
66         i_transit_list::iterator finger = i++;
67         if (!is_artefact(*finger))
68         {
69             ilist.erase(finger);
70
71             if (--how_many <= MAX_LOST)
72                 return;
73         }
74     }
75
76     // Second pass, drop randarts.
77     for (i_transit_list::iterator i = ilist.begin(); i != ilist.end();)
78     {
79         i_transit_list::iterator finger = i++;
80         if (is_random_artefact(*finger))
81         {
82             ilist.erase(finger);
83
84             if (--how_many <= MAX_LOST)
85                 return;
86         }
87     }
88
89     // Third pass, drop unrandarts.
90     for (i_transit_list::iterator i = ilist.begin(); i != ilist.end();)
91     {
92         i_transit_list::iterator finger = i++;
93         if (is_unrandom_artefact(*finger)
94             && !is_special_unrandom_artefact(*finger))
95         {
96             ilist.erase(finger);
97
98             if (--how_many <= MAX_LOST)
99                 return;
100         }
101     }
102
103     // If we're still over the limit (unlikely), just lose
104     // the old ones.
105     while (how_many-- > MAX_LOST && !ilist.empty())
106         ilist.erase(ilist.begin());
107 }
108
109 m_transit_list *get_transit_list(const level_id &lid)
110 {
111     monsters_in_transit::iterator i = the_lost_ones.find(lid);
112     return (i != the_lost_ones.end()? &i->second : NULL);
113 }
114
115 void add_monster_to_transit(const level_id &lid, const monster& m)
116 {
117     m_transit_list &mlist = the_lost_ones[lid];
118     mlist.push_back(m);
119
120     dprf("Monster in transit to %s: %s", lid.describe().c_str(),
121          m.name(DESC_PLAIN, true).c_str());
122
123     if (m.is_divine_companion())
124         move_companion_to(&m, lid);
125
126     const int how_many = mlist.size();
127     if (how_many > MAX_LOST)
128         cull_lost_mons(mlist, how_many);
129 }
130
131 void remove_monster_from_transit(const level_id &lid, mid_t mid)
132 {
133     m_transit_list &mlist = the_lost_ones[lid];
134
135     for (m_transit_list::iterator i = mlist.begin(); i != mlist.end(); ++i)
136     {
137         if (i->mons.mid == mid)
138         {
139             mlist.erase(i);
140             return;
141         }
142     }
143 }
144
145 static void _place_lost_ones(void (*placefn)(m_transit_list &ml))
146 {
147     level_id c = level_id::current();
148
149     monsters_in_transit::iterator i = the_lost_ones.find(c);
150     if (i == the_lost_ones.end())
151         return;
152     placefn(i->second);
153     if (i->second.empty())
154         the_lost_ones.erase(i);
155 }
156
157 void place_transiting_monsters()
158 {
159     _place_lost_ones(level_place_lost_monsters);
160 }
161
162 void place_followers()
163 {
164     _place_lost_ones(level_place_followers);
165 }
166
167 static bool place_lost_monster(follower &f)
168 {
169     dprf("Placing lost one: %s", f.mons.name(DESC_PLAIN, true).c_str());
170     return f.place(false);
171 }
172
173 static void level_place_lost_monsters(m_transit_list &m)
174 {
175     for (m_transit_list::iterator i = m.begin();
176          i != m.end();)
177     {
178         m_transit_list::iterator mon = i++;
179
180         // Monsters transiting to the Abyss have a 50% chance of being
181         // placed, otherwise a 100% chance.
182         if (player_in_branch(BRANCH_ABYSS) && coinflip())
183             continue;
184
185         if (place_lost_monster(*mon))
186             m.erase(mon);
187     }
188 }
189
190 static void level_place_followers(m_transit_list &m)
191 {
192     for (m_transit_list::iterator i = m.begin(); i != m.end();)
193     {
194         m_transit_list::iterator mon = i++;
195         if ((mon->mons.flags & MF_TAKING_STAIRS) && mon->place(true))
196         {
197             if (mon->mons.is_divine_companion())
198                 move_companion_to(monster_by_mid(mon->mons.mid), level_id::current());
199             m.erase(mon);
200         }
201     }
202 }
203
204 void add_item_to_transit(const level_id &lid, const item_def &i)
205 {
206     i_transit_list &ilist = transiting_items[lid];
207     ilist.push_back(i);
208
209     dprf("Item in transit: %s", i.name(DESC_PLAIN).c_str());
210
211     const int how_many = ilist.size();
212     if (how_many > MAX_LOST)
213         cull_lost_items(ilist, how_many);
214 }
215
216 void place_transiting_items()
217 {
218     level_id c = level_id::current();
219
220     items_in_transit::iterator i = transiting_items.find(c);
221     if (i == transiting_items.end())
222         return;
223
224     i_transit_list &ilist = i->second;
225     i_transit_list keep;
226     i_transit_list::iterator item;
227
228     for (item = ilist.begin(); item != ilist.end(); ++item)
229     {
230         coord_def pos = item->pos;
231
232         if (!in_bounds(pos))
233             pos = random_in_bounds();
234
235         const coord_def where_to_go =
236             dgn_find_nearby_stair(DNGN_ESCAPE_HATCH_DOWN,
237                                   pos, true);
238
239         // List of items we couldn't place.
240         if (!copy_item_to_grid(*item, where_to_go, 1, false, true))
241             keep.push_back(*item);
242     }
243
244     // Only unplaceable items are kept in list.
245     ilist = keep;
246 }
247
248 void apply_daction_to_transit(daction_type act)
249 {
250     for (monsters_in_transit::iterator i = the_lost_ones.begin();
251             i != the_lost_ones.end(); ++i)
252     {
253         m_transit_list* m = &i->second;
254         for (m_transit_list::iterator j = m->begin(); j != m->end(); ++j)
255         {
256             monster* mon = &j->mons;
257             if (mons_matches_daction(mon, act))
258                 apply_daction_to_mons(mon, act, false);
259         }
260     }
261 }
262
263 int count_daction_in_transit(daction_type act)
264 {
265     int count = 0;
266     for (monsters_in_transit::iterator i = the_lost_ones.begin();
267             i != the_lost_ones.end(); ++i)
268     {
269         m_transit_list* m = &i->second;
270         for (m_transit_list::iterator j = m->begin(); j != m->end(); ++j)
271         {
272             monster* mon = &j->mons;
273             if (mons_matches_daction(mon, act))
274                 count++;
275         }
276     }
277
278     return count;
279 }
280
281 //////////////////////////////////////////////////////////////////////////
282 // follower
283
284 follower::follower(const monster& m) : mons(m), items()
285 {
286     load_mons_items();
287 }
288
289 void follower::load_mons_items()
290 {
291     for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
292         if (mons.inv[i] != NON_ITEM)
293             items[i] = mitm[ mons.inv[i] ];
294         else
295             items[i].clear();
296 }
297
298 bool follower::place(bool near_player)
299 {
300     monster *m = get_free_monster();
301     if (!m)
302         return false;
303
304     // Copy the saved data.
305     *m = mons;
306
307     // Shafts no longer retain the position, if anything else would
308     // want to request a specific one, it should do so here if !near_player
309
310     if (m->find_place_to_live(near_player))
311     {
312         dprf("Placed follower: %s", m->name(DESC_PLAIN, true).c_str());
313         m->target.reset();
314
315         m->flags &= ~MF_TAKING_STAIRS & ~MF_BANISHED;
316         m->flags |= MF_JUST_SUMMONED;
317         restore_mons_items(*m);
318         env.mid_cache[m->mid] = m->mindex();
319         return true;
320     }
321
322     m->reset();
323     return false;
324 }
325
326 void follower::restore_mons_items(monster& m)
327 {
328     for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
329     {
330         if (items[i].base_type == OBJ_UNASSIGNED)
331             m.inv[i] = NON_ITEM;
332         else
333         {
334             const int islot = get_mitm_slot(0);
335             m.inv[i] = islot;
336             if (islot == NON_ITEM)
337                 continue;
338
339             item_def &it = mitm[islot];
340             it = items[i];
341             it.pos.set(-2, -2);
342             it.link = NON_ITEM + 1 + m.mindex();
343         }
344     }
345 }
346
347 static bool _is_religious_follower(const monster* mon)
348 {
349     return ((you.religion == GOD_YREDELEMNUL
350              || you.religion == GOD_BEOGH
351              || you.religion == GOD_FEDHAS)
352                  && is_follower(mon));
353 }
354
355 static bool _tag_follower_at(const coord_def &pos, bool &real_follower)
356 {
357     if (!in_bounds(pos) || pos == you.pos())
358         return false;
359
360     monster* fol = monster_at(pos);
361     if (fol == NULL)
362         return false;
363
364     if (!fol->alive()
365         || fol->speed_increment < 50
366         || fol->incapacitated()
367         || mons_is_boulder(fol)
368         || mons_is_stationary(fol))
369     {
370         return false;
371     }
372
373     if (!monster_habitable_grid(fol, DNGN_FLOOR))
374         return false;
375
376     // Only non-wandering friendly monsters or those actively
377     // seeking the player will follow up/down stairs.
378     if (!fol->friendly()
379           && (!mons_is_seeking(fol) || fol->foe != MHITYOU)
380         || fol->foe == MHITNOT)
381     {
382         return false;
383     }
384
385     // Monsters that are not directly adjacent are subject to more
386     // stringent checks.
387     if ((pos - you.pos()).abs() > 2)
388     {
389         if (!fol->friendly())
390             return false;
391
392         // Undead will follow Yredelemnul worshippers, orcs will follow
393         // Beogh worshippers, and plants will follow Fedhas worshippers.
394         if (!_is_religious_follower(fol))
395             return false;
396     }
397
398     // Monsters that can't use stairs can still be marked as followers
399     // (though they'll be ignored for transit), so any adjacent real
400     // follower can follow through. (jpeg)
401     if (!mons_can_use_stairs(fol))
402     {
403         if (_is_religious_follower(fol))
404         {
405             fol->flags |= MF_TAKING_STAIRS;
406             return true;
407         }
408         return false;
409     }
410
411     real_follower = true;
412
413     // Monster is chasing player through stairs.
414     fol->flags |= MF_TAKING_STAIRS;
415
416     // Clear patrolling/travel markers.
417     fol->patrol_point.reset();
418     fol->travel_path.clear();
419     fol->travel_target = MTRAV_NONE;
420
421     fol->clear_clinging();
422
423     dprf("%s is marked for following.",
424          fol->name(DESC_THE, true).c_str());
425
426     return true;
427 }
428
429 static int follower_tag_radius2()
430 {
431     // If only friendlies are adjacent, we set a max radius of 6, otherwise
432     // only adjacent friendlies may follow.
433     for (adjacent_iterator ai(you.pos()); ai; ++ai)
434     {
435         if (const monster* mon = monster_at(*ai))
436             if (!mon->friendly())
437                 return 2;
438     }
439
440     return (6 * 6);
441 }
442
443 void tag_followers()
444 {
445     const int radius2 = follower_tag_radius2();
446     int n_followers = 18;
447
448     vector<coord_def> places[2];
449     int place_set = 0;
450
451     places[place_set].push_back(you.pos());
452     memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
453     while (!places[place_set].empty())
454     {
455         for (int i = 0, size = places[place_set].size(); i < size; ++i)
456         {
457             const coord_def &p = places[place_set][i];
458             for (adjacent_iterator ai(p); ai; ++ai)
459             {
460                 if ((*ai - you.pos()).abs() > radius2
461                     || travel_point_distance[ai->x][ai->y])
462                 {
463                     continue;
464                 }
465                 travel_point_distance[ai->x][ai->y] = 1;
466
467                 bool real_follower = false;
468                 if (_tag_follower_at(*ai, real_follower))
469                 {
470                     // If we've run out of our follower allowance, bail.
471                     if (real_follower && --n_followers <= 0)
472                         return;
473                     places[!place_set].push_back(*ai);
474                 }
475             }
476         }
477         places[place_set].clear();
478         place_set = !place_set;
479     }
480 }
481
482 void untag_followers()
483 {
484     for (int m = 0; m < MAX_MONSTERS; ++m)
485         menv[m].flags &= (~MF_TAKING_STAIRS);
486 }