From 4bd11a2119ce0fd7f137233e16cd7f745c13a2dd Mon Sep 17 00:00:00 2001 From: Stefan O'Rear Date: Mon, 28 Dec 2009 01:52:41 -0800 Subject: Rename MF_CREATED_FRIENDLY to MF_NO_REWARD, since that's all it's used for these days. --- crawl-ref/source/spl-cast.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crawl-ref/source/spl-cast.cc') diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 7a65c84f2e..9f875c298f 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -1058,7 +1058,7 @@ static void _try_monster_cast(spell_type spell, int powc, mon->type = MONS_HUMAN; mon->behaviour = BEH_SEEK; mon->attitude = ATT_FRIENDLY; - mon->flags = (MF_CREATED_FRIENDLY | MF_JUST_SUMMONED | MF_SEEN + mon->flags = (MF_NO_REWARD | MF_JUST_SUMMONED | MF_SEEN | MF_WAS_IN_VIEW | MF_HARD_RESET); mon->hit_points = you.hp; mon->hit_dice = you.experience_level; -- cgit v1.2.3-54-g00ecf From 22c9c1dd3fe93c8ccab01eec647fb002877018f8 Mon Sep 17 00:00:00 2001 From: Darshan Shaligram Date: Tue, 29 Dec 2009 08:26:24 +0530 Subject: Merfolk (water/ice) elementalists join the Shoals guard. --- crawl-ref/source/actor.h | 5 ++ crawl-ref/source/beam.cc | 191 ++++++++++++++++++++++++++++++++++------- crawl-ref/source/beam.h | 6 ++ crawl-ref/source/dgn-shoals.cc | 37 +------- crawl-ref/source/dungeon.cc | 41 +++++++++ crawl-ref/source/dungeon.h | 6 ++ crawl-ref/source/enum.h | 21 +++-- crawl-ref/source/fight.cc | 7 ++ crawl-ref/source/main.cc | 2 +- crawl-ref/source/mon-cast.cc | 18 +++- crawl-ref/source/mon-data.h | 16 +++- crawl-ref/source/mon-gear.cc | 9 ++ crawl-ref/source/mon-pick.cc | 2 + crawl-ref/source/mon-spll.h | 12 +++ crawl-ref/source/monster.cc | 21 ++++- crawl-ref/source/monster.h | 5 +- crawl-ref/source/ouch.cc | 7 ++ crawl-ref/source/player.cc | 13 +++ crawl-ref/source/player.h | 5 ++ crawl-ref/source/spl-cast.cc | 6 ++ crawl-ref/source/spl-data.h | 14 +++ crawl-ref/source/spl-util.cc | 2 + 22 files changed, 368 insertions(+), 78 deletions(-) (limited to 'crawl-ref/source/spl-cast.cc') 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" @@ -517,6 +518,22 @@ const zap_info zap_data[] = { 6 }, + { + 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", @@ -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 _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 #include #include +#include #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 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,13 +1124,25 @@ 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, 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 @@ -2641,6 +2641,20 @@ false }, +{ + 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, 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: -- cgit v1.2.3-54-g00ecf