summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/Kills.cc6
-rw-r--r--crawl-ref/source/debug.cc16
-rw-r--r--crawl-ref/source/defines.h3
-rw-r--r--crawl-ref/source/describe.cc78
-rw-r--r--crawl-ref/source/describe.h2
-rw-r--r--crawl-ref/source/dungeon.cc8
-rw-r--r--crawl-ref/source/enum.h1
-rw-r--r--crawl-ref/source/externs.h44
-rw-r--r--crawl-ref/source/files.cc745
-rw-r--r--crawl-ref/source/files.h3
-rw-r--r--crawl-ref/source/ghost.cc592
-rw-r--r--crawl-ref/source/hiscores.cc128
-rw-r--r--crawl-ref/source/makefile.obj1
-rw-r--r--crawl-ref/source/mon-util.cc243
-rw-r--r--crawl-ref/source/mon-util.h4
-rw-r--r--crawl-ref/source/monplace.cc10
-rw-r--r--crawl-ref/source/monstuff.cc48
-rw-r--r--crawl-ref/source/player.cc27
-rw-r--r--crawl-ref/source/player.h2
-rw-r--r--crawl-ref/source/tags.cc121
-rw-r--r--crawl-ref/source/tags.h1
21 files changed, 1202 insertions, 881 deletions
diff --git a/crawl-ref/source/Kills.cc b/crawl-ref/source/Kills.cc
index 5dcb4ba495..7bf7e86388 100644
--- a/crawl-ref/source/Kills.cc
+++ b/crawl-ref/source/Kills.cc
@@ -612,16 +612,16 @@ void kill_def::load(FILE *file)
}
}
-kill_ghost::kill_ghost(const struct monsters *mon)
+kill_ghost::kill_ghost(const monsters *mon)
{
exp = exper_value(mon);
place = get_packed_place();
- ghost_name = ghost.name;
+ ghost_name = mon->ghost->name;
// Check whether this is really a ghost, since we also have to handle
// the Pandemonic demons.
if (mon->type == MONS_PLAYER_GHOST)
- ghost_name = "The ghost of " + ghost_description(true);
+ ghost_name = "The ghost of " + ghost_description(*mon, true);
}
std::string kill_ghost::info() const
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index aaef55481c..bca8987829 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -1192,7 +1192,8 @@ void stethoscope(int mwh)
// print type of monster
snprintf( info, INFO_SIZE, "%s (id #%d; type=%d loc=(%d,%d) align=%s)",
- monam( menv[i].number, menv[i].type, true, DESC_CAP_THE ),
+ monam( &menv[i], menv[i].number, menv[i].type, true,
+ DESC_CAP_THE ),
i, menv[i].type,
menv[i].x, menv[i].y,
((menv[i].attitude == ATT_FRIENDLY) ? "friendly" :
@@ -1233,8 +1234,9 @@ void stethoscope(int mwh)
((menv[i].foe == MHITYOU) ? "you" :
(menv[i].foe == MHITNOT) ? "none" :
(menv[menv[i].foe].type == -1) ? "unassigned monster"
- : monam( menv[menv[i].foe].number, menv[menv[i].foe].type,
- true, DESC_PLAIN )),
+ : monam( &menv[menv[i].foe],
+ menv[menv[i].foe].number, menv[menv[i].foe].type,
+ true, DESC_PLAIN )),
menv[i].foe,
menv[i].foe_memory,
@@ -1276,9 +1278,11 @@ void stethoscope(int mwh)
if (menv[i].type == MONS_PLAYER_GHOST
|| menv[i].type == MONS_PANDEMONIUM_DEMON)
{
- 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 ] );
}
} // end stethoscope()
#endif
diff --git a/crawl-ref/source/defines.h b/crawl-ref/source/defines.h
index f143a0ec51..d71743bc2e 100644
--- a/crawl-ref/source/defines.h
+++ b/crawl-ref/source/defines.h
@@ -47,6 +47,9 @@
// minimum value for strength required on armour and weapons
#define STR_REQ_THRESHOLD 10
+// Max ghosts on a level.
+#define MAX_GHOSTS 10
+
// max size of monter array {dlb}:
#define MAX_MONSTERS 200
// number of monster enchantments
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 20a66edbce..acb2f2b39f 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -364,14 +364,17 @@ int str_to_trap(const std::string &s)
// Describes the random demons you find in Pandemonium.
//
//---------------------------------------------------------------
-static std::string describe_demon(void)
+static std::string describe_demon(const monsters &mons)
{
+ ASSERT(mons.ghost.get());
+
long globby = 0;
-
- for (unsigned int i = 0; i < strlen( ghost.name ); i++)
+ const ghost_demon &ghost = *mons.ghost;
+
+ for (unsigned i = 0; i < ghost.name.length(); i++)
globby += ghost.name[i];
- globby *= strlen( ghost.name );
+ globby *= ghost.name.length();
push_rng_state();
seed_rng( globby );
@@ -4581,17 +4584,18 @@ static std::string describe_draconian(const monsters *mon)
void describe_monsters(int class_described, unsigned char which_mons)
{
std::string description;
+ monsters &mons = menv[which_mons];
description.reserve(200);
clrscr();
- description = ptr_monam( &(menv[ which_mons ]), DESC_CAP_A );
+ description = ptr_monam( &mons, DESC_CAP_A );
description += "$$";
// Now that the player has examined it, he knows it's a mimic.
- if (mons_is_mimic(menv[which_mons].type))
- menv[which_mons].flags |= MF_KNOWN_MIMIC;
+ if (mons_is_mimic(mons.type))
+ mons.flags |= MF_KNOWN_MIMIC;
switch (class_described)
{
@@ -5974,12 +5978,12 @@ void describe_monsters(int class_described, unsigned char which_mons)
}
case MONS_PLAYER_GHOST:
description += "The apparition of ";
- description += ghost_description();
+ description += ghost_description(mons);
description += ".$";
break;
case MONS_PANDEMONIUM_DEMON:
- description += describe_demon();
+ description += describe_demon(mons);
break;
// mimics -- I'm not considering these descriptions a bug. -- bwr
@@ -6253,8 +6257,9 @@ void describe_monsters(int class_described, unsigned char which_mons)
found_spell = true;
}
- snprintf( info, INFO_SIZE, " %d: %s$", i,
- mons_spell_name( hspell_pass[i] ) );
+ snprintf( info, INFO_SIZE, " %d: %s (%d)$", i,
+ mons_spell_name( hspell_pass[i] ),
+ hspell_pass[i] );
description += info;
}
@@ -6300,10 +6305,13 @@ void describe_monsters(int class_described, unsigned char which_mons)
// punctuation that's wanted.
//
//---------------------------------------------------------------
-std::string ghost_description(bool concise)
+std::string ghost_description(const monsters &mons, bool concise)
{
+ ASSERT(mons.ghost.get());
char tmp_buff[ INFO_SIZE ];
+ const ghost_demon &ghost = *mons.ghost;
+
// We're fudging stats so that unarmed combat gets based off
// of the ghost's species, not the player's stats... exact
// stats are required anyways, all that matters is whether
@@ -6312,34 +6320,34 @@ std::string ghost_description(bool concise)
int str;
switch (ghost.values[GVAL_SPECIES])
{
- 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_HUMAN:
- case SP_DEMIGOD:
- case SP_DEMONSPAWN:
- str = 10;
- 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;
+
+ case SP_HUMAN:
+ case SP_DEMIGOD:
+ case SP_DEMONSPAWN:
+ str = 10;
+ break;
- default:
- str = 5;
- break;
+ default:
+ str = 5;
+ break;
}
snprintf( tmp_buff, sizeof(tmp_buff),
"%s the %s, a%s %s %s",
- ghost.name,
+ ghost.name.c_str(),
skill_title( ghost.values[GVAL_BEST_SKILL],
ghost.values[GVAL_SKILL_LEVEL],
diff --git a/crawl-ref/source/describe.h b/crawl-ref/source/describe.h
index 32868ddaf7..97ad8848f3 100644
--- a/crawl-ref/source/describe.h
+++ b/crawl-ref/source/describe.h
@@ -62,7 +62,7 @@ void describe_spell(int spelled);
/* ***********************************************************************
* called from: describe_monsters - describe, kill_ghost - Kills
* *********************************************************************** */
-std::string ghost_description(bool concise = false);
+std::string ghost_description(const monsters &mons, bool concise = false);
const char *trap_name(trap_type trap);
int str_to_trap(const std::string &s);
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 04df22f0ef..9a45191132 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -6010,7 +6010,7 @@ static void dngn_place_item_explicit(int index, int x, int y,
dngn_place_item_explicit(spec, x, y, level);
}
-static void dngn_make_monster(
+static void dngn_place_monster(
const mons_spec &monster_type_thing,
int monster_level,
int vx, int vy)
@@ -6080,7 +6080,7 @@ static int vault_grid( vault_placement &place,
grd[vx][vy] = DNGN_FLOOR;
mons_spec mons = mapsp->get_mons();
- dngn_make_monster(mons, level_number, vx, vy);
+ dngn_place_monster(mons, level_number, vx, vy);
item_spec item = mapsp->get_item();
dngn_place_item_explicit(item, vx, vy, level_number);
@@ -6278,8 +6278,8 @@ static int vault_grid( vault_placement &place,
if (vgrid != '8' && vgrid != '9' && vgrid != '0')
monster_type_thing = place.map.mons.get_monster(vgrid - '1');
- dngn_make_monster(monster_type_thing, monster_level,
- vx, vy);
+ dngn_place_monster(monster_type_thing, monster_level,
+ vx, vy);
}
// again, this seems odd, given that this is just one of many
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 8ea5a59621..db03db6da9 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1362,6 +1362,7 @@ enum ghost_value_type
GVAL_SPELL_4,
GVAL_SPELL_5,
GVAL_SPELL_6, // 19
+ GVAL_SPEED,
NUM_GHOST_VALUES, // should always be last value
// these values are for demonlords, which override the above:
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index f4e4626c87..b5e419792f 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -814,9 +814,17 @@ struct mon_attack_def
}
};
+class ghost_demon;
+
class monsters : public actor
{
public:
+ monsters();
+ monsters(const monsters &other);
+
+ monsters &operator = (const monsters &other);
+
+public:
int type;
int hit_points;
int max_hit_points;
@@ -845,7 +853,15 @@ public:
god_type god; // Usually GOD_NO_GOD.
+ std::auto_ptr<ghost_demon> ghost; // Ghost information.
+
public:
+ void set_ghost(const ghost_demon &ghost);
+ void ghost_init();
+ void pandemon_init();
+ void reset();
+ void load_spells(int spellbook);
+
// actor interface
int id() const;
bool alive() const;
@@ -912,6 +928,9 @@ public:
int shield_bypass_ability(int tohit) const;
actor_type atype() const { return ACT_MONSTER; }
+
+private:
+ void init_with(const monsters &mons);
};
struct cloud_struct
@@ -998,14 +1017,31 @@ struct game_state
};
extern game_state crawl_state;
-struct ghost_struct
+struct ghost_demon
{
- char name[20];
+public:
+ std::string name;
FixedVector< short, NUM_GHOST_VALUES > values;
-};
+public:
+ ghost_demon();
+ void reset();
+ void init_random_demon();
+ void init_player_ghost();
+
+public:
+ static std::vector<ghost_demon> find_ghosts();
+
+private:
+ static int n_extra_ghosts();
+ static void find_extra_ghosts(std::vector<ghost_demon> &ghosts, int n);
+
+private:
+ void add_spells();
+ int translate_spell(int playerspell) const;
+};
-extern struct ghost_struct ghost;
+extern std::vector<ghost_demon> ghosts;
struct system_environment
{
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index 463cc14603..a45f76a32c 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -84,79 +84,16 @@
#define DO_CHMOD_PRIVATE(x) // empty command
#endif
-void save_level(int level_saved, bool was_a_labyrinth, char where_were_you);
-
-/*
- 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);
-/* Last slot (emergency) can only be teleport self or blink. */
+#define GHOST_MINOR_VERSION 1
+#define LEVEL_MINOR_VERSION 1
static void redraw_all(void)
{
@@ -173,16 +110,6 @@ static void redraw_all(void)
you.redraw_status_flags = REDRAW_LINE_1_MASK | REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK;
}
-struct ghost_struct ghost;
-
-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();
-
static bool determine_version( FILE *restoreFile,
char &majorVersion, char &minorVersion );
@@ -720,31 +647,21 @@ void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth,
was_a_labyrinth = false;
}
- // clear out ghost/demon lord information:
- strcpy( ghost.name, "" );
- for (int ic = 0; ic < NUM_GHOST_VALUES; ++ic)
- ghost.values[ic] = 0;
-
// Try to open level savefile.
FILE *levelFile = fopen(cha_fil.c_str(), "rb");
// GENERATE new level when the file can't be opened:
if (levelFile == NULL)
- {
- strcpy(ghost.name, "");
-
- for (int imn = 0; imn < NUM_GHOST_VALUES; ++imn)
- ghost.values[imn] = 0;
-
+ {
builder( you.your_level, you.level_type );
just_created_level = true;
- 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
+ }
}
else
{
@@ -1149,7 +1066,8 @@ void save_level(int level_saved, bool was_a_labyrinth, char where_were_you)
// nail all items to the ground
fix_item_coordinates();
- write_tagged_file( saveFile, SAVE_MAJOR_VERSION, 0, TAGTYPE_LEVEL );
+ write_tagged_file( saveFile, SAVE_MAJOR_VERSION,
+ LEVEL_MINOR_VERSION, TAGTYPE_LEVEL );
fclose(saveFile);
@@ -1264,8 +1182,6 @@ void load_ghost(void)
{
char majorVersion;
char minorVersion;
- int imn;
- int i;
std::string cha_fil = make_filename("bones", you.your_level,
you.where_are_you,
@@ -1289,6 +1205,16 @@ void load_ghost(void)
return;
}
+ if (majorVersion != SAVE_MAJOR_VERSION
+ || minorVersion != GHOST_MINOR_VERSION)
+ {
+
+ fclose(gfile);
+ unlink(cha_fil.c_str());
+ return;
+ }
+
+ ghosts.clear();
restore_ghost_version(gfile, majorVersion, minorVersion);
// sanity check - EOF
@@ -1313,44 +1239,15 @@ void load_ghost(void)
unlink(cha_fil.c_str());
// translate ghost to monster and place.
- for (imn = 0; imn < MAX_MONSTERS - 10; imn++)
+ for (int imn = 0; imn < MAX_MONSTERS - 10 && !ghosts.empty(); imn++)
{
if (menv[imn].type != -1)
continue;
- 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());
}
}
@@ -1561,18 +1458,16 @@ static void restore_ghost_version( FILE *ghostFile,
{
switch(majorVersion)
{
- 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;
}
}
void save_ghost( bool force )
{
- const int wpn = you.equip[EQ_WEAPON];
-
if (!force && (you.your_level < 2 || you.is_undead))
return;
@@ -1590,82 +1485,21 @@ void save_ghost( bool force )
return;
}
- snprintf(ghost.name, sizeof ghost.name, "%s", you.your_name);
-
- 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");
+ ghosts = ghost_demon::find_ghosts();
+
+ gfile = lk_open("wb", cha_fil);
if (gfile == NULL)
{
- snprintf(info, INFO_SIZE, "Error creating ghost file: %s",
- cha_fil.c_str());
- mpr(info);
+ mprf("Error creating ghost file: %s", cha_fil.c_str());
more();
return;
}
- write_tagged_file( gfile, SAVE_MAJOR_VERSION, 0, TAGTYPE_GHOST );
+ write_tagged_file( gfile, SAVE_MAJOR_VERSION,
+ GHOST_MINOR_VERSION, TAGTYPE_GHOST );
- fclose(gfile);
+ lk_close(gfile, "wb", cha_fil);
#if DEBUG_DIAGNOSTICS
mpr( "Saved ghost.", MSGCH_DIAGNOSTICS );
@@ -1674,211 +1508,10 @@ void save_ghost( bool force )
DO_CHMOD_PRIVATE(cha_fil.c_str());
} // end save_ghost()
-/*
- 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 (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- gs.values[i] = translate_spell( gs.values[i] );
-} // end add_spells()
-
-unsigned char search_first_list(unsigned char ignore_spell)
-{
- 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)
+void generate_random_demon()
{
int rdem = 0;
- int i = 0;
-
for (rdem = 0; rdem < MAX_MONSTERS + 1; rdem++)
{
if (rdem == MAX_MONSTERS)
@@ -1888,166 +1521,10 @@ void generate_random_demon(void)
break;
}
- 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();
} // end generate_random_demon()
// Largest string we'll save
@@ -2127,3 +1604,127 @@ long readLong(FILE *file)
return ((long) (unsigned short) readShort(file)) << 16 |
(long) (unsigned short) readShort(file);
}
+
+////////////////////////////////////////////////////////////////////////////
+// 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
+}
diff --git a/crawl-ref/source/files.h b/crawl-ref/source/files.h
index 577e443333..c2563cd479 100644
--- a/crawl-ref/source/files.h
+++ b/crawl-ref/source/files.h
@@ -91,4 +91,7 @@ void writeLong(FILE* file, long num);
long readLong(FILE *file);
+FILE *lk_open(const char *mode, const std::string &file);
+void lk_close(FILE *handle, const char *mode, const std::string &file);
+
#endif
diff --git a/crawl-ref/source/ghost.cc b/crawl-ref/source/ghost.cc
new file mode 100644
index 0000000000..52077c4e77
--- /dev/null
+++ b/crawl-ref/source/ghost.cc
@@ -0,0 +1,592 @@
+/*
+ * 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);
+}
diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc
index 2155e359aa..65db7627a0 100644
--- a/crawl-ref/source/hiscores.cc
+++ b/crawl-ref/source/hiscores.cc
@@ -36,6 +36,7 @@
#include "externs.h"
#include "branch.h"
+#include "files.h"
#include "hiscores.h"
#include "itemname.h"
#include "itemprop.h"
@@ -78,12 +79,6 @@ static int hs_nextint(const char *&inbuf);
static long hs_nextlong(const char *&inbuf);
static time_t parse_time(const std::string &st);
-// 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
-
std::string score_file_name()
{
if (!SysEnv.scorefile.empty())
@@ -363,129 +358,18 @@ std::string hiscores_format_single_long( const scorefile_entry &se,
// BEGIN private functions
// --------------------------------------------------------------------------
-// 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 *hs_open( const char *mode, const std::string &scores )
{
// allow reading from standard input
if ( scores == "-" )
return stdin;
- 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;
-
- if (handle && !lock_file_handle( handle, locktype ))
- {
- perror( "Could not lock scorefile... " );
- fclose( handle );
- handle = NULL;
- }
-#endif
- return handle;
+ return lk_open( mode, scores );
}
void hs_close( FILE *handle, const char *mode, const std::string &scores )
{
- 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);
}
bool hs_read( FILE *scores, scorefile_entry &dest )
@@ -1247,8 +1131,8 @@ void scorefile_entry::init_death_cause(int dam, int dsrc,
}
death_source_name =
- monam( monster->number, monster->type, true, DESC_NOCAP_A,
- monster->inv[MSLOT_WEAPON] );
+ monam( monster, monster->number, monster->type, true,
+ DESC_NOCAP_A, monster->inv[MSLOT_WEAPON] );
}
}
else
@@ -1484,7 +1368,7 @@ const char *scorefile_entry::death_source_desc() const
return (!death_source_name.empty()?
death_source_name.c_str()
- : monam( mon_num, death_source, true, DESC_NOCAP_A ) );
+ : monam( NULL, mon_num, death_source, true, DESC_NOCAP_A ) );
}
std::string scorefile_entry::damage_string(bool terse) const
diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj
index 594e7792a7..c192363222 100644
--- a/crawl-ref/source/makefile.obj
+++ b/crawl-ref/source/makefile.obj
@@ -20,6 +20,7 @@ fight.o \
files.o \
food.o \
format.o \
+ghost.o \
hiscores.o \
initfile.o \
insult.o \
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 49065dc41b..7ea248d7e3 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -136,6 +136,11 @@ static const char *monster_spell_name[] = {
"Poison Arrow",
"Summon Small Mammals",
"Summon Mushrooms",
+ "Ice Bolt",
+ "Magma",
+ "Shock",
+ "Berserk Rage",
+ "Might"
};
#endif
@@ -495,7 +500,7 @@ bool mons_is_unique( int mc )
char mons_see_invis( struct monsters *mon )
{
if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON)
- return (ghost.values[ GVAL_SEE_INVIS ]);
+ return (mon->ghost->values[ GVAL_SEE_INVIS ]);
else if (((seekmonster(mon->type))->bitfields & M_SEE_INVIS) != 0)
return (1);
else if (scan_mon_inv_randarts( mon, RAP_EYESIGHT ) > 0)
@@ -635,7 +640,7 @@ mon_attack_def mons_attack_spec(const monsters *mons, int attk_number)
if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
{
if (attk_number == 0)
- return mon_attack_def::attk(ghost.values[GVAL_DAMAGE]);
+ return mon_attack_def::attk(mons->ghost->values[GVAL_DAMAGE]);
else
return mon_attack_def::attk(0, AT_NONE);
}
@@ -734,7 +739,7 @@ int mons_res_elec( const monsters *mon )
int mc = mon->type;
if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (ghost.values[ GVAL_RES_ELEC ]);
+ return (mon->ghost->values[ GVAL_RES_ELEC ]);
/* this is a variable, not a player_xx() function, so can be above 1 */
int u = 0;
@@ -810,7 +815,7 @@ int mons_res_fire( const monsters *mon )
int mc = mon->type;
if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (ghost.values[ GVAL_RES_FIRE ]);
+ return (mon->ghost->values[ GVAL_RES_FIRE ]);
int u = 0, f = get_mons_resists(mon);
@@ -858,7 +863,7 @@ int mons_res_cold( const monsters *mon )
int mc = mon->type;
if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (ghost.values[ GVAL_RES_COLD ]);
+ return (mon->ghost->values[ GVAL_RES_COLD ]);
int u = 0, f = get_mons_resists(mon);
@@ -961,9 +966,6 @@ int mons_skeleton(int mc)
int mons_class_flies(int mc)
{
- if (mc == MONS_PANDEMONIUM_DEMON)
- return (ghost.values[ GVAL_DEMONLORD_FLY ]);
-
int f = smc->bitfields;
if (f & M_FLIES)
@@ -977,6 +979,12 @@ int mons_class_flies(int mc)
int mons_flies( const monsters *mon )
{
+ if (mon->type == MONS_PANDEMONIUM_DEMON
+ && mon->ghost->values[ GVAL_DEMONLORD_FLY ])
+ {
+ return (1);
+ }
+
int ret = mons_class_flies( mon->type );
return (ret ? ret : (scan_mon_inv_randarts(mon, RAP_LEVITATE) > 0) ? 2 : 0);
} // end mons_flies()
@@ -1165,48 +1173,7 @@ int exper_value( const struct monsters *monster )
void mons_load_spells( monsters *mon, int book )
{
- 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);
}
#if DEBUG_DIAGNOSTICS
@@ -1441,12 +1408,13 @@ const char *ptr_monam( const monsters *mon, char desc, bool force_seen )
return (mimic_name_buff);
}
- return (monam( mon->number, mon->type,
+ return (monam( mon, mon->number, mon->type,
force_seen || player_monster_visible( mon ),
desc, mon->inv[MSLOT_WEAPON] ));
}
-const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn )
+const char *monam( const monsters *mon,
+ int mons_num, int mons, bool vis, char desc, int mons_wpn )
{
static char gmo_n[ ITEMNAME_SIZE ];
char gmo_n2[ ITEMNAME_SIZE ] = "";
@@ -1550,12 +1518,12 @@ const char *monam( int mons_num, int mons, bool vis, char desc, int mons_wpn )
break;
case MONS_PLAYER_GHOST:
- strcpy(gmo_n, ghost.name);
+ strcpy(gmo_n, mon->ghost->name.c_str());
strcat(gmo_n, "'s ghost");
break;
case MONS_PANDEMONIUM_DEMON:
- strcpy(gmo_n, ghost.name);
+ strcpy(gmo_n, mon->ghost->name.c_str());
break;
default:
@@ -1759,7 +1727,7 @@ int mons_offhand_weapon_index(const monsters *m)
int mons_base_damage_brand(const monsters *m)
{
if (m->type == MONS_PLAYER_GHOST || m->type == MONS_PANDEMONIUM_DEMON)
- return ghost.values[ GVAL_BRAND ];
+ return m->ghost->values[ GVAL_BRAND ];
return (SPWPN_NORMAL);
}
@@ -2602,6 +2570,57 @@ bool monster_senior(const monsters *m1, const monsters *m2)
///////////////////////////////////////////////////////////////////////////////
// monsters methods
+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 ) );
+}
+
coord_def monsters::pos() const
{
return coord_def(x, y);
@@ -2976,3 +2995,117 @@ void monsters::slow_down(int strength)
slow.flavour = BEAM_SLOW;
mons_ench_f2(this, slow);
}
+
+void monsters::set_ghost(const ghost_demon &g)
+{
+ ghost.reset( new ghost_demon(g) );
+}
+
+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];
+ }
+ }
+}
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 454a80710f..20774df6d8 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -128,7 +128,9 @@ void init_monsters( FixedVector<unsigned short, 1000>& colour );
* spells4
* *********************************************************************** */
// mons_wpn only important for dancing weapons -- bwr
-const char *monam(int mons_num, int mons, bool vis, char desc, int mons_wpn = NON_ITEM);
+const char *monam(const monsters *mon,
+ int mons_num, int mons, bool vis, char desc,
+ int mons_wpn = NON_ITEM);
// these front for monam
const char *ptr_monam(const monsters *mon, char desc, bool force_seen = false);
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 9c70d802a2..0057b36025 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -571,7 +571,6 @@ static int place_monster_aux( int mon_type, char behaviour, int target,
}
}
-
// give manticores 8 to 16 spike volleys.
// they're not spellcasters so this doesn't screw anything up.
if (mon_type == MONS_MANTICORE)
@@ -604,6 +603,15 @@ static int place_monster_aux( int mon_type, char behaviour, int target,
menv[id].foe = target;
+ // Initialise pandemonium demons
+ if (menv[id].type == MONS_PANDEMONIUM_DEMON)
+ {
+ ghost_demon ghost;
+ ghost.init_random_demon();
+ menv[id].set_ghost(ghost);
+ menv[id].pandemon_init();
+ }
+
mark_interesting_monst(&menv[id], behaviour);
if (player_monster_visible(&menv[id]) && mons_near(&menv[id]))
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 0b3f48b85c..0b1fc60250 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -772,39 +772,15 @@ void monster_die(monsters *monster, char killer, int i, bool silent)
monster_cleanup(monster);
if ( xom_will_act )
- Xom_acts(true, xom_will_act, false);
-
+ Xom_acts(true, xom_will_act, false);
} // end monster_die
void monster_cleanup(monsters *monster)
{
unsigned int monster_killed = monster_index(monster);
- 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();
- for (dmi = 0; dmi < MAX_MONSTERS; dmi++)
+ for (int dmi = 0; dmi < MAX_MONSTERS; dmi++)
{
if (menv[dmi].foe == monster_killed)
menv[dmi].foe = MHITNOT;
@@ -812,7 +788,6 @@ void monster_cleanup(monsters *monster)
if (you.pet_target == monster_killed)
you.pet_target = MHITNOT;
-
} // end monster_cleanup()
static bool jelly_divide(struct monsters * parent)
@@ -1037,7 +1012,7 @@ bool monster_polymorph( struct monsters *monster, int targetc, int power )
strcat( str_polymon, "something you cannot see!" );
else
{
- strcat( str_polymon, monam( 250, targetc, true, DESC_NOCAP_A ) );
+ strcat( str_polymon, monam( NULL, 250, targetc, true, DESC_NOCAP_A ) );
if (targetc == MONS_PULSATING_LUMP)
strcat( str_polymon, " of flesh" );
@@ -2371,7 +2346,7 @@ static void handle_nearby_ability(struct monsters *monster)
break;
case MONS_PANDEMONIUM_DEMON:
- if (ghost.values[ GVAL_DEMONLORD_CYCLE_COLOUR ])
+ if (monster->ghost->values[ GVAL_DEMONLORD_CYCLE_COLOUR ])
monster->colour = random_colour();
break;
}
@@ -3164,7 +3139,7 @@ static bool handle_spell( monsters *monster, bolt & beem )
return (false);
}
else if (monster->type == MONS_PANDEMONIUM_DEMON
- && !ghost.values[ GVAL_DEMONLORD_SPELLCASTER ])
+ && !monster->ghost->values[ GVAL_DEMONLORD_SPELLCASTER ])
{
return (false);
}
@@ -4313,13 +4288,10 @@ static bool handle_pickup(struct monsters *monster)
case OBJ_GOLD: //mv - monsters now pick up gold (19 May 2001)
if (monsterNearby)
{
-
- 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 ));
}
if (monster->inv[MSLOT_GOLD] != NON_ITEM)
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 7cccd7b502..611ca70bc5 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -1331,6 +1331,28 @@ int player_prot_life(bool calc_unid)
return (pl);
}
+// Returns the movement speed for a player ghost. Note that this is a real
+// speed, not a movement cost, so higher is better.
+int player_ghost_base_movement_speed()
+{
+ int speed = you.species == SP_NAGA? 8 : 10;
+
+ if (you.mutation[MUT_FAST])
+ speed += you.mutation[MUT_FAST] + 1;
+
+ if (player_equip_ego_type( EQ_BOOTS, SPARM_RUNNING ))
+ speed += 2;
+
+ // Cap speeds.
+ if (speed < 6)
+ speed = 6;
+
+ if (speed > 13)
+ speed = 13;
+
+ return (speed);
+}
+
// New player movement speed system... allows for a bit more that
// "player runs fast" and "player walks slow" in that the speed is
// actually calculated (allowing for centaurs to get a bonus from
@@ -4553,11 +4575,6 @@ void player::init()
num_gifts[i] = 0;
}
- ghost.name[0] = 0;
-
- for (int i = 0; i < NUM_GHOST_VALUES; i++)
- ghost.values[i] = 0;
-
for (int i = EQ_WEAPON; i < NUM_EQUIP; i++)
equip[i] = -1;
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index 4dd31f58f2..2d68ade989 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -474,4 +474,6 @@ bool is_grid_dangerous(int grid);
void run_macro(const char *macroname = NULL);
+int player_ghost_base_movement_speed();
+
#endif
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index a95aad09d4..351aff12ea 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -116,6 +116,9 @@ static void tag_missing_level_attitude();
static void tag_construct_ghost(struct tagHeader &th);
static void tag_read_ghost(struct tagHeader &th, char minorVersion);
+static void marshallGhost(tagHeader &th, const ghost_demon &ghost);
+static ghost_demon unmarshallGhost( tagHeader &th );
+
// provide a wrapper for file writing, just in case.
int write2(FILE * file, const char *buffer, unsigned int count)
{
@@ -324,6 +327,21 @@ void unmarshallString(struct tagHeader &th, char *data, int maxSize)
th.offset += len;
}
+std::string unmarshallString(tagHeader &th, int maxSize)
+{
+ if (maxSize <= 0)
+ return ("");
+ char *buffer = new char [maxSize];
+ if (!buffer)
+ return ("");
+ *buffer = 0;
+ unmarshallString(th, buffer, maxSize);
+ const std::string res = buffer;
+ delete [] buffer;
+
+ return (res);
+}
+
// boolean (to avoid system-dependant bool implementations)
void marshallBoolean(struct tagHeader &th, bool data)
{
@@ -592,7 +610,7 @@ void tag_set_expected(char tags[], int fileType)
tags[i] = 1;
break;
case TAGTYPE_LEVEL:
- if (i >= TAG_LEVEL && i <= TAG_LEVEL_ATTITUDE)
+ if (i >= TAG_LEVEL && i <= TAG_LEVEL_ATTITUDE && i != TAG_GHOST)
tags[i] = 1;
break;
case TAGTYPE_GHOST:
@@ -1330,6 +1348,13 @@ static void tag_construct_level_monsters(struct tagHeader &th)
marshallShort(th, m.spells[j]);
marshallByte(th, m.god);
+
+ if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON)
+ {
+ // *Must* have ghost field set.
+ ASSERT(m.ghost.get());
+ marshallGhost(th, *m.ghost);
+ }
}
}
@@ -1474,40 +1499,45 @@ static void tag_read_level_monsters(struct tagHeader &th, char minorVersion)
for (i = 0; i < count; i++)
{
- 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);
// Avoid sign extension when loading files (Elethiomel's hang)
- 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);
for (j = 0; j < ecount; j++)
- menv[i].enchantment[j] = unmarshallByte(th);
+ m.enchantment[j] = unmarshallByte(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);
- menv[i].colour = unmarshallShort(th);
+ m.colour = unmarshallShort(th);
for (j = 0; j < icount; j++)
- menv[i].inv[j] = unmarshallShort(th);
+ m.inv[j] = unmarshallShort(th);
for (j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)
- menv[i].spells[j] = unmarshallShort(th);
+ m.spells[j] = unmarshallShort(th);
- menv[i].god = (god_type) unmarshallByte(th);
+ m.god = (god_type) unmarshallByte(th);
+
+ if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON)
+ m.set_ghost( unmarshallGhost(th) );
// place monster
- if (menv[i].type != -1)
- mgrd[menv[i].x][menv[i].y] = i;
+ if (m.type != -1)
+ mgrd[m.x][m.y] = i;
}
}
@@ -1579,28 +1609,51 @@ void tag_missing_level_attitude()
// ------------------------------- ghost tags ---------------------------- //
-static void tag_construct_ghost(struct tagHeader &th)
+static void marshallGhost(tagHeader &th, const ghost_demon &ghost)
{
- int i;
-
- marshallString(th, ghost.name, 20);
+ marshallString(th, ghost.name.c_str(), 20);
// how many ghost values?
- marshallByte(th, 20);
+ marshallByte(th, NUM_GHOST_VALUES);
- for (i = 0; i < 20; i++)
+ for (int i = 0; i < NUM_GHOST_VALUES; i++)
marshallShort( th, ghost.values[i] );
}
-static void tag_read_ghost(struct tagHeader &th, char minorVersion)
+static void tag_construct_ghost(struct tagHeader &th)
{
- int i, count_c;
+ // How many ghosts?
+ marshallShort(th, ghosts.size());
- unmarshallString(th, ghost.name, 20);
+ for (int i = 0, size = ghosts.size(); i < size; ++i)
+ marshallGhost(th, ghosts[i]);
+}
+
+static ghost_demon unmarshallGhost( tagHeader &th )
+{
+ ghost_demon ghost;
+
+ ghost.name = unmarshallString(th, 20);
// how many ghost values?
- count_c = unmarshallByte(th);
+ int count_c = unmarshallByte(th);
- for (i = 0; i < count_c; i++)
+ if (count_c > NUM_GHOST_VALUES)
+ count_c = NUM_GHOST_VALUES;
+
+ for (int i = 0; i < count_c; i++)
ghost.values[i] = unmarshallShort(th);
+
+ return (ghost);
+}
+
+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) );
}
diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h
index 553cdaee6d..3f98a0d216 100644
--- a/crawl-ref/source/tags.h
+++ b/crawl-ref/source/tags.h
@@ -51,6 +51,7 @@ long unmarshallLong(struct tagHeader &th);
float unmarshallFloat(struct tagHeader &th);
bool unmarshallBoolean(struct tagHeader &th);
void unmarshallString(struct tagHeader &th, char *data, int maxSize);
+std::string unmarshallString(tagHeader &th, int maxSize);
std::string make_date_string( time_t in_date );
time_t parse_date_string( char[20] );