summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-01-09 14:24:11 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-01-09 14:24:11 +0000
commitf2a95ba0d453ba6685ed29201beda8a2f703ab79 (patch)
treee7201beb995472236921334a363644e5782d648b /crawl-ref
parent447bc8ac9d8557be01da02c40349e4301f42c089 (diff)
downloadcrawl-ref-f2a95ba0d453ba6685ed29201beda8a2f703ab79.tar.gz
crawl-ref-f2a95ba0d453ba6685ed29201beda8a2f703ab79.zip
Monsters get multilevel resists (incomplete). Monster data needs to be
adjusted per monster to hand out the right resists. The current MR_RES_FIRE gives one level of resistance only. Added a real ghost structure, discarded the old ghost values array. Adjusted bones file format so bones will work out-of-the-box with Hearse. Breaks bones format, older bones will be rejected. Fixed some maps with bad DEPTHs. Added more safe answers in Y/N prompts, added a check to make it less likely that Crawl will spin in a tight loop reading input from a closed tty. (Experimental) !a will override existing foe of friendlies in LOS. Blademasters no longer pick up stuff to throw (Erik). Zombies of swimming things are also swimming things. Currently applies only to zombies explicitly placed in .des files, since fish zombies cannot be generated otherwise (can of worms). Morgue is now saved before showing the inventory and other boring end-of-game stuff. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3231 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/Kills.cc1
-rw-r--r--crawl-ref/source/acr.cc6
-rw-r--r--crawl-ref/source/beam.cc75
-rw-r--r--crawl-ref/source/dat/altar.des52
-rw-r--r--crawl-ref/source/debug.cc16
-rw-r--r--crawl-ref/source/decks.cc2
-rw-r--r--crawl-ref/source/describe.cc38
-rw-r--r--crawl-ref/source/direct.cc2
-rw-r--r--crawl-ref/source/dungeon.cc6
-rw-r--r--crawl-ref/source/effects.cc32
-rw-r--r--crawl-ref/source/enum.h33
-rw-r--r--crawl-ref/source/externs.h44
-rw-r--r--crawl-ref/source/fight.cc82
-rw-r--r--crawl-ref/source/fight.h4
-rw-r--r--crawl-ref/source/files.cc73
-rw-r--r--crawl-ref/source/ghost.cc216
-rw-r--r--crawl-ref/source/ghost.h52
-rw-r--r--crawl-ref/source/itemname.cc16
-rw-r--r--crawl-ref/source/itemprop.h5
-rw-r--r--crawl-ref/source/libunix.cc7
-rw-r--r--crawl-ref/source/message.cc5
-rw-r--r--crawl-ref/source/misc.cc2
-rw-r--r--crawl-ref/source/mon-data.h4
-rw-r--r--crawl-ref/source/mon-util.cc270
-rw-r--r--crawl-ref/source/mon-util.h71
-rw-r--r--crawl-ref/source/monplace.cc41
-rw-r--r--crawl-ref/source/monplace.h2
-rw-r--r--crawl-ref/source/monspeak.cc3
-rw-r--r--crawl-ref/source/monstuff.cc132
-rw-r--r--crawl-ref/source/ouch.cc25
-rw-r--r--crawl-ref/source/player.cc21
-rw-r--r--crawl-ref/source/tags.cc124
-rw-r--r--crawl-ref/source/view.cc3
33 files changed, 849 insertions, 616 deletions
diff --git a/crawl-ref/source/Kills.cc b/crawl-ref/source/Kills.cc
index 02c3decb20..380f4c33fd 100644
--- a/crawl-ref/source/Kills.cc
+++ b/crawl-ref/source/Kills.cc
@@ -14,6 +14,7 @@
#include "mon-util.h"
#include "monstuff.h"
#include "files.h"
+#include "ghost.h"
#include "itemname.h"
#include "place.h"
#include "travel.h"
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 89e6d0aad1..a5c7e4ed7b 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -974,7 +974,7 @@ static void handle_wizard_command( void )
if (testbits(env.level_flags, LFLAG_NOT_MAPPABLE)
|| testbits(get_branch_flags(), BFLAG_NOT_MAPPABLE))
{
- if (!yesno("Force level to be mappable? "))
+ if (!yesno("Force level to be mappable? ", true, 'n'))
{
canned_msg( MSG_OK );
return;
@@ -1069,7 +1069,7 @@ static bool recharge_rod( item_def &rod, bool wielded )
const int charge = rod.plus / ROD_CHARGE_MULT;
int rate = ((charge + 1) * ROD_CHARGE_MULT) / 10;
-
+
rate *= (10 + skill_bump( SK_EVOCATIONS ));
rate = div_rand_round( rate, 100 );
@@ -1240,7 +1240,7 @@ static bool cmd_is_repeatable(command_type cmd, bool is_again = false)
case CMD_MOVE_DOWN_RIGHT:
if (!i_feel_safe())
return yesno("Really repeat movement command while monsters "
- "are nearby?");
+ "are nearby?", false, 'n');
return true;
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 15d3f68a85..29719a3886 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -39,6 +39,7 @@
#include "delay.h"
#include "effects.h"
#include "enum.h"
+#include "fight.h"
#include "item_use.h"
#include "it_use2.h"
#include "items.h"
@@ -1591,28 +1592,28 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt,
// if we're not doing flavored effects, must be preliminary
// damage check only; do not print messages or apply any side
// effects!
- int resist;
+ int resist = 0;
+ int original = hurted;
switch (pbolt.flavour)
{
case BEAM_FIRE:
case BEAM_STEAM:
- resist = mons_res_fire(monster);
- if (resist > 1)
+ hurted = resist_adjust_damage(monster,
+ monster->res_fire(),
+ hurted,
+ true);
+ if (!hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
}
- else if (resist == 1)
+ else if (original > hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " resists.");
-
- hurted /= 3;
}
- else if (resist < 0)
+ else if (original < hurted)
{
if (mons_is_icy(monster))
{
@@ -1624,75 +1625,70 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt,
if (doFlavouredEffects)
simple_monster_message(monster, " is burned terribly!");
}
-
- hurted *= 15;
- hurted /= 10;
}
break;
-
case BEAM_COLD:
- resist = mons_res_cold(monster);
- if (resist > 1)
+ hurted = resist_adjust_damage(monster, monster->res_cold(),
+ hurted, true);
+ if (!hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
}
- else if (resist == 1)
+ else if (original > hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " resists.");
-
- hurted /= 3;
}
- else if (resist < 0)
+ else if (original < hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " is frozen!");
-
- hurted *= 15;
- hurted /= 10;
}
break;
case BEAM_ELECTRICITY:
- if (mons_res_elec(monster) > 0)
+ hurted = resist_adjust_damage(monster, monster->res_elec(),
+ hurted, true);
+ if (!hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
}
break;
case BEAM_ACID:
- if (mons_res_acid(monster))
+ hurted = resist_adjust_damage(monster, mons_res_acid(monster),
+ hurted, true);
+ if (!hurted)
{
if (doFlavouredEffects)
simple_monster_message(monster, " appears unharmed.");
-
- hurted = 0;
}
break;
case BEAM_POISON:
- if (mons_res_poison(monster) > 0)
+ {
+ int res = mons_res_poison(monster);
+ hurted = resist_adjust_damage(monster, res, hurted, true);
+ if (!hurted)
{
if (doFlavouredEffects)
simple_monster_message( monster, " appears unharmed." );
-
- hurted = 0;
}
- else if (doFlavouredEffects && !one_chance_in(3))
+ else if (res <= 0 && doFlavouredEffects && !one_chance_in(3))
{
poison_monster( monster, whose_kill(pbolt) );
}
break;
+ }
case BEAM_POISON_ARROW:
- if (mons_res_poison(monster) > 0)
+ hurted = resist_adjust_damage(monster,
+ std::min(mons_res_poison(monster), 1),
+ hurted);
+ if (hurted < original)
{
if (doFlavouredEffects)
{
@@ -1703,8 +1699,6 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt,
if (mons_has_lifeforce(monster))
poison_monster( monster, whose_kill(pbolt), 2, true );
}
-
- hurted /= 2;
}
else if (doFlavouredEffects)
{
@@ -1730,7 +1724,8 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt,
pbolt.obvious_effect = true;
if (YOU_KILL(pbolt.thrower))
- did_god_conduct(DID_NECROMANCY, 2 + random2(3), pbolt.effect_known);
+ did_god_conduct(DID_NECROMANCY, 2 + random2(3),
+ pbolt.effect_known);
if (one_chance_in(5))
{
@@ -2237,12 +2232,12 @@ void sticky_flame_monster( int mn, kill_category who, int levels )
if (!monster->alive())
return;
- if (mons_res_fire(monster) > 0)
+ if (mons_res_sticky_flame(monster))
return;
if (monster->add_ench(mon_enchant(ENCH_STICKY_FLAME, levels, who)))
simple_monster_message(monster, " is covered in liquid fire!");
-} // end sticky_flame_monster
+}
/*
* Used by monsters in "planning" which spell to cast. Fires off a "tracer"
diff --git a/crawl-ref/source/dat/altar.des b/crawl-ref/source/dat/altar.des
index 2fb39ee218..440960e7b6 100644
--- a/crawl-ref/source/dat/altar.des
+++ b/crawl-ref/source/dat/altar.des
@@ -20,7 +20,6 @@ TAGS: allow_dup
CHANCE: 20
MAP
cccccccccc
-cccccccccc
cBcBcBcBcc
G.c.c.c.Bc
@.......Bc
@@ -28,7 +27,6 @@ G.c.c.c.Bc
G.c.c.c.Bc
cBcBcBcBcc
cccccccccc
-cccccccccc
ENDMAP
NAME: jmf_multi_god_temple
@@ -124,15 +122,15 @@ TAGS: no_monster_gen
TAGS: no_monster_gen no_pool_fixup
: end
MAP
- .........
- ...wwwww...
-..wwwwwwwww..
-.wwwwwwwwwww..
-wwwwwwwwwwwww.
-wwwwwwCwwwwww.
-wwwwwwwwwwwww.
-.wwwwwwwwwww..
-..wwwwwwwww..
+ .........
+ ...wwwww...
+ ..wwwwwwwww..
+..wwwwwwwwwww..
+.wwwwwwwwwwwww.
+.wwwwwwCwwwwww.
+.wwwwwwwwwwwww.
+..wwwwwwwwwww..
+ .wwwwwwwww..
...wwwww...
........
ENDMAP
@@ -228,14 +226,19 @@ MAP
ENDMAP
NAME: lemuel_statue_altar
-DEPTH: 2-18, !Lair, !Hive, !Slime
+
+# Increased depth from 2-18 because getting a hostile statue can
+# instakill a low-level character.
+
+DEPTH: 10-18, !Lair, !Hive, !Slime
+
MAP
.....
.cFc.
.c.c.
.c.c.
.c.c.
-.c.c.
+.c>c.
.cCc.
.ccc.
.....
@@ -249,7 +252,7 @@ MAP
xxxxxxxxxxxxxxxxxxxx
...................x
c...c...c...c...c..x
-..................Cx
+@.................Cx
c...c...c...c...c..x
...................x
xxxxxxxxxxxxxxxxxxxx
@@ -281,20 +284,25 @@ ENDMAP
######################################
NAME: lemuel_angel_altar
-DEPTH: 2-18, !Lair, !Orc, !Hive, !Slime
+
+# Moved deeper since being teleported next to the Angel will be a
+# quick death at shallow levels.
+
+DEPTH: 9-18, !Lair, !Orc, !Hive, !Slime
MONS: angel
KFEAT: C = altar_elyvilon / altar_zin / altar_shining_one
MAP
.....
..xmx..
- ..xx.xx..
+ ..xx>xx..
..xxx.xxx..
.xxxx1xxxx.
-.m...C...m.
+.m>..C..>m.
.xxxx.xxxx.
..xxx.xxx..
- ..xx.xx..
- ..xmx..
+ ..xx>xx..
+ ..xmx..
+ .....
ENDMAP
NAME: lemuel_hellish_altar
@@ -340,13 +348,13 @@ ENDMAP
NAME: lemuel_blue_sif_altar
DEPTH: 2-18, !Lair, !Hive, !Slime, !Orc
-TAGS: no_monster_gen
+TAGS: no_monster_gen mini_float
COLOUR: . = blue
KFEAT: C = altar_sif_muna
MAP
xxxxxxxxxxxxxx
...........xxx
-...........+Cx
+@..........+Cx
...........xxx
xxxxxxxxxxxxxx
ENDMAP
@@ -361,7 +369,7 @@ MAP
xxxxxxxxxxxxxxxx
xxxx11111111xxxx
xxxxwwwwwwwwxxxx
-..............Cx
+@.............Cx
xxxxwwwwwwwwxxxx
xxxx11111111xxxx
xxxxxxxxxxxxxxxx
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index df323e2c23..a0eac1d559 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -48,6 +48,7 @@
#include "fight.h"
#include "files.h"
#include "food.h"
+#include "ghost.h"
#ifdef DEBUG_DIAGNOSTICS
#include "initfile.h"
@@ -469,7 +470,6 @@ void create_spec_monster_name(int x, int y)
ghost_demon ghost;
ghost.name = "John Doe";
- ghost.values.init(0);
char class_str[80];
mpr( "Make player ghost which class? ", MSGCH_PROMPT );
@@ -485,7 +485,7 @@ void create_spec_monster_name(int x, int y)
mpr("No such class, making it a Fighter.");
class_id = JOB_FIGHTER;
}
- ghost.values[GVAL_CLASS] = class_id;
+ ghost.job = static_cast<job_type>(class_id);
mon.set_ghost(ghost);
@@ -1412,7 +1412,7 @@ void stethoscope(int mwh)
// print behaviour information
- const habitat_type hab = mons_habitat( menv[i].type );
+ const habitat_type hab = mons_habitat( &menv[i] );
mprf(MSGCH_DIAGNOSTICS,
"hab=%s beh=%s(%d) foe=%s(%d) mem=%d target=(%d,%d)",
@@ -1453,8 +1453,8 @@ void stethoscope(int mwh)
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 ] );
+ "Ghost damage: %d; brand: %d",
+ ghost.damage, ghost.brand );
}
} // end stethoscope()
#endif
@@ -2295,14 +2295,14 @@ bool debug_add_mutation(void)
else
msg = "You are resistant to mutations, remove resistance?";
- if (yesno(msg))
+ if (yesno(msg, true, 'n'))
{
you.mutation[MUT_MUTATION_RESISTANCE] = 0;
crawl_state.cancel_cmd_repeat();
}
}
- bool force = yesno("Force mutation to happen?");
+ bool force = yesno("Force mutation to happen?", true, 'n');
if (you.mutation[MUT_MUTATION_RESISTANCE] == 3 && !force)
{
@@ -3262,7 +3262,7 @@ static void debug_load_map_by_name(std::string name)
std::string prompt = "Only match is '";
prompt += matches[0];
prompt += "', use that?";
- if (!yesno(prompt.c_str()))
+ if (!yesno(prompt.c_str(), true, 'y'))
return;
map = find_map_by_name(matches[0]);
diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc
index a37728d045..6ed7a2f148 100644
--- a/crawl-ref/source/decks.cc
+++ b/crawl-ref/source/decks.cc
@@ -683,7 +683,7 @@ static bool check_buggy_deck(item_def& deck)
you.wield_change = true;
if (!yesno("Problems might not have been completely fixed; "
- "still use deck?"))
+ "still use deck?", true, 'n'))
{
crawl_state.zero_turns_taken();
return true;
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 72382883b4..b53d77ac3b 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -40,6 +40,7 @@
#include "decks.h"
#include "fight.h"
#include "food.h"
+#include "ghost.h"
#include "it_use2.h"
#include "itemname.h"
#include "itemprop.h"
@@ -589,17 +590,17 @@ static std::string describe_demon(const monsters &mons)
description << "A powerful demon, " << ghost.name << " has a"
<< RANDOM_ELEMENT(body_descs) << "body";
- switch (ghost.values[GVAL_DEMONLORD_FLY])
+ switch (ghost.fly)
{
- case 1:
+ case FL_FLY:
description << RANDOM_ELEMENT(wing_names);
break;
- case 2: // levitation
+ case FL_LEVITATE:
description << RANDOM_ELEMENT(lev_names);
break;
- default: // does not fly
+ case FL_NONE: // does not fly
if ( !one_chance_in(4) )
description << RANDOM_ELEMENT(nonfly_names);
break;
@@ -2171,8 +2172,7 @@ std::string ghost_description(const monsters &mons, bool concise)
const ghost_demon &ghost = *(mons.ghost);
- const species_type gspecies =
- static_cast<species_type>(ghost.values[GVAL_SPECIES]);
+ const species_type gspecies = ghost.species;
// We're fudging stats so that unarmed combat gets based off
// of the ghost's species, not the player's stats... exact
@@ -2207,28 +2207,28 @@ std::string ghost_description(const monsters &mons, bool concise)
}
gstr << ghost.name << " the "
- << skill_title( ghost.values[GVAL_BEST_SKILL],
- ghost.values[GVAL_SKILL_LEVEL],
+ << skill_title( ghost.best_skill,
+ ghost.best_skill_level,
gspecies,
str, dex, GOD_NO_GOD )
<< ", a"
- << ((ghost.values[GVAL_EXP_LEVEL] < 4) ? " weakling" :
- (ghost.values[GVAL_EXP_LEVEL] < 7) ? "n average" :
- (ghost.values[GVAL_EXP_LEVEL] < 11) ? "n experienced" :
- (ghost.values[GVAL_EXP_LEVEL] < 16) ? " powerful" :
- (ghost.values[GVAL_EXP_LEVEL] < 22) ? " mighty" :
- (ghost.values[GVAL_EXP_LEVEL] < 26) ? " great" :
- (ghost.values[GVAL_EXP_LEVEL] < 27) ? "n awesomely powerful"
- : " legendary")
+ << ((ghost.xl < 4) ? " weakling" :
+ (ghost.xl < 7) ? "n average" :
+ (ghost.xl < 11) ? "n experienced" :
+ (ghost.xl < 16) ? " powerful" :
+ (ghost.xl < 22) ? " mighty" :
+ (ghost.xl < 26) ? " great" :
+ (ghost.xl < 27) ? "n awesomely powerful"
+ : " legendary")
<< " ";
if ( concise )
gstr << get_species_abbrev(gspecies)
- << get_class_abbrev(ghost.values[GVAL_CLASS]);
+ << get_class_abbrev(ghost.job);
else
gstr << species_name(gspecies,
- ghost.values[GVAL_EXP_LEVEL])
+ ghost.xl)
<< " "
- << get_class_name(ghost.values[GVAL_CLASS]);
+ << get_class_name(ghost.job);
return gstr.str();
}
diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc
index 8430d326fb..5e77792713 100644
--- a/crawl-ref/source/direct.cc
+++ b/crawl-ref/source/direct.cc
@@ -872,7 +872,7 @@ void direction(dist& moves, targeting_type restricts,
}
// Ask for confirmation if we're quitting for some odd reason
else if ( moves.isValid || moves.isCancel ||
- yesno("Are you sure you want to fizzle?") )
+ yesno("Are you sure you want to fizzle?", false, 'n') )
{
// Finalize whatever is inside the loop
// (moves-internal finalizations can be done later)
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index d2b0331ca1..566ba7f665 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -3384,13 +3384,9 @@ static bool build_minivaults(int level_number, int force_vault,
bool building_level, bool clobber,
bool make_no_exits, const coord_def &where)
{
- // for some weird reason can't put a vault on level 1, because monster equip
- // isn't generated.
int altar_count = 0;
FixedVector < object_class_type, 7 > acq_item_class;
- // hack - passing chars through '...' promotes them to ints, which
- // barfs under gcc in fixvec.h. So don't.
acq_item_class[0] = OBJ_WEAPONS;
acq_item_class[1] = OBJ_ARMOUR;
acq_item_class[2] = OBJ_WEAPONS;
@@ -4329,7 +4325,7 @@ bool dgn_place_monster(mons_spec &mspec,
if (mons_is_unique(mid) && you.unique_creatures[mid])
return (false);
- const habitat_type habitat = mons_habitat(mid);
+ const habitat_type habitat = mons_habitat_by_type(mid);
if (habitat != HT_LAND)
grd[vx][vy] = habitat2grid(habitat);
}
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 4658ddb393..5d0a72054e 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -1689,6 +1689,18 @@ bool recharge_wand(void)
return (true);
} // end recharge_wand()
+// Sets foe target of friendly monsters.
+static void set_friendly_foes()
+{
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *mon(&menv[i]);
+ if (!mon->alive() || !mons_near(mon) || !mons_friendly(mon))
+ continue;
+ mon->foe = you.pet_target;
+ }
+}
+
void yell(bool force)
{
bool targ_prev = false;
@@ -1723,10 +1735,12 @@ void yell(bool force)
if (force)
{
if (shout_verb == "__NONE" || you.paralysed())
- mprf("You feel a strong urge to %s, but you are unable to make a sound!",
+ mprf("You feel a strong urge to %s, but "
+ "you are unable to make a sound!",
shout_verb == "__NONE" ? "scream" : shout_verb.c_str());
else
- mprf("You feel a %s rip itself from your throat, but you make no sound!",
+ mprf("You feel a %s rip itself from your throat, "
+ "but you make no sound!",
shout_verb.c_str());
}
else
@@ -1751,9 +1765,9 @@ void yell(bool force)
if (!(you.prev_targ == MHITNOT || you.prev_targ == MHITYOU))
{
- struct monsters *target = &menv[you.prev_targ];
-
- if (mons_near(target) && player_monster_visible(target))
+ monsters *target = &menv[you.prev_targ];
+ if (target->alive() && mons_near(target)
+ && player_monster_visible(target))
{
mpr(" p - Order allies to attack your previous target");
targ_prev = true;
@@ -1818,7 +1832,11 @@ void yell(bool force)
return;
}
- you.pet_target = mons_targd;
+ if (mons_targd != MHITNOT)
+ {
+ you.pet_target = mons_targd;
+ set_friendly_foes();
+ }
noisy( 10, you.x_pos, you.y_pos );
mpr("Attack!");
@@ -2452,7 +2470,7 @@ void update_level( double elapsedTime )
continue;
// Don't move non-land or stationary monsters around
- if (mons_habitat( mon->type ) != HT_LAND
+ if (mons_habitat( mon ) != HT_LAND
|| mons_is_stationary( mon ))
{
continue;
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 28369c77ac..476bd92e88 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1180,39 +1180,6 @@ enum flush_reason_type
NUM_FLUSH_REASONS
};
-enum ghost_value_type
-{
- GVAL_MAX_HP, // 0
- GVAL_EV,
- GVAL_AC,
- GVAL_SEE_INVIS,
- GVAL_RES_FIRE,
- GVAL_RES_COLD, // 5
- GVAL_RES_ELEC,
- GVAL_DAMAGE,
- GVAL_BRAND,
- GVAL_SPECIES,
- GVAL_BEST_SKILL, // 10
- GVAL_SKILL_LEVEL,
- GVAL_EXP_LEVEL,
- GVAL_CLASS,
- GVAL_SPELL_1, // 14
- GVAL_SPELL_2,
- GVAL_SPELL_3,
- 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:
- GVAL_DEMONLORD_SPELLCASTER = 9,
- GVAL_DEMONLORD_FLY, // 10
- GVAL_DEMONLORD_UNUSED, // 11
- GVAL_DEMONLORD_HIT_DICE, // 12
- GVAL_DEMONLORD_CYCLE_COLOUR // 13
-};
-
// The order of this enum must match the order of DNGN_ALTAR_FOO.
enum god_type
{
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index f936981c99..dd382f2aa4 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -93,6 +93,8 @@ public:
virtual bool submerged() const = 0;
virtual bool floundering() const = 0;
+ virtual int get_experience_level() const = 0;
+
virtual bool can_pass_through(const dungeon_feature_type grid) const = 0;
virtual bool can_pass_through(const int x, const int y) const = 0;
virtual bool can_pass_through(const coord_def &c) const = 0;
@@ -107,6 +109,11 @@ public:
virtual item_def *weapon(int which_attack = -1) = 0;
virtual item_def *shield() = 0;
virtual item_def *slot_item(equipment_type eq) = 0;
+ virtual const item_def *slot_item(equipment_type eq) const
+ {
+ return const_cast<actor*>(this)->slot_item(eq);
+ }
+ virtual bool has_equipped(equipment_type eq, int sub_type) const;
virtual int hunger_level() const { return HS_ENGORGED; }
virtual void make_hungry(int nutrition, bool silent = true)
@@ -175,6 +182,7 @@ public:
virtual mon_holy_type holiness() const = 0;
virtual int res_fire() const = 0;
+ virtual int res_steam() const = 0;
virtual int res_cold() const = 0;
virtual int res_elec() const = 0;
virtual int res_poison() const = 0;
@@ -784,16 +792,16 @@ public:
size_type transform_size(int psize = PSIZE_TORSO) const;
std::string shout_verb() const;
- const item_def *slot_item(equipment_type eq) const;
item_def *slot_item(equipment_type eq);
// actor
int id() const;
+ int get_experience_level() const;
actor_type atype() const { return ACT_PLAYER; }
god_type deity() const;
bool alive() const;
-
+
coord_def pos() const;
bool swimming() const;
bool submerged() const;
@@ -849,6 +857,7 @@ public:
mon_holy_type holiness() const;
int res_fire() const;
+ int res_steam() const;
int res_cold() const;
int res_elec() const;
int res_poison() const;
@@ -984,6 +993,7 @@ class monsters : public actor
public:
monsters();
monsters(const monsters &other);
+ ~monsters();
monsters &operator = (const monsters &other);
@@ -1076,6 +1086,7 @@ public:
// actor interface
int id() const;
+ int get_experience_level() const;
god_type deity() const;
bool alive() const;
coord_def pos() const;
@@ -1146,6 +1157,7 @@ public:
mon_holy_type holiness() const;
int res_fire() const;
+ int res_steam() const;
int res_cold() const;
int res_elec() const;
int res_poison() const;
@@ -1379,34 +1391,6 @@ public:
extern struct crawl_environment env;
-struct ghost_demon
-{
-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);
- static void find_transiting_ghosts(std::vector<ghost_demon> &gs, int n);
- static void announce_ghost(const ghost_demon &g);
-
-private:
- void add_spells();
- int translate_spell(int playerspell) const;
-};
-
-extern std::vector<ghost_demon> ghosts;
-
struct message_filter
{
int channel; // Use -1 to match any channel.
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 09fa35a48f..632a606213 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -1654,17 +1654,16 @@ bool melee_attack::player_monattk_hit_effects(bool mondied)
return (false);
}
-int melee_attack::resist_adjust_damage(int res, int rawdamage)
+int resist_adjust_damage(actor *defender, int res, int rawdamage,
+ bool ranged)
{
+ if (defender->atype() == ACT_MONSTER && res >= 3)
+ return (0);
+
if (res > 0)
- {
- if (defender->atype() == ACT_MONSTER)
- rawdamage = 0;
- else
- rawdamage /= 1 + res * res;
- }
+ rawdamage /= 1 + res * res;
else if (res < 0)
- rawdamage *= 2;
+ rawdamage = rawdamage * (ranged? 15 : 20) / 10;
if (rawdamage < 0)
rawdamage = 0;
@@ -1676,7 +1675,8 @@ void melee_attack::calc_elemental_brand_damage(
int res,
const char *verb)
{
- special_damage = resist_adjust_damage(res, random2(damage_done) / 2 + 1);
+ special_damage =
+ resist_adjust_damage(defender, res, random2(damage_done) / 2 + 1);
if (special_damage > 0 && verb && needs_message)
{
@@ -2075,28 +2075,32 @@ bool melee_attack::apply_damage_brand()
case SPWPN_CONFUSE:
{
- // FIXME: Generalise.
- if (defender->atype() != ACT_MONSTER || attacker->atype() != ACT_PLAYER)
- break;
-
emit_nodmg_hit_message();
// FIXME Currently Confusing Touch is the *only* way to get
// here. Generalise.
const int hdcheck =
(defender->holiness() == MH_NATURAL? random2(30) : random2(22));
- if (hdcheck >= def->hit_dice)
+ if (hdcheck >= defender->get_experience_level())
{
// declaring these just to pass to the enchant function
bolt beam_temp;
- beam_temp.thrower = KILL_YOU;
- beam_temp.flavour = BEAM_CONFUSION;
+ beam_temp.thrower =
+ attacker->atype() == ACT_PLAYER? KILL_YOU : KILL_MON;
+ beam_temp.flavour = BEAM_CONFUSION;
+ beam_temp.beam_source =
+ attacker->atype() == ACT_PLAYER? MHITYOU
+ : monster_index(atk);
mons_ench_f2( def, beam_temp );
}
- you.duration[DUR_CONFUSING_TOUCH] -= roll_dice(3, 5);
+
+ if (attacker->atype() == ACT_PLAYER)
+ {
+ you.duration[DUR_CONFUSING_TOUCH] -= roll_dice(3, 5);
- if (you.duration[DUR_CONFUSING_TOUCH] < 1)
- you.duration[DUR_CONFUSING_TOUCH] = 1;
+ if (you.duration[DUR_CONFUSING_TOUCH] < 1)
+ you.duration[DUR_CONFUSING_TOUCH] = 1;
+ }
break;
}
}
@@ -2231,7 +2235,6 @@ void melee_attack::player_apply_staff_damage()
return;
const int staff_cost = 2;
-
if (you.magic_points < staff_cost
|| random2(15) > you.skills[SK_EVOCATIONS])
{
@@ -2244,10 +2247,10 @@ void melee_attack::player_apply_staff_damage()
if (damage_done + you.skills[SK_AIR_MAGIC] <= random2(20))
break;
- if (mons_res_elec(def))
- break;
-
- special_damage = player_staff_damage(SK_AIR_MAGIC);
+ special_damage =
+ resist_adjust_damage(defender,
+ defender->res_elec(),
+ player_staff_damage(SK_AIR_MAGIC));
if (special_damage)
special_damage_message =
@@ -2257,13 +2260,10 @@ void melee_attack::player_apply_staff_damage()
break;
case STAFF_COLD:
- if (mons_res_cold(def) > 0)
- break;
-
- special_damage = player_staff_damage(SK_ICE_MAGIC);
-
- if (mons_res_cold(def) < 0)
- special_damage += player_staff_damage(SK_ICE_MAGIC);
+ special_damage =
+ resist_adjust_damage(defender,
+ defender->res_cold(),
+ player_staff_damage(SK_ICE_MAGIC));
if (special_damage)
{
@@ -2287,13 +2287,10 @@ void melee_attack::player_apply_staff_damage()
break;
case STAFF_FIRE:
- if (mons_res_fire(def) > 0)
- break;
-
- special_damage = player_staff_damage(SK_FIRE_MAGIC);
-
- if (mons_res_fire(def) < 0)
- special_damage += player_staff_damage(SK_FIRE_MAGIC);
+ special_damage =
+ resist_adjust_damage(defender,
+ defender->res_fire(),
+ player_staff_damage(SK_FIRE_MAGIC));
if (special_damage)
{
@@ -2357,8 +2354,6 @@ void melee_attack::player_apply_staff_damage()
if (special_damage > 0)
{
- dec_mp(staff_cost);
-
if (!item_type_known(*weapon))
{
set_ident_flags( *weapon, ISFLAG_KNOW_TYPE );
@@ -3279,7 +3274,8 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk)
atk->hit_points = -10;
special_damage =
- resist_adjust_damage(defender->res_fire(),
+ resist_adjust_damage(defender,
+ defender->res_fire(),
atk->hit_dice + random2(atk->hit_dice));
if (needs_message && special_damage)
mprf("%s %s engulfed in flames%s",
@@ -3292,7 +3288,8 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk)
case AF_COLD:
special_damage =
- resist_adjust_damage(defender->res_cold(),
+ resist_adjust_damage(defender,
+ defender->res_cold(),
atk->hit_dice + random2( 2 * atk->hit_dice ));
if (needs_message && special_damage)
mprf("%s %s %s!",
@@ -3304,6 +3301,7 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk)
case AF_ELEC:
special_damage =
resist_adjust_damage(
+ defender,
defender->res_elec(),
atk->hit_dice + random2( atk->hit_dice / 2 ));
diff --git a/crawl-ref/source/fight.h b/crawl-ref/source/fight.h
index 03984b03b9..1a62c70b41 100644
--- a/crawl-ref/source/fight.h
+++ b/crawl-ref/source/fight.h
@@ -36,6 +36,9 @@ struct mon_attack_def;
* *********************************************************************** */
int effective_stat_bonus( int wepType = -1 );
+int resist_adjust_damage(actor *defender, int res, int rawdamage,
+ bool ranged = false);
+
// added Sept 18, 2000 -- bwr
/* ***********************************************************************
* called from: describe.cc
@@ -164,7 +167,6 @@ private:
bool attack_shield_blocked(bool verbose);
bool apply_damage_brand();
void calc_elemental_brand_damage(int res, const char *verb);
- int resist_adjust_damage(int res, int rawdamage);
int fire_res_apply_cerebov_downgrade(int res);
void drain_defender();
void drain_player();
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index 78d8edb5ef..932c0a6a2c 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -63,6 +63,7 @@
#include "direct.h"
#include "dungeon.h"
#include "effects.h"
+#include "ghost.h"
#include "initfile.h"
#include "itemname.h"
#include "itemprop.h"
@@ -107,6 +108,8 @@ void save_level(int level_saved, level_area_type lt,
#define LEVEL_MINOR_VERSION 1
#define YOU_MINOR_VERSION 1
+const short GHOST_SIGNATURE = static_cast<short>( 0xDC55 );
+
static void redraw_all(void)
{
you.redraw_hit_points = 1;
@@ -601,8 +604,41 @@ std::string make_filename( const char *prefix, int level, branch_type where,
isGhost );
}
+static void write_version( FILE *dataFile, int majorVersion, int minorVersion,
+ bool extended_version )
+{
+ // write version
+ tagHeader versionTag;
+ versionTag.offset = 0;
+ versionTag.tagID = TAG_VERSION;
+
+ marshallByte(versionTag, majorVersion);
+ marshallByte(versionTag, minorVersion);
+
+ // extended_version just pads the version out to four 32-bit words.
+ // This makes the bones file compatible with Hearse with no extra
+ // munging needed.
+ if (extended_version)
+ {
+ // Use a single signature 16-bit word to indicate that this is
+ // Stone Soup and to disambiguate this (unmunged) bones file
+ // from the munged bones files offered by the old Crawl-aware
+ // hearse.pl. Crawl-aware hearse.pl will prefix the bones file
+ // with the first 16-bits of the Crawl version, and the following
+ // 7 16-bit words set to 0.
+ marshallShort(versionTag, GHOST_SIGNATURE);
+
+ // Write the three remaining 32-bit words of padding.
+ for (int i = 0; i < 3; ++i)
+ marshallLong(versionTag, 0);
+ }
+
+ tag_write(versionTag, dataFile);
+}
+
static void write_tagged_file( FILE *dataFile, char majorVersion,
- char minorVersion, int fileType )
+ char minorVersion, int fileType,
+ bool extended_version = false )
{
struct tagHeader th;
@@ -610,13 +646,7 @@ static void write_tagged_file( FILE *dataFile, char majorVersion,
char tags[NUM_TAGS];
tag_set_expected(tags, fileType);
- // write version
- struct tagHeader versionTag;
- versionTag.offset = 0;
- versionTag.tagID = TAG_VERSION;
- marshallByte(versionTag, majorVersion);
- marshallByte(versionTag, minorVersion);
- tag_write(versionTag, dataFile);
+ write_version( dataFile, majorVersion, minorVersion, extended_version );
// all other tags
for(int i=1; i<NUM_TAGS; i++)
@@ -1606,21 +1636,23 @@ static bool determine_ghost_version( FILE *ghostFile,
if (read2(ghostFile, buf, 2) != 2)
return false; // empty file?
- // check for pre-v4 -- simply started right in with ghost name.
- if (isprint(buf[0]) && buf[0] > 4)
- {
- majorVersion = 0;
- minorVersion = 0;
- rewind(ghostFile);
- return true;
- }
-
// otherwise, read version and validate.
majorVersion = buf[0];
minorVersion = buf[1];
- if (majorVersion == SAVE_MAJOR_VERSION)
- return true;
+ // check for the DCSS ghost signature.
+ if (readShort(ghostFile) != GHOST_SIGNATURE)
+ return (false);
+
+ if (majorVersion == SAVE_MAJOR_VERSION
+ && minorVersion <= GHOST_MINOR_VERSION)
+ {
+ // Discard three more 32-bit words of padding.
+ for (int i = 0; i < 3; ++i)
+ readLong(ghostFile);
+
+ return !feof(ghostFile);
+ }
return false; // if its not SAVE_MAJOR_VERSION, no idea!
}
@@ -1669,7 +1701,8 @@ void save_ghost( bool force )
}
write_tagged_file( gfile, SAVE_MAJOR_VERSION,
- GHOST_MINOR_VERSION, TAGTYPE_GHOST );
+ GHOST_MINOR_VERSION, TAGTYPE_GHOST,
+ true );
lk_close(gfile, "wb", cha_fil);
diff --git a/crawl-ref/source/ghost.cc b/crawl-ref/source/ghost.cc
index 2ddde666ff..b5075aaccf 100644
--- a/crawl-ref/source/ghost.cc
+++ b/crawl-ref/source/ghost.cc
@@ -8,6 +8,8 @@
#include "AppHdr.h"
+#include "ghost.h"
+
#include "externs.h"
#include "itemname.h"
#include "itemprop.h"
@@ -98,7 +100,7 @@ static spell_type search_order_misc[] = {
/* Last slot (emergency) can only be teleport self or blink. */
-ghost_demon::ghost_demon() : name(), values()
+ghost_demon::ghost_demon()
{
reset();
}
@@ -106,8 +108,22 @@ ghost_demon::ghost_demon() : name(), values()
void ghost_demon::reset()
{
name.clear();
- values.init(0);
- values[GVAL_SPEED] = 10;
+ species = SP_UNKNOWN;
+ job = JOB_UNKNOWN;
+ best_skill = SK_FIGHTING;
+ best_skill_level = 0;
+ xl = 0;
+ max_hp = 0;
+ ev = 0;
+ ac = 0;
+ damage = 0;
+ speed = 10;
+ see_invis = false;
+ brand = SPWPN_NORMAL;
+ resists = mon_resist_def();
+ spellcaster = false;
+ cycle_colours = false;
+ fly = FL_NONE;
}
void ghost_demon::init_random_demon()
@@ -115,172 +131,166 @@ void ghost_demon::init_random_demon()
name = make_name(random_int(), false);
// hp - could be defined below (as could ev, AC etc). Oh well, too late:
- values[ GVAL_MAX_HP ] = 100 + roll_dice( 3, 50 );
+ max_hp = 100 + roll_dice( 3, 50 );
- values[ GVAL_EV ] = 5 + random2(20);
- values[ GVAL_AC ] = 5 + random2(20);
+ ev = 5 + random2(20);
+ ac = 5 + random2(20);
- values[ GVAL_SEE_INVIS ] = (one_chance_in(10) ? 0 : 1);
+ see_invis = !one_chance_in(10);
if (!one_chance_in(3))
- values[ GVAL_RES_FIRE ] = (coinflip() ? 2 : 3);
+ resists.fire = random_range(1, 2);
else
{
- values[ GVAL_RES_FIRE ] = 0; /* res_fire */
+ resists.fire = 0; /* res_fire */
if (one_chance_in(10))
- values[ GVAL_RES_FIRE ] = -1;
+ resists.fire = -1;
}
if (!one_chance_in(3))
- values[ GVAL_RES_COLD ] = 2;
+ resists.cold = random_range(1, 2);
else
{
- values[ GVAL_RES_COLD ] = 0; /* res_cold */
-
+ resists.cold = 0;
if (one_chance_in(10))
- values[ GVAL_RES_COLD ] = -1;
+ resists.cold = -1;
}
// demons, like ghosts, automatically get poison res. and life prot.
// resist electricity:
- values[ GVAL_RES_ELEC ] = !one_chance_in(3);
+ resists.elec = one_chance_in(3);
// HTH damage:
- values[ GVAL_DAMAGE ] = 20 + roll_dice( 2, 20 );
+ damage = 20 + roll_dice( 2, 20 );
// special attack type (uses weapon brand code):
- values[ GVAL_BRAND ] = SPWPN_NORMAL;
+ brand = SPWPN_NORMAL;
if (!one_chance_in(3))
{
do {
- values[ GVAL_BRAND ] = random2(17);
+ brand = static_cast<brand_type>( random2(MAX_PAN_LORD_BRANDS) );
/* some brands inappropriate (e.g. holy wrath) */
- } while (values[ GVAL_BRAND ] == SPWPN_HOLY_WRATH
- || (values[ GVAL_BRAND ] == SPWPN_ORC_SLAYING
+ } while (brand == SPWPN_HOLY_WRATH
+ || (brand == SPWPN_ORC_SLAYING
&& you.mons_species() != MONS_ORC)
- || (values[ GVAL_BRAND ] == SPWPN_DRAGON_SLAYING
+ || (brand == SPWPN_DRAGON_SLAYING
&& you.mons_species() != MONS_DRACONIAN)
- || values[ GVAL_BRAND ] == SPWPN_PROTECTION
- || values[ GVAL_BRAND ] == SPWPN_FLAME
- || values[ GVAL_BRAND ] == SPWPN_FROST);
+ || brand == SPWPN_PROTECTION
+ || brand == SPWPN_FLAME
+ || brand == SPWPN_FROST);
}
// 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);
-
- // 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;
+ spellcaster = !one_chance_in(10);
+ // does demon fly?
+ fly = (one_chance_in(3)? FL_NONE :
+ one_chance_in(5)? FL_LEVITATE : FL_FLY);
+
// hit dice:
- values[GVAL_DEMONLORD_HIT_DICE] = 10 + roll_dice(2, 10);
+ xl = 10 + roll_dice(2, 10);
// does demon cycle colours?
- values[GVAL_DEMONLORD_CYCLE_COLOUR] = (one_chance_in(10) ? 1 : 0);
+ cycle_colours = one_chance_in(10);
- for (int i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- values[i] = SPELL_NO_SPELL;
+ spells.init(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)
+ if (spellcaster)
{
if (coinflip())
- values[GVAL_SPELL_1] = RANDOM_ELEMENT(search_order_conj);
+ spells[0] = RANDOM_ELEMENT(search_order_conj);
// Might duplicate the first spell, but that isn't a problem.
if (coinflip())
- values[GVAL_SPELL_2] = RANDOM_ELEMENT(search_order_conj);
+ spells[1] = RANDOM_ELEMENT(search_order_conj);
if (!one_chance_in(4))
- values[GVAL_SPELL_3] = RANDOM_ELEMENT(search_order_third);
+ spells[2] = RANDOM_ELEMENT(search_order_third);
if (coinflip())
{
- values[GVAL_SPELL_4] = RANDOM_ELEMENT(search_order_misc);
- if ( values[GVAL_SPELL_4] == SPELL_DIG )
- values[GVAL_SPELL_4] = SPELL_NO_SPELL;
+ spells[3] = RANDOM_ELEMENT(search_order_misc);
+ if ( spells[3] == SPELL_DIG )
+ spells[3] = SPELL_NO_SPELL;
}
if (coinflip())
- values[GVAL_SPELL_5] = RANDOM_ELEMENT(search_order_misc);
-
+ spells[4] = RANDOM_ELEMENT(search_order_misc);
if (coinflip())
- values[ GVAL_SPELL_6 ] = SPELL_BLINK;
+ spells[5] = SPELL_BLINK;
if (coinflip())
- values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
+ spells[5] = 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] );
+ for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
+ spells[i] = translate_spell( spells[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] = SPELL_HELLFIRE_BURST;
+ spells[0] = SPELL_HELLFIRE_BURST;
if (one_chance_in(25))
- values[GVAL_SPELL_1] = SPELL_METAL_SPLINTERS;
+ spells[0] = SPELL_METAL_SPLINTERS;
if (one_chance_in(25))
- values[GVAL_SPELL_1] = SPELL_ENERGY_BOLT; /* eye of devas */
+ spells[0] = SPELL_ENERGY_BOLT; /* eye of devas */
if (one_chance_in(25))
- values[GVAL_SPELL_2] = SPELL_STEAM_BALL;
+ spells[1] = SPELL_STEAM_BALL;
if (one_chance_in(25))
- values[GVAL_SPELL_2] = SPELL_ISKENDERUNS_MYSTIC_BLAST;
+ spells[1] = SPELL_ISKENDERUNS_MYSTIC_BLAST;
if (one_chance_in(25))
- values[GVAL_SPELL_2] = SPELL_HELLFIRE;
+ spells[1] = SPELL_HELLFIRE;
if (one_chance_in(25))
- values[GVAL_SPELL_3] = SPELL_SMITING;
+ spells[2] = SPELL_SMITING;
if (one_chance_in(25))
- values[GVAL_SPELL_3] = SPELL_HELLFIRE_BURST;
+ spells[2] = SPELL_HELLFIRE_BURST;
if (one_chance_in(12))
- values[GVAL_SPELL_3] = SPELL_SUMMON_GREATER_DEMON;
+ spells[2] = SPELL_SUMMON_GREATER_DEMON;
if (one_chance_in(12))
- values[GVAL_SPELL_3] = SPELL_SUMMON_DEMON;
+ spells[2] = SPELL_SUMMON_DEMON;
if (one_chance_in(20))
- values[GVAL_SPELL_4] = SPELL_SUMMON_GREATER_DEMON;
+ spells[3] = SPELL_SUMMON_GREATER_DEMON;
if (one_chance_in(20))
- values[GVAL_SPELL_4] = SPELL_SUMMON_DEMON;
+ spells[3] = SPELL_SUMMON_DEMON;
/* at least they can summon demons */
- if (values[GVAL_SPELL_4] == SPELL_NO_SPELL)
- values[GVAL_SPELL_4] = SPELL_SUMMON_DEMON;
+ if (spells[3] == SPELL_NO_SPELL)
+ spells[3] = SPELL_SUMMON_DEMON;
if (one_chance_in(15))
- values[GVAL_SPELL_5] = SPELL_DIG;
+ spells[4] = SPELL_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();
+ max_hp = ((you.hp_max >= 400) ? 400 : you.hp_max);
+ ev = player_evasion();
+ 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();
+ if (ev > 60)
+ ev = 60;
+
+ see_invis = player_see_invis();
+ resists.fire = player_res_fire();
+ resists.cold = player_res_cold();
+ resists.elec = player_res_electricity();
+ speed = player_ghost_base_movement_speed();
int d = 4;
- int e = 0;
+ int e = SPWPN_NORMAL;
const int wpn = you.equip[EQ_WEAPON];
if (wpn != -1)
@@ -320,19 +330,18 @@ void ghost_demon::init_player_ghost()
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;
+ damage = d;
+ brand = static_cast<brand_type>( e );
+ species = you.species;
+ best_skill = ::best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99);
+ best_skill_level = you.skills[best_skill];
+ xl = you.experience_level;
+ job = you.char_class;
add_spells();
}
-static int search_first_list(int ignore_spell)
+static spell_type search_first_list(int ignore_spell)
{
for (unsigned i = 0;
i < sizeof(search_order_conj) / sizeof(*search_order_conj); i++)
@@ -350,7 +359,7 @@ static int search_first_list(int ignore_spell)
return SPELL_NO_SPELL;
} // end search_first_list()
-static int search_second_list(int ignore_spell)
+static spell_type search_second_list(int ignore_spell)
{
for (unsigned i = 0;
i < sizeof(search_order_third) / sizeof(*search_order_third); i++)
@@ -368,7 +377,7 @@ static int search_second_list(int ignore_spell)
return SPELL_NO_SPELL;
} // end search_second_list()
-static int search_third_list(int ignore_spell)
+static spell_type search_third_list(int ignore_spell)
{
for (unsigned i = 0;
i < sizeof(search_order_misc) / sizeof(*search_order_misc); i++)
@@ -394,44 +403,43 @@ void ghost_demon::add_spells( )
{
int i = 0;
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- values[i] = SPELL_NO_SPELL;
+ spells.init(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_DIG);
+ spells[ 0 ] = search_first_list(SPELL_NO_SPELL);
+ spells[ 1 ] = search_first_list(spells[0]);
+ spells[ 2 ] = search_second_list(SPELL_NO_SPELL);
+ spells[ 3 ] = search_third_list(SPELL_DIG);
- if (values[ GVAL_SPELL_4 ] == SPELL_NO_SPELL)
- values[ GVAL_SPELL_4 ] = search_first_list(SPELL_NO_SPELL);
+ if (spells[ 3 ] == SPELL_NO_SPELL)
+ spells[ 3 ] = search_first_list(SPELL_NO_SPELL);
- values[ GVAL_SPELL_5 ] = search_third_list(values[GVAL_SPELL_4]);
+ spells[ 4 ] = search_third_list(spells[3]);
- if (values[ GVAL_SPELL_5 ] == SPELL_NO_SPELL)
- values[ GVAL_SPELL_5 ] = search_first_list(values[GVAL_SPELL_4]);
+ if (spells[ 4 ] == SPELL_NO_SPELL)
+ spells[ 4 ] = search_first_list(spells[3]);
if (player_has_spell( SPELL_DIG ))
- values[ GVAL_SPELL_5 ] = SPELL_DIG;
+ spells[ 4 ] = 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;
+ spells[ 5 ] = SPELL_CONTROLLED_BLINK;
}
if (player_has_spell( SPELL_TELEPORT_SELF ))
- values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
+ spells[ 5 ] = SPELL_TELEPORT_SELF;
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- values[i] = translate_spell( values[i] );
+ for (i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
+ spells[i] = translate_spell( spells[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
+spell_type ghost_demon::translate_spell(spell_type spel) const
{
switch (spel)
{
diff --git a/crawl-ref/source/ghost.h b/crawl-ref/source/ghost.h
new file mode 100644
index 0000000000..b4f861ca3d
--- /dev/null
+++ b/crawl-ref/source/ghost.h
@@ -0,0 +1,52 @@
+#ifndef GHOST_H
+#define GHOST_H
+
+#include "externs.h"
+#include "enum.h"
+#include "itemprop.h"
+#include "mon-util.h"
+
+struct ghost_demon
+{
+public:
+ std::string name;
+
+ species_type species;
+ job_type job;
+ skill_type best_skill;
+ short best_skill_level;
+ short xl;
+
+ short max_hp, ev, ac, damage, speed;
+ bool see_invis;
+ brand_type brand;
+ mon_resist_def resists;
+
+ bool spellcaster, cycle_colours;
+ flight_type fly;
+
+ monster_spells spells;
+
+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);
+ static void find_transiting_ghosts(std::vector<ghost_demon> &gs, int n);
+ static void announce_ghost(const ghost_demon &g);
+
+private:
+ void add_spells();
+ spell_type translate_spell(spell_type playerspell) const;
+};
+
+extern std::vector<ghost_demon> ghosts;
+
+#endif
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index b018483cb6..afb978dfa1 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -987,7 +987,7 @@ std::string item_def::name_aux( description_level_type desc,
const bool know_brand =
!basename && !qualname && !dbname
&& !testbits(ignore_flags, ISFLAG_KNOW_TYPE)
- && (ident || item_ident(*this, ISFLAG_KNOW_TYPE));
+ && (ident || item_type_known(*this));
const bool know_ego = know_brand;
@@ -1068,9 +1068,9 @@ std::string item_def::name_aux( description_level_type desc,
buff << racial_description_string(*this, terse);
if (know_brand && !terse &&
- (get_weapon_brand(*this) == SPWPN_VAMPIRICISM))
+ (get_weapon_brand(*this) == SPWPN_VAMPIRICISM))
{
- buff << "vampiric ";
+ buff << "vampiric ";
}
buff << item_base_name(*this);
@@ -1649,9 +1649,17 @@ bool item_type_known( const item_def& item )
// Artefacts have different descriptions from other items,
// so we can't use general item knowledge for them.
- if ( is_artefact(item) )
+ if (is_artefact(item))
return false;
+ // Poisoned missiles are always identified.
+ if (item.base_type == OBJ_MISSILES)
+ {
+ int ammo_brand = get_ammo_brand(item);
+ if (ammo_brand == SPMSL_POISONED || ammo_brand == SPMSL_CURARE)
+ return (true);
+ }
+
const item_type_id_type idt = objtype_to_idtype(item.base_type);
if ( idt != NUM_IDTYPE && item.sub_type < 50 )
return ( type_ids[idt][item.sub_type] == ID_KNOWN_TYPE );
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index 70057a19d8..1c680e69c5 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -96,7 +96,10 @@ enum brand_type // equivalent to (you.inv[].special or mitm[].special) % 30
SPWPN_PAIN, // 15
SPWPN_DISTORTION,
SPWPN_REACHING, // 17
- SPWPN_RETURNING,
+
+ MAX_PAN_LORD_BRANDS, // 18
+
+ SPWPN_RETURNING = MAX_PAN_LORD_BRANDS,
SPWPN_CONFUSE,
SPWPN_RANDART_I = 25, // 25
SPWPN_RANDART_II,
diff --git a/crawl-ref/source/libunix.cc b/crawl-ref/source/libunix.cc
index a2f1cc2fbc..93ce48d10a 100644
--- a/crawl-ref/source/libunix.cc
+++ b/crawl-ref/source/libunix.cc
@@ -290,7 +290,10 @@ static int raw_m_getch()
}
#endif
default:
- return (c);
+ // getch() returns -1 on EOF, convert that into an Escape. Evil hack,
+ // but the alternative is to explicitly check for -1 everywhere where
+ // we might otherwise spin in a tight keyboard input loop.
+ return (c == -1? ESCAPE : c);
}
}
@@ -301,7 +304,7 @@ int m_getch()
c = raw_m_getch();
while ((c == CK_MOUSE_MOVE || c == CK_MOUSE_CLICK)
&& !crawl_state.mouse_enabled);
-
+
return (c);
}
diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc
index 1f36b305db..9c919f1fe5 100644
--- a/crawl-ref/source/message.cc
+++ b/crawl-ref/source/message.cc
@@ -746,7 +746,7 @@ void more(void)
if (Options.show_more_prompt && !suppress_messages)
{
- char keypress = 0;
+ int keypress = 0;
if (Options.tutorial_left)
message_out(crawl_view.msgsz.y - 1,
@@ -764,7 +764,8 @@ void more(void)
do
keypress = getch();
- while (keypress != ' ' && keypress != '\r' && keypress != '\n');
+ while (keypress != ' ' && keypress != '\r' && keypress != '\n'
+ && keypress != -1);
}
mesclr(true);
} // end more()
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 7111bc8881..b6df1ef8d5 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -656,7 +656,7 @@ void up_stairs(dungeon_feature_type force_stair,
mpr("Warning: level annotation for next level is:", MSGCH_PROMPT);
mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PROMPT);
- if (!yesno("Enter next level anyways?", true, 0, true, false))
+ if (!yesno("Enter next level anyway?", true, 0, true, false))
{
interrupt_activity( AI_FORCE_INTERRUPT );
return;
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index 100d7edd66..5a094e8c72 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -1433,7 +1433,7 @@
{
MONS_MOTTLED_DRAGON, 'D', MAGENTA, "mottled dragon",
M_SPELLCASTER | M_FLIES,
- MR_RES_POISON | MR_RES_FIRE,
+ MR_RES_POISON | MR_RES_FIRE | MR_RES_STICKY_FLAME,
1100, 10, MONS_DRAGON, MONS_MOTTLED_DRAGON, MH_NATURAL, -3,
{ {AT_BITE, AF_PLAIN, 15}, {AT_CLAW, AF_PLAIN, 6}, AT_NO_ATK, AT_NO_ATK },
{ 5, 3, 5, 0 },
@@ -3480,7 +3480,7 @@
{
MONS_MOTTLED_DRACONIAN, 'd', LIGHTMAGENTA, "mottled draconian",
M_HUMANOID | M_COLD_BLOOD,
- MR_RES_FIRE,
+ MR_RES_FIRE | MR_RES_STICKY_FLAME,
900, 10, MONS_DRACONIAN, MONS_MOTTLED_DRACONIAN, MH_NATURAL, -2,
{ {AT_HIT, AF_PLAIN, 20}, {AT_HIT, AF_PLAIN, 0}, {AT_HIT, AF_PLAIN, 0} },
{ 14, 5, 4, 0 },
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 8fad0cfef9..8a9eef2355 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -34,6 +34,8 @@
#include "debug.h"
#include "delay.h"
#include "dgnevent.h"
+#include "fight.h"
+#include "ghost.h"
#include "insult.h"
#include "itemname.h"
#include "itemprop.h"
@@ -256,14 +258,18 @@ void init_monster_symbols()
}
}
-unsigned long get_mons_class_resists(int mc)
+const mon_resist_def &get_mons_class_resists(int mc)
{
- return (smc->resists);
+ const monsterentry *me = get_monster_data(mc);
+ return (me? me->resists : get_monster_data(MONS_PROGRAM_BUG)->resists);
}
-unsigned long get_mons_resists(const monsters *mon)
+mon_resist_def get_mons_resists(const monsters *mon)
{
- unsigned long resists = get_mons_class_resists(mon->type);
+ if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON)
+ return (mon->ghost->resists);
+
+ mon_resist_def resists = get_mons_class_resists(mon->type);
if ((mons_genus(mon->type) == MONS_DRACONIAN &&
mon->type != MONS_DRACONIAN) ||
mon->type == MONS_TIAMAT)
@@ -290,11 +296,6 @@ int mons_piety(const monsters *mon)
return (mon->hit_dice * 14);
}
-unsigned long mons_resist(const monsters *mon, unsigned long flags)
-{
- return (get_mons_resists(mon) & flags);
-}
-
bool mons_class_flag(int mc, int bf)
{
const monsterentry *me = smc;
@@ -394,7 +395,7 @@ bool mons_class_is_slowable(int mc)
bool mons_is_wall_shielded(int mc)
{
- return (mons_habitat(mc) == HT_ROCK);
+ return (mons_habitat_by_type(mc) == HT_ROCK);
}
// returns whether a monster is non-solid
@@ -605,7 +606,7 @@ bool mons_sense_invis(const monsters *mon)
bool mons_see_invis(const monsters *mon)
{
if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_SEE_INVIS ]);
+ return (mon->ghost->see_invis);
else if (mons_class_flag(mon->type, M_SEE_INVIS))
return (true);
else if (scan_mon_inv_randarts( mon, RAP_EYESIGHT ) > 0)
@@ -739,7 +740,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(mons->ghost->values[GVAL_DAMAGE]);
+ return mon_attack_def::attk(mons->ghost->damage);
else
return mon_attack_def::attk(0, AT_NONE);
}
@@ -852,14 +853,10 @@ int mons_res_elec( const monsters *mon )
{
int mc = mon->type;
- if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_RES_ELEC ]);
-
/* this is a variable, not a player_xx() function, so can be above 1 */
int u = 0;
- if (mons_resist(mon, MR_RES_ELEC))
- u++;
+ u += get_mons_resists(mon).elec;
// don't bother checking equipment if the monster can't use it
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
@@ -885,26 +882,19 @@ bool mons_res_asphyx( const monsters *mon )
|| holiness == MH_DEMONIC
|| holiness == MH_NONLIVING
|| holiness == MH_PLANT
- || mons_resist(mon, MR_RES_ASPHYX));
+ || get_mons_resists(mon).asphyx > 0);
}
int mons_res_acid( const monsters *mon )
{
- const unsigned long f = get_mons_resists(mon);
- return ((f & MR_RES_ACID) != 0);
+ return get_mons_resists(mon).acid;
}
int mons_res_poison( const monsters *mon )
{
int mc = mon->type;
- unsigned long u = 0, f = get_mons_resists(mon);
-
- if (f & MR_RES_POISON)
- u++;
-
- if (f & MR_VUL_POISON)
- u--;
+ int u = get_mons_resists(mon).poison;
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -930,26 +920,27 @@ int mons_res_poison( const monsters *mon )
return (u);
} // end mons_res_poison()
+bool mons_res_sticky_flame( const monsters *mon )
+{
+ return (get_mons_resists(mon).sticky_flame
+ || mon->has_equipped(EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR));
+}
+
+int mons_res_steam( const monsters *mon )
+{
+ int res = get_mons_resists(mon).steam;
+ if (mon->has_equipped(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR))
+ res += 3;
+ return (res + mons_res_fire(mon) / 2);
+}
+
int mons_res_fire( const monsters *mon )
{
int mc = mon->type;
- if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_RES_FIRE ]);
-
- int u = 0, f = get_mons_resists(mon);
-
- // no Big Prize (tm) here either if you set all three flags. It's a pity uh?
- //
- // Note that natural monster resistance is two levels, this is duplicate
- // the fact that having this flag used to be a lot better than armour
- // for monsters (it used to make them immune in a lot of cases) -- bwr
- if (f & MR_RES_HELLFIRE)
- u += 3;
- else if (f & MR_RES_FIRE)
- u += 2;
- else if (f & MR_VUL_FIRE)
- u--;
+ const mon_resist_def res = get_mons_resists(mon);
+
+ int u = std::min(res.fire + res.hellfire * 3, 3);
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -982,18 +973,7 @@ int mons_res_cold( const monsters *mon )
{
int mc = mon->type;
- if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON)
- return (mon->ghost->values[ GVAL_RES_COLD ]);
-
- int u = 0, f = get_mons_resists(mon);
-
- // Note that natural monster resistance is two levels, this is duplicate
- // the fact that having this flag used to be a lot better than armour
- // for monsters (it used to make them immune in a lot of cases) -- bwr
- if (f & MR_RES_COLD)
- u += 2;
- else if (f & MR_VUL_COLD)
- u--;
+ int u = get_mons_resists(mon).cold;
if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -1104,9 +1084,9 @@ flight_type mons_class_flies(int mc)
flight_type mons_flies(const monsters *mon)
{
if (mon->type == MONS_PANDEMONIUM_DEMON
- && mon->ghost->values[ GVAL_DEMONLORD_FLY ])
+ && mon->ghost->fly)
{
- return (FL_FLY);
+ return (mon->ghost->fly);
}
const flight_type ret = mons_class_flies( mon->type );
@@ -1686,9 +1666,16 @@ mon_intel_type mons_intel(int mc)
return (smc->intel);
}
-habitat_type mons_habitat(int mc)
+habitat_type mons_habitat_by_type(int mc)
+{
+ const monsterentry *me = get_monster_data(mc);
+ return (me? me->habitat : HT_LAND);
+}
+
+habitat_type mons_habitat(const monsters *m)
{
- return (smc->habitat);
+ return mons_habitat_by_type(
+ mons_is_zombified(m)? mons_zombie_base(m) : m->type );
}
bool intelligent_ally(const monsters *monster)
@@ -1761,7 +1748,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 m->ghost->values[ GVAL_BRAND ];
+ return m->ghost->brand;
return (SPWPN_NORMAL);
}
@@ -1986,6 +1973,15 @@ bool ms_waste_of_time( const monsters *mon, spell_type monspell )
int intel, est_magic_resist, power, diff;
const actor *foe = mon->get_foe();
+
+ // Keep friendly summoners from spamming summons constantly.
+ if (mons_friendly(mon)
+ && (!foe || foe == &you)
+ && spell_typematch(monspell, SPTYP_SUMMONING))
+ {
+ return (true);
+ }
+
// Eventually, we'll probably want to be able to have monsters
// learn which of their elemental bolts were resisted and have those
// handled here as well. -- bwr
@@ -2337,6 +2333,11 @@ monsters::monsters()
{
}
+// Empty destructor to keep auto_ptr happy with incomplete ghost_demon type.
+monsters::~monsters()
+{
+}
+
monsters::monsters(const monsters &mon)
{
init_with(mon);
@@ -2395,7 +2396,7 @@ coord_def monsters::target_pos() const
bool monsters::swimming() const
{
const dungeon_feature_type grid = grd[x][y];
- return (grid_is_watery(grid) && mons_habitat(type) == HT_WATER);
+ return (grid_is_watery(grid) && mons_habitat(this) == HT_WATER);
}
bool monsters::submerged() const
@@ -2409,7 +2410,7 @@ bool monsters::floundering() const
return (grid_is_water(grid)
// Can't use monster_habitable_grid because that'll return true
// for non-water monsters in shallow water.
- && mons_habitat(type) != HT_WATER
+ && mons_habitat(this) != HT_WATER
&& !mons_amphibious(type)
&& !mons_flies(this));
}
@@ -2693,6 +2694,10 @@ bool monsters::can_use_missile(const item_def &item) const
// grabbing missiles.
if (has_spell_of_type(SPTYP_CONJURATION | SPTYP_SUMMONING))
return (false);
+
+ // Blademasters don't want to throw stuff.
+ if (type == MONS_DEEP_ELF_BLADEMASTER)
+ return (false);
if (item.base_type == OBJ_WEAPONS)
return (is_throwable(item));
@@ -3392,6 +3397,11 @@ int monsters::id() const
return (type);
}
+int monsters::get_experience_level() const
+{
+ return (hit_dice);
+}
+
bool monsters::fumbles_attack(bool verbose)
{
if (floundering() && one_chance_in(4))
@@ -3434,7 +3444,7 @@ void monsters::go_berserk(bool /* intentional */)
simple_monster_message(
this,
make_stringf(" shakes off %s lethargy.",
- name(DESC_NOCAP_YOUR).c_str()).c_str());
+ pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str()).c_str());
}
del_ench(ENCH_HASTE);
del_ench(ENCH_FATIGUE, true); // give no additional message
@@ -3556,6 +3566,11 @@ int monsters::res_fire() const
return (mons_res_fire(this));
}
+int monsters::res_steam() const
+{
+ return (mons_res_steam(this));
+}
+
int monsters::res_cold() const
{
return (mons_res_cold(this));
@@ -3707,12 +3722,12 @@ void monsters::set_ghost(const 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));
+ hit_dice = ghost->xl;
+ hit_points = ghost->max_hp;
+ max_hit_points = ghost->max_hp;
+ ac = ghost->ac;
+ ev = ghost->ev;
+ speed = (one_chance_in(3) ? 10 : 8 + roll_dice(2, 9));
speed_increment = 70;
if (you.char_direction == GDT_ASCENDING && you.level_type == LEVEL_DUNGEON)
colour = LIGHTRED;
@@ -3724,12 +3739,12 @@ void monsters::pandemon_init()
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 ];
+ hit_dice = ghost->xl;
+ hit_points = ghost->max_hp;
+ max_hit_points = ghost->max_hp;
+ ac = ghost->ac;
+ ev = ghost->ev;
+ speed = ghost->speed;
speed_increment = 70;
attitude = ATT_HOSTILE;
behaviour = BEH_WANDER;
@@ -3896,16 +3911,7 @@ void monsters::load_spells(mon_spellbook_type book)
#endif
if (book == MST_GHOST)
- {
- for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
- {
- spells[i] =
- static_cast<spell_type>( ghost->values[ GVAL_SPELL_1 + i ] );
-#if DEBUG_DIAGNOSTICS
- mprf( MSGCH_DIAGNOSTICS, "spell #%d: %d", i, spells[i] );
-#endif
- }
- }
+ spells = ghost->spells;
else
{
for (unsigned int i = 0; i < ARRAYSIZE(mspell_list); ++i)
@@ -3918,6 +3924,11 @@ void monsters::load_spells(mon_spellbook_type book)
}
}
}
+#if DEBUG_DIAGNOSTICS
+ for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
+ mprf( MSGCH_DIAGNOSTICS, "Spell #%d: %d (%s)",
+ i, spells[i], spell_title(spells[i]) );
+#endif
}
bool monsters::has_ench(enchant_type ench) const
@@ -4588,12 +4599,12 @@ void monsters::apply_enchantment(const mon_enchant &me)
}
// Now we handle the others:
- int grid = grd[x][y];
+ const dungeon_feature_type grid = grd[x][y];
// Badly injured monsters prefer to stay submerged...
// electrical eels and lava snakes have ranged attacks
// and are more likely to surface. -- bwr
- if (!monster_can_submerge(type, grid))
+ if (!monster_can_submerge(this, grid))
del_ench(ENCH_SUBMERGED); // forced to surface
else if (hit_points <= max_hit_points / 2)
break;
@@ -4658,10 +4669,8 @@ void monsters::apply_enchantment(const mon_enchant &me)
// assumption: mons_res_fire has already been checked
case ENCH_STICKY_FLAME:
{
- int dam = roll_dice( 2, 4 ) - 1;
-
- if (mons_res_fire( this ) < 0)
- dam += roll_dice( 2, 5 ) - 1;
+ int dam =
+ resist_adjust_damage(this, res_fire(), roll_dice( 2, 4 ) - 1);
if (dam > 0)
{
@@ -5002,7 +5011,7 @@ void monsters::apply_location_effects()
mons_check_pool(this);
if (alive() && has_ench(ENCH_SUBMERGED)
- && !monster_can_submerge(type, grd[x][y]))
+ && !monster_can_submerge(this, grd[x][y]))
{
del_ench(ENCH_SUBMERGED);
}
@@ -5430,7 +5439,7 @@ std::string do_mon_str_replacements(const std::string &in_msg,
if (mons_shouts(monster->type) >= NUM_SHOUTS)
{
mpr("Invalid @says@ type.", MSGCH_DIAGNOSTICS);
- msg = replace_all(msg, "@says@", "bugilly says");
+ msg = replace_all(msg, "@says@", "buggily says");
}
else
msg = replace_all(msg, "@says@",
@@ -5448,7 +5457,7 @@ static mon_body_shape get_ghost_shape(const monsters *mon)
{
const ghost_demon &ghost = *(mon->ghost);
- switch(ghost.values[GVAL_SPECIES])
+ switch (ghost.species)
{
case SP_NAGA:
return (MON_SHAPE_NAGA);
@@ -5472,9 +5481,10 @@ static mon_body_shape get_ghost_shape(const monsters *mon)
case SP_UNK1_DRACONIAN:
case SP_BASE_DRACONIAN:
return (MON_SHAPE_HUMANOID_TAILED);
- }
- return (MON_SHAPE_HUMANOID);
+ default:
+ return (MON_SHAPE_HUMANOID);
+ }
}
mon_body_shape get_mon_shape(const monsters *mon)
@@ -5706,3 +5716,71 @@ std::string get_mon_shape_str(const mon_body_shape shape)
return (shape_names[shape]);
}
+
+/////////////////////////////////////////////////////////////////////////
+// mon_resist_def
+
+mon_resist_def::mon_resist_def()
+ : elec(0), poison(0), fire(0), steam(0), cold(0), hellfire(0),
+ asphyx(0), acid(0), sticky_flame(false), pierce(0),
+ slice(0), bludgeon(0)
+{
+}
+
+mon_resist_def::mon_resist_def(int flags, short level)
+ : elec(0), poison(0), fire(0), steam(0), cold(0), hellfire(0),
+ asphyx(0), acid(0), sticky_flame(false), pierce(0),
+ slice(0), bludgeon(0)
+{
+ for (int i = 0; i < 32; ++i)
+ {
+ switch (flags & (1 << i))
+ {
+ case MR_RES_STEAM: steam = 3; break;
+ case MR_RES_ELEC: elec = level; break;
+ case MR_RES_POISON: poison = level; break;
+ case MR_RES_FIRE: fire = level; break;
+ case MR_RES_HELLFIRE: hellfire = level; break;
+ case MR_RES_COLD: cold = level; break;
+ case MR_RES_ASPHYX: asphyx = level; break;
+ case MR_RES_ACID: acid = level; break;
+ case MR_VUL_ELEC: elec = -level; break;
+ case MR_VUL_POISON: poison = -level; break;
+ case MR_VUL_FIRE: fire = -level; break;
+ case MR_VUL_COLD: cold = -level; break;
+
+ case MR_RES_PIERCE: pierce = level; break;
+ case MR_RES_SLICE: slice = level; break;
+ case MR_RES_BLUDGEON: bludgeon = level; break;
+ case MR_VUL_PIERCE: pierce = -level; break;
+ case MR_VUL_SLICE: slice = -level; break;
+ case MR_VUL_BLUDGEON: bludgeon = -level; break;
+
+ case MR_RES_STICKY_FLAME: sticky_flame = true; break;
+
+ default: break;
+ }
+ }
+}
+
+const mon_resist_def &mon_resist_def::operator |= (const mon_resist_def &o)
+{
+ elec += o.elec;
+ poison += o.poison;
+ fire += o.fire;
+ cold += o.cold;
+ hellfire += o.hellfire;
+ asphyx += o.asphyx;
+ acid += o.acid;
+ pierce += o.pierce;
+ slice += o.slice;
+ bludgeon += o.bludgeon;
+ sticky_flame = sticky_flame || o.sticky_flame;
+ return (*this);
+}
+
+mon_resist_def mon_resist_def::operator | (const mon_resist_def &o) const
+{
+ mon_resist_def c(*this);
+ return (c |= o);
+}
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 4877271260..9aaccb551e 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -201,7 +201,11 @@ enum mon_resist_flags
MR_VUL_PIERCE = (1<<14),
MR_VUL_SLICE = (1<<15),
- MR_VUL_BLUDGEON = (1<<16)
+ MR_VUL_BLUDGEON = (1<<16),
+
+ // immune to stickiness of sticky flame.
+ MR_RES_STICKY_FLAME = (1 << 17),
+ MR_RES_STEAM = (1 << 18)
};
enum shout_type
@@ -275,6 +279,37 @@ struct mon_energy_usage
char pickup_percent;
};
+struct mon_resist_def
+{
+ // All values are actually saved as single-bytes, so practical
+ // range is -128 - 127, and the game only distinguishes values in
+ // the range -1 to 3.
+
+ short elec;
+ short poison;
+ short fire;
+ short steam;
+ short cold;
+ short hellfire;
+ short asphyx;
+ short acid;
+
+ bool sticky_flame;
+
+ // Physical damage resists (currently unused)
+ short pierce;
+ short slice;
+ short bludgeon;
+
+ mon_resist_def();
+ mon_resist_def(int flags, short level = 1);
+
+ mon_resist_def operator | (const mon_resist_def &other) const;
+ const mon_resist_def &operator |= (const mon_resist_def &other);
+};
+
+typedef mon_resist_def mrd;
+
struct monsterentry
{
short mc; // monster number
@@ -283,7 +318,7 @@ struct monsterentry
const char *name;
unsigned long bitfields;
- unsigned long resists;
+ mon_resist_def resists;
short weight;
// [Obsolete] Experience used to be calculated like this:
@@ -446,35 +481,18 @@ bool mons_is_summoned(const monsters *m);
* *********************************************************************** */
mon_intel_type mons_intel(int mc);
-habitat_type mons_habitat(int mc);
+// Use mons_habitat(const monster *) wherever possible since the other
+// variant does not handle zombies correctly.
+habitat_type mons_habitat(const monsters *m);
+habitat_type mons_habitat_by_type(int mc);
bool intelligent_ally(const monsters *mon);
-// last updated 12may2000 {dlb}
-/* ***********************************************************************
- * called from: beam - fight - monstuff
- * *********************************************************************** */
+bool mons_res_sticky_flame( const monsters *mon );
int mons_res_cold( const monsters *mon );
-
-
-// last updated 12may2000 {dlb}
-/* ***********************************************************************
- * called from: beam - fight - spells4
- * *********************************************************************** */
int mons_res_elec( const monsters *mon );
-
-
-// last updated 12may2000 {dlb}
-/* ***********************************************************************
- * called from: beam - fight - monstuff
- * *********************************************************************** */
int mons_res_fire( const monsters *mon );
-
-
-// last updated 12may2000 {dlb}
-/* ***********************************************************************
- * called from: beam - monstuff - spells4
- * *********************************************************************** */
+int mons_res_steam( const monsters *mon );
int mons_res_poison( const monsters *mon );
int mons_res_acid( const monsters *mon );
int mons_res_negative_energy( const monsters *mon );
@@ -509,7 +527,8 @@ int mons_speed(int mclass);
* called from: dungeon - mon-util - spells2
* *********************************************************************** */
int mons_zombie_size(int mc);
-
+int mons_zombie_base(const monsters *monster);
+bool mons_is_zombified(const monsters *monster);
// last updated 12may2000 {dlb}
/* ***********************************************************************
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index c2485d3824..ceec4e4890 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -15,6 +15,7 @@
#include "branch.h"
#include "externs.h"
+#include "ghost.h"
#include "lev-pand.h"
#include "makeitem.h"
#include "monstuff.h"
@@ -77,7 +78,9 @@ bool grid_compatible(dungeon_feature_type grid_wanted,
bool monster_habitable_grid(const monsters *m,
dungeon_feature_type actual_grid)
{
- return (monster_habitable_grid(m->type, actual_grid, mons_flies(m),
+ // Zombified monsters enjoy the same habitat as their original.
+ const int type = mons_is_zombified(m)? mons_zombie_base(m) : m->type;
+ return (monster_habitable_grid(type, actual_grid, mons_flies(m),
m->paralysed()));
}
@@ -101,7 +104,7 @@ bool monster_habitable_grid(int monster_class,
bool paralysed)
{
const dungeon_feature_type preferred_habitat =
- habitat2grid( mons_habitat(monster_class) );
+ habitat2grid( mons_habitat_by_type(monster_class) );
return (grid_compatible(preferred_habitat, actual_grid)
// [dshaligram] Flying creatures are all DNGN_FLOOR, so we
// only have to check for the additional valid grids of deep
@@ -124,22 +127,20 @@ bool monster_habitable_grid(int monster_class,
}
// Returns true if the monster can submerge in the given grid
-bool monster_can_submerge(int monster_class, int grid)
+bool monster_can_submerge(const monsters *mons, dungeon_feature_type grid)
{
- const habitat_type habitat = mons_habitat(monster_class);
-
- if (habitat == HT_WATER &&
- (grid == DNGN_DEEP_WATER || grid == DNGN_BLUE_FOUNTAIN))
+ switch (mons_habitat(mons))
{
- return true;
- }
+ case HT_WATER:
+ // Monsters can submerge in shallow water - this is intentional.
+ return grid_is_watery(grid);
- if (habitat == HT_LAVA && grid == DNGN_LAVA)
- {
- return true;
- }
+ case HT_LAVA:
+ return (grid == DNGN_LAVA);
- return false;
+ default:
+ return false;
+ }
}
static bool need_super_ood(int lev_mons)
@@ -485,7 +486,8 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
// a) not occupied
// b) compatible
// c) in the 'correct' proximity to the player
- dungeon_feature_type grid_wanted = habitat2grid( mons_habitat(mon_type) );
+ dungeon_feature_type grid_wanted =
+ habitat2grid( mons_habitat_by_type(mon_type) );
while(true)
{
// handled above, won't change anymore
@@ -689,7 +691,7 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
}
else
{
- grid_wanted = habitat2grid( mons_habitat(mon_type) );
+ grid_wanted = habitat2grid( mons_habitat_by_type(mon_type) );
// we'll try 1000 times for a good spot
for (i = 0; i < 1000; i++)
@@ -773,7 +775,7 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
menv[id].flags |= MF_BATTY;
}
- if (monster_can_submerge(mon_type, grd[fx][fy])
+ if (monster_can_submerge(&menv[id], grd[fx][fy])
&& !one_chance_in(5))
menv[id].add_ench(ENCH_SUBMERGED);
@@ -1570,8 +1572,9 @@ coord_def find_newmons_square(int mons_class, int x, int y)
if (mons_class == WANDERING_MONSTER)
mons_class = RANDOM_MONSTER;
- dungeon_feature_type spcw = ((mons_class == RANDOM_MONSTER) ? DNGN_FLOOR
- : habitat2grid( mons_habitat(mons_class) ));
+ dungeon_feature_type spcw =
+ ((mons_class == RANDOM_MONSTER) ? DNGN_FLOOR
+ : habitat2grid( mons_habitat_by_type(mons_class) ));
// Might be better if we chose a space and tried to match the monster
// to it in the case of RANDOM_MONSTER, that way if the target square
diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h
index 2b318710ef..0bf912b5d5 100644
--- a/crawl-ref/source/monplace.h
+++ b/crawl-ref/source/monplace.h
@@ -180,7 +180,7 @@ bool monster_habitable_grid(int monster_class,
dungeon_feature_type actual_grid,
int flies = -1,
bool paralysed = false);
-bool monster_can_submerge(int monster_class, int grid);
+bool monster_can_submerge(const monsters *mons, dungeon_feature_type grid);
coord_def find_newmons_square(int mons_class, int x, int y);
void spawn_random_monsters();
diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc
index f1230a6fbd..dbf428ed2a 100644
--- a/crawl-ref/source/monspeak.cc
+++ b/crawl-ref/source/monspeak.cc
@@ -26,6 +26,7 @@
#include "database.h"
#include "debug.h"
#include "fight.h"
+#include "ghost.h"
#include "insult.h"
#include "itemname.h"
#include "message.h"
@@ -127,7 +128,7 @@ static std::string player_ghost_speak_str(const monsters *monster,
const std::vector<std::string> prefixes)
{
const ghost_demon &ghost = *(monster->ghost);
- std::string ghost_class = get_class_name(ghost.values[GVAL_CLASS]);
+ std::string ghost_class = get_class_name(ghost.job);
std::string prefix = "";
for (int i = 0, size = prefixes.size(); i < size; i++)
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index cf8ddec129..7dce081e7a 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -38,6 +38,7 @@
#include "describe.h"
#include "dgnevent.h"
#include "fight.h"
+#include "ghost.h"
#include "hiscores.h"
#include "it_use2.h"
#include "itemname.h"
@@ -1885,7 +1886,7 @@ static void handle_behaviour(monsters *mon)
mon->foe = you.pet_target;
}
- // instead berserkers attack nearest monsters
+ // instead berserkers attack nearest monsters.
if (mon->has_ench(ENCH_BERSERK)
&& (mon->foe == MHITNOT || isFriendly && mon->foe == MHITYOU))
{
@@ -2431,7 +2432,7 @@ static void handle_nearby_ability(monsters *monster)
mons_speaks(monster);
}
- if (monster_can_submerge(monster->type, grd[monster->x][monster->y])
+ if (monster_can_submerge(monster, grd[monster->x][monster->y])
&& ( !player_beheld_by(monster) // no submerging if player entranced
&& (one_chance_in(5)
|| ((grid_distance( monster->x, monster->y,
@@ -2492,7 +2493,7 @@ static void handle_nearby_ability(monsters *monster)
break;
case MONS_PANDEMONIUM_DEMON:
- if (monster->ghost->values[ GVAL_DEMONLORD_CYCLE_COLOUR ])
+ if (monster->ghost->cycle_colours)
monster->colour = random_colour();
break;
@@ -3676,7 +3677,7 @@ static bool handle_spell( monsters *monster, bolt & beem )
return (false);
}
else if (monster->type == MONS_PANDEMONIUM_DEMON
- && !monster->ghost->values[ GVAL_DEMONLORD_SPELLCASTER ])
+ && !monster->ghost->spellcaster)
{
return (false);
}
@@ -4134,7 +4135,7 @@ static void monster_regenerate(monsters *monster)
return;
// Non-land creatures out of their element cannot regenerate.
- if (mons_habitat(monster->type) != HT_LAND
+ if (mons_habitat(monster) != HT_LAND
&& !monster_habitable_grid(monster, grd(monster->pos())))
{
return;
@@ -4324,7 +4325,7 @@ static void handle_monster_move(int i, monsters *monster)
handle_behaviour(monster);
// submerging monsters will hide from clouds
- if (monster_can_submerge(monster->type, grd[monster->x][monster->y])
+ if (monster_can_submerge(monster, grd[monster->x][monster->y])
&& env.cgrid[monster->x][monster->y] != EMPTY_CLOUD)
{
monster->add_ench(ENCH_SUBMERGED);
@@ -4988,7 +4989,7 @@ void mons_check_pool(monsters *mons, killer_type killer, int killnum)
mons->name(DESC_CAP_THE).c_str(),
(grid == DNGN_LAVA ? "lava" : "water"));
- if (grid == DNGN_LAVA && mons_res_fire(mons) > 0)
+ if (grid == DNGN_LAVA && mons_res_fire(mons) >= 2)
grid = DNGN_DEEP_WATER;
// Even fire resistant monsters perish in lava, but undead can survive
@@ -5025,68 +5026,45 @@ void mons_check_pool(monsters *mons, killer_type killer, int killnum)
// returns true for monsters that obviously (to the player) feel
// "thematically at home" in a branch
// currently used for native monsters recognizing traps
-static bool is_native_in_branch(const monsters *monster, const branch_type branch)
+static bool is_native_in_branch(const monsters *monster,
+ const branch_type branch)
{
switch (branch)
{
- case BRANCH_ELVEN_HALLS:
- if (mons_species(monster->type) == MONS_ELF)
- return true;
- return false;
+ case BRANCH_ELVEN_HALLS:
+ return (mons_species(monster->type) == MONS_ELF);
- case BRANCH_ORCISH_MINES:
- if (mons_species(monster->type) == MONS_ORC)
- return true;
- return false;
+ case BRANCH_ORCISH_MINES:
+ return (mons_species(monster->type) == MONS_ORC);
- case BRANCH_SHOALS:
- if (mons_species(monster->type) == MONS_CYCLOPS
+ case BRANCH_SHOALS:
+ return (mons_species(monster->type) == MONS_CYCLOPS
|| mons_species(monster->type) == MONS_MERFOLK
- || mons_species(monster->type) == MONS_MERMAID)
- {
- return true;
- }
- return false;
+ || mons_species(monster->type) == MONS_MERMAID);
- case BRANCH_SLIME_PITS:
- if (mons_species(monster->type) == MONS_JELLY)
- return true;
- return false;
+ case BRANCH_SLIME_PITS:
+ return (mons_species(monster->type) == MONS_JELLY);
- case BRANCH_SNAKE_PIT:
- if (mons_species(monster->type) == MONS_NAGA
- || mons_species(monster->type) == MONS_SNAKE)
- {
- return true;
- }
- return false;
+ case BRANCH_SNAKE_PIT:
+ return (mons_species(monster->type) == MONS_NAGA
+ || mons_species(monster->type) == MONS_SNAKE);
- case BRANCH_HALL_OF_ZOT:
- if (mons_species(monster->type) == MONS_DRACONIAN)
- return true;
- return false;
+ case BRANCH_HALL_OF_ZOT:
+ return (mons_species(monster->type) == MONS_DRACONIAN);
- case BRANCH_TOMB:
- if (mons_species(monster->type) == MONS_MUMMY)
- return true;
- return false;
+ case BRANCH_TOMB:
+ return (mons_species(monster->type) == MONS_MUMMY);
- case BRANCH_HIVE:
- if (monster->type == MONS_KILLER_BEE_LARVA
+ case BRANCH_HIVE:
+ return (monster->type == MONS_KILLER_BEE_LARVA
|| monster->type == MONS_KILLER_BEE
- || monster->type == MONS_QUEEN_BEE)
- {
- return true;
- }
- return false;
+ || monster->type == MONS_QUEEN_BEE);
- case BRANCH_HALL_OF_BLADES:
- if (monster->type == MONS_DANCING_WEAPON)
- return true;
- return false;
+ case BRANCH_HALL_OF_BLADES:
+ return (monster->type == MONS_DANCING_WEAPON);
- default:
- return false;
+ default:
+ return false;
}
}
@@ -5268,7 +5246,7 @@ bool mon_can_move_to_pos(const monsters *monster, const int count_x,
}
const dungeon_feature_type target_grid = grd[targ_x][targ_y];
- const habitat_type habitat = mons_habitat(monster->type);
+ const habitat_type habitat = mons_habitat(monster);
// effectively slows down monster movement across water.
// Fire elementals can't cross at all.
@@ -5405,7 +5383,7 @@ bool mon_can_move_to_pos(const monsters *monster, const int count_x,
return (mons_res_miasma(monster) > 0);
case CLOUD_FIRE:
- if (mons_res_fire(monster) > 0)
+ if (mons_res_fire(monster) > 1)
return true;
if (monster->hit_points >= 15 + random2avg(46, 5))
@@ -5422,7 +5400,7 @@ bool mon_can_move_to_pos(const monsters *monster, const int count_x,
break;
case CLOUD_COLD:
- if (mons_res_cold(monster) > 0)
+ if (mons_res_cold(monster) > 1)
return true;
if (monster->hit_points >= 15 + random2avg(46, 5))
@@ -5469,7 +5447,7 @@ static bool monster_move(monsters *monster)
int count_x, count_y, count;
int okmove = DNGN_SHALLOW_WATER; // what does this actually do?
- const habitat_type habitat = mons_habitat(monster->type);
+ const habitat_type habitat = mons_habitat(monster);
bool deep_water_available = false;
// Berserking monsters make a lot of racket
@@ -5917,15 +5895,11 @@ static void mons_in_cloud(monsters *monster)
simple_monster_message(monster, " is engulfed in flame!");
- if (mons_res_fire(monster) > 0)
- return;
-
- hurted += ((random2avg(16, 3) + 6) * 10) / speed;
-
- if (mons_res_fire(monster) < 0)
- hurted += (random2(15) * 10) / speed;
+ hurted +=
+ resist_adjust_damage( monster,
+ monster->res_fire(),
+ ((random2avg(16, 3) + 6) * 10) / speed );
- // remember that the above is in addition to the other you.damage.
hurted -= random2(1 + monster->ac);
break; // to damage routine at end {dlb}
@@ -5949,15 +5923,11 @@ static void mons_in_cloud(monsters *monster)
case CLOUD_COLD:
simple_monster_message(monster, " is engulfed in freezing vapours!");
- if (mons_res_cold(monster) > 0)
- return;
-
- hurted += ((6 + random2avg(16, 3)) * 10) / speed;
+ hurted +=
+ resist_adjust_damage( monster,
+ monster->res_cold(),
+ ((6 + random2avg(16, 3)) * 10) / speed );
- if (mons_res_cold(monster) < 0)
- hurted += (random2(15) * 10) / speed;
-
- // remember that the above is in addition to the other damage.
hurted -= random2(1 + monster->ac);
break; // to damage routine at end {dlb}
@@ -5987,14 +5957,12 @@ static void mons_in_cloud(monsters *monster)
simple_monster_message(monster, " is engulfed in steam!");
- if (mons_res_fire(monster) > 0)
- return;
-
const int steam_base_damage = steam_cloud_damage(cloud);
- hurted += (random2avg(steam_base_damage, 2) * 10) / speed;
-
- if (mons_res_fire(monster) < 0)
- hurted += (random2(steam_base_damage / 2 + 1) * 10) / speed;
+ hurted +=
+ resist_adjust_damage(
+ monster,
+ monster->res_steam(),
+ (random2avg(steam_base_damage, 2) * 10) / speed);
hurted -= random2(1 + monster->ac);
break; // to damage routine at end {dlb}
diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc
index 2d28cd9465..2740c81c40 100644
--- a/crawl-ref/source/ouch.cc
+++ b/crawl-ref/source/ouch.cc
@@ -923,11 +923,18 @@ static std::string morgue_name(time_t when_crawl_got_even)
#endif // SHORT_FILE_NAMES
}
-void end_game( struct scorefile_entry &se )
+void end_game( scorefile_entry &se )
{
- int i;
bool dead = true;
+ if (!dump_char( morgue_name(se.death_time), !dead, true, &se ))
+ {
+ mpr("Char dump unsuccessful! Sorry about that.");
+ if (!crawl_state.seen_hups)
+ more();
+ clrscr();
+ }
+
if (se.death_type == KILLED_BY_LEAVING ||
se.death_type == KILLED_BY_QUITTING ||
se.death_type == KILLED_BY_WINNING)
@@ -975,7 +982,7 @@ void end_game( struct scorefile_entry &se )
const int num_suffixes = sizeof(suffixes) / sizeof(const char*);
- for ( i = 0; i < num_suffixes; ++i ) {
+ for (int i = 0; i < num_suffixes; ++i) {
std::string tmpname = basename + suffixes[i];
unlink( tmpname.c_str() );
}
@@ -1000,10 +1007,10 @@ void end_game( struct scorefile_entry &se )
if (!crawl_state.seen_hups)
more();
- for (i = 0; i < ENDOFPACK; i++)
+ for (int i = 0; i < ENDOFPACK; i++)
set_ident_flags( you.inv[i], ISFLAG_IDENT_MASK );
- for (i = 0; i < ENDOFPACK; i++)
+ for (int i = 0; i < ENDOFPACK; i++)
{
if (you.inv[i].base_type != 0)
{
@@ -1016,14 +1023,6 @@ void end_game( struct scorefile_entry &se )
textcolor( LIGHTGREY );
clrscr();
- if (!dump_char( morgue_name(se.death_time), !dead, true, &se ))
- {
- mpr("Char dump unsuccessful! Sorry about that.");
- if (!crawl_state.seen_hups)
- more();
- clrscr();
- }
-
clrscr();
cprintf( "Goodbye, %s.", you.your_name );
cprintf( EOL EOL " " ); // Space padding where # would go in list format
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index bb2c592c7d..bc8348e39f 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -5152,6 +5152,12 @@ actor::~actor()
{
}
+bool actor::has_equipped(equipment_type eq, int sub_type) const
+{
+ const item_def *item = slot_item(eq);
+ return (item && item->sub_type == sub_type);
+}
+
bool actor::will_trigger_shaft() const
{
return (!airborne() && total_weight() > 0 && is_valid_shaft_level());
@@ -5674,11 +5680,6 @@ item_def *player::slot_item(equipment_type eq)
return (item == -1? NULL : &inv[item]);
}
-const item_def *player::slot_item(equipment_type eq) const
-{
- return const_cast<player*>(this)->slot_item(eq);
-}
-
// Returns the item in the player's weapon slot.
item_def *player::weapon(int /* which_attack */)
{
@@ -5718,6 +5719,11 @@ int player::id() const
return (-1);
}
+int player::get_experience_level() const
+{
+ return (experience_level);
+}
+
bool player::alive() const
{
// Simplistic, but if the player dies the game is over anyway, so
@@ -5941,6 +5947,11 @@ int player::res_fire() const
return (player_res_fire());
}
+int player::res_steam() const
+{
+ return (player_res_steam());
+}
+
int player::res_cold() const
{
return (player_res_cold());
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index c962301856..041429135a 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -75,6 +75,7 @@
#include "enum.h"
#include "externs.h"
#include "files.h"
+#include "ghost.h"
#include "itemname.h"
#include "itemprop.h"
#include "mapmark.h"
@@ -132,6 +133,13 @@ static void tag_read_ghost(tagHeader &th, char minorVersion);
static void marshallGhost(tagHeader &th, const ghost_demon &ghost);
static ghost_demon unmarshallGhost( tagHeader &th );
+
+static void marshallResists(tagHeader &, const mon_resist_def &);
+static void unmarshallResists(tagHeader &, mon_resist_def &);
+
+static void marshallSpells(tagHeader &, const monster_spells &);
+static void unmarshallSpells(tagHeader &, monster_spells &);
+
static void marshall_monster(tagHeader &th, const monsters &m);
static void unmarshall_monster(tagHeader &th, monsters &m);
@@ -1757,9 +1765,7 @@ static void marshall_monster(tagHeader &th, const monsters &m)
for (int j = 0; j < NUM_MONSTER_SLOTS; j++)
marshallShort(th, m.inv[j]);
- for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)
- marshallShort(th, m.spells[j]);
-
+ marshallSpells(th, m.spells);
marshallByte(th, m.god);
if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON)
@@ -2022,8 +2028,7 @@ static void unmarshall_monster(tagHeader &th, monsters &m)
for (int j = 0; j < NUM_MONSTER_SLOTS; j++)
m.inv[j] = unmarshallShort(th);
- for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)
- m.spells[j] = static_cast<spell_type>( unmarshallShort(th) );
+ unmarshallSpells(th, m.spells);
m.god = (god_type) unmarshallByte(th);
@@ -2210,24 +2215,74 @@ static void tag_missing_level_tiles()
// ------------------------------- ghost tags ---------------------------- //
-static void marshallGhost(tagHeader &th, const ghost_demon &ghost)
+static void marshallResists(tagHeader &th, const mon_resist_def &res)
{
- marshallString(th, ghost.name.c_str(), 20);
+ marshallByte(th, res.elec);
+ marshallByte(th, res.poison);
+ marshallByte(th, res.fire);
+ marshallByte(th, res.steam);
+ marshallByte(th, res.cold);
+ marshallByte(th, res.hellfire);
+ marshallByte(th, res.asphyx);
+ marshallByte(th, res.acid);
+ marshallByte(th, res.sticky_flame);
+ marshallByte(th, res.pierce);
+ marshallByte(th, res.slice);
+ marshallByte(th, res.bludgeon);
+}
- // how many ghost values?
- marshallByte(th, NUM_GHOST_VALUES);
+static void unmarshallResists(tagHeader &th, mon_resist_def &res)
+{
+ res.elec = unmarshallByte(th);
+ res.poison = unmarshallByte(th);
+ res.fire = unmarshallByte(th);
+ res.steam = unmarshallByte(th);
+ res.cold = unmarshallByte(th);
+ res.hellfire = unmarshallByte(th);
+ res.asphyx = unmarshallByte(th);
+ res.acid = unmarshallByte(th);
+ res.sticky_flame = unmarshallByte(th);
+ res.pierce = unmarshallByte(th);
+ res.slice = unmarshallByte(th);
+ res.bludgeon = unmarshallByte(th);
+}
- for (int i = 0; i < NUM_GHOST_VALUES; i++)
- marshallShort( th, ghost.values[i] );
+static void marshallSpells(tagHeader &th, const monster_spells &spells)
+{
+ for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)
+ marshallShort(th, spells[j]);
}
-static void tag_construct_ghost(tagHeader &th)
+static void unmarshallSpells(tagHeader &th, monster_spells &spells)
{
- // How many ghosts?
- marshallShort(th, ghosts.size());
+ for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)
+ spells[j] = static_cast<spell_type>( unmarshallShort(th) );
+}
- for (int i = 0, size = ghosts.size(); i < size; ++i)
- marshallGhost(th, ghosts[i]);
+static void marshallGhost(tagHeader &th, const ghost_demon &ghost)
+{
+ marshallString(th, ghost.name.c_str(), 20);
+
+ marshallShort(th, ghost.species);
+ marshallShort(th, ghost.job);
+ marshallShort(th, ghost.best_skill);
+ marshallShort(th, ghost.best_skill_level);
+ marshallShort(th, ghost.xl);
+ marshallShort(th, ghost.max_hp);
+ marshallShort(th, ghost.ev);
+ marshallShort(th, ghost.ac);
+ marshallShort(th, ghost.damage);
+ marshallShort(th, ghost.speed);
+ marshallByte(th, ghost.see_invis);
+ marshallShort(th, ghost.brand);
+
+ marshallResists(th, ghost.resists);
+
+ marshallByte(th, ghost.spellcaster);
+ marshallByte(th, ghost.cycle_colours);
+ marshallShort(th, ghost.fly);
+
+ marshallSpells(th, ghost.spells);
}
static ghost_demon unmarshallGhost( tagHeader &th )
@@ -2236,18 +2291,39 @@ static ghost_demon unmarshallGhost( tagHeader &th )
ghost.name = unmarshallString(th, 20);
- // how many ghost values?
- int count_c = unmarshallByte(th);
-
- if (count_c > NUM_GHOST_VALUES)
- count_c = NUM_GHOST_VALUES;
+ ghost.species = static_cast<species_type>( unmarshallShort(th) );
+ ghost.job = static_cast<job_type>( unmarshallShort(th) );
+ ghost.best_skill = static_cast<skill_type>( unmarshallShort(th) );
+ ghost.best_skill_level = unmarshallShort(th);
+ ghost.xl = unmarshallShort(th);
+ ghost.max_hp = unmarshallShort(th);
+ ghost.ev = unmarshallShort(th);
+ ghost.ac = unmarshallShort(th);
+ ghost.damage = unmarshallShort(th);
+ ghost.speed = unmarshallShort(th);
+ ghost.see_invis = unmarshallByte(th);
+ ghost.brand = static_cast<brand_type>( unmarshallShort(th) );
+
+ unmarshallResists(th, ghost.resists);
+
+ ghost.spellcaster = unmarshallByte(th);
+ ghost.cycle_colours = unmarshallByte(th);
+ ghost.fly = static_cast<flight_type>( unmarshallShort(th) );
+
+ unmarshallSpells(th, ghost.spells);
- for (int i = 0; i < count_c; i++)
- ghost.values[i] = unmarshallShort(th);
-
return (ghost);
}
+static void tag_construct_ghost(tagHeader &th)
+{
+ // How many ghosts?
+ marshallShort(th, ghosts.size());
+
+ for (int i = 0, size = ghosts.size(); i < size; ++i)
+ marshallGhost(th, ghosts[i]);
+}
+
static void tag_read_ghost(tagHeader &th, char minorVersion)
{
int nghosts = unmarshallShort(th);
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index cf22365b0a..f1a3deb2bc 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -48,6 +48,7 @@
#include "direct.h"
#include "dungeon.h"
#include "format.h"
+#include "ghost.h"
#include "initfile.h"
#include "itemprop.h"
#include "luadgn.h"
@@ -912,7 +913,7 @@ void handle_monster_shouts(monsters* monster, bool force)
else if (monster->type == MONS_PLAYER_GHOST)
{
const ghost_demon &ghost = *(monster->ghost);
- std::string ghost_class = get_class_name(ghost.values[GVAL_CLASS]);
+ std::string ghost_class = get_class_name(ghost.job);
key = ghost_class + " player ghost";