summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarshan Shaligram <dshaligram@users.sourceforge.net>2009-12-29 08:26:24 +0530
committerDarshan Shaligram <dshaligram@users.sourceforge.net>2009-12-29 08:26:24 +0530
commit22c9c1dd3fe93c8ccab01eec647fb002877018f8 (patch)
tree61647db7597a0c8b9055bfdba86ee1ffcaa5fb10
parentda206768a436941bf7acfc67f005a518fb90ca7e (diff)
downloadcrawl-ref-22c9c1dd3fe93c8ccab01eec647fb002877018f8.tar.gz
crawl-ref-22c9c1dd3fe93c8ccab01eec647fb002877018f8.zip
Merfolk (water/ice) elementalists join the Shoals guard.
-rw-r--r--crawl-ref/source/actor.h5
-rw-r--r--crawl-ref/source/beam.cc191
-rw-r--r--crawl-ref/source/beam.h6
-rw-r--r--crawl-ref/source/dgn-shoals.cc37
-rw-r--r--crawl-ref/source/dungeon.cc41
-rw-r--r--crawl-ref/source/dungeon.h6
-rw-r--r--crawl-ref/source/enum.h21
-rw-r--r--crawl-ref/source/fight.cc7
-rw-r--r--crawl-ref/source/main.cc2
-rw-r--r--crawl-ref/source/mon-cast.cc18
-rw-r--r--crawl-ref/source/mon-data.h16
-rw-r--r--crawl-ref/source/mon-gear.cc9
-rw-r--r--crawl-ref/source/mon-pick.cc2
-rw-r--r--crawl-ref/source/mon-spll.h12
-rw-r--r--crawl-ref/source/monster.cc21
-rw-r--r--crawl-ref/source/monster.h5
-rw-r--r--crawl-ref/source/ouch.cc7
-rw-r--r--crawl-ref/source/player.cc13
-rw-r--r--crawl-ref/source/player.h5
-rw-r--r--crawl-ref/source/spl-cast.cc6
-rw-r--r--crawl-ref/source/spl-data.h14
-rw-r--r--crawl-ref/source/spl-util.cc2
22 files changed, 368 insertions, 78 deletions
diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h
index a6d4899bbb..e3b3ae4af6 100644
--- a/crawl-ref/source/actor.h
+++ b/crawl-ref/source/actor.h
@@ -37,6 +37,10 @@ public:
// occupied.
virtual bool move_to_pos(const coord_def &c) = 0;
+ virtual void apply_location_effects(const coord_def &oldpos,
+ killer_type killer = KILL_NONE,
+ int killernum = -1) = 0;
+
virtual void set_position(const coord_def &c);
virtual const coord_def& pos() const { return position; }
@@ -213,6 +217,7 @@ public:
virtual int res_poison() const = 0;
virtual int res_rotting() const = 0;
virtual int res_asphyx() const = 0;
+ virtual int res_water_drowning() const = 0;
virtual int res_sticky_flame() const = 0;
virtual int res_holy_energy(const actor *attacker) const = 0;
virtual int res_negative_energy() const = 0;
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 8b05a833d9..abe97ceb66 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -28,6 +28,7 @@
#include "coord.h"
#include "coordit.h"
#include "delay.h"
+#include "dungeon.h"
#include "dgnevent.h"
#include "effects.h"
#include "env.h"
@@ -518,6 +519,22 @@ const zap_info zap_data[] = {
},
{
+ ZAP_PRIMAL_WAVE,
+ "great wave of water",
+ 200,
+ new calcdice_calculator<4, 14, 3, 5>,
+ new tohit_calculator<10, 1, 25>,
+ LIGHTBLUE,
+ false,
+ BEAM_WATER,
+ DCHAR_WAVY,
+ true,
+ false,
+ false,
+ 6
+ },
+
+ {
ZAP_CONFUSION,
"0",
100,
@@ -1915,6 +1932,13 @@ coord_def bolt::pos() const
return ray.pos();
}
+bool bolt::need_regress() const
+{
+ return ((is_explosion && !in_explosion_phase)
+ || drop_item
+ || origin_spell == SPELL_PRIMAL_WAVE);
+}
+
// Returns true if the beam ended due to hitting the wall.
bool bolt::hit_wall()
{
@@ -1976,8 +2000,7 @@ bool bolt::hit_wall()
{
// Regress for explosions: blow up in an open grid (if regressing
// makes any sense). Also regress when dropping items.
- if (pos() != source
- && ((is_explosion && !in_explosion_phase) || drop_item))
+ if (pos() != source && need_regress())
{
do
ray.regress();
@@ -2315,6 +2338,17 @@ int mons_adjust_flavoured(monsters *monster, bolt &pbolt, int hurted,
}
break;
+ case BEAM_WATER:
+ hurted = resist_adjust_damage(monster, pbolt.flavour,
+ monster->res_asphyx(),
+ hurted, true);
+ if (doFlavouredEffects)
+ {
+ if (!hurted)
+ simple_monster_message(monster, " shrugs off the wave.");
+ }
+ break;
+
case BEAM_COLD:
hurted = resist_adjust_damage(monster, pbolt.flavour,
monster->res_cold(),
@@ -2926,6 +2960,31 @@ void mimic_alert(monsters *mimic)
mimic->flags |= MF_KNOWN_MIMIC;
}
+void create_feat_at(coord_def center,
+ dungeon_feature_type overwriteable,
+ dungeon_feature_type newfeat)
+{
+ if (grd(center) == overwriteable)
+ dungeon_terrain_changed(center, newfeat, true, false, true);
+}
+
+void create_feat_splash(coord_def center,
+ dungeon_feature_type overwriteable,
+ dungeon_feature_type newfeat,
+ int radius,
+ int nattempts)
+{
+ // Always affect center.
+ create_feat_at(center, overwriteable, newfeat);
+ for (int i = 0; i < nattempts; ++i)
+ {
+ const coord_def newp(dgn_random_point_visible_from(center, radius));
+ if (newp.origin() || grd(newp) != overwriteable)
+ continue;
+ create_feat_at(newp, overwriteable, newfeat);
+ }
+}
+
bool bolt::is_bouncy(dungeon_feature_type feat) const
{
if (real_flavour == BEAM_CHAOS && feat_is_solid(feat))
@@ -3009,6 +3068,24 @@ void bolt::affect_endpoint()
if (is_tracer)
return;
+ if (origin_spell == SPELL_PRIMAL_WAVE) // &&coinflip()
+ {
+ if (you.see_cell(pos()))
+ {
+ mprf("The wave splashes down.");
+ noisy(25, pos());
+ }
+ else
+ {
+ noisy(25, pos(), "You hear a splash.");
+ }
+ create_feat_splash(pos(),
+ DNGN_FLOOR,
+ DNGN_SHALLOW_WATER,
+ 2,
+ random_range(1, 9, 2));
+ }
+
// FIXME: why don't these just have is_explosion set?
// They don't explode in tracers: why not?
if (name == "orb of electricity"
@@ -4312,6 +4389,9 @@ void bolt::affect_player()
internal_ouch(hurted);
range_used += range_used_on_hit(&you);
+
+ if (flavour == BEAM_WATER)
+ water_hits_actor(&you);
}
int bolt::beam_source_as_target() const
@@ -4678,6 +4758,23 @@ void bolt::monster_post_hit(monsters* mon, int dmg)
mimic_alert(mon);
else if (dmg)
beogh_follower_convert(mon, true);
+
+ if (flavour == BEAM_WATER)
+ water_hits_actor(mon);
+}
+
+void bolt::water_hits_actor(actor *act)
+{
+ const coord_def oldpos(act->pos());
+ if (knockback_actor(act))
+ {
+ if (you.can_see(act))
+ mprf("%s %s knocked back by the %s.",
+ act->name(DESC_CAP_THE).c_str(),
+ act->conj_verb("are").c_str(),
+ this->name.c_str());
+ act->apply_location_effects(oldpos, killer(), beam_source);
+ }
}
// Return true if the block succeeded (including reflections.)
@@ -4989,21 +5086,24 @@ void bolt::affect_monster(monsters* mon)
// Apply flavoured specials.
mons_adjust_flavoured(mon, *this, postac, true);
- // If the beam is an actual missile or of the MMISSILE type (Earth magic)
- // we might bleed on the floor.
- if (!engulfs
- && (flavour == BEAM_MISSILE || flavour == BEAM_MMISSILE)
- && !mon->is_summoned() && !mon->submerged())
+ // mons_adjust_flavoured may kill the monster directly.
+ if (mon->alive())
{
- // Using raw_damage instead of the flavoured one!
- // assumes DVORP_PIERCING, factor: 0.5
- const int blood = std::min(postac/2, mon->hit_points);
- bleed_onto_floor(mon->pos(), mon->type, blood, true);
+ // If the beam is an actual missile or of the MMISSILE type
+ // (Earth magic) we might bleed on the floor.
+ if (!engulfs
+ && (flavour == BEAM_MISSILE || flavour == BEAM_MMISSILE)
+ && !mon->is_summoned() && !mon->submerged())
+ {
+ // Using raw_damage instead of the flavoured one!
+ // assumes DVORP_PIERCING, factor: 0.5
+ const int blood = std::min(postac/2, mon->hit_points);
+ bleed_onto_floor(mon->pos(), mon->type, blood, true);
+ }
+ // Now hurt monster.
+ mon->hurt(agent(), final, flavour, false);
}
- // Now hurt monster.
- mon->hurt(agent(), final, flavour, false);
-
int corpse = -1;
monsters orig = *mon;
@@ -5541,6 +5641,34 @@ int bolt::range_used_on_hit(const actor* victim) const
return (used);
}
+// Checks whether the beam knocks back the supplied actor. The actor
+// should have already failed their EV check, so the save is entirely
+// body-mass-based.
+bool bolt::knockback_actor(actor *act)
+{
+ ASSERT(ray.pos() == act->pos());
+
+ const coord_def oldpos(ray.pos());
+ const ray_def ray_copy(ray);
+ ray.advance();
+
+ const coord_def newpos(ray.pos());
+ if (newpos == oldpos || actor_at(newpos) || feat_is_solid(grd(newpos))
+ || !act->can_pass_through(newpos)
+ // Save is based on target's body weight.
+ || random2(2500) < act->body_weight())
+ {
+ ray = ray_copy;
+ return false;
+ }
+
+ act->move_to_pos(newpos);
+
+ // Knockback cannot ever kill the actor directly - caller must do
+ // apply_location_effects after messaging.
+ return true;
+}
+
// Takes a bolt and refines it for use in the explosion function.
// Explosions which do not follow from beams (e.g., scrolls of
// immolation) bypass this function.
@@ -5717,7 +5845,6 @@ static sweep_type _radial_sweep(int r)
}
#define MAX_EXPLOSION_RADIUS 9
-
// Returns true if we saw something happening.
bool bolt::explode(bool show_more, bool hole_in_the_middle)
{
@@ -6048,21 +6175,24 @@ bool bolt::nice_to(const monsters *mon) const
//
// TODO: Eventually it'd be nice to have a proper factory for these things
// (extended from setup_mons_cast() and zapping() which act as limited ones).
-bolt::bolt() : range(-2), type('*'), colour(BLACK), flavour(BEAM_MAGIC),
- real_flavour(BEAM_MAGIC), drop_item(false), item(NULL), source(), target(),
- damage(0, 0), ench_power(0), hit(0), thrower(KILL_MISC), ex_size(0),
- beam_source(MHITNOT), source_name(), name(), short_name(), hit_verb(),
- loudness(0), noise_msg(), is_beam(false), is_explosion(false),
- is_big_cloud(false), aimed_at_spot(false), aux_source(),
- affects_nothing(false), affects_items(true), effect_known(true),
- draw_delay(15), special_explosion(NULL), range_funcs(), damage_funcs(),
- hit_funcs(), aoe_funcs(), obvious_effect(false), seen(false), heard(false),
- path_taken(), range_used(0), is_tracer(false), aimed_at_feet(false),
- msg_generated(false), passed_target(false), in_explosion_phase(false),
- smart_monster(false), can_see_invis(false), attitude(ATT_HOSTILE),
- foe_ratio(0), chose_ray(false), beam_cancelled(false),
- dont_stop_player(false), bounces(false), bounce_pos(), reflections(0),
- reflector(-1), auto_hit(false)
+bolt::bolt() : origin_spell(SPELL_NO_SPELL),
+ range(-2), type('*'), colour(BLACK), flavour(BEAM_MAGIC),
+ real_flavour(BEAM_MAGIC), drop_item(false), item(NULL),
+ source(), target(), damage(0, 0), ench_power(0), hit(0),
+ thrower(KILL_MISC), ex_size(0), beam_source(MHITNOT),
+ source_name(), name(), short_name(), hit_verb(),
+ loudness(0), noise_msg(), is_beam(false), is_explosion(false),
+ is_big_cloud(false), aimed_at_spot(false), aux_source(),
+ affects_nothing(false), affects_items(true), effect_known(true),
+ draw_delay(15), special_explosion(NULL), range_funcs(),
+ damage_funcs(), hit_funcs(), aoe_funcs(), obvious_effect(false),
+ seen(false), heard(false), path_taken(), range_used(0),
+ is_tracer(false), aimed_at_feet(false), msg_generated(false),
+ passed_target(false), in_explosion_phase(false),
+ smart_monster(false), can_see_invis(false),
+ attitude(ATT_HOSTILE), foe_ratio(0), chose_ray(false),
+ beam_cancelled(false), dont_stop_player(false), bounces(false),
+ bounce_pos(), reflections(0), reflector(-1), auto_hit(false)
{
}
@@ -6191,6 +6321,7 @@ std::string beam_type_name(beam_type type)
case BEAM_POTION_COLD: // fall through
case BEAM_COLD: return ("cold");
+ case BEAM_WATER: return ("water");
case BEAM_MAGIC: return ("magic");
case BEAM_ELECTRICITY: return ("electricity");
diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h
index 61f5640d07..7c60170f41 100644
--- a/crawl-ref/source/beam.h
+++ b/crawl-ref/source/beam.h
@@ -62,6 +62,8 @@ typedef bool (*explosion_aoe_func)(bolt& beam, const coord_def& target);
struct bolt
{
// INPUT parameters set by caller
+ spell_type origin_spell; // may be SPELL_NO_SPELL for non-spell
+ // beams.
int range;
unsigned type; // missile gfx
int colour;
@@ -192,6 +194,7 @@ public:
// Return whether any affected cell was seen.
bool explode(bool show_more = true, bool hole_in_the_middle = false);
+ bool knockback_actor(actor *actor);
private:
void do_fire();
@@ -213,6 +216,7 @@ private:
bool nasty_to(const monsters* mon) const;
bool nice_to(const monsters* mon) const;
bool found_player() const;
+ bool need_regress() const;
int beam_source_as_target() const;
int range_used_on_hit(const actor* victim) const;
@@ -241,6 +245,8 @@ public:
void affect_place_explosion_clouds();
void affect_endpoint();
+ void water_hits_actor(actor *act);
+
// Stuff when a monster or player is hit.
void affect_player_enchantment();
void tracer_affect_player();
diff --git a/crawl-ref/source/dgn-shoals.cc b/crawl-ref/source/dgn-shoals.cc
index 10b49fafb8..c72cd41bf6 100644
--- a/crawl-ref/source/dgn-shoals.cc
+++ b/crawl-ref/source/dgn-shoals.cc
@@ -61,11 +61,6 @@ enum tide_direction
static tide_direction _shoals_tide_direction;
-static double _to_radians(int degrees)
-{
- return degrees * M_PI / 180;
-}
-
static dungeon_feature_type _shoals_feature_by_height(int height)
{
return height >= SHT_STONE ? DNGN_STONE_WALL :
@@ -122,33 +117,9 @@ static void _shoals_init_heights()
shoals_heights(*ri) = SHT_SHALLOW_WATER - 3;
}
-static double _angle_fuzz()
-{
- double fuzz = _to_radians(random2(15));
- return coinflip()? fuzz : -fuzz;
-}
-
-static coord_def _random_point_from(const coord_def &c, int radius,
- int directed_angle = -1)
+static coord_def _random_point_from(const coord_def &c, int radius)
{
- const double directed_radians(
- directed_angle == -1? 0.0 : _to_radians(directed_angle));
- int attempts = 70;
- while (attempts-- > 0)
- {
- const double angle =
- directed_angle == -1? _to_radians(random2(360))
- : ((coinflip()? directed_radians : -directed_radians)
- + _angle_fuzz());
- coord_def res = c + coord_def(radius * cos(angle),
- radius * sin(angle));
- if (res.x >= _shoals_margin && res.x < GXM - _shoals_margin
- && res.y >= _shoals_margin && res.y < GYM - _shoals_margin)
- {
- return res;
- }
- }
- return coord_def();
+ return dgn_random_point_from(c, radius, _shoals_margin);
}
static coord_def _random_point(int offset = 0)
@@ -228,7 +199,7 @@ static void _shoals_build_cliff()
if (in_bounds(cliffc))
{
const int length = random_range(6, 15);
- double angle = _to_radians(random2(360));
+ double angle = dgn_degrees_to_radians(random2(360));
for (int i = 0; i < length; i += 3)
{
int distance = i - length / 2;
@@ -698,7 +669,7 @@ static coord_def _int_coord(const coord_dbl &c)
static std::vector<coord_def> _shoals_windshadows(grid_bool &windy)
{
const int wind_angle_degrees = random2(360);
- const double wind_angle(_to_radians(wind_angle_degrees));
+ const double wind_angle(dgn_degrees_to_radians(wind_angle_degrees));
const coord_dbl wi(cos(wind_angle), sin(wind_angle));
const double epsilon = 1e-5;
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 04ce67e162..1c1d27f642 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -13,6 +13,7 @@
#include <set>
#include <sstream>
#include <algorithm>
+#include <cmath>
#include "abyss.h"
#include "artefact.h"
@@ -7619,6 +7620,46 @@ static coord_def _dgn_find_closest_to_stone_stairs(coord_def base_pos)
return (np.nearest);
}
+
+double dgn_degrees_to_radians(int degrees)
+{
+ return degrees * M_PI / 180;
+}
+
+coord_def dgn_random_point_from(const coord_def &c, int radius, int margin)
+{
+ int attempts = 70;
+ while (attempts-- > 0)
+ {
+ const double angle = dgn_degrees_to_radians(random2(360));
+ const coord_def res = c + coord_def(radius * cos(angle),
+ radius * sin(angle));
+ if (res.x >= margin && res.x < GXM - margin
+ && res.y >= margin && res.y < GYM - margin)
+ {
+ return res;
+ }
+ }
+ return coord_def();
+}
+
+coord_def dgn_random_point_visible_from(const coord_def &c,
+ int radius,
+ int margin,
+ int tries)
+{
+ while (tries-- > 0)
+ {
+ const coord_def point = dgn_random_point_from(c, radius, margin);
+ if (point.origin())
+ continue;
+ if (!cell_see_cell(c, point))
+ continue;
+ return point;
+ }
+ return coord_def();
+}
+
coord_def dgn_find_feature_marker(dungeon_feature_type feat)
{
std::vector<map_marker*> markers = env.markers.get_all();
diff --git a/crawl-ref/source/dungeon.h b/crawl-ref/source/dungeon.h
index f58c17355c..ded9cea53a 100644
--- a/crawl-ref/source/dungeon.h
+++ b/crawl-ref/source/dungeon.h
@@ -179,6 +179,12 @@ bool builder(int level_number, int level_type);
void dgn_flush_map_memory();
+double dgn_degrees_to_radians(int degrees);
+coord_def dgn_random_point_from(const coord_def &c, int radius, int margin = 1);
+coord_def dgn_random_point_visible_from(const coord_def &c,
+ int radius,
+ int margin = 1,
+ int tries = 5);
coord_def dgn_find_feature_marker(dungeon_feature_type feat);
// Set floor/wall colour based on the mons_alloc array. Used for
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 9bc022522f..bf570798f1 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -199,23 +199,24 @@ enum beam_type // beam[].flavour
BEAM_MMISSILE, // and similarly irresistible things
BEAM_FIRE,
BEAM_COLD,
- BEAM_MAGIC, // 5
+ BEAM_WATER,
+ BEAM_MAGIC,
BEAM_ELECTRICITY,
BEAM_POISON,
BEAM_NEG,
BEAM_ACID,
- BEAM_MIASMA, // 10
+ BEAM_MIASMA,
BEAM_SPORE,
BEAM_POISON_ARROW,
BEAM_HELLFIRE,
BEAM_NAPALM,
- BEAM_STEAM, // 15
+ BEAM_STEAM,
BEAM_ENERGY,
BEAM_HOLY,
BEAM_FRAG,
BEAM_LAVA,
- BEAM_ICE, // 20
+ BEAM_ICE,
BEAM_NUKE,
BEAM_RANDOM, // currently translates into FIRE..ACID
BEAM_CHAOS,
@@ -223,22 +224,22 @@ enum beam_type // beam[].flavour
// Enchantments
BEAM_SLOW,
BEAM_FIRST_ENCHANTMENT = BEAM_SLOW,
- BEAM_HASTE, // 25
+ BEAM_HASTE,
BEAM_MIGHT,
BEAM_HEALING,
BEAM_PARALYSIS,
BEAM_CONFUSION,
- BEAM_INVISIBILITY, // 30
+ BEAM_INVISIBILITY,
BEAM_DIGGING,
BEAM_TELEPORT,
BEAM_POLYMORPH,
BEAM_CHARM,
- BEAM_BANISH, // 35
+ BEAM_BANISH,
BEAM_DEGENERATE,
BEAM_ENSLAVE_UNDEAD,
BEAM_ENSLAVE_SOUL,
BEAM_PAIN,
- BEAM_DISPEL_UNDEAD, // 40
+ BEAM_DISPEL_UNDEAD,
BEAM_DISINTEGRATION,
BEAM_ENSLAVE_DEMON,
BEAM_BLINK,
@@ -1821,6 +1822,7 @@ enum monster_type // (int) menv[].type
// Shoals guardians
MONS_MERFOLK_GLADIATOR,
+ MONS_MERFOLK_ELEMENTALIST,
//jmf: end new monsters
MONS_WHITE_IMP = 220, // 220
@@ -2323,6 +2325,7 @@ enum mon_spellbook_type
MST_HAROLD,
MST_MARA,
MST_MARA_FAKE,
+ MST_MERFOLK_ELEMENTALIST,
MST_TEST_SPAWNER = 200,
NUM_MSTYPES,
@@ -2925,6 +2928,7 @@ enum spell_type
SPELL_FAKE_MARA_SUMMON,
SPELL_SUMMON_RAKSHASA,
SPELL_SUMMON_PLAYER_GHOST,
+ SPELL_PRIMAL_WAVE,
NUM_SPELLS
};
@@ -3115,6 +3119,7 @@ enum zap_type
ZAP_PARALYSIS,
ZAP_FIRE,
ZAP_COLD,
+ ZAP_PRIMAL_WAVE,
ZAP_CONFUSION,
ZAP_INVISIBILITY,
ZAP_DIGGING,
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index b2237a92cb..57254b012b 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -2158,6 +2158,8 @@ static bool is_boolean_resist(beam_type flavour)
case BEAM_ELECTRICITY:
case BEAM_MIASMA: // rotting
case BEAM_NAPALM:
+ case BEAM_WATER: // water asphyxiation damage,
+ // bypassed by being water inhabitant.
return (true);
default:
return (false);
@@ -2170,6 +2172,11 @@ static inline int get_resistible_fraction(beam_type flavour)
{
switch (flavour)
{
+ // Drowning damage from water is resistible by being a water thing, or
+ // otherwise asphyx resistant.
+ case BEAM_WATER:
+ return (40);
+
// Assume ice storm and throw icicle are mostly solid.
case BEAM_ICE:
return (25);
diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc
index 9dcd774a00..7f3ccb2592 100644
--- a/crawl-ref/source/main.cc
+++ b/crawl-ref/source/main.cc
@@ -4631,7 +4631,7 @@ static void _compile_time_asserts()
COMPILE_CHECK(SP_VAMPIRE == 30 , c3);
COMPILE_CHECK(SPELL_DEBUGGING_RAY == 103 , c4);
COMPILE_CHECK(SPELL_RETURNING_AMMUNITION == 162 , c5);
- COMPILE_CHECK(NUM_SPELLS == 215 , c6);
+ COMPILE_CHECK(NUM_SPELLS == 216 , c6);
//jmf: NEW ASSERTS: we ought to do a *lot* of these
COMPILE_CHECK(NUM_SPECIES < SP_UNKNOWN , c7);
diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc
index 7bbbd77862..42b7b52da5 100644
--- a/crawl-ref/source/mon-cast.cc
+++ b/crawl-ref/source/mon-cast.cc
@@ -217,6 +217,7 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power,
beam.type = dchar_glyph(DCHAR_FIRED_ZAP); // default
beam.thrower = KILL_MON_MISSILE;
+ beam.origin_spell = real_spell;
// FIXME: this should use the zap_data[] struct from beam.cc!
switch (real_spell)
@@ -361,6 +362,19 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power,
beam.is_beam = true;
break;
+ case SPELL_PRIMAL_WAVE:
+ beam.name = "great wave of water";
+ // Water attack is weaker than the pure elemental damage
+ // attacks, but also less resistible.
+ beam.damage = dice_def( 3, 6 + power / 12 );
+ beam.colour = LIGHTBLUE;
+ beam.flavour = BEAM_WATER;
+ // Huge wave of water is hard to dodge.
+ beam.hit = 20 + power / 20;
+ beam.is_beam = false;
+ beam.type = dchar_glyph(DCHAR_WAVY);
+ break;
+
case SPELL_FREEZING_CLOUD:
beam.name = "freezing blast";
beam.damage = dice_def( 2, 9 + power / 11 );
@@ -840,6 +854,8 @@ bool setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
bolt theBeam = mons_spells(monster, spell_cast, power);
+ // [ds] remind me again why we're doing this piecemeal copying?
+ pbolt.origin_spell = theBeam.origin_spell;
pbolt.colour = theBeam.colour;
pbolt.range = theBeam.range;
pbolt.hit = theBeam.hit;
@@ -1821,7 +1837,7 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
if (created == -1)
continue;
- // Mara's clones are special; they have the same stats as him, and
+ // Mara's clones are special; they have the same stats as him, and
// are exact clones, so they are created damaged if necessary, with
// identical enchants and with the same items.
monsters *new_fake = &menv[created];
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index b65b314b65..574a1b916d 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -1124,14 +1124,26 @@ static monsterentry mondata[] = {
MR_NO_FLAGS,
500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -3,
{ {AT_HIT, AF_PLAIN, 35}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
- { 14, 5, 3, 0 },
+ { 14, 6, 3, 0 },
// Gladiators prefer light armour, and are dodging experts.
- 0, 16, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT,
+ 0, 17, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT,
I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, ATTACK_ENERGY(6),
MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM
},
{
+ MONS_MERFOLK_ELEMENTALIST, 'm', LIGHTGREEN, "merfolk elementalist",
+ M_WARM_BLOOD | M_SPELLCASTER | M_ACTUAL_SPELLS,
+ MR_RES_COLD,
+ 500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -4,
+ { {AT_HIT, AF_PLAIN, 15}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
+ { 15, 3, 3, 0 },
+ 0, 12, MST_MERFOLK_ELEMENTALIST, CE_CONTAMINATED, Z_SMALL, S_SHOUT,
+ I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, DEFAULT_ENERGY,
+ MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM
+},
+
+{
MONS_MERMAID, 'm', CYAN, "mermaid",
M_SPELLCASTER | M_WARM_BLOOD | M_SPEAKS,
MR_NO_FLAGS,
diff --git a/crawl-ref/source/mon-gear.cc b/crawl-ref/source/mon-gear.cc
index 9f284736d2..c1a4dace95 100644
--- a/crawl-ref/source/mon-gear.cc
+++ b/crawl-ref/source/mon-gear.cc
@@ -412,6 +412,14 @@ static item_make_species_type _give_weapon(monsters *mon, int level,
item.sub_type = WPN_LONGBOW;
break;
+ case MONS_MERFOLK_ELEMENTALIST:
+ item_race = MAKE_ITEM_NO_RACE;
+ item.base_type = OBJ_WEAPONS;
+ item.sub_type = WPN_SABRE;
+ if (coinflip())
+ level = MAKE_GOOD_ITEM;
+ break;
+
case MONS_DEEP_ELF_ANNIHILATOR:
case MONS_DEEP_ELF_CONJURER:
case MONS_DEEP_ELF_DEATH_MAGE:
@@ -1489,6 +1497,7 @@ void give_armour(monsters *mon, int level)
case MONS_WIZARD:
case MONS_ILSUIW:
case MONS_MARA:
+ case MONS_MERFOLK_ELEMENTALIST:
if (item_race == MAKE_ITEM_RANDOM_RACE)
item_race = MAKE_ITEM_NO_RACE;
item.base_type = OBJ_ARMOUR;
diff --git a/crawl-ref/source/mon-pick.cc b/crawl-ref/source/mon-pick.cc
index e1a429c91b..a79945b990 100644
--- a/crawl-ref/source/mon-pick.cc
+++ b/crawl-ref/source/mon-pick.cc
@@ -1722,6 +1722,7 @@ int mons_shoals_level(int mcls)
case MONS_SIREN:
case MONS_HARPY:
case MONS_MERFOLK_GLADIATOR:
+ case MONS_MERFOLK_ELEMENTALIST:
mlev += 3;
break;
@@ -1766,6 +1767,7 @@ int mons_shoals_rare(int mcls)
case MONS_SIREN:
case MONS_YAKTAUR:
+ case MONS_MERFOLK_ELEMENTALIST:
return 25;
case MONS_CYCLOPS:
diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h
index 526d37f378..76c561e352 100644
--- a/crawl-ref/source/mon-spll.h
+++ b/crawl-ref/source/mon-spll.h
@@ -1319,6 +1319,18 @@
}
},
+ { MST_MERFOLK_ELEMENTALIST,
+ {
+ SPELL_PRIMAL_WAVE,
+ SPELL_BOLT_OF_COLD,
+ // Ice form would be neat.
+ SPELL_THROW_ICICLE,
+ SPELL_NO_SPELL,
+ SPELL_NO_SPELL,
+ SPELL_BLINK
+ }
+ },
+
{ MST_TEST_SPAWNER,
{
SPELL_SHADOW_CREATURES,
diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc
index f9d18c97d9..f6003c3b81 100644
--- a/crawl-ref/source/monster.cc
+++ b/crawl-ref/source/monster.cc
@@ -3308,6 +3308,21 @@ int monsters::res_asphyx() const
return (res);
}
+int monsters::res_water_drowning() const
+{
+ const int res = res_asphyx();
+ if (res)
+ return res;
+ switch (mons_habitat(this))
+ {
+ case HT_WATER:
+ case HT_AMPHIBIOUS_WATER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
int monsters::res_poison() const
{
int u = get_mons_resists(this).poison;
@@ -5550,7 +5565,9 @@ void monsters::check_redraw(const coord_def &old) const
}
}
-void monsters::apply_location_effects(const coord_def &oldpos)
+void monsters::apply_location_effects(const coord_def &oldpos,
+ killer_type killer,
+ int killernum)
{
if (oldpos != pos())
dungeon_events.fire_position_event(DET_MONSTER_MOVED, pos());
@@ -5583,7 +5600,7 @@ void monsters::apply_location_effects(const coord_def &oldpos)
ptrap->trigger(*this);
if (alive())
- mons_check_pool(this, pos());
+ mons_check_pool(this, pos(), killer, killernum);
if (alive() && has_ench(ENCH_SUBMERGED)
&& (!monster_can_submerge(this, grd(pos()))
diff --git a/crawl-ref/source/monster.h b/crawl-ref/source/monster.h
index 109dc876fb..16ec367332 100644
--- a/crawl-ref/source/monster.h
+++ b/crawl-ref/source/monster.h
@@ -128,7 +128,9 @@ public:
bool is_summoned(int* duration = NULL, int* summon_type = NULL) const;
bool has_action_energy() const;
void check_redraw(const coord_def &oldpos) const;
- void apply_location_effects(const coord_def &oldpos);
+ void apply_location_effects(const coord_def &oldpos,
+ killer_type killer = KILL_NONE,
+ int killernum = -1);
void moveto(const coord_def& c);
bool move_to_pos(const coord_def &newpos);
@@ -317,6 +319,7 @@ public:
int res_poison() const;
int res_rotting() const;
int res_asphyx() const;
+ int res_water_drowning() const;
int res_sticky_flame() const;
int res_holy_energy(const actor *) const;
int res_negative_energy() const;
diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc
index 40352ecc0c..34f8bc736c 100644
--- a/crawl-ref/source/ouch.cc
+++ b/crawl-ref/source/ouch.cc
@@ -91,6 +91,13 @@ int check_your_resists(int hurted, beam_type flavour)
switch (flavour)
{
+ case BEAM_WATER:
+ hurted = resist_adjust_damage(&you, flavour,
+ you.res_water_drowning(), hurted, true);
+ if (!hurted)
+ mpr("You shrug off the wave.");
+ break;
+
case BEAM_STEAM:
hurted = resist_adjust_damage(&you, flavour,
player_res_steam(), hurted, true);
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 7ba87a20ff..8ebf0a0655 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -6452,6 +6452,12 @@ int player::res_elec() const
return (player_res_electricity() * 2);
}
+int player::res_water_drowning() const
+{
+ return (res_asphyx() ||
+ (you.species == SP_MERFOLK && !transform_changed_physiology()));
+}
+
int player::res_asphyx() const
{
// The undead are immune to asphyxiation, or so we'll assume.
@@ -7049,6 +7055,13 @@ bool player::move_to_pos(const coord_def &c)
return false;
}
+void player::apply_location_effects(const coord_def &oldpos,
+ killer_type killer,
+ int killernum)
+{
+ move_player_to_grid(pos(), false, true, true, false);
+}
+
void player::shiftto(const coord_def &c)
{
crawl_view.shift_player_to(c);
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index ee34bd703e..09b66c2053 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -464,6 +464,7 @@ public:
int res_poison() const;
int res_rotting() const;
int res_asphyx() const;
+ int res_water_drowning() const;
int res_sticky_flame() const;
int res_holy_energy(const actor *) const;
int res_negative_energy() const;
@@ -513,6 +514,10 @@ public:
bool do_shaft();
+ void apply_location_effects(const coord_def &oldpos,
+ killer_type killer = KILL_NONE,
+ int killernum = -1);
+
////////////////////////////////////////////////////////////////
PlaceInfo& get_place_info() const ; // Current place info
diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc
index 9f875c298f..acaaa7ebca 100644
--- a/crawl-ref/source/spl-cast.cc
+++ b/crawl-ref/source/spl-cast.cc
@@ -1139,6 +1139,7 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail)
dist spd;
bolt beam;
+ beam.origin_spell = spell;
// [dshaligram] Any action that depends on the spellcasting attempt to have
// succeeded must be performed after the switch().
@@ -1419,6 +1420,11 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail)
return (SPRET_ABORT);
break;
+ case SPELL_PRIMAL_WAVE:
+ if (!zapping(ZAP_PRIMAL_WAVE, powc, beam, true))
+ return (SPRET_ABORT);
+ break;
+
case SPELL_STONE_ARROW:
if (!zapping(ZAP_STONE_ARROW, powc, beam, true))
return (SPRET_ABORT);
diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h
index 303306cc83..6a62b9ed09 100644
--- a/crawl-ref/source/spl-data.h
+++ b/crawl-ref/source/spl-data.h
@@ -2642,6 +2642,20 @@
},
{
+ SPELL_PRIMAL_WAVE, "Primal Wave",
+ SPTYP_CONJURATION | SPTYP_ICE,
+ SPFLAG_DIR_OR_TARGET,
+ 6,
+ 200,
+ 7, 7,
+ 0,
+ NULL,
+ true,
+ false
+},
+
+
+{
SPELL_NO_SPELL, "nonexistent spell",
0,
SPFLAG_TESTING,
diff --git a/crawl-ref/source/spl-util.cc b/crawl-ref/source/spl-util.cc
index 75d7264e07..4e61296b5b 100644
--- a/crawl-ref/source/spl-util.cc
+++ b/crawl-ref/source/spl-util.cc
@@ -997,6 +997,8 @@ spell_type zap_type_to_spell(zap_type zap)
return(SPELL_BOLT_OF_FIRE);
case ZAP_COLD:
return(SPELL_BOLT_OF_COLD);
+ case ZAP_PRIMAL_WAVE:
+ return(SPELL_PRIMAL_WAVE);
case ZAP_CONFUSION:
return(SPELL_CONFUSE);
case ZAP_INVISIBILITY: