From cc6a658772e7c26cf2dedceb7028601c9863268f Mon Sep 17 00:00:00 2001 From: Stefan O'Rear Date: Sun, 27 Dec 2009 22:18:44 -0800 Subject: Fix insane non-monotonic fulsome behavior, per Eronarn The following two coder's discretion choices were made: 1. "Lots of chunks" is 5+, as for a draconian 2. "Self affliction at low power" means less than 1dN-1 where N depends on the potion. --- crawl-ref/source/spl-data.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crawl-ref/source/spl-data.h') diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index 3adc30d1d3..303306cc83 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -927,7 +927,7 @@ SPTYP_TRANSMUTATION | SPTYP_NECROMANCY, SPFLAG_NONE, 1, - 50, + 100, -1, -1, 0, NULL, -- cgit v1.2.3-54-g00ecf From 7bf522535b9a6f90f8272043cb7af97b7c7cc2bf Mon Sep 17 00:00:00 2001 From: Stefan O'Rear Date: Mon, 28 Dec 2009 15:45:19 -0800 Subject: Fulsome Distillation tweaks (dshaligram) Fulsome no longer uses power; the chance for self-affliction is removed. The typo which caused contaminated corpses to make poison is gone. --- crawl-ref/source/spells4.cc | 22 ++-------------------- crawl-ref/source/spl-data.h | 2 +- 2 files changed, 3 insertions(+), 21 deletions(-) (limited to 'crawl-ref/source/spl-data.h') diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 9d1c969ea6..7da3ee46af 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -1177,10 +1177,8 @@ bool cast_evaporate(int pow, bolt& beem, int pot_idx) // Producing helpful potions would break game balance here... // and producing more than one potion from a corpse, or not // using up the corpse might also lead to game balance problems. - bwr -void cast_fulsome_distillation(int pow) +void cast_fulsome_distillation(int /*pow*/) { - pow = std::min(100, pow); - int corpse = -1; // Search items at the player's location for corpses. @@ -1206,39 +1204,32 @@ void cast_fulsome_distillation(int pow) } potion_type pot_type = POT_WATER; - int difficulty = 0; switch (mons_corpse_effect(mitm[corpse].plus)) { case CE_CLEAN: pot_type = POT_WATER; - difficulty = 0; break; case CE_CONTAMINATED: pot_type = (mons_weight(mitm[corpse].plus) >= 900) - ? POT_DEGENERATION : POT_POISON; - difficulty = (pot_type == POT_DEGENERATION) ? 100 : 40; + ? POT_DEGENERATION : POT_CONFUSION; break; case CE_POISONOUS: pot_type = POT_POISON; - difficulty = 50; break; case CE_MUTAGEN_RANDOM: case CE_MUTAGEN_GOOD: // unused case CE_RANDOM: // unused pot_type = POT_MUTATION; - // this is a potentially good potion, so it never tries to get into you - difficulty = 0; break; case CE_MUTAGEN_BAD: // unused case CE_ROTTEN: // actually this only occurs via mangling case CE_HCL: // necrophage pot_type = POT_DECAY; - difficulty = 100; break; case CE_NOCORPSE: // shouldn't occur @@ -1250,12 +1241,10 @@ void cast_fulsome_distillation(int pow) { case MONS_RED_WASP: // paralysis attack pot_type = POT_PARALYSIS; - difficulty = 100; break; case MONS_YELLOW_WASP: // slowing attack pot_type = POT_SLOWING; - difficulty = 50; break; default: @@ -1271,7 +1260,6 @@ void cast_fulsome_distillation(int pow) smc->attack[nattk].flavour == AF_POISON_STR) { pot_type = POT_STRONG_POISON; - difficulty = 75; } } @@ -1292,12 +1280,6 @@ void cast_fulsome_distillation(int pow) mprf("You extract %s from the corpse.", mitm[corpse].name(DESC_NOCAP_A).c_str()); - if (random2(difficulty + 1) > pow) - { - mpr("Oops! You accidentally inhaled the fumes!"); - potion_effect(pot_type, 40); - } - // Try to move the potion to the player (for convenience). if (move_item_to_player(corpse, 1) != 1) mpr("Unfortunately, you can't carry it right now!"); diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index 303306cc83..61b544f635 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -927,7 +927,7 @@ SPTYP_TRANSMUTATION | SPTYP_NECROMANCY, SPFLAG_NONE, 1, - 100, + 0, -1, -1, 0, NULL, -- 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-data.h') 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 From 9cc1ba155f353a235d38e785c1eefc984f4d42c1 Mon Sep 17 00:00:00 2001 From: Darshan Shaligram Date: Tue, 29 Dec 2009 19:48:21 +0530 Subject: Boost Ilsuiw stats and give her Call Tide, which strongly boosts the tide in Shoals, pegs it towards high tide, and includes a local high tide maximum centered on Ilsuiw, which can be double the height of the normal high tide. --- crawl-ref/source/dgn-shoals.cc | 108 +++++++++++++++++++++++++++++++++++------ crawl-ref/source/dgn-shoals.h | 3 +- crawl-ref/source/enum.h | 2 + crawl-ref/source/main.cc | 2 +- crawl-ref/source/mon-abil.cc | 12 +---- crawl-ref/source/mon-cast.cc | 13 +++++ crawl-ref/source/mon-data.h | 2 +- crawl-ref/source/mon-gear.cc | 12 +++++ crawl-ref/source/mon-spll.h | 5 +- crawl-ref/source/mon-stuff.cc | 3 ++ crawl-ref/source/mon-util.cc | 7 +++ crawl-ref/source/monster.cc | 7 ++- crawl-ref/source/monster.h | 1 + crawl-ref/source/spl-data.h | 12 +++++ crawl-ref/source/view.cc | 13 +++++ crawl-ref/source/view.h | 1 + 16 files changed, 169 insertions(+), 34 deletions(-) (limited to 'crawl-ref/source/spl-data.h') diff --git a/crawl-ref/source/dgn-shoals.cc b/crawl-ref/source/dgn-shoals.cc index b88873d5b5..b91dc3a195 100644 --- a/crawl-ref/source/dgn-shoals.cc +++ b/crawl-ref/source/dgn-shoals.cc @@ -1,6 +1,7 @@ #include "AppHdr.h" #include "branch.h" +#include "colour.h" #include "coord.h" #include "coordit.h" #include "dungeon.h" @@ -10,10 +11,12 @@ #include "items.h" #include "maps.h" #include "mgen_data.h" +#include "mon-iter.h" #include "mon-place.h" #include "mon-util.h" #include "random.h" #include "terrain.h" +#include "view.h" #include #include @@ -42,6 +45,23 @@ const int N_PERTURB_OFFSET_HIGH = 45; const int PERTURB_OFFSET_RADIUS_LOW = 2; const int PERTURB_OFFSET_RADIUS_HIGH = 7; +// The raw tide height / TIDE_MULTIPLIER is the actual tide height. The higher +// the tide multiplier, the slower the tide advances and recedes. A multiplier +// of X implies that the tide will advance visibly about once in X turns. +const int TIDE_MULTIPLIER = 30; + +const int LOW_TIDE = -18 * TIDE_MULTIPLIER; +const int HIGH_TIDE = 25 * TIDE_MULTIPLIER; + +// The highest a tide can be called by a tide caller such as Ilsuiw. +const int HIGH_CALLED_TIDE = 25; +const int TIDE_DECEL_MARGIN = 8; +const int PEAK_TIDE_ACCEL = 2; + +// The area around the user of a call tide spell that is subject to +// local tide elevation. +const int TIDE_CALL_RADIUS = 8; + const int _shoals_margin = 6; enum shoals_height_thresholds @@ -60,6 +80,10 @@ enum tide_direction }; static tide_direction _shoals_tide_direction; +static monsters *tide_caller = NULL; +static coord_def tide_caller_pos; +static long tide_called_turns = 0L; +static int tide_called_peak = 0; static dungeon_feature_type _shoals_feature_by_height(int height) { @@ -816,18 +840,16 @@ void shoals_postprocess_level() } } -// The raw tide height / TIDE_MULTIPLIER is the actual tide height. The higher -// the tide multiplier, the slower the tide advances and recedes. A multiplier -// of X implies that the tide will advance visibly about once in X turns. -const int TIDE_MULTIPLIER = 30; - -const int LOW_TIDE = -18 * TIDE_MULTIPLIER; -const int HIGH_TIDE = 25 * TIDE_MULTIPLIER; -const int TIDE_DECEL_MARGIN = 8; -const int START_TIDE_RISE = 2; - static void _shoals_run_tide(int &tide, int &acc) { + // If someone is calling the tide, the acceleration is clamped high. + if (tide_caller) + acc = 15; + // If there's no tide caller and our acceleration is suspiciously high, + // reset it to a falling tide at peak acceleration. + else if (abs(acc) > PEAK_TIDE_ACCEL) + acc = -PEAK_TIDE_ACCEL; + tide += acc; tide = std::max(std::min(tide, HIGH_TIDE), LOW_TIDE); if ((tide == HIGH_TIDE && acc > 0) @@ -836,7 +858,7 @@ static void _shoals_run_tide(int &tide, int &acc) bool in_decel_margin = (abs(tide - HIGH_TIDE) < TIDE_DECEL_MARGIN) || (abs(tide - LOW_TIDE) < TIDE_DECEL_MARGIN); - if ((abs(acc) == 2) == in_decel_margin) + if ((abs(acc) > 1) == in_decel_margin) acc = in_decel_margin? acc / 2 : acc * 2; } @@ -956,6 +978,23 @@ static void _shoals_apply_tide_at(coord_def c, int tide) _shoals_apply_tide_feature_at(c, newfeat); } +static int _shoals_tide_at(coord_def pos, int base_tide) +{ + if (!tide_caller) + return base_tide; + + const int rl_distance = grid_distance(pos, tide_caller_pos); + if (rl_distance > TIDE_CALL_RADIUS) + return base_tide; + + const int distance = + static_cast(sqrt((pos - tide_caller->pos()).abs())); + if (distance > TIDE_CALL_RADIUS) + return base_tide; + + return (base_tide + std::max(0, tide_called_peak - distance * 3)); +} + static void _shoals_apply_tide(int tide) { std::vector pages[2]; @@ -980,7 +1019,7 @@ static void _shoals_apply_tide(int tide) coord_def c(cpage[i]); const bool was_wet(_shoals_tide_passable_feat(grd(c))); seen_points(c) = true; - _shoals_apply_tide_at(c, tide); + _shoals_apply_tide_at(c, _shoals_tide_at(c, tide)); const bool is_wet(feat_is_water(grd(c))); // Only squares that were wet (before applying tide @@ -1017,13 +1056,22 @@ static void _shoals_init_tide() if (!env.properties.exists(ENVP_SHOALS_TIDE_KEY)) { env.properties[ENVP_SHOALS_TIDE_KEY] = short(0); - env.properties[ENVP_SHOALS_TIDE_VEL] = short(2); + env.properties[ENVP_SHOALS_TIDE_VEL] = short(PEAK_TIDE_ACCEL); } } -void shoals_apply_tides(int turns_elapsed) +static monsters *_shoals_find_tide_caller() +{ + for (monster_iterator mi; mi; ++mi) + if (mi->has_ench(ENCH_TIDE)) + return *mi; + return NULL; +} + +void shoals_apply_tides(int turns_elapsed, bool force) { - if (!player_in_branch(BRANCH_SHOALS) || !turns_elapsed + if (!player_in_branch(BRANCH_SHOALS) + || (!turns_elapsed && !force) || !env.heightmap.get()) { return; @@ -1035,6 +1083,20 @@ void shoals_apply_tides(int turns_elapsed) turns_elapsed = turns_elapsed % TIDE_UNIT + TIDE_UNIT; _shoals_init_tide(); + + unwind_var tide_caller_unwind(tide_caller, + _shoals_find_tide_caller()); + if (tide_caller) + { + tide_called_turns = tide_caller->props[TIDE_CALL_TURN].get_long(); + tide_called_turns = you.num_turns - tide_called_turns; + if (tide_called_turns < 1L) + tide_called_turns = 1L; + tide_called_peak = std::min(HIGH_CALLED_TIDE, + int(tide_called_turns * 5)); + tide_caller_pos = tide_caller->pos(); + } + int tide = env.properties[ENVP_SHOALS_TIDE_KEY].get_short(); int acc = env.properties[ENVP_SHOALS_TIDE_VEL].get_short(); const int old_tide = tide; @@ -1042,10 +1104,24 @@ void shoals_apply_tides(int turns_elapsed) _shoals_run_tide(tide, acc); env.properties[ENVP_SHOALS_TIDE_KEY] = short(tide); env.properties[ENVP_SHOALS_TIDE_VEL] = short(acc); - if (old_tide / TIDE_MULTIPLIER != tide / TIDE_MULTIPLIER) + if (force + || tide_caller + || old_tide / TIDE_MULTIPLIER != tide / TIDE_MULTIPLIER) { _shoals_tide_direction = tide > old_tide ? TIDE_RISING : TIDE_FALLING; _shoals_apply_tide(tide / TIDE_MULTIPLIER); } } + +void shoals_release_tide(monsters *mons) +{ + if (player_in_branch(BRANCH_SHOALS) + && player_can_hear(you.pos())) + { + mprf(MSGCH_SOUND, "The tide is released from %s call.", + mons->name(DESC_NOCAP_YOUR, true).c_str()); + flash_view_delay(ETC_WATER, 150); + shoals_apply_tides(0, true); + } +} diff --git a/crawl-ref/source/dgn-shoals.h b/crawl-ref/source/dgn-shoals.h index f558f6606f..1d14c620f2 100644 --- a/crawl-ref/source/dgn-shoals.h +++ b/crawl-ref/source/dgn-shoals.h @@ -3,6 +3,7 @@ void prepare_shoals(int level_number); void shoals_postprocess_level(); -void shoals_apply_tides(int turns_elapsed); +void shoals_apply_tides(int turns_elapsed, bool force = false); +void shoals_release_tide(monsters *caller); #endif diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 08ae63c36e..f770323cfc 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1254,6 +1254,7 @@ enum enchant_type ENCH_SPORE_PRODUCTION, // 35 ENCH_SLOUCH, ENCH_SWIFT, + ENCH_TIDE, // Update enchantment names in mon-util.cc when adding or removing // enchantments. @@ -2930,6 +2931,7 @@ enum spell_type SPELL_SUMMON_RAKSHASA, SPELL_SUMMON_PLAYER_GHOST, SPELL_PRIMAL_WAVE, + SPELL_CALL_TIDE, NUM_SPELLS }; diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc index 7f3ccb2592..1ad6e1af6c 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 == 216 , c6); + COMPILE_CHECK(NUM_SPELLS == 217 , 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-abil.cc b/crawl-ref/source/mon-abil.cc index 5325d3f27b..781778d6d3 100644 --- a/crawl-ref/source/mon-abil.cc +++ b/crawl-ref/source/mon-abil.cc @@ -303,17 +303,7 @@ static bool _do_merge(monsters *initial_slime, monsters *merge_to) merge_to->name(DESC_NOCAP_A).c_str()); } - flash_view(LIGHTGREEN); - - int flash_delay = 150; - // Scale delay to match change in arena_delay. - if (crawl_state.arena) - { - flash_delay *= Options.arena_delay; - flash_delay /= 600; - } - - delay(flash_delay); + flash_view_delay(LIGHTGREEN, 150); } else if (you.can_see(initial_slime)) mpr("A slime creature suddenly disappears!"); diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index 42b7b52da5..5190625c61 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -839,6 +839,7 @@ bool setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast, case SPELL_SUMMON_EYEBALLS: case SPELL_SUMMON_BUTTERFLIES: case SPELL_MISLEAD: + case SPELL_CALL_TIDE: return (true); default: if (check_validity) @@ -1678,6 +1679,18 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast, simple_monster_message(monster, " seems to move somewhat quicker."); return; + case SPELL_CALL_TIDE: + { + const int tide_duration = random_range(18, 50, 2); + monster->add_ench(mon_enchant(ENCH_TIDE, 0, KC_OTHER, + tide_duration * 10)); + monster->props[TIDE_CALL_TURN] = you.num_turns; + simple_monster_message(monster, + " sings a water chant to call the tide!"); + flash_view_delay(ETC_WATER, 300); + return; + } + case SPELL_SUMMON_SMALL_MAMMALS: case SPELL_VAMPIRE_SUMMON: if (spell_cast == SPELL_SUMMON_SMALL_MAMMALS) diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index e5e5fb7b98..6ce2103a22 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -4626,7 +4626,7 @@ static monsterentry mondata[] = { MR_RES_POISON | MR_RES_COLD, 500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -7, { {AT_HIT, AF_PLAIN, 10}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, - { 9, 0, 0, 54 }, + { 16, 0, 0, 150 }, 5, 18, MST_ILSUIW, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM diff --git a/crawl-ref/source/mon-gear.cc b/crawl-ref/source/mon-gear.cc index 393d4680b7..8affc9587f 100644 --- a/crawl-ref/source/mon-gear.cc +++ b/crawl-ref/source/mon-gear.cc @@ -12,6 +12,7 @@ #include "mon-gear.h" #include "artefact.h" +#include "colour.h" #include "dungeon.h" #include "env.h" #include "itemprop.h" @@ -582,6 +583,17 @@ static item_make_species_type _give_weapon(monsters *mon, int level, } break; + case MONS_ILSUIW: + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_WEAPONS; + item.sub_type = WPN_TRIDENT; + item.special = SPWPN_FREEZING; + item.plus = random_range(-1, 6, 2); + item.plus2 = random_range(-1, 6, 2); + item.colour = ETC_ICE; + force_item = true; + break; + case MONS_MERFOLK_IMPALER: item_race = MAKE_ITEM_NO_RACE; item.base_type = OBJ_WEAPONS; diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h index 2137cbbacb..344ae8e6b8 100644 --- a/crawl-ref/source/mon-spll.h +++ b/crawl-ref/source/mon-spll.h @@ -1043,8 +1043,8 @@ { MST_ILSUIW, { - SPELL_THROW_FROST, // was: SPELL_CONFUSED (jpeg) - SPELL_SLOW, + SPELL_THROW_ICICLE, + SPELL_CALL_TIDE, SPELL_INVISIBILITY, SPELL_BLINK, SPELL_WATER_ELEMENTALS, @@ -1323,7 +1323,6 @@ { SPELL_PRIMAL_WAVE, SPELL_BOLT_OF_COLD, - // Ice form would be neat. SPELL_THROW_ICICLE, SPELL_NO_SPELL, SPELL_NO_SPELL, diff --git a/crawl-ref/source/mon-stuff.cc b/crawl-ref/source/mon-stuff.cc index 3db902c13b..ca7b9cf12c 100644 --- a/crawl-ref/source/mon-stuff.cc +++ b/crawl-ref/source/mon-stuff.cc @@ -1422,6 +1422,9 @@ int monster_die(monsters *monster, killer_type killer, return (-1); } + // If the monster was calling the tide, let go now. + monster->del_ench(ENCH_TIDE); + crawl_state.inc_mon_acting(monster); ASSERT(!( YOU_KILL(killer) && crawl_state.arena )); diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 3687e1711c..5895a0c542 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -2390,6 +2390,13 @@ bool ms_waste_of_time( const monsters *mon, spell_type monspell ) // handled here as well. - bwr switch (monspell) { + case SPELL_CALL_TIDE: + return (!player_in_branch(BRANCH_SHOALS) + || mon->has_ench(ENCH_TIDE) + || !foe + || (grd(mon->pos()) == DNGN_DEEP_WATER + && grd(foe->pos()) == DNGN_DEEP_WATER)); + case SPELL_BRAIN_FEED: ret = (foe != &you); break; diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc index f6003c3b81..2f83c01d6f 100644 --- a/crawl-ref/source/monster.cc +++ b/crawl-ref/source/monster.cc @@ -12,6 +12,7 @@ #include "coordit.h" #include "delay.h" #include "dgnevent.h" +#include "dgn-shoals.h" #include "directn.h" #include "env.h" #include "fight.h" @@ -4305,6 +4306,10 @@ void monsters::remove_enchantment_effect(const mon_enchant &me, bool quiet) { switch (me.ench) { + case ENCH_TIDE: + shoals_release_tide(this); + break; + case ENCH_BERSERK: scale_hp(2, 3); break; @@ -6031,7 +6036,7 @@ static const char *enchant_names[] = "short-lived", "paralysis", "sick", "sleep", "fatigue", "held", "blood-lust", "neutral", "petrifying", "petrified", "magic-vulnerable", "soul-ripe", "decay", "hungry", "flopping", "spore-producing", - "downtrodden", "swift", "bug" + "downtrodden", "swift", "tide", "bug" }; static const char *_mons_enchantment_name(enchant_type ench) diff --git a/crawl-ref/source/monster.h b/crawl-ref/source/monster.h index 16ec367332..b93ed65571 100644 --- a/crawl-ref/source/monster.h +++ b/crawl-ref/source/monster.h @@ -4,6 +4,7 @@ #include "actor.h" const int KRAKEN_TENTACLE_RANGE = 3; +#define TIDE_CALL_TURN "tide-call-turn" class mon_enchant { diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index 6a62b9ed09..db5979a031 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -2654,6 +2654,18 @@ false }, +{ + SPELL_CALL_TIDE, "Call Tide", + SPTYP_TRANSLOCATION, + SPFLAG_MONSTER, + 7, + 0, + -1, -1, + 0, + NULL, + false, + false +}, { SPELL_NO_SPELL, "nonexistent spell", diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index b8e6447a89..94ad2a572c 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -635,6 +635,19 @@ void flash_view(int colour) viewwindow(false, false); } +void flash_view_delay(int colour, long flash_delay) +{ + flash_view(colour); + // Scale delay to match change in arena_delay. + if (crawl_state.arena) + { + flash_delay *= Options.arena_delay; + flash_delay /= 600; + } + + delay(flash_delay); +} + static void _debug_pane_bounds() { #if DEBUG_PANE_BOUNDS diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h index 7a618c182b..70029c000d 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -37,6 +37,7 @@ std::string screenshot(bool fullscreen = false); bool view_update(); void view_update_at(const coord_def &pos); void flash_view(int colour = BLACK); // inside #ifndef USE_TILE? +void flash_view_delay(int colour = BLACK, long delay = 150); #ifndef USE_TILE void flash_monster_colour(const monsters *mon, unsigned char fmc_colour, int fmc_delay); -- cgit v1.2.3-54-g00ecf