Can now have multiple ghosts or Pandemonium demons on a level. Ghosts and Pan demons can coexist. (D:9 and later are eligible for >1 ghost.) Enabled loading ghosts in Pandemonium.
Pandemonium demons can now be created outside Pan. Not that you'd want to do it, but you can.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1043 c06c8d41-db1a-0410-9941-cceddc491573
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
VAYRSLA2HKHRQRQRT4PDCOUKEEBB746S76UMS57CMG3M2L6KFWHQC
G725PD3HGAFFFQ3SKJ7E2O6I422Y54HSDFVVBM2QGLC26TGQXWEAC
Y46M2XO74VYDTBTFFUUCI275UGELTXUXS4GEIBBXCY5USQKJ5O6AC
ERGVGYEUQY6JY5RVA5PM7RISVVAHICJCVSHC5FMJVY554O573ERQC
4XGOVPFCU6KZIYHKWCHUTZY6G5S326DKBG3UREPR34Q4TSDD3TAAC
CRCKW7MAFIP2MB6ZNPVZXUHBGSPQNTYHGDVF2TCM2K6XLRUTUW4QC
RISMOCQM6BKK4XSIRKYLOBB2UPDYJNDAL6OGIIR5GGNZQAK5YSZAC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
5ASC3STDYCNLZFEBN6UTMUCGDETHBR2OCBZCF5VIAZ5RRWLOTDYQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
AY7Z23L4WPEFEL2HMF3KX3IM6OYMLB2QWY3AIHHQ37CMRVCXRCMAC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC
SVFEYUMU7YSZYB3B33RJYJZRRELJ2PJFJXX6NRUV3NSWKMBT56EQC
UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC
RM4LRL6W56XHFWFZIA6LQLR6TPAHVSSXBVCNYZJIZSBPKFWDAJJQC
AOAJ6D3OKSELEYKAT55XCVU5LYJ7SMCZKC6DIEGLLB3TF2LEENWQC
YRY2TC3VHOYE47M23UJGUWDGF7H7WGU7WLWI4SUNM4EDNTGUPHGAC
5P64LHKJKGKIO3FUV63KFQ2OHZ5RNRV7WXS25OHXVNYYFZAVGLMAC
QDWDUURSNLMT6AXNNJ3DEQCWAKCAIHV6MP5F7QGIBGXOG2BI2NPQC
menv[i].ac = unmarshallByte(th);
menv[i].ev = unmarshallByte(th);
menv[i].hit_dice = unmarshallByte(th);
menv[i].speed = unmarshallByte(th);
monsters &m = menv[i];
m.ac = unmarshallByte(th);
m.ev = unmarshallByte(th);
m.hit_dice = unmarshallByte(th);
m.speed = unmarshallByte(th);
menv[i].speed_increment = (unsigned char) unmarshallByte(th);
menv[i].behaviour = unmarshallByte(th);
menv[i].x = unmarshallByte(th);
menv[i].y = unmarshallByte(th);
menv[i].target_x = unmarshallByte(th);
menv[i].target_y = unmarshallByte(th);
menv[i].flags = unmarshallLong(th);
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);
menv[i].type = unmarshallShort(th);
menv[i].hit_points = unmarshallShort(th);
menv[i].max_hit_points = unmarshallShort(th);
menv[i].number = unmarshallShort(th);
m.type = unmarshallShort(th);
m.hit_points = unmarshallShort(th);
m.max_hit_points = unmarshallShort(th);
m.number = unmarshallShort(th);
static void tag_read_ghost(struct tagHeader &th, char minorVersion)
{
int nghosts = unmarshallShort(th);
if (nghosts < 1 || nghosts > MAX_GHOSTS)
return;
for (int i = 0; i < nghosts; ++i)
ghosts.push_back( unmarshallGhost(th) );
}
int dmi = 0;
for (unsigned char j = 0; j < NUM_MON_ENCHANTS; j++)
monster->enchantment[j] = ENCH_NONE;
monster->flags = 0;
monster->type = -1;
monster->hit_points = 0;
monster->max_hit_points = 0;
monster->hit_dice = 0;
monster->ac = 0;
monster->ev = 0;
monster->speed_increment = 0;
monster->attitude = ATT_HOSTILE;
monster->behaviour = BEH_SLEEP;
monster->foe = MHITNOT;
if (in_bounds(monster->x, monster->y))
mgrd[monster->x][monster->y] = NON_MONSTER;
for (dmi = MSLOT_GOLD; dmi >= MSLOT_WEAPON; dmi--)
{
monster->inv[dmi] = NON_ITEM;
}
monster->reset();
strcpy(info, monam( monster->number, monster->type,
player_monster_visible( monster ),
DESC_CAP_THE ));
strcat(info, " picks up some gold.");
mpr(info);
mprf("%s picks up some gold.",
monam( monster, monster->number, monster->type,
player_monster_visible( monster ),
DESC_CAP_THE ));
int x, y;
if (book == MST_NO_SPELLS)
{
for (y = 0; y < NUM_MONSTER_SPELL_SLOTS; y++)
mon->spells[y] = MS_NO_SPELL;
return;
}
#if DEBUG_DIAGNOSTICS
mprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d",
ptr_monam( mon, DESC_PLAIN ), book );
#endif
for (x = 0; x < 6; x++)
mon->spells[x] = MS_NO_SPELL;
if (book == MST_GHOST)
{
for (y = 0; y < NUM_MONSTER_SPELL_SLOTS; y++)
{
mon->spells[y] = ghost.values[ GVAL_SPELL_1 + y ];
#if DEBUG_DIAGNOSTICS
mprf( MSGCH_DIAGNOSTICS, "spell #%d: %d", y, mon->spells[y] );
#endif
}
}
else
{
// this needs to be rewritten a la the monsterseek rewrite {dlb}:
for (x = 0; x < NUM_MSTYPES; x++)
{
if (mspell_list[x][0] == book)
break;
}
if (x < NUM_MSTYPES)
{
for (y = 0; y < 6; y++)
mon->spells[y] = mspell_list[x][y + 1];
}
}
mon->load_spells(book);
monsters::monsters()
: type(-1), hit_points(0), max_hit_points(0), hit_dice(0),
ac(0), ev(0), speed(0), speed_increment(0), x(0), y(0),
target_x(0), target_y(0), inv(), spells(), attitude(ATT_NEUTRAL),
behaviour(BEH_WANDER), foe(MHITYOU), enchantment(), flags(0L),
number(0), colour(BLACK), foe_memory(0), god(GOD_NO_GOD),
ghost()
{
}
monsters::monsters(const monsters &mon)
{
init_with(mon);
}
monsters &monsters::operator = (const monsters &mon)
{
init_with(mon);
return (*this);
}
void monsters::init_with(const monsters &mon)
{
type = mon.type;
hit_points = mon.hit_points;
max_hit_points = mon.max_hit_points;
hit_dice = mon.hit_dice;
ac = mon.ac;
ev = mon.ev;
speed = mon.speed;
speed_increment = mon.speed_increment;
x = mon.x;
y = mon.y;
target_x = mon.target_x;
target_y = mon.target_y;
inv = mon.inv;
spells = mon.spells;
attitude = mon.attitude;
behaviour = mon.behaviour;
foe = mon.foe;
enchantment = mon.enchantment;
flags = mon.flags;
number = mon.number;
colour = mon.colour;
foe_memory = mon.foe_memory;
god = mon.god;
if (mon.ghost.get())
ghost.reset( new ghost_demon( *mon.ghost ) );
}
void monsters::pandemon_init()
{
hit_dice = ghost->values[ GVAL_DEMONLORD_HIT_DICE ];
hit_points = ghost->values[ GVAL_MAX_HP ];
max_hit_points = ghost->values[ GVAL_MAX_HP ];
ac = ghost->values[ GVAL_AC ];
ev = ghost->values[ GVAL_EV ];
speed = (one_chance_in(3) ? 10 : 6 + roll_dice(2, 9));
speed_increment = 70;
colour = random_colour(); // demon's colour
load_spells(MST_GHOST);
}
void monsters::ghost_init()
{
type = MONS_PLAYER_GHOST;
hit_dice = ghost->values[ GVAL_EXP_LEVEL ];
hit_points = ghost->values[ GVAL_MAX_HP ];
max_hit_points = ghost->values[ GVAL_MAX_HP ];
ac = ghost->values[ GVAL_AC];
ev = ghost->values[ GVAL_EV ];
speed = ghost->values[ GVAL_SPEED ];
speed_increment = 70;
attitude = ATT_HOSTILE;
behaviour = BEH_WANDER;
flags = 0;
foe = MHITNOT;
foe_memory = 0;
colour = mons_class_colour(MONS_PLAYER_GHOST);
number = 250;
load_spells(MST_GHOST);
inv.init(NON_ITEM);
enchantment.init(ENCH_NONE);
do
{
x = random2(GXM - 20) + 10;
y = random2(GYM - 20) + 10;
}
while ((grd[x][y] != DNGN_FLOOR)
|| (mgrd[x][y] != NON_MONSTER));
mgrd[x][y] = monster_index(this);
}
void monsters::reset()
{
enchantment.init(ENCH_NONE);
inv.init(NON_ITEM);
flags = 0;
type = -1;
hit_points = 0;
max_hit_points = 0;
hit_dice = 0;
ac = 0;
ev = 0;
speed_increment = 0;
attitude = ATT_HOSTILE;
behaviour = BEH_SLEEP;
foe = MHITNOT;
if (in_bounds(x, y))
mgrd[x][y] = NON_MONSTER;
x = y = 0;
ghost.reset(NULL);
}
void monsters::load_spells(int book)
{
spells.init(MS_NO_SPELL);
if (book == MST_NO_SPELLS || (book == MST_GHOST && !ghost.get()))
return;
#if DEBUG_DIAGNOSTICS
mprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d",
name(DESC_PLAIN).c_str(), book );
#endif
if (book == MST_GHOST)
{
for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
{
spells[i] = ghost->values[ GVAL_SPELL_1 + i ];
#if DEBUG_DIAGNOSTICS
mprf( MSGCH_DIAGNOSTICS, "spell #%d: %d", i, spells[i] );
#endif
}
}
else
{
int i = 0;
// this needs to be rewritten a la the monsterseek rewrite {dlb}:
for (; i < NUM_MSTYPES; i++)
{
if (mspell_list[i][0] == book)
break;
}
if (i < NUM_MSTYPES)
{
for (int z = 0; z < NUM_MONSTER_SPELL_SLOTS; z++)
spells[z] = mspell_list[i][z + 1];
}
}
}
// first, some file locking stuff for multiuser crawl
#ifdef USE_FILE_LOCKING
static bool lock_file_handle( FILE *handle, int type )
{
struct flock lock;
int status;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = type;
#ifdef USE_BLOCKING_LOCK
status = fcntl( fileno( handle ), F_SETLKW, &lock );
#else
for (int i = 0; i < 30; i++)
{
status = fcntl( fileno( handle ), F_SETLK, &lock );
// success
if (status == 0)
break;
// known failure
if (status == -1 && (errno != EACCES && errno != EAGAIN))
break;
perror( "Problems locking file... retrying..." );
delay( 1000 );
}
#endif
return (status == 0);
}
static bool unlock_file_handle( FILE *handle )
{
struct flock lock;
int status;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = F_UNLCK;
#ifdef USE_BLOCKING_LOCK
status = fcntl( fileno( handle ), F_SETLKW, &lock );
#else
for (int i = 0; i < 30; i++)
{
status = fcntl( fileno( handle ), F_SETLK, &lock );
// success
if (status == 0)
break;
// known failure
if (status == -1 && (errno != EACCES && errno != EAGAIN))
break;
perror( "Problems unlocking file... retrying..." );
delay( 1000 );
}
#endif
return (status == 0);
}
#endif
FILE *handle = fopen(scores.c_str(), mode);
#ifdef SHARED_FILES_CHMOD_PUBLIC
chmod(scores.c_str(), SHARED_FILES_CHMOD_PUBLIC);
#endif
#ifdef USE_FILE_LOCKING
int locktype = F_RDLCK;
if (stricmp(mode, "r"))
locktype = F_WRLCK;
UNUSED( mode );
if (handle == NULL || handle == stdin)
return;
#ifdef USE_FILE_LOCKING
unlock_file_handle( handle );
#endif
// actually close
fclose(handle);
#ifdef SHARED_FILES_CHMOD_PUBLIC
if (stricmp(mode, "w") == 0)
chmod(scores.c_str(), SHARED_FILES_CHMOD_PUBLIC);
#endif
lk_close(handle, mode, scores);
/*
* File: ghost.cc
* Summary: Player ghost and random Pandemonium demon handling.
*
* Created for Dungeon Crawl Reference by $Author:dshaligram $ on
* $Date: 2007-03-15 $.
*/
#include "AppHdr.h"
#include "externs.h"
#include "itemname.h"
#include "itemprop.h"
#include "randart.h"
#include "skills2.h"
#include "stuff.h"
#include "misc.h"
#include "player.h"
#include <vector>
std::vector<ghost_demon> ghosts;
/*
Order for looking for conjurations for the 1st & 2nd spell slots,
when finding spells to be remembered by a player's ghost:
*/
static int search_order_conj[] = {
SPELL_LEHUDIBS_CRYSTAL_SPEAR,
SPELL_BOLT_OF_DRAINING,
SPELL_AGONY,
SPELL_DISINTEGRATE,
SPELL_LIGHTNING_BOLT,
SPELL_STICKY_FLAME,
SPELL_ISKENDERUNS_MYSTIC_BLAST,
SPELL_BOLT_OF_MAGMA,
SPELL_ICE_BOLT,
SPELL_BOLT_OF_FIRE,
SPELL_BOLT_OF_COLD,
SPELL_FIREBALL,
SPELL_DELAYED_FIREBALL,
SPELL_VENOM_BOLT,
SPELL_BOLT_OF_IRON,
SPELL_STONE_ARROW,
SPELL_THROW_FLAME,
SPELL_THROW_FROST,
SPELL_PAIN,
SPELL_STING,
SPELL_SHOCK,
SPELL_MAGIC_DART,
SPELL_NO_SPELL, // end search
};
/*
Order for looking for summonings and self-enchants for the 3rd spell slot:
*/
static int search_order_third[] = {
/* 0 */
SPELL_SYMBOL_OF_TORMENT,
SPELL_SUMMON_GREATER_DEMON,
SPELL_SUMMON_WRAITHS,
SPELL_SUMMON_HORRIBLE_THINGS,
SPELL_SUMMON_DEMON,
SPELL_DEMONIC_HORDE,
SPELL_HASTE,
SPELL_ANIMATE_DEAD,
SPELL_INVISIBILITY,
SPELL_CALL_IMP,
SPELL_SUMMON_SMALL_MAMMAL,
/* 10 */
SPELL_CONTROLLED_BLINK,
SPELL_BLINK,
SPELL_NO_SPELL, // end search
};
/*
Order for looking for enchants for the 4th + 5th spell slot. If fails, will
go through conjs.
Note: Dig must be in misc2 (5th) position to work.
*/
static int search_order_misc[] = {
/* 0 */
SPELL_AGONY,
SPELL_BANISHMENT,
SPELL_PARALYZE,
SPELL_CONFUSE,
SPELL_SLOW,
SPELL_POLYMORPH_OTHER,
SPELL_TELEPORT_OTHER,
SPELL_DIG,
SPELL_NO_SPELL, // end search
};
/* Last slot (emergency) can only be teleport self or blink. */
ghost_demon::ghost_demon() : name(), values()
{
reset();
}
void ghost_demon::reset()
{
name.clear();
values.init(0);
values[GVAL_SPEED] = 10;
}
void ghost_demon::init_random_demon()
{
char st_p[ITEMNAME_SIZE];
make_name(random_int(), false, st_p);
name = st_p;
// hp - could be defined below (as could ev, AC etc). Oh well, too late:
values[ GVAL_MAX_HP ] = 100 + roll_dice( 3, 50 );
values[ GVAL_EV ] = 5 + random2(20);
values[ GVAL_AC ] = 5 + random2(20);
values[ GVAL_SEE_INVIS ] = (one_chance_in(10) ? 0 : 1);
if (!one_chance_in(3))
values[ GVAL_RES_FIRE ] = (coinflip() ? 2 : 3);
else
{
values[ GVAL_RES_FIRE ] = 0; /* res_fire */
if (one_chance_in(10))
values[ GVAL_RES_FIRE ] = -1;
}
if (!one_chance_in(3))
values[ GVAL_RES_COLD ] = 2;
else
{
values[ GVAL_RES_COLD ] = 0; /* res_cold */
if (one_chance_in(10))
values[ GVAL_RES_COLD ] = -1;
}
// demons, like ghosts, automatically get poison res. and life prot.
// resist electricity:
values[ GVAL_RES_ELEC ] = (!one_chance_in(3) ? 1 : 0);
// HTH damage:
values[ GVAL_DAMAGE ] = 20 + roll_dice( 2, 20 );
// special attack type (uses weapon brand code):
values[ GVAL_BRAND ] = SPWPN_NORMAL;
if (!one_chance_in(3))
{
do {
values[ GVAL_BRAND ] = random2(17);
/* some brands inappropriate (eg holy wrath) */
} while (values[ GVAL_BRAND ] == SPWPN_HOLY_WRATH
|| values[ GVAL_BRAND ] == SPWPN_ORC_SLAYING
|| values[ GVAL_BRAND ] == SPWPN_PROTECTION
|| values[ GVAL_BRAND ] == SPWPN_FLAME
|| values[ GVAL_BRAND ] == SPWPN_FROST
|| values[ GVAL_BRAND ] == SPWPN_DISRUPTION);
}
// is demon a spellcaster?
// upped from one_chance_in(3)... spellcasters are more interesting
// and I expect named demons to typically have a trick or two -- bwr
values[GVAL_DEMONLORD_SPELLCASTER] = (one_chance_in(10) ? 0 : 1);
// does demon fly? (0 = no, 1 = fly, 2 = levitate)
values[GVAL_DEMONLORD_FLY] = (one_chance_in(3) ? 0 :
one_chance_in(5) ? 2 : 1);
// vacant <ghost best skill level>:
values[GVAL_DEMONLORD_UNUSED] = 0;
// hit dice:
values[GVAL_DEMONLORD_HIT_DICE] = 10 + roll_dice(2, 10);
// does demon cycle colours?
values[GVAL_DEMONLORD_CYCLE_COLOUR] = (one_chance_in(10) ? 1 : 0);
for (int i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
values[i] = SPELL_NO_SPELL;
/* This bit uses the list of player spells to find appropriate spells
for the demon, then converts those spells to the monster spell indices.
Some special monster-only spells are at the end. */
if (values[ GVAL_DEMONLORD_SPELLCASTER ] == 1)
{
#define RANDOM_ARRAY_ELEMENT(x) x[random2(sizeof(x) / sizeof(x[0]))]
if (coinflip())
values[GVAL_SPELL_1]=RANDOM_ARRAY_ELEMENT(search_order_conj);
// Might duplicate the first spell, but that isn't a problem.
if (coinflip())
values[GVAL_SPELL_2]=RANDOM_ARRAY_ELEMENT(search_order_conj);
if (!one_chance_in(4))
values[GVAL_SPELL_3]=RANDOM_ARRAY_ELEMENT(search_order_third);
if (coinflip())
values[GVAL_SPELL_4]=RANDOM_ARRAY_ELEMENT(search_order_misc);
if (coinflip())
values[GVAL_SPELL_5]=RANDOM_ARRAY_ELEMENT(search_order_misc);
#undef RANDOM_ARRAY_ELEMENT
if (coinflip())
values[ GVAL_SPELL_6 ] = SPELL_BLINK;
if (coinflip())
values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
/* Converts the player spell indices to monster spell ones */
for (int i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
values[i] = translate_spell( values[i] );
/* give demon a chance for some monster-only spells: */
/* and demon-summoning should be fairly common: */
if (one_chance_in(25))
values[GVAL_SPELL_1] = MS_HELLFIRE_BURST;
if (one_chance_in(25))
values[GVAL_SPELL_1] = MS_METAL_SPLINTERS;
if (one_chance_in(25))
values[GVAL_SPELL_1] = MS_ENERGY_BOLT; /* eye of devas */
if (one_chance_in(25))
values[GVAL_SPELL_2] = MS_STEAM_BALL;
if (one_chance_in(25))
values[GVAL_SPELL_2] = MS_PURPLE_BLAST;
if (one_chance_in(25))
values[GVAL_SPELL_2] = MS_HELLFIRE;
if (one_chance_in(25))
values[GVAL_SPELL_3] = MS_SMITE;
if (one_chance_in(25))
values[GVAL_SPELL_3] = MS_HELLFIRE_BURST;
if (one_chance_in(12))
values[GVAL_SPELL_3] = MS_SUMMON_DEMON_GREATER;
if (one_chance_in(12))
values[GVAL_SPELL_3] = MS_SUMMON_DEMON;
if (one_chance_in(20))
values[GVAL_SPELL_4] = MS_SUMMON_DEMON_GREATER;
if (one_chance_in(20))
values[GVAL_SPELL_4] = MS_SUMMON_DEMON;
/* at least they can summon demons */
if (values[GVAL_SPELL_4] == SPELL_NO_SPELL)
values[GVAL_SPELL_4] = MS_SUMMON_DEMON;
if (one_chance_in(15))
values[GVAL_SPELL_5] = MS_DIG;
}
}
void ghost_demon::init_player_ghost()
{
name = you.your_name;
values[ GVAL_MAX_HP ] = ((you.hp_max >= 400) ? 400 : you.hp_max);
values[ GVAL_EV ] = player_evasion();
values[ GVAL_AC ] = player_AC();
if (values[GVAL_EV] > 40)
values[GVAL_EV] = 40;
values[ GVAL_SEE_INVIS ] = player_see_invis();
values[ GVAL_RES_FIRE ] = player_res_fire();
values[ GVAL_RES_COLD ] = player_res_cold();
values[ GVAL_RES_ELEC ] = player_res_electricity();
values[ GVAL_SPEED ] = player_ghost_base_movement_speed();
int d = 4;
int e = 0;
const int wpn = you.equip[EQ_WEAPON];
if (wpn != -1)
{
if (you.inv[wpn].base_type == OBJ_WEAPONS
|| you.inv[wpn].base_type == OBJ_STAVES)
{
d = property( you.inv[wpn], PWPN_DAMAGE );
d *= 25 + you.skills[weapon_skill( you.inv[wpn].base_type,
you.inv[wpn].sub_type )];
d /= 25;
if (you.inv[wpn].base_type == OBJ_WEAPONS)
{
if (is_random_artefact( you.inv[wpn] ))
e = randart_wpn_property( you.inv[wpn], RAP_BRAND );
else
e = you.inv[wpn].special;
}
}
}
else
{
/* Unarmed combat */
if (you.species == SP_TROLL)
d += you.experience_level;
d += you.skills[SK_UNARMED_COMBAT];
}
d *= 30 + you.skills[SK_FIGHTING];
d /= 30;
d += you.strength / 4;
if (d > 50)
d = 50;
values[ GVAL_DAMAGE ] = d;
values[ GVAL_BRAND ] = e;
values[ GVAL_SPECIES ] = you.species;
values[ GVAL_BEST_SKILL ] = best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99);
values[ GVAL_SKILL_LEVEL ] =
you.skills[best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99)];
values[ GVAL_EXP_LEVEL ] = you.experience_level;
values[ GVAL_CLASS ] = you.char_class;
add_spells();
}
static int search_first_list(int ignore_spell)
{
for (unsigned i = 0;
i < sizeof(search_order_conj) / sizeof(*search_order_conj); i++)
{
if (search_order_conj[i] == SPELL_NO_SPELL)
return SPELL_NO_SPELL;
if (search_order_conj[i] == ignore_spell)
continue;
if (player_has_spell(search_order_conj[i]))
return search_order_conj[i];
}
return SPELL_NO_SPELL;
} // end search_first_list()
static int search_second_list(int ignore_spell)
{
for (unsigned i = 0;
i < sizeof(search_order_third) / sizeof(*search_order_third); i++)
{
if (search_order_third[i] == SPELL_NO_SPELL)
return SPELL_NO_SPELL;
if (search_order_third[i] == ignore_spell)
continue;
if (player_has_spell(search_order_third[i]))
return search_order_third[i];
}
return SPELL_NO_SPELL;
} // end search_second_list()
static int search_third_list(int ignore_spell)
{
for (unsigned i = 0;
i < sizeof(search_order_misc) / sizeof(*search_order_misc); i++)
{
if (search_order_misc[i] == SPELL_NO_SPELL)
return SPELL_NO_SPELL;
if (search_order_misc[i] == ignore_spell)
continue;
if (player_has_spell(search_order_misc[i]))
return search_order_misc[i];
}
return SPELL_NO_SPELL;
} // end search_third_list()
/*
Used when creating ghosts: goes through and finds spells for the ghost to
cast. Death is a traumatic experience, so ghosts only remember a few spells.
*/
void ghost_demon::add_spells( )
{
int i = 0;
for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
values[i] = SPELL_NO_SPELL;
values[ GVAL_SPELL_1 ] = search_first_list(SPELL_NO_SPELL);
values[ GVAL_SPELL_2 ] = search_first_list(values[GVAL_SPELL_1]);
values[ GVAL_SPELL_3 ] = search_second_list(SPELL_NO_SPELL);
values[ GVAL_SPELL_4 ] = search_third_list(SPELL_NO_SPELL);
if (values[ GVAL_SPELL_4 ] == SPELL_NO_SPELL)
values[ GVAL_SPELL_4 ] = search_first_list(SPELL_NO_SPELL);
values[ GVAL_SPELL_5 ] = search_first_list(values[GVAL_SPELL_4]);
if (values[ GVAL_SPELL_5 ] == SPELL_NO_SPELL)
values[ GVAL_SPELL_5 ] = search_first_list(values[GVAL_SPELL_4]);
if (player_has_spell( SPELL_DIG ))
values[ GVAL_SPELL_5 ] = SPELL_DIG;
/* Looks for blink/tport for emergency slot */
if (player_has_spell( SPELL_CONTROLLED_BLINK )
|| player_has_spell( SPELL_BLINK ))
{
values[ GVAL_SPELL_6 ] = SPELL_CONTROLLED_BLINK;
}
if (player_has_spell( SPELL_TELEPORT_SELF ))
values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
values[i] = translate_spell( values[i] );
} // end add_spells()
/*
When passed the number for a player spell, returns the equivalent monster
spell. Returns SPELL_NO_SPELL on failure (no equiv).
*/
int ghost_demon::translate_spell(int spel) const
{
switch (spel)
{
case SPELL_TELEPORT_SELF:
return (MS_TELEPORT);
case SPELL_ICE_BOLT:
return (MS_ICE_BOLT);
case SPELL_SHOCK:
return (MS_SHOCK);
case SPELL_BOLT_OF_MAGMA:
return (MS_MAGMA);
case SPELL_MAGIC_DART:
return (MS_MMISSILE);
case SPELL_FIREBALL:
case SPELL_DELAYED_FIREBALL:
return (MS_FIREBALL);
case SPELL_DIG:
return (MS_DIG);
case SPELL_BOLT_OF_FIRE:
return (MS_FIRE_BOLT);
case SPELL_BOLT_OF_COLD:
return (MS_COLD_BOLT);
case SPELL_LIGHTNING_BOLT:
return (MS_LIGHTNING_BOLT);
case SPELL_POLYMORPH_OTHER:
return (MS_MUTATION);
case SPELL_SLOW:
return (MS_SLOW);
case SPELL_HASTE:
return (MS_HASTE);
case SPELL_PARALYZE:
return (MS_PARALYSIS);
case SPELL_CONFUSE:
return (MS_CONFUSE);
case SPELL_INVISIBILITY:
return (MS_INVIS);
case SPELL_THROW_FLAME:
return (MS_FLAME);
case SPELL_THROW_FROST:
return (MS_FROST);
case SPELL_CONTROLLED_BLINK:
return (MS_BLINK); /* approximate */
/* case FREEZING_CLOUD: return ; no freezing/mephitic cloud yet
case MEPHITIC_CLOUD: return ; */
case SPELL_VENOM_BOLT:
return (MS_VENOM_BOLT);
case SPELL_POISON_ARROW:
return (MS_POISON_ARROW);
case SPELL_TELEPORT_OTHER:
return (MS_TELEPORT_OTHER);
case SPELL_SUMMON_SMALL_MAMMAL:
return (MS_SUMMON_SMALL_MAMMALS);
case SPELL_BOLT_OF_DRAINING:
return (MS_NEGATIVE_BOLT);
case SPELL_LEHUDIBS_CRYSTAL_SPEAR:
return (MS_CRYSTAL_SPEAR);
case SPELL_BLINK:
return (MS_BLINK);
case SPELL_ISKENDERUNS_MYSTIC_BLAST:
return (MS_ORB_ENERGY);
case SPELL_SUMMON_HORRIBLE_THINGS:
return (MS_LEVEL_SUMMON); /* approximate */
case SPELL_SHADOW_CREATURES:
return (MS_LEVEL_SUMMON); /* approximate */
case SPELL_ANIMATE_DEAD:
return (MS_ANIMATE_DEAD);
case SPELL_PAIN:
return (MS_PAIN);
case SPELL_SUMMON_WRAITHS:
return (MS_SUMMON_UNDEAD); /* approximate */
case SPELL_STICKY_FLAME:
return (MS_STICKY_FLAME);
case SPELL_CALL_IMP:
return (MS_SUMMON_DEMON_LESSER);
case SPELL_BANISHMENT:
return (MS_BANISHMENT);
case SPELL_STING:
return (MS_STING);
case SPELL_SUMMON_DEMON:
return (MS_SUMMON_DEMON);
case SPELL_DEMONIC_HORDE:
return (MS_SUMMON_DEMON_LESSER);
case SPELL_SUMMON_GREATER_DEMON:
return (MS_SUMMON_DEMON_GREATER);
case SPELL_BOLT_OF_IRON:
return (MS_IRON_BOLT);
case SPELL_STONE_ARROW:
return (MS_STONE_ARROW);
case SPELL_DISINTEGRATE:
return (MS_DISINTEGRATE);
case SPELL_AGONY:
/* Too powerful to give ghosts Torment for Agony? Nah. */
return (MS_TORMENT);
case SPELL_SYMBOL_OF_TORMENT:
return (MS_TORMENT);
default:
break;
}
return (MS_NO_SPELL);
}
std::vector<ghost_demon> ghost_demon::find_ghosts()
{
std::vector<ghost_demon> gs;
ghost_demon player;
player.init_player_ghost();
gs.push_back(player);
find_extra_ghosts( gs, n_extra_ghosts() );
return (gs);
}
void ghost_demon::find_extra_ghosts( std::vector<ghost_demon> &gs, int n )
{
for (int i = 0; n > 0 && i < MAX_MONSTERS; ++i)
{
if (!menv[i].alive())
continue;
if (menv[i].type == MONS_PLAYER_GHOST && menv[i].ghost.get())
{
// Bingo!
gs.push_back( *menv[i].ghost );
--n;
}
}
}
int ghost_demon::n_extra_ghosts()
{
const int lev = you.your_level + 1;
const int subdepth = subdungeon_depth(you.where_are_you, you.your_level);
if (you.level_type == LEVEL_PANDEMONIUM
|| you.level_type == LEVEL_ABYSS
|| (you.level_type == LEVEL_DUNGEON
&& (you.where_are_you == BRANCH_CRYPT
|| you.where_are_you == BRANCH_TOMB
|| you.where_are_you == BRANCH_HALL_OF_ZOT
|| player_in_hell()))
|| lev > 22)
{
return (MAX_GHOSTS - 1);
}
if (you.where_are_you == BRANCH_ECUMENICAL_TEMPLE)
return (0);
// No multiple ghosts until level 14 of the Main Dungeon.
if ((lev < 9 && you.where_are_you == BRANCH_MAIN_DUNGEON)
|| (subdepth < 2 && you.where_are_you == BRANCH_LAIR)
|| (subdepth < 2 && you.where_are_you == BRANCH_ORCISH_MINES))
return (0);
if (you.where_are_you == BRANCH_LAIR
|| you.where_are_you == BRANCH_ORCISH_MINES
|| (you.where_are_you == BRANCH_MAIN_DUNGEON && lev < 15))
return (1);
return 1 + (random2(20) < lev) + (random2(40) < lev);
}
/*
Order for looking for conjurations for the 1st & 2nd spell slots,
when finding spells to be remembered by a player's ghost:
*/
unsigned char search_order_conj[] = {
SPELL_LEHUDIBS_CRYSTAL_SPEAR,
SPELL_BOLT_OF_DRAINING,
SPELL_AGONY,
SPELL_DISINTEGRATE,
SPELL_LIGHTNING_BOLT,
SPELL_STICKY_FLAME,
SPELL_ISKENDERUNS_MYSTIC_BLAST,
SPELL_BOLT_OF_MAGMA,
SPELL_ICE_BOLT,
SPELL_BOLT_OF_FIRE,
SPELL_BOLT_OF_COLD,
SPELL_FIREBALL,
SPELL_DELAYED_FIREBALL,
SPELL_VENOM_BOLT,
SPELL_BOLT_OF_IRON,
SPELL_STONE_ARROW,
SPELL_THROW_FLAME,
SPELL_THROW_FROST,
SPELL_PAIN,
SPELL_STING,
SPELL_SHOCK,
SPELL_MAGIC_DART,
SPELL_NO_SPELL, // end search
};
/*
Order for looking for summonings and self-enchants for the 3rd spell slot:
*/
unsigned char search_order_third[] = {
/* 0 */
SPELL_SYMBOL_OF_TORMENT,
SPELL_SUMMON_GREATER_DEMON,
SPELL_SUMMON_WRAITHS,
SPELL_SUMMON_HORRIBLE_THINGS,
SPELL_SUMMON_DEMON,
SPELL_DEMONIC_HORDE,
SPELL_HASTE,
SPELL_ANIMATE_DEAD,
SPELL_INVISIBILITY,
SPELL_CALL_IMP,
SPELL_SUMMON_SMALL_MAMMAL,
/* 10 */
SPELL_CONTROLLED_BLINK,
SPELL_BLINK,
SPELL_NO_SPELL, // end search
};
// file locking stuff
#ifdef USE_FILE_LOCKING
static bool lock_file_handle( FILE *handle, int type );
static bool unlock_file_handle( FILE *handle );
#endif // USE_FILE_LOCKING
/*
Order for looking for enchants for the 4th + 5th spell slot. If fails, will
go through conjs.
Note: Dig must be in misc2 (5th) position to work.
*/
unsigned char search_order_misc[] = {
/* 0 */
SPELL_AGONY,
SPELL_BANISHMENT,
SPELL_PARALYZE,
SPELL_CONFUSE,
SPELL_SLOW,
SPELL_POLYMORPH_OTHER,
SPELL_TELEPORT_OTHER,
SPELL_DIG,
SPELL_NO_SPELL, // end search
};
void save_level(int level_saved, bool was_a_labyrinth, char where_were_you);
unsigned char translate_spell(unsigned char spel);
unsigned char search_third_list(unsigned char ignore_spell);
unsigned char search_second_list(unsigned char ignore_spell);
unsigned char search_first_list(unsigned char ignore_spell);
void add_spells( struct ghost_struct &gs );
void generate_random_demon();
// clear out ghost/demon lord information:
strcpy( ghost.name, "" );
for (int ic = 0; ic < NUM_GHOST_VALUES; ++ic)
ghost.values[ic] = 0;
if (you.level_type == LEVEL_PANDEMONIUM)
generate_random_demon();
else if (you.your_level > 1
&& one_chance_in(3)
&& you.level_type != LEVEL_LABYRINTH)
load_ghost(); // no ghosts in Pan or Labyrinth
if (you.your_level > 1
&& one_chance_in(3)
&& you.level_type != LEVEL_LABYRINTH)
{
load_ghost(); // no ghosts in Labyrinth
}
menv[imn].type = MONS_PLAYER_GHOST;
menv[imn].hit_dice = ghost.values[ GVAL_EXP_LEVEL ];
menv[imn].hit_points = ghost.values[ GVAL_MAX_HP ];
menv[imn].max_hit_points = ghost.values[ GVAL_MAX_HP ];
menv[imn].ac = ghost.values[ GVAL_AC];
menv[imn].ev = ghost.values[ GVAL_EV ];
menv[imn].speed = 10;
menv[imn].speed_increment = 70;
menv[imn].attitude = ATT_HOSTILE;
menv[imn].behaviour = BEH_WANDER;
menv[imn].flags = 0;
menv[imn].foe = MHITNOT;
menv[imn].foe_memory = 0;
menv[imn].colour = mons_class_colour(MONS_PLAYER_GHOST);
menv[imn].number = 250;
mons_load_spells(&menv[imn], MST_GHOST);
for (i = 0; i < NUM_MONSTER_SLOTS; i++)
menv[imn].inv[i] = NON_ITEM;
for (i = 0; i < NUM_MON_ENCHANTS; i++)
menv[imn].enchantment[i] = ENCH_NONE;
do
{
menv[imn].x = random2(GXM - 20) + 10;
menv[imn].y = random2(GYM - 20) + 10;
}
while ((grd[menv[imn].x][menv[imn].y] != DNGN_FLOOR)
|| (mgrd[menv[imn].x][menv[imn].y] != NON_MONSTER));
mgrd[menv[imn].x][menv[imn].y] = imn;
break;
menv[imn].set_ghost(ghosts[0]);
menv[imn].ghost_init();
ghosts.erase(ghosts.begin());
case SAVE_MAJOR_VERSION:
restore_tagged_file(ghostFile, TAGTYPE_GHOST, minorVersion);
break;
default:
break;
case SAVE_MAJOR_VERSION:
restore_tagged_file(ghostFile, TAGTYPE_GHOST, minorVersion);
break;
default:
break;
ghost.values[ GVAL_MAX_HP ] = ((you.hp_max >= 150) ? 150 : you.hp_max);
ghost.values[ GVAL_EV ] = player_evasion();
ghost.values[ GVAL_AC ] = player_AC();
ghost.values[ GVAL_SEE_INVIS ] = player_see_invis();
ghost.values[ GVAL_RES_FIRE ] = player_res_fire();
ghost.values[ GVAL_RES_COLD ] = player_res_cold();
ghost.values[ GVAL_RES_ELEC ] = player_res_electricity();
/* note - as ghosts, automatically get res poison + prot_life */
int d = 4;
int e = 0;
if (wpn != -1)
{
if (you.inv[wpn].base_type == OBJ_WEAPONS
|| you.inv[wpn].base_type == OBJ_STAVES)
{
d = property( you.inv[wpn], PWPN_DAMAGE );
d *= 25 + you.skills[weapon_skill( you.inv[wpn].base_type,
you.inv[wpn].sub_type )];
d /= 25;
if (you.inv[wpn].base_type == OBJ_WEAPONS)
{
if (is_random_artefact( you.inv[wpn] ))
e = randart_wpn_property( you.inv[wpn], RAP_BRAND );
else
e = you.inv[wpn].special;
}
}
}
else
{
/* Unarmed combat */
if (you.species == SP_TROLL)
d += you.experience_level;
d += you.skills[SK_UNARMED_COMBAT];
}
d *= 30 + you.skills[SK_FIGHTING];
d /= 30;
d += you.strength / 4;
if (d > 50)
d = 50;
ghost.values[ GVAL_DAMAGE ] = d;
ghost.values[ GVAL_BRAND ] = e;
ghost.values[ GVAL_SPECIES ] = you.species;
ghost.values[ GVAL_BEST_SKILL ] = best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99);
ghost.values[ GVAL_SKILL_LEVEL ] = you.skills[best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99)];
ghost.values[ GVAL_EXP_LEVEL ] = you.experience_level;
ghost.values[ GVAL_CLASS ] = you.char_class;
add_spells(ghost);
gfile = fopen(cha_fil.c_str(), "wb");
/*
Used when creating ghosts: goes through and finds spells for the ghost to
cast. Death is a traumatic experience, so ghosts only remember a few spells.
*/
void add_spells( struct ghost_struct &gs )
{
int i = 0;
for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
gs.values[i] = SPELL_NO_SPELL;
gs.values[ GVAL_SPELL_1 ] = search_first_list(SPELL_NO_SPELL);
gs.values[ GVAL_SPELL_2 ] = search_first_list(gs.values[GVAL_SPELL_1]);
gs.values[ GVAL_SPELL_3 ] = search_second_list(SPELL_NO_SPELL);
gs.values[ GVAL_SPELL_4 ] = search_third_list(SPELL_NO_SPELL);
if (gs.values[ GVAL_SPELL_4 ] == SPELL_NO_SPELL)
gs.values[ GVAL_SPELL_4 ] = search_first_list(SPELL_NO_SPELL);
gs.values[ GVAL_SPELL_5 ] = search_first_list(gs.values[GVAL_SPELL_4]);
if (gs.values[ GVAL_SPELL_5 ] == SPELL_NO_SPELL)
gs.values[ GVAL_SPELL_5 ] = search_first_list(gs.values[GVAL_SPELL_4]);
if (player_has_spell( SPELL_DIG ))
gs.values[ GVAL_SPELL_5 ] = SPELL_DIG;
/* Looks for blink/tport for emergency slot */
if (player_has_spell( SPELL_CONTROLLED_BLINK )
|| player_has_spell( SPELL_BLINK ))
{
gs.values[ GVAL_SPELL_6 ] = SPELL_CONTROLLED_BLINK;
}
if (player_has_spell( SPELL_TELEPORT_SELF ))
gs.values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
for (int i = 0; i < 20; i++)
{
if (search_order_conj[i] == SPELL_NO_SPELL)
return SPELL_NO_SPELL;
if (search_order_conj[i] == ignore_spell)
continue;
if (player_has_spell(search_order_conj[i]))
return search_order_conj[i];
}
return SPELL_NO_SPELL;
} // end search_first_list()
unsigned char search_second_list(unsigned char ignore_spell)
{
for (int i = 0; i < 20; i++)
{
if (search_order_third[i] == SPELL_NO_SPELL)
return SPELL_NO_SPELL;
if (search_order_third[i] == ignore_spell)
continue;
if (player_has_spell(search_order_third[i]))
return search_order_third[i];
}
return SPELL_NO_SPELL;
} // end search_second_list()
unsigned char search_third_list(unsigned char ignore_spell)
{
for (int i = 0; i < 20; i++)
{
if (search_order_misc[i] == SPELL_NO_SPELL)
return SPELL_NO_SPELL;
if (search_order_misc[i] == ignore_spell)
continue;
if (player_has_spell(search_order_misc[i]))
return search_order_misc[i];
}
return SPELL_NO_SPELL;
} // end search_third_list()
/*
When passed the number for a player spell, returns the equivalent monster
spell. Returns SPELL_NO_SPELL on failure (no equiv).
*/
unsigned char translate_spell(unsigned char spel)
{
switch (spel)
{
case SPELL_TELEPORT_SELF:
return (MS_TELEPORT);
case SPELL_ICE_BOLT:
return (MS_ICE_BOLT);
case SPELL_SHOCK:
return (MS_SHOCK);
case SPELL_BOLT_OF_MAGMA:
return (MS_MAGMA);
case SPELL_MAGIC_DART:
return (MS_MMISSILE);
case SPELL_FIREBALL:
case SPELL_DELAYED_FIREBALL:
return (MS_FIREBALL);
case SPELL_DIG:
return (MS_DIG);
case SPELL_BOLT_OF_FIRE:
return (MS_FIRE_BOLT);
case SPELL_BOLT_OF_COLD:
return (MS_COLD_BOLT);
case SPELL_LIGHTNING_BOLT:
return (MS_LIGHTNING_BOLT);
case SPELL_POLYMORPH_OTHER:
return (MS_MUTATION);
case SPELL_SLOW:
return (MS_SLOW);
case SPELL_HASTE:
return (MS_HASTE);
case SPELL_PARALYZE:
return (MS_PARALYSIS);
case SPELL_CONFUSE:
return (MS_CONFUSE);
case SPELL_INVISIBILITY:
return (MS_INVIS);
case SPELL_THROW_FLAME:
return (MS_FLAME);
case SPELL_THROW_FROST:
return (MS_FROST);
case SPELL_CONTROLLED_BLINK:
return (MS_BLINK); /* approximate */
/* case FREEZING_CLOUD: return ; no freezing/mephitic cloud yet
case MEPHITIC_CLOUD: return ; */
case SPELL_VENOM_BOLT:
return (MS_VENOM_BOLT);
case SPELL_POISON_ARROW:
return (MS_POISON_ARROW);
case SPELL_TELEPORT_OTHER:
return (MS_TELEPORT_OTHER);
case SPELL_SUMMON_SMALL_MAMMAL:
return (MS_SUMMON_SMALL_MAMMALS);
case SPELL_BOLT_OF_DRAINING:
return (MS_NEGATIVE_BOLT);
case SPELL_LEHUDIBS_CRYSTAL_SPEAR:
return (MS_CRYSTAL_SPEAR);
case SPELL_BLINK:
return (MS_BLINK);
case SPELL_ISKENDERUNS_MYSTIC_BLAST:
return (MS_ORB_ENERGY);
case SPELL_SUMMON_HORRIBLE_THINGS:
return (MS_LEVEL_SUMMON); /* approximate */
case SPELL_SHADOW_CREATURES:
return (MS_LEVEL_SUMMON); /* approximate */
case SPELL_ANIMATE_DEAD:
return (MS_ANIMATE_DEAD);
case SPELL_PAIN:
return (MS_PAIN);
case SPELL_SUMMON_WRAITHS:
return (MS_SUMMON_UNDEAD); /* approximate */
case SPELL_STICKY_FLAME:
return (MS_STICKY_FLAME);
case SPELL_CALL_IMP:
return (MS_SUMMON_DEMON_LESSER);
case SPELL_BANISHMENT:
return (MS_BANISHMENT);
case SPELL_STING:
return (MS_STING);
case SPELL_SUMMON_DEMON:
return (MS_SUMMON_DEMON);
case SPELL_DEMONIC_HORDE:
return (MS_SUMMON_DEMON_LESSER);
case SPELL_SUMMON_GREATER_DEMON:
return (MS_SUMMON_DEMON_GREATER);
case SPELL_BOLT_OF_IRON:
return (MS_IRON_BOLT);
case SPELL_STONE_ARROW:
return (MS_STONE_ARROW);
case SPELL_DISINTEGRATE:
return (MS_DISINTEGRATE);
case SPELL_AGONY:
/* Too powerful to give ghosts Torment for Agony? Nah. */
return (MS_TORMENT);
case SPELL_SYMBOL_OF_TORMENT:
return (MS_TORMENT);
default:
break;
}
return (MS_NO_SPELL);
}
void generate_random_demon(void)
{
}
char st_p[ITEMNAME_SIZE];
make_name(random_int(), false, st_p);
strcpy(ghost.name, st_p);
// hp - could be defined below (as could ev, AC etc). Oh well, too late:
ghost.values[ GVAL_MAX_HP ] = 100 + roll_dice( 3, 50 );
ghost.values[ GVAL_EV ] = 5 + random2(20);
ghost.values[ GVAL_AC ] = 5 + random2(20);
ghost.values[ GVAL_SEE_INVIS ] = (one_chance_in(10) ? 0 : 1);
if (!one_chance_in(3))
ghost.values[ GVAL_RES_FIRE ] = (coinflip() ? 2 : 3);
else
{
ghost.values[ GVAL_RES_FIRE ] = 0; /* res_fire */
if (one_chance_in(10))
ghost.values[ GVAL_RES_FIRE ] = -1;
}
if (!one_chance_in(3))
ghost.values[ GVAL_RES_COLD ] = 2;
else
{
ghost.values[ GVAL_RES_COLD ] = 0; /* res_cold */
if (one_chance_in(10))
ghost.values[ GVAL_RES_COLD ] = -1;
// demons, like ghosts, automatically get poison res. and life prot.
// resist electricity:
ghost.values[ GVAL_RES_ELEC ] = (!one_chance_in(3) ? 1 : 0);
// HTH damage:
ghost.values[ GVAL_DAMAGE ] = 20 + roll_dice( 2, 20 );
// special attack type (uses weapon brand code):
ghost.values[ GVAL_BRAND ] = SPWPN_NORMAL;
if (!one_chance_in(3))
{
do {
ghost.values[ GVAL_BRAND ] = random2(17);
/* some brands inappropriate (eg holy wrath) */
} while (ghost.values[ GVAL_BRAND ] == SPWPN_HOLY_WRATH
|| ghost.values[ GVAL_BRAND ] == SPWPN_ORC_SLAYING
|| ghost.values[ GVAL_BRAND ] == SPWPN_PROTECTION
|| ghost.values[ GVAL_BRAND ] == SPWPN_FLAME
|| ghost.values[ GVAL_BRAND ] == SPWPN_FROST
|| ghost.values[ GVAL_BRAND ] == SPWPN_DISRUPTION);
}
// is demon a spellcaster?
// upped from one_chance_in(3)... spellcasters are more interesting
// and I expect named demons to typically have a trick or two -- bwr
ghost.values[GVAL_DEMONLORD_SPELLCASTER] = (one_chance_in(10) ? 0 : 1);
// does demon fly? (0 = no, 1 = fly, 2 = levitate)
ghost.values[GVAL_DEMONLORD_FLY] = (one_chance_in(3) ? 0 :
one_chance_in(5) ? 2 : 1);
// vacant <ghost best skill level>:
ghost.values[GVAL_DEMONLORD_UNUSED] = 0;
// hit dice:
ghost.values[GVAL_DEMONLORD_HIT_DICE] = 10 + roll_dice(2, 10);
// does demon cycle colours?
ghost.values[GVAL_DEMONLORD_CYCLE_COLOUR] = (one_chance_in(10) ? 1 : 0);
menv[rdem].hit_dice = ghost.values[ GVAL_DEMONLORD_HIT_DICE ];
menv[rdem].hit_points = ghost.values[ GVAL_MAX_HP ];
menv[rdem].max_hit_points = ghost.values[ GVAL_MAX_HP ];
menv[rdem].ac = ghost.values[ GVAL_AC ];
menv[rdem].ev = ghost.values[ GVAL_EV ];
menv[rdem].speed = (one_chance_in(3) ? 10 : 6 + roll_dice(2, 9));
menv[rdem].speed_increment = 70;
menv[rdem].colour = random_colour(); // demon's colour
for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
ghost.values[i] = SPELL_NO_SPELL;
/* This bit uses the list of player spells to find appropriate spells
for the demon, then converts those spells to the monster spell indices.
Some special monster-only spells are at the end. */
if (ghost.values[ GVAL_DEMONLORD_SPELLCASTER ] == 1)
{
#define RANDOM_ARRAY_ELEMENT(x) x[random2(sizeof(x) / sizeof(x[0]))]
if (coinflip())
ghost.values[GVAL_SPELL_1]=RANDOM_ARRAY_ELEMENT(search_order_conj);
// Might duplicate the first spell, but that isn't a problem.
if (coinflip())
ghost.values[GVAL_SPELL_2]=RANDOM_ARRAY_ELEMENT(search_order_conj);
if (!one_chance_in(4))
ghost.values[GVAL_SPELL_3]=RANDOM_ARRAY_ELEMENT(search_order_third);
if (coinflip())
ghost.values[GVAL_SPELL_4]=RANDOM_ARRAY_ELEMENT(search_order_misc);
if (coinflip())
ghost.values[GVAL_SPELL_5]=RANDOM_ARRAY_ELEMENT(search_order_misc);
#undef RANDOM_ARRAY_ELEMENT
if (coinflip())
ghost.values[ GVAL_SPELL_6 ] = SPELL_BLINK;
if (coinflip())
ghost.values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
/* Converts the player spell indices to monster spell ones */
for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
ghost.values[i] = translate_spell( ghost.values[i] );
/* give demon a chance for some monster-only spells: */
/* and demon-summoning should be fairly common: */
if (one_chance_in(25))
ghost.values[GVAL_SPELL_1] = MS_HELLFIRE_BURST;
if (one_chance_in(25))
ghost.values[GVAL_SPELL_1] = MS_METAL_SPLINTERS;
if (one_chance_in(25))
ghost.values[GVAL_SPELL_1] = MS_ENERGY_BOLT; /* eye of devas */
if (one_chance_in(25))
ghost.values[GVAL_SPELL_2] = MS_STEAM_BALL;
if (one_chance_in(25))
ghost.values[GVAL_SPELL_2] = MS_PURPLE_BLAST;
if (one_chance_in(25))
ghost.values[GVAL_SPELL_2] = MS_HELLFIRE;
if (one_chance_in(25))
ghost.values[GVAL_SPELL_3] = MS_SMITE;
if (one_chance_in(25))
ghost.values[GVAL_SPELL_3] = MS_HELLFIRE_BURST;
if (one_chance_in(12))
ghost.values[GVAL_SPELL_3] = MS_SUMMON_DEMON_GREATER;
if (one_chance_in(12))
ghost.values[GVAL_SPELL_3] = MS_SUMMON_DEMON;
if (one_chance_in(20))
ghost.values[GVAL_SPELL_4] = MS_SUMMON_DEMON_GREATER;
if (one_chance_in(20))
ghost.values[GVAL_SPELL_4] = MS_SUMMON_DEMON;
/* at least they can summon demons */
if (ghost.values[GVAL_SPELL_4] == SPELL_NO_SPELL)
ghost.values[GVAL_SPELL_4] = MS_SUMMON_DEMON;
if (one_chance_in(15))
ghost.values[GVAL_SPELL_5] = MS_DIG;
mons_load_spells(&menv[rdem], MST_GHOST);
}
ghost_demon pandemon;
pandemon.init_random_demon();
menv[rdem].set_ghost(pandemon);
menv[rdem].pandemon_init();
}
////////////////////////////////////////////////////////////////////////////
// Locking for multiuser systems
// first, some file locking stuff for multiuser crawl
#ifdef USE_FILE_LOCKING
static bool lock_file_handle( FILE *handle, int type )
{
struct flock lock;
int status;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = type;
#ifdef USE_BLOCKING_LOCK
status = fcntl( fileno( handle ), F_SETLKW, &lock );
#else
for (int i = 0; i < 30; i++)
{
status = fcntl( fileno( handle ), F_SETLK, &lock );
// success
if (status == 0)
break;
// known failure
if (status == -1 && (errno != EACCES && errno != EAGAIN))
break;
perror( "Problems locking file... retrying..." );
delay( 1000 );
}
#endif
return (status == 0);
static bool unlock_file_handle( FILE *handle )
{
struct flock lock;
int status;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_type = F_UNLCK;
#ifdef USE_BLOCKING_LOCK
status = fcntl( fileno( handle ), F_SETLKW, &lock );
#else
for (int i = 0; i < 30; i++)
{
status = fcntl( fileno( handle ), F_SETLK, &lock );
// success
if (status == 0)
break;
// known failure
if (status == -1 && (errno != EACCES && errno != EAGAIN))
break;
perror( "Problems unlocking file... retrying..." );
delay( 1000 );
}
#endif
return (status == 0);
}
#endif
FILE *lk_open(const char *mode, const std::string &file)
{
FILE *handle = fopen(file.c_str(), mode);
#ifdef SHARED_FILES_CHMOD_PUBLIC
chmod(file.c_str(), SHARED_FILES_CHMOD_PUBLIC);
#endif
#ifdef USE_FILE_LOCKING
int locktype = F_RDLCK;
if (mode && mode[0] != 'r')
locktype = F_WRLCK;
if (handle && !lock_file_handle( handle, locktype ))
{
perror( "Could not lock file... " );
fclose( handle );
handle = NULL;
}
#endif
return handle;
}
void lk_close(FILE *handle, const char *mode, const std::string &file)
{
UNUSED( mode );
if (handle == NULL || handle == stdin)
return;
#ifdef USE_FILE_LOCKING
unlock_file_handle( handle );
#endif
// actually close
fclose(handle);
#ifdef SHARED_FILES_CHMOD_PUBLIC
if (mode && mode[0] == 'w')
chmod(file.c_str(), SHARED_FILES_CHMOD_PUBLIC);
#endif
}
case SP_HILL_DWARF:
case SP_MOUNTAIN_DWARF:
case SP_TROLL:
case SP_OGRE:
case SP_OGRE_MAGE:
case SP_MINOTAUR:
case SP_HILL_ORC:
case SP_CENTAUR:
case SP_NAGA:
case SP_MUMMY:
case SP_GHOUL:
str = 15;
break;
case SP_HILL_DWARF:
case SP_MOUNTAIN_DWARF:
case SP_TROLL:
case SP_OGRE:
case SP_OGRE_MAGE:
case SP_MINOTAUR:
case SP_HILL_ORC:
case SP_CENTAUR:
case SP_NAGA:
case SP_MUMMY:
case SP_GHOUL:
str = 15;
break;
snprintf( info, INFO_SIZE, "Ghost damage: %d; brand: %d",
ghost.values[ GVAL_DAMAGE ], ghost.values[ GVAL_BRAND ] );
mpr( info, MSGCH_DIAGNOSTICS );
ASSERT(menv[i].ghost.get());
const ghost_demon &ghost = *menv[i].ghost;
mprf( MSGCH_DIAGNOSTICS,
"Ghost damage: %d; brand: %d",
ghost.values[ GVAL_DAMAGE ], ghost.values[ GVAL_BRAND ] );