summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/mon-transit.cc
diff options
context:
space:
mode:
authorVsevolod Kozlov <zaba@thorium.homeunix.org>2009-11-14 12:21:50 +0300
committerVsevolod Kozlov <zaba@thorium.homeunix.org>2009-11-14 12:21:50 +0300
commit6bb806bdcaeb3cd2db10957fb4b7376319faab00 (patch)
tree635215cef631b0ab1ecac146267712f37f62ba38 /crawl-ref/source/mon-transit.cc
parent54c2248f1dd169774698f95c33381a856a915503 (diff)
downloadcrawl-ref-6bb806bdcaeb3cd2db10957fb4b7376319faab00.tar.gz
crawl-ref-6bb806bdcaeb3cd2db10957fb4b7376319faab00.zip
Move mtransit to mon-transit.
Diffstat (limited to 'crawl-ref/source/mon-transit.cc')
-rw-r--r--crawl-ref/source/mon-transit.cc315
1 files changed, 315 insertions, 0 deletions
diff --git a/crawl-ref/source/mon-transit.cc b/crawl-ref/source/mon-transit.cc
new file mode 100644
index 0000000000..0ed945f1b4
--- /dev/null
+++ b/crawl-ref/source/mon-transit.cc
@@ -0,0 +1,315 @@
+/*
+ * File: mon-transit.cc
+ * Summary: Tracks monsters that are in suspended animation between levels.
+ * Written by: Darshan Shaligram
+ */
+
+#include "AppHdr.h"
+
+#include <algorithm>
+
+#include "mon-transit.h"
+
+#include "artefact.h"
+#include "dungeon.h"
+#include "items.h"
+#include "mon-place.h"
+#include "mon-util.h"
+#include "random.h"
+
+#define MAX_LOST 100
+
+monsters_in_transit the_lost_ones;
+items_in_transit transiting_items;
+
+static void level_place_lost_monsters(m_transit_list &m);
+static void level_place_followers(m_transit_list &m);
+
+static void cull_lost_mons(m_transit_list &mlist, int how_many)
+{
+ // First pass, drop non-uniques.
+ for (m_transit_list::iterator i = mlist.begin(); i != mlist.end(); )
+ {
+ m_transit_list::iterator finger = i++;
+ if (!mons_is_unique(finger->mons.type))
+ {
+ mlist.erase(finger);
+
+ if (--how_many <= MAX_LOST)
+ return;
+ }
+ }
+
+ // If we're still over the limit (unlikely), just lose
+ // the old ones.
+ while (how_many-- > MAX_LOST && !mlist.empty())
+ mlist.erase( mlist.begin() );
+}
+
+static void cull_lost_items(i_transit_list &ilist, int how_many)
+{
+ // First pass, drop non-artefacts.
+ for (i_transit_list::iterator i = ilist.begin(); i != ilist.end(); )
+ {
+ i_transit_list::iterator finger = i++;
+ if (!is_artefact(*finger))
+ {
+ ilist.erase(finger);
+
+ if (--how_many <= MAX_LOST)
+ return;
+ }
+ }
+
+ // Second pass, drop randarts.
+ for (i_transit_list::iterator i = ilist.begin(); i != ilist.end(); )
+ {
+ i_transit_list::iterator finger = i++;
+ if (is_random_artefact(*finger))
+ {
+ ilist.erase(finger);
+
+ if (--how_many <= MAX_LOST)
+ return;
+ }
+ }
+
+ // Third pass, drop unrandarts.
+ for (i_transit_list::iterator i = ilist.begin(); i != ilist.end(); )
+ {
+ i_transit_list::iterator finger = i++;
+ if (is_unrandom_artefact(*finger)
+ && !is_special_unrandom_artefact(*finger))
+ {
+ ilist.erase(finger);
+
+ if (--how_many <= MAX_LOST)
+ return;
+ }
+ }
+
+ // If we're still over the limit (unlikely), just lose
+ // the old ones.
+ while (how_many-- > MAX_LOST && !ilist.empty())
+ ilist.erase( ilist.begin() );
+}
+
+m_transit_list *get_transit_list(const level_id &lid)
+{
+ monsters_in_transit::iterator i = the_lost_ones.find(lid);
+ return (i != the_lost_ones.end()? &i->second : NULL);
+}
+
+void add_monster_to_transit(const level_id &lid, const monsters &m)
+{
+ m_transit_list &mlist = the_lost_ones[lid];
+ mlist.push_back(m);
+
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Monster in transit: %s",
+ m.name(DESC_PLAIN).c_str());
+#endif
+
+ const int how_many = mlist.size();
+ if (how_many > MAX_LOST)
+ cull_lost_mons(mlist, how_many);
+}
+
+void place_lost_ones(void (*placefn)(m_transit_list &ml))
+{
+ level_id c = level_id::current();
+
+ monsters_in_transit::iterator i = the_lost_ones.find(c);
+ if (i == the_lost_ones.end())
+ return;
+ placefn(i->second);
+ if (i->second.empty())
+ the_lost_ones.erase(i);
+}
+
+void place_transiting_monsters()
+{
+ place_lost_ones(level_place_lost_monsters);
+}
+
+void place_followers()
+{
+ place_lost_ones(level_place_followers);
+}
+
+static bool place_lost_monster(follower &f)
+{
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Placing lost one: %s",
+ f.mons.name(DESC_PLAIN).c_str());
+#endif
+ return (f.place(false));
+}
+
+static void level_place_lost_monsters(m_transit_list &m)
+{
+ for (m_transit_list::iterator i = m.begin();
+ i != m.end(); )
+ {
+ m_transit_list::iterator mon = i++;
+
+ // Monsters transiting to the Abyss have a 50% chance of being
+ // placed, otherwise a 100% chance.
+ if (you.level_type == LEVEL_ABYSS && coinflip())
+ continue;
+
+ if (place_lost_monster(*mon))
+ m.erase(mon);
+ }
+}
+
+static void level_place_followers(m_transit_list &m)
+{
+ for (m_transit_list::iterator i = m.begin(); i != m.end(); )
+ {
+ m_transit_list::iterator mon = i++;
+ if ((mon->mons.flags & MF_TAKING_STAIRS) && mon->place(true))
+ m.erase(mon);
+ }
+}
+
+void add_item_to_transit(const level_id &lid, const item_def &i)
+{
+ i_transit_list &ilist = transiting_items[lid];
+ ilist.push_back(i);
+
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Item in transit: %s",
+ i.name(DESC_PLAIN).c_str());
+#endif
+
+ const int how_many = ilist.size();
+ if (how_many > MAX_LOST)
+ cull_lost_items(ilist, how_many);
+}
+
+void place_transiting_items()
+{
+ level_id c = level_id::current();
+
+ items_in_transit::iterator i = transiting_items.find(c);
+ if (i == transiting_items.end())
+ return;
+
+ i_transit_list &ilist = i->second;
+ i_transit_list keep;
+ i_transit_list::iterator item;
+
+ for (item = ilist.begin(); item != ilist.end(); item++)
+ {
+ coord_def pos = item->pos;
+
+ if (!in_bounds(pos))
+ pos = random_in_bounds();
+
+ const coord_def where_to_go =
+ dgn_find_nearby_stair(DNGN_ESCAPE_HATCH_DOWN,
+ pos, true);
+
+ // List of items we couldn't place.
+ if (!copy_item_to_grid(*item, where_to_go))
+ keep.push_back(*item);
+ }
+
+ // Only unplaceable items are kept in list.
+ ilist = keep;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// follower
+
+follower::follower(const monsters &m) : mons(m), items()
+{
+ load_mons_items();
+}
+
+void follower::load_mons_items()
+{
+ for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
+ if (mons.inv[i] != NON_ITEM)
+ items[i] = mitm[ mons.inv[i] ];
+ else
+ items[i].clear();
+}
+
+bool follower::place(bool near_player)
+{
+ for (int i = 0; i < MAX_MONSTERS - 5; ++i)
+ {
+ // Find first empty slot in menv and copy monster into it.
+ monsters &m = menv[i];
+ if (m.alive())
+ continue;
+ m = mons;
+
+ bool placed = false;
+
+ // In certain instances (currently, falling through a shaft)
+ // try to place monster a close as possible to its previous
+ // <x,y> coordinates.
+ if (!near_player && you.level_type == LEVEL_DUNGEON
+ && in_bounds(m.pos()))
+ {
+ const coord_def where_to_go =
+ dgn_find_nearby_stair(DNGN_ESCAPE_HATCH_DOWN,
+ m.pos(), true);
+
+ if (where_to_go == you.pos())
+ near_player = true;
+ else if (m.find_home_near_place(where_to_go))
+ {
+ mgrd(m.pos()) = m.mindex();
+ placed = true;
+ }
+ }
+
+ if (!placed)
+ placed = m.find_place_to_live(near_player);
+
+ if (placed)
+ {
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Placed follower: %s",
+ m.name(DESC_PLAIN).c_str());
+#endif
+ m.target.reset();
+
+ m.flags &= ~MF_TAKING_STAIRS;
+ m.flags |= MF_JUST_SUMMONED;
+
+ restore_mons_items(m);
+ return (true);
+ }
+
+ m.reset();
+ break;
+ }
+
+ return (false);
+}
+
+void follower::restore_mons_items(monsters &m)
+{
+ for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
+ {
+ if (items[i].base_type == OBJ_UNASSIGNED)
+ m.inv[i] = NON_ITEM;
+ else
+ {
+ const int islot = get_item_slot(0);
+ m.inv[i] = islot;
+ if (islot == NON_ITEM)
+ continue;
+
+ item_def &it = mitm[islot];
+ it = items[i];
+ it.pos.set(-2, -2);
+ it.link = NON_ITEM + 1 + m.mindex();
+ }
+ }
+}