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); // unused
marshallShort(th, igrd[item.x][item.y]); // unused
marshallByte(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 -- unused
unmarshallShort(th); // igrd[item.x][item.y] -- unused
item.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 -- unused
unmarshallShort(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 100
monsters_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_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(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);
}
}
//////////////////////////////////////////////////////////////////////////
// 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)
{
monsters &m = menv[i];
if (m.alive())
continue;
m = mons;
if (m.find_place_to_live(near_player))
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Placed follower: %s",
m.name(DESC_PLAIN).c_str());
#endif
m.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);