#include "AppHdr.h"
#ifdef USE_TILE
#include "env.h"
#include "tilemcache.h"
#include "tags.h"
#include "ghost.h"
#include "mon-util.h"
#include "tiledef-player.h"
mcache_manager mcache;
// Used internally for streaming.
enum mcache_type
{
MCACHE_MONSTER,
MCACHE_DRACO,
MCACHE_GHOST,
MCACHE_DEMON,
MCACHE_MAX,
MCACHE_NULL
};
// Custom marshall/unmarshall functions.
static void unmarshallDoll(reader &th, dolls_data &doll)
{
for (unsigned int i = 0; i < TILEP_PART_MAX; i++)
doll.parts[i] = unmarshallLong(th);
}
static void marshallDoll(writer &th, const dolls_data &doll)
{
for (unsigned int i = 0; i < TILEP_PART_MAX; i++)
marshallLong(th, doll.parts[i]);
}
static void unmarshallDemon(reader &th, demon_data &demon)
{
demon.head = unmarshallLong(th);
demon.body = unmarshallLong(th);
demon.wings = unmarshallLong(th);
}
static void marshallDemon(writer &th, const demon_data &demon)
{
marshallLong(th, demon.head);
marshallLong(th, demon.body);
marshallLong(th, demon.wings);
}
// Internal mcache classes. The mcache_manager creates these internally.
// The only access external clients need is through the virtual
// info function.
class mcache_monster : public mcache_entry
{
public:
mcache_monster(const monsters *mon);
mcache_monster(reader &th);
virtual unsigned int info(tile_draw_info *dinfo) const;
static bool valid(const monsters *mon);
static bool get_weapon_offset(int mon_tile, int &ofs_x, int &ofs_y);
virtual void construct(writer &th);
protected:
int m_mon_tile;
int m_equ_tile;
};
class mcache_draco : public mcache_entry
{
public:
mcache_draco(const monsters *mon);
mcache_draco(reader &th);
virtual unsigned int info(tile_draw_info *dinfo) const;
static bool valid(const monsters *mon);
virtual void construct(writer &th);
protected:
int m_mon_tile;
int m_job_tile;
int m_equ_tile;
};
class mcache_ghost : public mcache_entry
{
public:
mcache_ghost(const monsters *mon);
mcache_ghost(reader &th);
virtual const dolls_data *doll() const;
static bool valid(const monsters *mon);
virtual void construct(writer &th);
protected:
dolls_data m_doll;
};
class mcache_demon : public mcache_entry
{
public:
mcache_demon(const monsters *mon);
mcache_demon(reader &th);
virtual unsigned int info(tile_draw_info *dinfo) const;
static bool valid(const monsters *mon);
virtual void construct(writer &th);
protected:
demon_data m_demon;
};
/////////////////////////////////////////////////////////////////////////////
// tile_fg_store
unsigned int tile_fg_store::operator=(unsigned int tile)
{
if ((tile & TILE_FLAG_MASK) == (m_tile & TILE_FLAG_MASK))
{
// Update, as flags may have changed.
m_tile = tile;
return m_tile;
}
mcache_entry *old_entry = mcache.get(m_tile);
if (old_entry)
old_entry->dec_ref();
m_tile = tile;
mcache_entry *new_entry = mcache.get(m_tile);
if (new_entry)
new_entry->inc_ref();
return m_tile;
}
/////////////////////////////////////////////////////////////////////////////
// mcache_manager
mcache_manager::~mcache_manager()
{
clear_all();
}
unsigned int mcache_manager::register_monster(const monsters *mon)
{
assert(mon);
if (!mon)
return 0;
// TODO enne - is it worth it to search against all mcache entries?
// TODO enne - pool mcache types to avoid too much alloc/dealloc?
mcache_entry *entry;
if (mcache_demon::valid(mon))
entry = new mcache_demon(mon);
else if (mcache_ghost::valid(mon))
entry = new mcache_ghost(mon);
else if (mcache_draco::valid(mon))
entry = new mcache_draco(mon);
else if (mcache_monster::valid(mon))
entry = new mcache_monster(mon);
else
return 0;
unsigned int idx = ~0;
for (unsigned int i = 0; i < m_entries.size(); i++)
{
if (!m_entries[i])
{
m_entries[i] = entry;
idx = i;
break;
}
}
if (idx > m_entries.size())
{
idx = m_entries.size();
m_entries.push_back(entry);
}
return (TILEP_MCACHE_START + idx);
}
void mcache_manager::clear_nonref()
{
for (unsigned int i = 0; i < m_entries.size(); i++)
{
if (!m_entries[i] || m_entries[i]->ref_count() > 0)
continue;
delete m_entries[i];
m_entries[i] = NULL;
}
}
void mcache_manager::clear_all()
{
for (unsigned int i = 0; i < m_entries.size(); i++)
delete m_entries[i];
m_entries.resize(0);
}
mcache_entry *mcache_manager::get(unsigned int tile)
{
unsigned int idx = tile & TILE_FLAG_MASK;
if (idx < TILEP_MCACHE_START)
return NULL;
if (idx >= TILEP_MCACHE_START + m_entries.size())
return NULL;
mcache_entry *entry = m_entries[idx - TILEP_MCACHE_START];
return (entry);
}
void mcache_manager::read(reader &th)
{
unsigned int size = unmarshallLong(th);
m_entries.reserve(size);
m_entries.clear();
for (unsigned int i = 0; i < size; i++)
{
char type = unmarshallByte(th);
mcache_entry *entry;
switch (type)
{
case MCACHE_MONSTER:
entry = new mcache_monster(th);
break;
case MCACHE_DRACO:
entry = new mcache_draco(th);
break;
case MCACHE_GHOST:
entry = new mcache_ghost(th);
break;
case MCACHE_DEMON:
entry = new mcache_demon(th);
break;
default:
ASSERT(!"Invalid streamed mcache type.");
case MCACHE_NULL:
entry = NULL;
break;
}
m_entries.push_back(entry);
}
}
void mcache_manager::construct(writer &th)
{
marshallLong(th, m_entries.size());
for (unsigned int i = 0; i < m_entries.size(); i++)
{
if (m_entries[i] == NULL)
{
marshallByte(th, MCACHE_NULL);
continue;
}
if (dynamic_cast<mcache_monster*>(m_entries[i]))
marshallByte(th, MCACHE_MONSTER);
else if (dynamic_cast<mcache_draco*>(m_entries[i]))
marshallByte(th, MCACHE_DRACO);
else if (dynamic_cast<mcache_ghost*>(m_entries[i]))
marshallByte(th, MCACHE_GHOST);
else if (dynamic_cast<mcache_demon*>(m_entries[i]))
marshallByte(th, MCACHE_DEMON);
else
{
marshallByte(th, MCACHE_NULL);
continue;
}
m_entries[i]->construct(th);
}
}
/////////////////////////////////////////////////////////////////////////////
// mcache_entry
mcache_entry::mcache_entry(reader &th)
{
m_ref_count = unmarshallLong(th);
}
void mcache_entry::construct(writer &th)
{
marshallLong(th, m_ref_count);
}
/////////////////////////////////////////////////////////////////////////////
// mcache_monster
mcache_monster::mcache_monster(const monsters *mon)
{
assert(mcache_monster::valid(mon));
m_mon_tile = tileidx_monster(mon, false) & TILE_FLAG_MASK;
int mon_wep = mon->inv[MSLOT_WEAPON];
m_equ_tile = tilep_equ_weapon(mitm[mon_wep]);
}
// Returns the amount of pixels necessary to shift a wielded weapon
// from its default placement. Tiles showing monsters already wielding
// a weapon should not be listed here.
bool mcache_monster::get_weapon_offset(int mon_tile, int &ofs_x, int &ofs_y)
{
switch (mon_tile)
{
// No shift necessary.
case TILEP_MONS_VAULT_GUARD:
case TILEP_MONS_HOBGOBLIN:
case TILEP_MONS_DEEP_ELF_MASTER_ARCHER:
case TILEP_MONS_DEEP_ELF_BLADEMASTER:
case TILEP_MONS_IMP:
case TILEP_MONS_NORRIS:
case TILEP_MONS_MAUD:
case TILEP_MONS_DUANE:
case TILEP_MONS_EDMUND:
case TILEP_MONS_FRANCES:
case TILEP_MONS_HAROLD:
case TILEP_MONS_JOSEPH:
case TILEP_MONS_JOZEF:
case TILEP_MONS_RUPERT:
case TILEP_MONS_TERENCE:
case TILEP_MONS_WAYNE:
case TILEP_MONS_FREDERICK:
case TILEP_MONS_RAKSHASA:
case TILEP_MONS_RAKSHASA_FAKE:
case TILEP_MONS_VAMPIRE_KNIGHT:
case TILEP_MONS_SKELETAL_WARRIOR:
case TILEP_MONS_MERFOLK:
case TILEP_MONS_MERFOLK_WATER:
case TILEP_MONS_MERFOLK_JAVELINEER:
case TILEP_MONS_MERFOLK_JAVELINEER_WATER:
case TILEP_MONS_MERFOLK_IMPALER:
case TILEP_MONS_MERFOLK_IMPALER_WATER:
case TILEP_MONS_MERFOLK_AQUAMANCER:
case TILEP_MONS_MERFOLK_AQUAMANCER_WATER:
case TILEP_MONS_MERMAID:
case TILEP_MONS_MERMAID_WATER:
case TILEP_MONS_SIREN:
case TILEP_MONS_SIREN_WATER:
case TILEP_MONS_ILSUIW:
case TILEP_MONS_ILSUIW_WATER:
ofs_x = 0;
ofs_y = 0;
break;
// Shift to the left.
case TILEP_MONS_GNOLL:
case TILEP_MONS_GRUM:
case TILEP_MONS_CRAZY_YIUF:
case TILEP_MONS_DEEP_ELF_DEATH_MAGE:
ofs_x = -1;
ofs_y = 0;
break;
case TILEP_MONS_TIAMAT:
ofs_x = -2;
ofs_y = 0;
break;
// Shift to the right.
case TILEP_MONS_YAKTAUR_MELEE:
ofs_x = 2;
ofs_y = 0;
break;
case TILEP_MONS_YAKTAUR_CAPTAIN_MELEE:
ofs_x = 4;
ofs_y = 0;
break;
// Shift upwards.
case TILEP_MONS_CENTAUR_WARRIOR_MELEE:
case TILEP_MONS_DEEP_ELF_SORCERER:
case TILEP_MONS_DEEP_ELF_HIGH_PRIEST:
ofs_x = 0;
ofs_y = -1;
break;
case TILEP_MONS_MIDGE:
ofs_x = 0;
ofs_y = -2;
break;
case TILEP_MONS_KOBOLD_DEMONOLOGIST:
ofs_x = 0;
ofs_y = -10;
break;
// Shift downwards.
case TILEP_MONS_DEEP_ELF_KNIGHT:
case TILEP_MONS_NAGA:
case TILEP_MONS_GREATER_NAGA:
case TILEP_MONS_NAGA_WARRIOR:
case TILEP_MONS_GUARDIAN_SERPENT:
case TILEP_MONS_NAGA_MAGE:
ofs_x = 0;
ofs_y = 1;
break;
case TILEP_MONS_ORC:
case TILEP_MONS_URUG:
case TILEP_MONS_BLORK_THE_ORC:
case TILEP_MONS_SAINT_ROKA:
case TILEP_MONS_ORC_WARRIOR:
case TILEP_MONS_ORC_KNIGHT:
case TILEP_MONS_ORC_WARLORD:
case TILEP_MONS_BOGGART:
case TILEP_MONS_DEEP_ELF_FIGHTER:
case TILEP_MONS_DEEP_ELF_SOLDIER:
ofs_x = 0;
ofs_y = 2;
break;
case TILEP_MONS_GOBLIN:
case TILEP_MONS_IJYB:
ofs_x = 0;
ofs_y = 4;
break;
// Shift upwards and to the left.
case TILEP_MONS_DEEP_ELF_MAGE:
case TILEP_MONS_DEEP_ELF_SUMMONER:
case TILEP_MONS_DEEP_ELF_CONJURER:
case TILEP_MONS_DEEP_ELF_PRIEST:
case TILEP_MONS_DEEP_ELF_DEMONOLOGIST:
case TILEP_MONS_DEEP_ELF_ANNIHILATOR:
ofs_x = -1;
ofs_y = -2;
break;
case TILEP_MONS_CENTAUR_MELEE:
ofs_x = -1;
ofs_y = -3;
break;
case TILEP_MONS_MAURICE:
ofs_x = -2;
ofs_y = -2;
break;
case TILEP_MONS_SONJA:
ofs_x = -2;
ofs_y = -7;
break;
case TILEP_MONS_OGRE_MAGE:
ofs_x = -4;
ofs_y = -2;
break;
// Shift upwards and to the right.
case TILEP_MONS_AGNES:
ofs_x = 1;
ofs_y = -3;
break;
case TILEP_MONS_WIZARD:
ofs_x = 2;
ofs_y = -2;
break;
case TILEP_MONS_RED_DEVIL:
ofs_x = 2;
ofs_y = -3;
break;
// Shift downwards and to the left.
case TILEP_MONS_HELL_KNIGHT:
ofs_x = -1;
ofs_y = 3;
break;
// Shift downwards and to the right.
case TILEP_MONS_BIG_KOBOLD:
ofs_x = 2;
ofs_y = 3;
break;
case TILEP_MONS_KOBOLD:
ofs_x = 3;
ofs_y = 4;
break;
case TILEP_MONS_ELF:
case TILEP_MONS_ZOMBIE_LARGE:
ofs_x = 4;
ofs_y = 1;
break;
case TILEP_MONS_ZOMBIE_SMALL:
ofs_x = 4;
ofs_y = 3;
break;
case TILEP_MONS_HUMAN:
ofs_x = 5;
ofs_y = 2;
break;
default:
// This monster cannot be displayed with a weapon.
return (false);
}
return (true);
}
unsigned int mcache_monster::info(tile_draw_info *dinfo) const
{
int ofs_x, ofs_y;
get_weapon_offset(m_mon_tile, ofs_x, ofs_y);
dinfo[0].set(m_mon_tile);
dinfo[1].set(m_equ_tile, ofs_x, ofs_y);
// In some cases, overlay a second weapon tile...
if (m_mon_tile == TILEP_MONS_DEEP_ELF_BLADEMASTER)
{
int eq2;
switch (m_equ_tile)
{
case TILEP_HAND1_DAGGER:
eq2 = TILEP_HAND2_DAGGER;
break;
case TILEP_HAND1_SABRE:
eq2 = TILEP_HAND2_SABRE;
break;
default:
case TILEP_HAND1_SHORT_SWORD_SLANT:
eq2 = TILEP_HAND2_SHORT_SWORD_SLANT;
break;
};
dinfo[2].set(eq2, -ofs_x, ofs_y);
return 3;
}
else
return 2;
}
bool mcache_monster::valid(const monsters *mon)
{
if (!mon)
return (false);
int mon_wep = mon->inv[MSLOT_WEAPON];
if (mon_wep == NON_ITEM)
return (false);
int mon_tile = tileidx_monster(mon, false) & TILE_FLAG_MASK;
int ox, oy;
return get_weapon_offset(mon_tile, ox, oy);
}
mcache_monster::mcache_monster(reader &th) : mcache_entry(th)
{
m_mon_tile = unmarshallLong(th);
m_equ_tile = unmarshallLong(th);
}
void mcache_monster::construct(writer &th)
{
mcache_entry::construct(th);
marshallLong(th, m_mon_tile);
marshallLong(th, m_equ_tile);
}
/////////////////////////////////////////////////////////////////////////////
// mcache_draco
mcache_draco::mcache_draco(const monsters *mon)
{
assert(mcache_draco::valid(mon));
int draco = draco_subspecies(mon);
int colour;
switch (draco)
{
default:
case MONS_DRACONIAN: colour = 0; break;
case MONS_BLACK_DRACONIAN: colour = 1; break;
case MONS_YELLOW_DRACONIAN: colour = 2; break;
case MONS_GREEN_DRACONIAN: colour = 3; break;
case MONS_MOTTLED_DRACONIAN:colour = 4; break;
case MONS_PALE_DRACONIAN: colour = 5; break;
case MONS_PURPLE_DRACONIAN: colour = 6; break;
case MONS_RED_DRACONIAN: colour = 7; break;
case MONS_WHITE_DRACONIAN: colour = 8; break;
}
m_mon_tile = TILEP_DRACO_BASE + colour;
int mon_wep = mon->inv[MSLOT_WEAPON];
m_equ_tile = (mon_wep != NON_ITEM) ? tilep_equ_weapon(mitm[mon_wep]) : 0;
switch (mon->type)
{
case MONS_DRACONIAN_CALLER:
m_job_tile = TILEP_DRACO_CALLER;
break;
case MONS_DRACONIAN_MONK:
m_job_tile = TILEP_DRACO_MONK;
break;
case MONS_DRACONIAN_ZEALOT:
m_job_tile = TILEP_DRACO_ZEALOT;
break;
case MONS_DRACONIAN_SHIFTER:
m_job_tile = TILEP_DRACO_SHIFTER;
break;
case MONS_DRACONIAN_ANNIHILATOR:
m_job_tile = TILEP_DRACO_ANNIHILATOR;
break;
case MONS_DRACONIAN_KNIGHT:
m_job_tile = TILEP_DRACO_KNIGHT;
break;
case MONS_DRACONIAN_SCORCHER:
m_job_tile = TILEP_DRACO_SCORCHER;
break;
default:
m_job_tile = 0;
break;
}
}
unsigned int mcache_draco::info(tile_draw_info *dinfo) const
{
unsigned int i = 0;
dinfo[i++].set(m_mon_tile);
if (m_job_tile)
dinfo[i++].set(m_job_tile);
if (m_equ_tile)
dinfo[i++].set(m_equ_tile, -2, 0);
return i;
}
bool mcache_draco::valid(const monsters *mon)
{
return (mon && mon->type >= MONS_FIRST_DRACONIAN
&& mon->type <= MONS_LAST_DRACONIAN);
}
mcache_draco::mcache_draco(reader &th) : mcache_entry(th)
{
m_mon_tile = unmarshallLong(th);
m_job_tile = unmarshallLong(th);
m_equ_tile = unmarshallLong(th);
}
void mcache_draco::construct(writer &th)
{
mcache_entry::construct(th);
marshallLong(th, m_mon_tile);
marshallLong(th, m_job_tile);
marshallLong(th, m_equ_tile);
}
/////////////////////////////////////////////////////////////////////////////
// mcache_ghost
mcache_ghost::mcache_ghost(const monsters *mon)
{
assert(mcache_ghost::valid(mon));
const struct ghost_demon &ghost = *mon->ghost;
unsigned int pseudo_rand = ghost.max_hp * 54321 * 54321;
int gender = (pseudo_rand >> 8) & 1;
tilep_race_default(ghost.species, gender,
ghost.xl, m_doll.parts);
tilep_job_default(ghost.job, gender, m_doll.parts);
for (int p = TILEP_PART_CLOAK; p < TILEP_PART_MAX; p++)
{
if (m_doll.parts[p] == TILEP_SHOW_EQUIP)
{
int part_offset = pseudo_rand % tile_player_part_count[p];
m_doll.parts[p] = tile_player_part_start[p] + part_offset;
}
}
int ac = ghost.ac;
ac *= (5 + (pseudo_rand / 11) % 11);
ac /= 10;
if (ac > 25)
m_doll.parts[TILEP_PART_BODY] = TILEP_BODY_PLATE_BLACK;
else if (ac > 20)
m_doll.parts[TILEP_PART_BODY]= TILEP_BODY_BANDED;
else if (ac > 15)
m_doll.parts[TILEP_PART_BODY]= TILEP_BODY_SCALEMAIL;
else if (ac > 10)
m_doll.parts[TILEP_PART_BODY]= TILEP_BODY_CHAINMAIL;
else if (ac > 5 )
m_doll.parts[TILEP_PART_BODY]= TILEP_BODY_LEATHER_HEAVY;
else
m_doll.parts[TILEP_PART_BODY]= TILEP_BODY_ROBE_BLUE;
int sk = ghost.best_skill;
int dam = ghost.damage;
dam *= (5 + pseudo_rand % 11);
dam /= 10;
switch (sk)
{
case SK_MACES_FLAILS:
if (dam > 30)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_GREAT_FLAIL;
else if (dam > 25)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_GREAT_MACE;
else if (dam > 20)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SPIKED_FLAIL;
else if (dam > 15)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_MORNINGSTAR;
else if (dam > 10)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_FLAIL;
else if (dam > 5)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_MACE;
else
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_CLUB_SLANT;
break;
case SK_SHORT_BLADES:
if (dam > 20)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SABRE;
else if (dam > 10)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SHORT_SWORD_SLANT;
else
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_DAGGER_SLANT;
break;
case SK_LONG_BLADES:
if (dam > 25)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_GREAT_SWORD_SLANT;
else if (dam > 20)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_KATANA_SLANT;
else if (dam > 15)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SCIMITAR;
else if (dam > 10)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_LONG_SWORD_SLANT;
else
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_FALCHION;
break;
case SK_AXES:
if (dam > 30)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_EXECUTIONERS_AXE;
else if (dam > 20)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_BATTLEAXE;
else if (dam > 15)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_BROAD_AXE;
else if (dam > 10)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_WAR_AXE;
else
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_HAND_AXE;
break;
case SK_POLEARMS:
if (dam > 30)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_GLAIVE;
else if (dam > 20)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SCYTHE;
else if (dam > 15)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_HALBERD;
else if (dam > 10)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_TRIDENT2;
else if (dam > 10)
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_HAMMER;
else
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SPEAR;
break;
case SK_BOWS:
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_BOW2;
break;
case SK_CROSSBOWS:
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_CROSSBOW;
break;
case SK_SLINGS:
m_doll.parts[TILEP_PART_HAND1] = TILEP_HAND1_SLING;
break;
case SK_UNARMED_COMBAT:
default:
m_doll.parts[TILEP_PART_HAND1] = m_doll.parts[TILEP_PART_HAND2] = 0;
break;
}
}
const dolls_data *mcache_ghost::doll() const
{
return &m_doll;
}
bool mcache_ghost::valid(const monsters *mon)
{
return (mon && mon->type == MONS_PLAYER_GHOST);
}
mcache_ghost::mcache_ghost(reader &th) : mcache_entry(th)
{
unmarshallDoll(th, m_doll);
}
void mcache_ghost::construct(writer &th)
{
mcache_entry::construct(th);
marshallDoll(th, m_doll);
}
/////////////////////////////////////////////////////////////////////////////
// mcache_demon
mcache_demon::mcache_demon(const monsters *mon)
{
assert(mcache_demon::valid(mon));
const struct ghost_demon &ghost = *mon->ghost;
unsigned int pseudo_rand1 = ghost.max_hp * 54321 * 54321;
unsigned int pseudo_rand2 = ghost.ac * 54321 * 54321;
unsigned int pseudo_rand3 = ghost.ev * 54321 * 54321;
int head_offset = pseudo_rand1 % tile_player_count(TILEP_DEMON_HEAD);
m_demon.head = TILEP_DEMON_HEAD + head_offset;
int body_offset = pseudo_rand2 % tile_player_count(TILEP_DEMON_BODY);
m_demon.body = TILEP_DEMON_BODY + body_offset;
if (ghost.ev % 2)
{
int wings_offset = pseudo_rand3 % tile_player_count(TILEP_DEMON_WINGS);
m_demon.wings = TILEP_DEMON_WINGS + wings_offset;
}
else
m_demon.wings = 0;
}
unsigned int mcache_demon::info(tile_draw_info *dinfo) const
{
if (m_demon.wings)
{
dinfo[0].set(m_demon.wings);
dinfo[1].set(m_demon.body);
dinfo[2].set(m_demon.head);
return 3;
}
else
{
dinfo[0].set(m_demon.body);
dinfo[1].set(m_demon.head);
return 2;
}
}
bool mcache_demon::valid(const monsters *mon)
{
return (mon && mon->type == MONS_PANDEMONIUM_DEMON);
}
mcache_demon::mcache_demon(reader &th) : mcache_entry(th)
{
unmarshallDemon(th, m_demon);
}
void mcache_demon::construct(writer &th)
{
mcache_entry::construct(th);
marshallDemon(th, m_demon);
}
#endif