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_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d",ptr_monam( mon, DESC_PLAIN ), book );#endiffor (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_DIAGNOSTICSmprf( 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 colourload_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_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d",name(DESC_PLAIN).c_str(), book );#endifif (book == MST_GHOST){for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++){spells[i] = ghost->values[ GVAL_SPELL_1 + i ];#if DEBUG_DIAGNOSTICSmprf( 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_LOCKINGstatic 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_LOCKstatus = fcntl( fileno( handle ), F_SETLKW, &lock );#elsefor (int i = 0; i < 30; i++){status = fcntl( fileno( handle ), F_SETLK, &lock );// successif (status == 0)break;// known failureif (status == -1 && (errno != EACCES && errno != EAGAIN))break;perror( "Problems locking file... retrying..." );delay( 1000 );}#endifreturn (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_LOCKstatus = fcntl( fileno( handle ), F_SETLKW, &lock );#elsefor (int i = 0; i < 30; i++){status = fcntl( fileno( handle ), F_SETLK, &lock );// successif (status == 0)break;// known failureif (status == -1 && (errno != EACCES && errno != EAGAIN))break;perror( "Problems unlocking file... retrying..." );delay( 1000 );}#endifreturn (status == 0);}#endif
FILE *handle = fopen(scores.c_str(), mode);#ifdef SHARED_FILES_CHMOD_PUBLICchmod(scores.c_str(), SHARED_FILES_CHMOD_PUBLIC);#endif#ifdef USE_FILE_LOCKINGint locktype = F_RDLCK;if (stricmp(mode, "r"))locktype = F_WRLCK;
UNUSED( mode );if (handle == NULL || handle == stdin)return;#ifdef USE_FILE_LOCKINGunlock_file_handle( handle );#endif// actually closefclose(handle);#ifdef SHARED_FILES_CHMOD_PUBLICif (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, willgo 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 -- bwrvalues[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 spellsfor 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_ELEMENTif (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 );elsee = 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 tocast. 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 monsterspell. 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 yetcase 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_LOCKINGstatic 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, willgo 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 );elsee = 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 tocast. 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 monsterspell. 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 yetcase 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 -- bwrghost.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 colourfor (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 spellsfor 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_ELEMENTif (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_LOCKINGstatic 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_LOCKstatus = fcntl( fileno( handle ), F_SETLKW, &lock );#elsefor (int i = 0; i < 30; i++){status = fcntl( fileno( handle ), F_SETLK, &lock );// successif (status == 0)break;// known failureif (status == -1 && (errno != EACCES && errno != EAGAIN))break;perror( "Problems locking file... retrying..." );delay( 1000 );}#endifreturn (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_LOCKstatus = fcntl( fileno( handle ), F_SETLKW, &lock );#elsefor (int i = 0; i < 30; i++){status = fcntl( fileno( handle ), F_SETLK, &lock );// successif (status == 0)break;// known failureif (status == -1 && (errno != EACCES && errno != EAGAIN))break;perror( "Problems unlocking file... retrying..." );delay( 1000 );}#endifreturn (status == 0);}#endifFILE *lk_open(const char *mode, const std::string &file){FILE *handle = fopen(file.c_str(), mode);#ifdef SHARED_FILES_CHMOD_PUBLICchmod(file.c_str(), SHARED_FILES_CHMOD_PUBLIC);#endif#ifdef USE_FILE_LOCKINGint 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;}#endifreturn handle;}void lk_close(FILE *handle, const char *mode, const std::string &file){UNUSED( mode );if (handle == NULL || handle == stdin)return;#ifdef USE_FILE_LOCKINGunlock_file_handle( handle );#endif// actually closefclose(handle);#ifdef SHARED_FILES_CHMOD_PUBLICif (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 ] );