non-uniques may also get the Abyss treatment if they clear a HD roll.
Crawl tries very hard not to lose banished monsters, preserving them across Abyss shifts and teleports, and saving them on the transit list when the player escapes the Abyss.
Breaks savefile compatibility.
Toned down Vehumet and wizardry boosts a touch.
Fixed bugginess where player could get aux unarmed attacks on a monster that just teleported away (by weapon of distortion).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1052 c06c8d41-db1a-0410-9941-cceddc491573
7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC OUFJSYTIBMN6DSI7A77NJP5S6QDIXALXI2B6OKDPOXEQ5SCDC6MAC 7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC T4IH76FA5TWHFOZUJFHLQXQJENJHWTUZZP4EGNA7D4GTZY7D4ZKAC 5ASC3STDYCNLZFEBN6UTMUCGDETHBR2OCBZCF5VIAZ5RRWLOTDYQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC 2EF3QUVPUQAKBTZKLKQ5B73Z26TXX2H2G2MKIMXD7B7BSDCYE7SAC QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC 5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC QDWDUURSNLMT6AXNNJ3DEQCWAKCAIHV6MP5F7QGIBGXOG2BI2NPQC WHY6LRRJ5T2NSBE3IUCR4X3TOAH7TTK5NPUPUIFT7TPNJ6J4HBDAC static void marshall_follower(tagHeader &th, const follower &f){marshall_monster(th, f.mons);for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)marshall_item(th, f.items[i]);}static void unmarshall_follower(tagHeader &th, follower &f){unmarshall_monster(th, f.mons);for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)unmarshall_item(th, f.items[i]);}static void marshall_follower_list(tagHeader &th, const m_transit_list &mlist){marshallShort( th, mlist.size() );for (m_transit_list::const_iterator mi = mlist.begin();mi != mlist.end(); ++mi){marshall_follower( th, *mi );}}static m_transit_list unmarshall_follower_list(tagHeader &th){m_transit_list mlist;const int size = unmarshallShort(th);for (int i = 0; i < size; ++i){follower f;unmarshall_follower(th, f);mlist.push_back(f);}return (mlist);}static void tag_construct_lost_monsters(tagHeader &th){marshallMap( th, the_lost_ones, marshall_level_id,marshall_follower_list );}
}static void marshall_item(tagHeader &th, const item_def &item){marshallByte(th, item.base_type);marshallByte(th, item.sub_type);marshallShort(th, item.plus);marshallShort(th, item.plus2);marshallLong(th, item.special);marshallShort(th, item.quantity);marshallByte(th, item.colour);marshallShort(th, item.x);marshallShort(th, item.y);marshallLong(th, item.flags);marshallShort(th, item.link); // unusedmarshallShort(th, igrd[item.x][item.y]); // unusedmarshallByte(th, item.slot);marshallShort(th, item.orig_place);marshallShort(th, item.orig_monnum);marshallString(th, item.inscription.c_str(), 80);
static void unmarshall_item(tagHeader &th, item_def &item){item.base_type = (unsigned char) unmarshallByte(th);item.sub_type = (unsigned char) unmarshallByte(th);item.plus = unmarshallShort(th);item.plus2 = unmarshallShort(th);item.special = unmarshallLong(th);item.quantity = unmarshallShort(th);item.colour = (unsigned char) unmarshallByte(th);item.x = unmarshallShort(th);item.y = unmarshallShort(th);item.flags = (unsigned long) unmarshallLong(th);// [dshaligram] FIXME, remove this kludge when ARM_CAP is fully// integrated.if (item.base_type == OBJ_ARMOUR && item.sub_type == ARM_CAP)item.sub_type = ARM_HELMET;unmarshallShort(th); // mitm[].link -- unusedunmarshallShort(th); // igrd[item.x][item.y] -- unuseditem.slot = unmarshallByte(th);item.inscription.clear();item.orig_place = unmarshallShort(th);item.orig_monnum = unmarshallShort(th);char insstring[80];unmarshallString(th, insstring, 80);item.inscription = std::string(insstring);}
marshallByte(th, mitm[i].colour);marshallShort(th, mitm[i].x);marshallShort(th, mitm[i].y);marshallLong(th, mitm[i].flags);
static void marshall_monster(tagHeader &th, const monsters &m){marshallByte(th, m.ac);marshallByte(th, m.ev);marshallByte(th, m.hit_dice);marshallByte(th, m.speed);marshallByte(th, m.speed_increment);marshallByte(th, m.behaviour);marshallByte(th, m.x);marshallByte(th, m.y);marshallByte(th, m.target_x);marshallByte(th, m.target_y);marshallLong(th, m.flags);
marshallByte(th, mitm[i].slot);
marshallShort(th, m.type);marshallShort(th, m.hit_points);marshallShort(th, m.max_hit_points);marshallShort(th, m.number);marshallShort(th, m.colour);for (int j = 0; j < NUM_MONSTER_SLOTS; j++)marshallShort(th, m.inv[j]);
marshallShort(th, mitm[i].orig_place);marshallShort(th, mitm[i].orig_monnum);marshallString(th, mitm[i].inscription.c_str(), 80);
for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)marshallShort(th, m.spells[j]);marshallByte(th, m.god);if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON){// *Must* have ghost field set.ASSERT(m.ghost.get());marshallGhost(th, *m.ghost);
marshallByte(th, m.ac);marshallByte(th, m.ev);marshallByte(th, m.hit_dice);marshallByte(th, m.speed);marshallByte(th, m.speed_increment);marshallByte(th, m.behaviour);marshallByte(th, m.x);marshallByte(th, m.y);marshallByte(th, m.target_x);marshallByte(th, m.target_y);marshallLong(th, m.flags);for (j = 0; j < NUM_MON_ENCHANTS; j++)marshallByte(th, m.enchantment[j]);marshallShort(th, m.type);marshallShort(th, m.hit_points);marshallShort(th, m.max_hit_points);marshallShort(th, m.number);marshallShort(th, m.colour);for (j = 0; j < NUM_MONSTER_SLOTS; j++)marshallShort(th, m.inv[j]);for (j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)marshallShort(th, m.spells[j]);marshallByte(th, m.god);if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON){// *Must* have ghost field set.ASSERT(m.ghost.get());marshallGhost(th, *m.ghost);}}
for (int i = 0; i < MAX_MONSTERS; i++)marshall_monster(th, menv[i]);
{mitm[i].base_type = (unsigned char) unmarshallByte(th);mitm[i].sub_type = (unsigned char) unmarshallByte(th);mitm[i].plus = unmarshallShort(th);mitm[i].plus2 = unmarshallShort(th);mitm[i].special = unmarshallLong(th);mitm[i].quantity = unmarshallShort(th);mitm[i].colour = (unsigned char) unmarshallByte(th);mitm[i].x = unmarshallShort(th);mitm[i].y = unmarshallShort(th);mitm[i].flags = (unsigned long) unmarshallLong(th);// [dshaligram] FIXME, remove this kludge when ARM_CAP is fully// integrated.if (mitm[i].base_type == OBJ_ARMOUR && mitm[i].sub_type == ARM_CAP)mitm[i].sub_type = ARM_HELMET;
unmarshall_item(th, mitm[i]);}
unmarshallShort(th); // mitm[].link -- unusedunmarshallShort(th); // igrd[mitm[i].x][mitm[i].y] -- unused
static void unmarshall_monster(tagHeader &th, monsters &m){m.ac = unmarshallByte(th);m.ev = unmarshallByte(th);m.hit_dice = unmarshallByte(th);m.speed = unmarshallByte(th);// Avoid sign extension when loading files (Elethiomel's hang)m.speed_increment = (unsigned char) unmarshallByte(th);m.behaviour = unmarshallByte(th);m.x = unmarshallByte(th);m.y = unmarshallByte(th);m.target_x = unmarshallByte(th);m.target_y = unmarshallByte(th);m.flags = unmarshallLong(th);
mitm[i].slot = unmarshallByte(th);mitm[i].inscription.clear();
for (int j = 0; j < NUM_MON_ENCHANTS; j++)m.enchantment[j] = unmarshallByte(th);m.type = unmarshallShort(th);m.hit_points = unmarshallShort(th);m.max_hit_points = unmarshallShort(th);m.number = unmarshallShort(th);
mitm[i].orig_place = unmarshallShort(th);mitm[i].orig_monnum = unmarshallShort(th);char insstring[80];unmarshallString(th, insstring, 80);mitm[i].inscription = std::string(insstring);}
m.colour = unmarshallShort(th);for (int j = 0; j < NUM_MONSTER_SLOTS; j++)m.inv[j] = unmarshallShort(th);for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)m.spells[j] = unmarshallShort(th);m.god = (god_type) unmarshallByte(th);if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON)m.set_ghost( unmarshallGhost(th) );
m.ac = unmarshallByte(th);m.ev = unmarshallByte(th);m.hit_dice = unmarshallByte(th);m.speed = unmarshallByte(th);// Avoid sign extension when loading files (Elethiomel's hang)m.speed_increment = (unsigned char) unmarshallByte(th);m.behaviour = unmarshallByte(th);m.x = unmarshallByte(th);m.y = unmarshallByte(th);m.target_x = unmarshallByte(th);m.target_y = unmarshallByte(th);m.flags = unmarshallLong(th);for (j = 0; j < ecount; j++)m.enchantment[j] = unmarshallByte(th);m.type = unmarshallShort(th);m.hit_points = unmarshallShort(th);m.max_hit_points = unmarshallShort(th);m.number = unmarshallShort(th);m.colour = unmarshallShort(th);for (j = 0; j < icount; j++)m.inv[j] = unmarshallShort(th);for (j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)m.spells[j] = unmarshallShort(th);m.god = (god_type) unmarshallByte(th);if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON)m.set_ghost( unmarshallGhost(th) );
unmarshall_monster(th, m);
/** File: mtransit.h* Summary: Tracking monsters in transit between levels.* Written by: Darshan Shaligram** Modified for Crawl Reference by $Author: dshaligram $ on $Date: 2007-03-15T20:10:20.648083Z $*/#ifndef MTRANSIT_H#define MTRANSIT_H#include "AppHdr.h"#include "travel.h"#include <map>#include <list>struct follower{monsters mons;FixedVector<item_def, NUM_MONSTER_SLOTS> items;follower() : mons(), items() { }follower(const monsters &m);bool place(bool near_player = false);void load_mons_items();void restore_mons_items(monsters &m);};typedef std::list<follower> m_transit_list;typedef std::map<level_id, m_transit_list> monsters_in_transit;extern monsters_in_transit the_lost_ones;void add_monster_to_transit(level_id dest, const monsters &m);// Places (some of the) monsters eligible to be placed on this level.void place_transiting_monsters();#endif
/** File: mtransit.cc* Summary: Tracks monsters that are in suspended animation between levels.* Written by: Darshan Shaligram** Modified for Crawl Reference by $Author: dshaligram $ on $Date: 2007-03-15T20:10:20.648083Z $*/#include "AppHdr.h"#include "mtransit.h"#include "items.h"#include "mon-util.h"#include "stuff.h"#define MAX_LOST 100monsters_in_transit the_lost_ones;static void place_lost_monsters(m_transit_list &m);static void cull_lost(m_transit_list &mlist, int how_many){// First pass, drop non-uniques.m_transit_list::iterator i = mlist.begin();for ( ; i != mlist.end(); ){m_transit_list::iterator finger = i++;if (!mons_is_unique(finger->mons.type)){mlist.erase(finger);if (--how_many <= 0)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() );}void add_monster_to_transit(level_id lid, const monsters &m){m_transit_list &mlist = the_lost_ones[lid];mlist.push_back(m);#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS, "Monster in transit: %s",m.name(DESC_PLAIN).c_str());#endifconst int how_many = mlist.size();if (how_many > MAX_LOST)cull_lost(mlist, how_many);}void place_transiting_monsters(){level_id c = level_id::current();monsters_in_transit::iterator i = the_lost_ones.find(c);if (i == the_lost_ones.end())return;place_lost_monsters(i->second);if (i->second.empty())the_lost_ones.erase(i);}static bool place_lost_monster(follower &f){return (f.place(false));}static void place_lost_monsters(m_transit_list &m){for (m_transit_list::iterator i = m.begin();i != m.end(); ){m_transit_list::iterator mon = i++;// Transiting monsters have a 50% chance of being placed.if (coinflip())continue;if (place_lost_monster(*mon))m.erase(mon);}}//////////////////////////////////////////////////////////////////////////// followerfollower::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] ];elseitems[i].clear();}bool follower::place(bool near_player){for (int i = 0; i < MAX_MONSTERS - 5; ++i){monsters &m = menv[i];if (m.alive())continue;m = mons;if (m.find_place_to_live(near_player)){#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS, "Placed follower: %s",m.name(DESC_PLAIN).c_str());#endifm.target_x = m.target_y = 0;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.x = it.y = 0;it.link = NON_ITEM;}}}
find_place_to_live();}bool monsters::find_home_in(coord_def s, coord_def e){for (int iy = s.y; iy <= e.y; ++iy){for (int ix = s.x; ix <= e.x; ++ix){if (!in_bounds(ix, iy))continue;if (ix == you.x_pos && iy == you.y_pos)continue;if (mgrd[ix][iy] != NON_MONSTER || grd[ix][iy] < DNGN_FLOOR)continue;x = ix;y = iy;return (true);}}return (false);}
while ((grd[x][y] != DNGN_FLOOR)|| (mgrd[x][y] != NON_MONSTER));
while ((grd[x][y] != DNGN_FLOOR|| mgrd[x][y] != NON_MONSTER)&& tries-- > 0);return (tries >= 0);}bool monsters::find_place_to_live(bool near_player){if ((near_player && find_place_near_player())|| find_home_anywhere()){mgrd[x][y] = monster_index(this);return (true);}return (false);}
bool monsters::needs_transit() const{return ((mons_is_unique(type)|| (flags & MF_BANISHED)|| (you.level_type == LEVEL_DUNGEON && hit_dice > 8 + random2(25)))&& !mons_has_ench(this, ENCH_ABJ_I, ENCH_ABJ_VI));}void monsters::set_transit(level_id dest){add_monster_to_transit(dest, *this);}
for (int minvc = 0; minvc < NUM_MONSTER_SLOTS; ++minvc){const int item = fmenv->inv[minvc];if (item == NON_ITEM){f.items.push_back(item_def());continue;}f.items.push_back(mitm[item]);destroy_item( item );}
for (count_x = you.x_pos - 6; count_x < you.x_pos + 7;count_x++){for (count_y = you.y_pos - 6; count_y < you.y_pos + 7;count_y++){if (ic == 0&& ((count_x < you.x_pos - 1)|| (count_x > you.x_pos + 1)|| (count_y < you.y_pos - 1)|| (count_y > you.y_pos + 1))){continue;}if (count_x == you.x_pos && count_y == you.y_pos)continue;if (mgrd[count_x][count_y] != NON_MONSTER|| grd[count_x][count_y] < DNGN_FLOOR){continue;}while (menv[following].type != -1){following++;if (following >= MAX_MONSTERS)goto out_of_foll;}if (followers.size()){follower f = followers.front();followers.erase(followers.begin());menv[following] = f.mons;menv[following].x = count_x;menv[following].y = count_y;menv[following].target_x = 0;menv[following].target_y = 0;menv[following].flags |= MF_JUST_SUMMONED;for (int minvc = 0; minvc < NUM_MONSTER_SLOTS; minvc++){menv[following].inv[minvc] = NON_ITEM;const item_def &minvitem = f.items[minvc];if (minvitem.base_type != OBJ_UNASSIGNED){int itmf = get_item_slot(0);if (itmf == NON_ITEM){menv[following].inv[minvc] = NON_ITEM;continue;}mitm[itmf] = minvitem;mitm[itmf].x = 0;mitm[itmf].y = 0;mitm[itmf].link = NON_ITEM;menv[following].inv[minvc] = itmf;}}mgrd[count_x][count_y] = following;} // followers.size()}}
follower f = followers.front();followers.erase(followers.begin());f.place(true);
if (menv[i].x < you.x_pos - 10|| menv[i].x >= you.x_pos + 11|| menv[i].y < you.y_pos - 10 || menv[i].y >= you.y_pos + 11){menv[i].type = -1;mgrd[menv[i].x][menv[i].y] = NON_MONSTER;for (unsigned int j = 0; j < NUM_MONSTER_SLOTS; j++){if (menv[i].inv[j] != NON_ITEM){destroy_item( menv[i].inv[j] );menv[i].inv[j] = NON_ITEM;}}}
if (grid_distance(m.x, m.y, you.x_pos, you.y_pos) > 10)abyss_lose_monster(m);