diff options
Diffstat (limited to 'crawl-ref')
78 files changed, 1289 insertions, 332 deletions
diff --git a/crawl-ref/settings/052_monster_glyphs.txt b/crawl-ref/settings/052_monster_glyphs.txt new file mode 100644 index 0000000000..091890a9f7 --- /dev/null +++ b/crawl-ref/settings/052_monster_glyphs.txt @@ -0,0 +1,31 @@ +# With current Dungeon Crawl Stone Soup trunk (tentatively +# titled 0.6), there has been a new shift of glyphs and +# colours. Some demons have been promoted, and others have +# been demoted, while queen monsters have been moved into +# their relevant non-queen glyph. + +########################### +# monsters +########################### + +# bees +mon_glyph = killer bee : yellow +mon_glyph = queen bee : yellow Q + +# ants/cockroaches +mon_glyph = queen ant : lightgrey Q +mon_glyph = giant cockroach : brown a + +# hybrids +mon_glyph = minotaur : red t + +# spiders +mon_glyph = trapdoor spider : brown +mon_glyph = wolf spider : brown + +# tier 4 demons +mon_glpyh = smoke demon : lightgrey 4 + +# tier 3 demons +mon_glyph = hellion : fire 3 +mon_glyph = ynoxinul : cyan 3 diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index a16fdf3ca6..3079b924d0 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -702,8 +702,6 @@ static talent _get_talent(ability_type ability, bool check_confused) case ABIL_TROG_BERSERK: // piety >= 30 invoc = true; failure = 30 - you.piety; // starts at 0% - if (player_mental_clarity(true)) - failure += 80; break; case ABIL_TROG_REGEN_MR: // piety >= 50 @@ -1101,8 +1099,7 @@ static bool _check_ability_possible(const ability_def& abil, mpr("You're too hungry to berserk."); return (false); } - return (you.can_go_berserk(true, abil.ability == ABIL_TROG_BERSERK) - && berserk_check_wielded_weapon()); + return (you.can_go_berserk(true) && berserk_check_wielded_weapon()); case ABIL_FLY_II: if (you.duration[DUR_EXHAUSTED]) @@ -1161,7 +1158,7 @@ static bool _activate_talent(const talent& tal) } if ((tal.which == ABIL_EVOKE_BERSERK || tal.which == ABIL_TROG_BERSERK) - && !you.can_go_berserk(true, tal.which == ABIL_TROG_BERSERK)) + && !you.can_go_berserk(true)) { crawl_state.zero_turns_taken(); return (false); @@ -1745,7 +1742,7 @@ static bool _do_ability(const ability_def& abil) case ABIL_TROG_BERSERK: // Trog abilities don't use or train invocations. - go_berserk(true, true); + go_berserk(true); break; case ABIL_TROG_REGEN_MR: 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/arena.h b/crawl-ref/source/arena.h index 619767d841..d79d2f834d 100644 --- a/crawl-ref/source/arena.h +++ b/crawl-ref/source/arena.h @@ -10,7 +10,7 @@ class level_id; class monsters; -class mgen_data; +struct mgen_data; struct coord_def; diff --git a/crawl-ref/source/artefact.h b/crawl-ref/source/artefact.h index 0bc887c647..aec4951dd7 100644 --- a/crawl-ref/source/artefact.h +++ b/crawl-ref/source/artefact.h @@ -10,7 +10,7 @@ #include "externs.h" -class bolt; +struct bolt; // NOTE: NO_UNRANDARTS is automatically set by util/art-data.pl #define NO_UNRANDARTS 82 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..d1a47e1a0f 100644 --- a/crawl-ref/source/beam.h +++ b/crawl-ref/source/beam.h @@ -31,7 +31,7 @@ enum mon_resist_type MON_OTHER // monster unaffected, but for other reasons }; -struct dist; +class dist; typedef FixedArray<int, 19, 19> explosion_map; @@ -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/dat/descript/items.txt b/crawl-ref/source/dat/descript/items.txt index 25277aff17..2b1dc4c4ea 100644 --- a/crawl-ref/source/dat/descript/items.txt +++ b/crawl-ref/source/dat/descript/items.txt @@ -8,7 +8,8 @@ one is skilled in the appropriate elemental magic. %%%% amulet of clarity -This amulet protects its wearer from some forms of mental confusion. +This amulet protects its wearer from some forms of mental confusion, +including being infuriated against one's will. %%%% amulet of conservation diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt index da05efc32f..579bbeb6a3 100644 --- a/crawl-ref/source/dat/descript/monsters.txt +++ b/crawl-ref/source/dat/descript/monsters.txt @@ -1048,7 +1048,19 @@ This tall and powerful demon is Mara, Lord of Illusions, mighty among dreamers. %%%% merfolk -Half fish, half man, the merfolk are citizens of both water and land, and they'll fiercely protect their chosen territory. +Half fish, half man, the merfolk are citizens of both water and land, and fierce protectors of their chosen territory. +%%%% +merfolk impaler + +A powerfully muscled merfolk warrior, bearing a great trident. +%%%% +merfolk aquamancer + +A slender merfolk mystic with unusually webby hands. Its form shifts and glistens as if seen through ocean spray. +%%%% +merfolk javelineer + +A sinewy merfolk fighter with a piercing gaze and a large bundle of javelins. %%%% mermaid diff --git a/crawl-ref/source/dat/float.des b/crawl-ref/source/dat/float.des index 493d018b9e..a66a5961dc 100644 --- a/crawl-ref/source/dat/float.des +++ b/crawl-ref/source/dat/float.des @@ -709,15 +709,23 @@ NAME: statue_cache ORIENT: north TAGS: no_monster_gen DEPTH: D:12-20, Elf, Vault -MONS: statue name:archer name_adjective ; crossbow ego:flame . bolt q:30 -MONS: statue name:archer name_adjective ; crossbow ego:frost . bolt q:30 -MONS: statue name:warrior name_adjective ; battleaxe ego:flaming good_item -MONS: statue name:warrior name_adjective ; battleaxe ego:freezing good_item -MONS: statue name:warrior name_adjective ; great mace ego:flaming good_item -MONS: statue name:warrior name_adjective ; great mace ego:freezing good_item +MONS: statue name:archer name_adjective tile:mons_statue_crossbow ; crossbow \ + ego:flame . bolt q:30 +MONS: statue name:archer name_adjective tile:mons_statue_crossbow ; crossbow \ + ego:frost . bolt q:30 +MONS: statue name:warrior name_adjective tile:mons_statue_axe ; battleaxe \ + ego:flaming good_item +MONS: statue name:warrior name_adjective tile:mons_statue_axe ; battleaxe \ + ego:freezing good_item +MONS: statue name:warrior name_adjective tile:mons_statue_mace ; great mace \ + ego:flaming good_item +MONS: statue name:warrior name_adjective tile:mons_statue_mace ; great mace \ + ego:freezing good_item MONS: silver statue -KMONS: 8 = statue name:wizard name_adjective spells:lehudib's_crystal_spear;iskenderun's_mystic_blast;slow;stone_arrow -KMONS: 9 = statue name:wizard name_adjective spells:freezing_cloud;mephitic_cloud;throw_icicle;confuse +KMONS: 8 = statue name:wizard name_adjective tile:mons_statue_mage \ + spells:lehudib's_crystal_spear;iskenderun's_mystic_blast;slow;stone_arrow +KMONS: 9 = statue name:wizard name_adjective tile:mons_statue_mage \ + spells:freezing_cloud;mephitic_cloud;throw_icicle;confuse MAP cccccccccccccc cccccccc7ccccc diff --git a/crawl-ref/source/dat/mini.des b/crawl-ref/source/dat/mini.des index 2927e8362c..d29eeff639 100644 --- a/crawl-ref/source/dat/mini.des +++ b/crawl-ref/source/dat/mini.des @@ -2667,7 +2667,7 @@ ENDMAP # NAME: archer_statue DEPTH: D:8-, Vault, Elf -MONS: statue tile:mons_archer_statue name:archer name_adjective; longbow . arrow q:30 +MONS: statue tile:mons_statue_archer name:archer name_adjective; longbow . arrow q:30 MAP ccc ccccc1ccccc diff --git a/crawl-ref/source/dat/shoals.des b/crawl-ref/source/dat/shoals.des index 2b52eb6ade..c89fea0aad 100644 --- a/crawl-ref/source/dat/shoals.des +++ b/crawl-ref/source/dat/shoals.des @@ -234,39 +234,44 @@ wwwwwwwww ENDMAP ################################################################################ -# Shoal hut with rune inside +# Shoal hut with rune inside. +# +# This hut is also used for the decoy huts, with the rune replaced with a good +# item. # -# Shoal:$ is hand-hacked to force lots of minivaults. NAME: shoalhut_rune -TAGS: shoal_rune water_ok no_dump +TAGS: shoal_rune water_ok no_dump allow_dup SHUFFLE: ABCD SUBST: A:x, B:x, C:x=, D=+ LROCKTILE: wall_vines MAP xxCxx -xx...xx -x.....x -B..O..D -x.....x -xx...xx +xx.1.xx +x.1.1.x +B1.O.1D +x.1.1.x +xx.1.xx xxAxx ENDMAP ################################################################################ -# Shoal hut with no rune inside -# -# Shoal:$ is hand-hacked to force lots of minivaults. -NAME: shoalhut_norune -DEPTH: Shoal:$ -TAGS: allow_dup water_ok shoal no_dump -SHUFFLE: ABCD -SUBST: A:x, B:x, C:x=, D=+ +# Shoal hangout of Ilsuiw. Using PLACE: makes this quite likely to turn up. +# The hut itself may be empty if Ilsuiw had other engagements. + +NAME: shoal_ilsuiw +PLACE: Shoal:$ +TAGS: mini_float +KMONS: 1 = Ilsuiw band, siren +KITEM: 1 = | +KMONS: 2 = merfolk impaler / merfolk javelineer +KFEAT: 2 = w +LROCKTILE: wall_vines MAP - xxCxx -xx...xx -x.....x -B..|..D -x.....x -xx...xx - xxAxx + wxwxw +wxw2wxw +xwwWwwx +wwW1Www +xwwWwwx +wxw2wxw + wxwxw ENDMAP diff --git a/crawl-ref/source/dgn-shoals.cc b/crawl-ref/source/dgn-shoals.cc index a3a2b12ea9..367ca497d4 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" @@ -9,15 +10,21 @@ #include "flood_find.h" #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 <algorithm> #include <vector> #include <cmath> +typedef FixedArray<bool, GXM, GYM> grid_bool; +typedef FixedArray<short, GXM, GYM> grid_short; + const char *ENVP_SHOALS_TIDE_KEY = "shoals-tide-height"; const char *ENVP_SHOALS_TIDE_VEL = "shoals-tide-velocity"; @@ -38,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 @@ -56,11 +80,10 @@ enum tide_direction }; static tide_direction _shoals_tide_direction; - -static double _to_radians(int degrees) -{ - return degrees * M_PI / 180; -} +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) { @@ -118,33 +141,9 @@ static void _shoals_init_heights() shoals_heights(*ri) = SHT_SHALLOW_WATER - 3; } -static double _angle_fuzz() +static coord_def _random_point_from(const coord_def &c, int radius) { - 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) -{ - 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) @@ -224,12 +223,13 @@ 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; - coord_def place = cliffc + coord_def(distance * cos(angle), - distance * sin(angle)); + coord_def place = + cliffc + coord_def(static_cast<int>(distance * cos(angle)), + static_cast<int>(distance * sin(angle))); coord_def fuzz = coord_def(random_range(-2, 2), random_range(-2, 2)); place += fuzz; @@ -401,16 +401,21 @@ static void _shoals_furniture(int margin) const coord_def p = _pick_shoals_island_distant_from(c); // Place the rune const map_def *vault = random_map_for_tag("shoal_rune"); - dgn_place_map(vault, false, true, p); + dgn_ensure_vault_placed(dgn_place_map(vault, false, true, p), + false); const int nhuts = std::min(8, int(_shoals_islands.size())); for (int i = 2; i < nhuts; ++i) { - // Place (non-rune) minivaults on the other islands + // Place (non-rune) minivaults on the other islands. We + // reuse the shoal rune huts, but do not place the rune + // again. + int tries = 5; do - vault = random_map_for_tag("shoal"); - while (!vault); - dgn_place_map(vault, false, true, _pick_shoals_island()); + vault = random_map_for_tag("shoal_rune"); + while (!vault && --tries > 0); + if (vault) + dgn_place_map(vault, false, true, _pick_shoals_island(), 0); } } else @@ -464,6 +469,334 @@ static void _shoals_deepen_edges() } } +static int _shoals_contiguous_feature_flood( + FixedArray<short, GXM, GYM> &rmap, + coord_def c, + dungeon_feature_type feat, + int nregion, + int size_limit) +{ + std::vector<coord_def> visit; + visit.push_back(c); + int npoints = 1; + for (size_t i = 0; i < visit.size() && npoints < size_limit; ++i) + { + const coord_def p(visit[i]); + rmap(p) = nregion; + + if (npoints < size_limit) + { + for (adjacent_iterator ai(p); ai && npoints < size_limit; ++ai) + { + const coord_def adj(*ai); + if (in_bounds(adj) && !rmap(adj) && grd(adj) == feat + && unforbidden(adj, MMT_VAULT)) + { + rmap(adj) = nregion; + visit.push_back(adj); + ++npoints; + } + } + } + } + return npoints; +} + +static coord_def _shoals_region_center( + FixedArray<short, GXM, GYM> &rmap, + coord_def c) +{ + const int nregion(rmap(c)); + int nseen = 0; + + double cx = 0.0, cy = 0.0; + std::vector<coord_def> visit; + visit.push_back(c); + FixedArray<bool, GXM, GYM> visited(false); + for (size_t i = 0; i < visit.size(); ++i) + { + const coord_def p(visit[i]); + visited(p) = true; + + ++nseen; + if (nseen == 1) + { + cx = p.x; + cy = p.y; + } + else + { + cx = (cx * (nseen - 1) + p.x) / nseen; + cy = (cy * (nseen - 1) + p.y) / nseen; + } + + for (adjacent_iterator ai(p); ai; ++ai) + { + const coord_def adj(*ai); + if (in_bounds(adj) && !visited(adj) && rmap(adj) == nregion) + { + visited(adj) = true; + visit.push_back(adj); + } + } + } + + const coord_def cgravity(static_cast<int>(cx), static_cast<int>(cy)); + coord_def closest_to_center; + int closest_distance = 0; + for (int i = 0, size = visit.size(); i < size; ++i) + { + const coord_def p(visit[i]); + const int dist2 = (p - cgravity).abs(); + if (closest_to_center.origin() || closest_distance > dist2) + { + closest_to_center = p; + closest_distance = dist2; + } + } + return closest_to_center; +} + +struct weighted_region +{ + int weight; + coord_def pos; + + weighted_region(int _weight, coord_def _pos) : weight(_weight), pos(_pos) + { + } +}; + +static std::vector<weighted_region> +_shoals_point_feat_cluster(dungeon_feature_type feat, + const int wanted_count, + grid_short ®ion_map) +{ + std::vector<weighted_region> regions; + int region = 1; + for (rectangle_iterator ri(1); ri; ++ri) + { + coord_def c(*ri); + if (!region_map(c) && grd(c) == feat + && unforbidden(c, MMT_VAULT)) + { + const int featcount = + _shoals_contiguous_feature_flood(region_map, + c, + feat, + region++, + wanted_count * 3 / 2); + if (featcount >= wanted_count) + regions.push_back(weighted_region(featcount, c)); + } + } + return (regions); +} + +static coord_def _shoals_pick_region( + grid_short ®ion_map, + const std::vector<weighted_region> ®ions) +{ + if (regions.empty()) + return coord_def(); + return _shoals_region_center(region_map, + regions[random2(regions.size())].pos); +} + +static void _shoals_make_plant_at(coord_def p) +{ + // [ds] Why is hostile_at() saddled with unnecessary parameters + // related to summoning? + mons_place(mgen_data::hostile_at(MONS_PLANT, "", false, 0, 0, p)); +} + +static bool _shoals_plantworthy_feat(dungeon_feature_type feat) +{ + return (feat == DNGN_SHALLOW_WATER || feat == DNGN_FLOOR); +} + +static void _shoals_make_plant_near(coord_def c, int radius, + dungeon_feature_type preferred_feat, + grid_bool *verboten) +{ + const int ntries = 5; + for (int i = 0; i < ntries; ++i) + { + const coord_def plant_place(_random_point_from(c, random2(1 + radius))); + if (!plant_place.origin() + && !monster_at(plant_place)) + { + const dungeon_feature_type feat(grd(plant_place)); + if (_shoals_plantworthy_feat(feat) + && (feat == preferred_feat || coinflip()) + && (!verboten || !(*verboten)(plant_place))) + { + _shoals_make_plant_at(plant_place); + return; + } + } + } +} + +static void _shoals_plant_cluster(coord_def c, int nplants, int radius, + dungeon_feature_type favoured_feat, + grid_bool *verboten) +{ + for (int i = 0; i < nplants; ++i) + _shoals_make_plant_near(c, radius, favoured_feat, verboten); +} + +static void _shoals_plant_supercluster(coord_def c, + dungeon_feature_type favoured_feat, + grid_bool *verboten = NULL) +{ + _shoals_plant_cluster(c, random_range(10, 25, 2), + random_range(3, 9), favoured_feat, + verboten); + + const int nadditional_clusters(std::max(0, random_range(-1, 4, 2))); + for (int i = 0; i < nadditional_clusters; ++i) + { + const coord_def satellite( + _random_point_from(c, random_range(2, 12))); + if (!satellite.origin()) + _shoals_plant_cluster(satellite, random_range(5, 23, 2), + random_range(2, 7), + favoured_feat, + verboten); + } +} + +static void _shoals_generate_water_plants(coord_def mangrove_central) +{ + if (!mangrove_central.origin()) + _shoals_plant_supercluster(mangrove_central, DNGN_SHALLOW_WATER); +} + +struct coord_dbl +{ + double x, y; + + coord_dbl(double _x, double _y) : x(_x), y(_y) { } + coord_dbl operator + (const coord_dbl &o) const + { + return coord_dbl(x + o.x, y + o.y); + } + coord_dbl &operator += (const coord_dbl &o) + { + x += o.x; + y += o.y; + return *this; + } +}; + +static coord_def _int_coord(const coord_dbl &c) +{ + return coord_def(static_cast<int>(c.x), static_cast<int>(c.y)); +} + +static std::vector<coord_def> _shoals_windshadows(grid_bool &windy) +{ + const int wind_angle_degrees = random2(360); + 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; + + std::vector<coord_dbl> wind_points; + if (wi.x > epsilon || wi.x < -epsilon) + { + for (int y = 1; y < GYM - 1; ++y) + wind_points.push_back(coord_dbl(wi.x > epsilon ? 1 : GXM - 2, y)); + } + if (wi.y > epsilon || wi.y < -epsilon) + { + for (int x = 1; x < GXM - 1; ++x) + wind_points.push_back(coord_dbl(x, wi.y > epsilon ? 1 : GYM - 2)); + } + + for (size_t i = 0; i < wind_points.size(); ++i) + { + const coord_def here(_int_coord(wind_points[i])); + windy(here) = true; + + coord_dbl next = wind_points[i] + wi; + while (_int_coord(next) == here) + next += wi; + + const coord_def nextp(_int_coord(next)); + if (in_bounds(nextp) && !windy(nextp) && !feat_is_solid(grd(nextp))) + { + windy(nextp) = true; + wind_points.push_back(next); + } + } + + // To avoid plants cropping up inside vaults, mark everything inside + // vaults as "windy". + for (rectangle_iterator ri(1); ri; ++ri) + if (!unforbidden(*ri, MMT_VAULT)) + windy(*ri) = true; + + // Now we know the places in the wind shadow: + std::vector<coord_def> wind_shadows; + for (rectangle_iterator ri(1); ri; ++ri) + { + const coord_def p(*ri); + if (!windy(p) && grd(p) == DNGN_FLOOR + && (_has_adjacent_feat(p, DNGN_STONE_WALL) + || _has_adjacent_feat(p, DNGN_ROCK_WALL))) + wind_shadows.push_back(p); + } + return wind_shadows; +} + +static void _shoals_generate_wind_sheltered_plants( + std::vector<coord_def> &places, grid_bool &windy) +{ + if (places.empty()) + return; + + const int chosen = random2(places.size()); + const coord_def spot = places[random2(places.size())]; + places.erase(places.begin() + chosen); + + _shoals_plant_supercluster(spot, DNGN_FLOOR, &windy); +} + +static void _shoals_generate_flora() +{ + // Water clusters are groups of plants clustered near the water. + // Wind clusters are groups of plants clustered in wind shadow -- + // possibly because they can grow better without being exposed to the + // strong winds of the Shoals. + // + // Yeah, the strong winds aren't there yet, but they could be! + // + const int n_water_clusters = std::max(0, random_range(-1, 6, 2)); + const int n_wind_clusters = std::max(0, random_range(-2, 2, 2)); + + if (n_water_clusters) + { + grid_short region_map(0); + std::vector<weighted_region> regions( + _shoals_point_feat_cluster(DNGN_SHALLOW_WATER, 6, region_map)); + + for (int i = 0; i < n_water_clusters; ++i) + { + const coord_def p(_shoals_pick_region(region_map, regions)); + _shoals_generate_water_plants(p); + } + } + + if (n_wind_clusters) + { + grid_bool windy(false); + std::vector<coord_def> wind_shadows = _shoals_windshadows(windy); + for (int i = 0; i < n_wind_clusters; ++i) + _shoals_generate_wind_sheltered_plants(wind_shadows, windy); + } +} + void prepare_shoals(int level_number) { dgn_Build_Method += make_stringf(" shoals+ [%d]", level_number); @@ -481,6 +814,9 @@ void prepare_shoals(int level_number) _shoals_deepen_edges(); _shoals_smooth_water(); _shoals_furniture(_shoals_margin); + + // This has to happen after placing shoal rune vault! + _shoals_generate_flora(); } // Search the map for vaults and set the terrain heights for features @@ -508,18 +844,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) @@ -528,7 +862,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; } @@ -648,6 +982,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<int>(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<coord_def> pages[2]; @@ -672,7 +1023,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 @@ -709,13 +1060,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; @@ -727,6 +1087,20 @@ void shoals_apply_tides(int turns_elapsed) turns_elapsed = turns_elapsed % TIDE_UNIT + TIDE_UNIT; _shoals_init_tide(); + + unwind_var<monsters*> 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; @@ -734,10 +1108,27 @@ 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)) + { + if (player_can_hear(mons->pos())) + { + mprf(MSGCH_SOUND, "The tide is released from %s call.", + mons->name(DESC_NOCAP_YOUR, true).c_str()); + if (you.see_cell(mons->pos())) + 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/dungeon.cc b/crawl-ref/source/dungeon.cc index 48268f2067..5cae7491fe 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" @@ -897,8 +898,8 @@ void dgn_register_place(const vault_placement &place, bool register_vault) #endif } -static bool _ensure_vault_placed(bool vault_success, - bool disable_further_vaults) +bool dgn_ensure_vault_placed(bool vault_success, + bool disable_further_vaults) { if (!vault_success) dgn_level_vetoed = true; @@ -909,9 +910,9 @@ static bool _ensure_vault_placed(bool vault_success, static bool _ensure_vault_placed_ex( bool vault_success, const map_def *vault ) { - return _ensure_vault_placed( vault_success, - (!vault->has_tag("extra") - && vault->orient == MAP_ENCOMPASS) ); + return dgn_ensure_vault_placed( vault_success, + (!vault->has_tag("extra") + && vault->orient == MAP_ENCOMPASS) ); } static coord_def _find_level_feature(int feat) @@ -1766,7 +1767,7 @@ static void _build_overflow_temples(int level_number) // find the overflow temple map, so don't veto the level. return; - if (!_ensure_vault_placed(_build_vaults(level_number, vault), false)) + if (!dgn_ensure_vault_placed(_build_vaults(level_number, vault), false)) { #ifdef DEBUG_TEMPLES mprf(MSGCH_DIAGNOSTICS, "Couldn't place overlfow temple '%s', " @@ -2245,7 +2246,7 @@ static builder_rc_type _builder_by_type(int level_number, char level_type) pandemon_level_names[which_demon]); } - _ensure_vault_placed( _build_vaults(level_number, vault), true ); + dgn_ensure_vault_placed( _build_vaults(level_number, vault), true ); } else { @@ -2322,7 +2323,7 @@ static void _portal_vault_level(int level_number) dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, vault->border_fill_type); - _ensure_vault_placed( _build_vaults(level_number, vault), true ); + dgn_ensure_vault_placed( _build_vaults(level_number, vault), true ); } else { @@ -4084,8 +4085,11 @@ void _fixup_after_vault() // clobber: If true, assumes the newly placed vault can clobber existing // items and monsters (items may be destroyed, monsters may be // teleported). -bool dgn_place_map(const map_def *mdef, bool clobber, bool make_no_exits, - const coord_def &where) +bool dgn_place_map(const map_def *mdef, + bool clobber, + bool make_no_exits, + const coord_def &where, + int rune_subst) { const dgn_colour_override_manager colour_man; @@ -4113,8 +4117,7 @@ bool dgn_place_map(const map_def *mdef, bool clobber, bool make_no_exits, } } - int rune_subst = -1; - if (mdef->has_tag_suffix("_entry")) + if (rune_subst == -1 && mdef->has_tag_suffix("_entry")) rune_subst = _dgn_find_rune_subst_tags(mdef->tags); did_map = _build_secondary_vault(you.your_level, mdef, rune_subst, clobber, make_no_exits, where); @@ -4937,6 +4940,11 @@ static void _vault_grid(vault_placement &place, int which_depth; int spec = 250; + // If rune_subst is set to 0, the rune was already placed, + // take appropriate steps. + if (place.rune_subst == 0 && vgrid == 'O') + place.num_runes++; + if (vgrid == '$') { which_class = OBJ_GOLD; @@ -6002,7 +6010,7 @@ static bool _plan_1(int level_number) ASSERT(vault); bool success = _build_vaults(level_number, vault); - _ensure_vault_placed(success, false); + dgn_ensure_vault_placed(success, false); return false; } @@ -6016,7 +6024,7 @@ static bool _plan_2(int level_number) ASSERT(vault); bool success = _build_vaults(level_number, vault); - _ensure_vault_placed(success, false); + dgn_ensure_vault_placed(success, false); return false; } @@ -6030,7 +6038,7 @@ static bool _plan_3(int level_number) ASSERT(vault); bool success = _build_vaults(level_number, vault); - _ensure_vault_placed(success, false); + dgn_ensure_vault_placed(success, false); return true; } @@ -6174,7 +6182,7 @@ static bool _plan_6(int level_number) ASSERT(vault); bool success = _build_vaults(level_number, vault); - _ensure_vault_placed(success, false); + dgn_ensure_vault_placed(success, false); // This "back door" is often one of the easier ways to get out of // pandemonium... the easiest is to use the banish spell. @@ -7619,6 +7627,47 @@ 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(static_cast<int>(radius * cos(angle)), + static_cast<int>(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 bc140c3f2e..38a9aef6d9 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 @@ -187,7 +193,8 @@ void dgn_set_colours_from_monsters(); void dgn_set_grid_colour_at(const coord_def &c, int colour); bool dgn_place_map(const map_def *map, bool clobber, bool make_no_exits, - const coord_def &pos = coord_def(-1, -1)); + const coord_def &pos = coord_def(-1, -1), + int rune_subst = -1); void level_clear_vault_memory(); void level_welcome_messages(); @@ -268,6 +275,9 @@ void dgn_replace_area(int sx, int sy, int ex, int ey, dungeon_feature_type feature, unsigned mmask = 0, bool needs_update = false); +bool dgn_ensure_vault_placed(bool vault_success, + bool disable_further_vaults); + inline int count_feature_in_box( const coord_def& p1, const coord_def& p2, dungeon_feature_type feat ) { diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 2db6cbe37d..27560aa979 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, @@ -1253,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. @@ -1818,6 +1820,12 @@ enum monster_type // (int) menv[].type MONS_TOADSTOOL, MONS_BUSH, MONS_BALLISTOMYCETE, // 200 + + // Shoals guardians + MONS_MERFOLK_IMPALER, + MONS_MERFOLK_AQUAMANCER, + MONS_MERFOLK_JAVELINEER, + //jmf: end new monsters MONS_WHITE_IMP = 220, // 220 MONS_LEMURE, @@ -2319,6 +2327,7 @@ enum mon_spellbook_type MST_HAROLD, MST_MARA, MST_MARA_FAKE, + MST_MERFOLK_AQUAMANCER, MST_TEST_SPAWNER = 200, NUM_MSTYPES, @@ -2921,6 +2930,8 @@ enum spell_type SPELL_FAKE_MARA_SUMMON, SPELL_SUMMON_RAKSHASA, SPELL_SUMMON_PLAYER_GHOST, + SPELL_PRIMAL_WAVE, + SPELL_CALL_TIDE, NUM_SPELLS }; @@ -3111,6 +3122,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/externs.h b/crawl-ref/source/externs.h index 5f4f5e7e40..1c3a962d5d 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -92,6 +92,11 @@ class KillMaster; class ghost_demon; struct glyph; +template <typename Z> inline Z sgn(Z x) +{ + return (x < 0 ? -1 : (x > 0 ? 1 : 0)); +} + struct coord_def { int x; @@ -215,6 +220,11 @@ struct coord_def return (copy *= mul); } + coord_def sgn() const + { + return coord_def(::sgn(x), ::sgn(y)); + } + int abs() const { return (x * x + y * y); diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index b2237a92cb..0df1de443c 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -1652,7 +1652,10 @@ void melee_attack::player_weapon_auto_id() int melee_attack::player_stab_weapon_bonus(int damage) { if (weapon && weapon->base_type == OBJ_WEAPONS - && (weapon->sub_type == WPN_CLUB || weapon->sub_type == WPN_SPEAR)) + && (weapon->sub_type == WPN_CLUB + || weapon->sub_type == WPN_SPEAR + || weapon->sub_type == WPN_TRIDENT + || weapon->sub_type == WPN_DEMON_TRIDENT)) { goto ok_weaps; } @@ -2158,6 +2161,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 +2175,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/files.cc b/crawl-ref/source/files.cc index 8ae63853a1..00a862a3ab 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -1677,13 +1677,6 @@ static void _save_game_base() fclose(dollf); DO_CHMOD_PRIVATE(dollFile.c_str()); } -#else - // Don't overwrite old tile dolls. - if (!file_exists(dollFile)) - { - FILE *dollf = fopen(dollFile.c_str(), "wb"); - fclose(dollf); - } #endif std::string charFile = get_savedir_filename(you.your_name, "", "sav"); diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h index 8df61d07b6..a76dc3843c 100644 --- a/crawl-ref/source/item_use.h +++ b/crawl-ref/source/item_use.h @@ -39,7 +39,7 @@ enum fire_type }; struct bolt; -struct dist; +class dist; bool armour_prompt(const std::string & mesg, int *index, operation_types oper); diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc index 9dcd774a00..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 == 215 , 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/mapdef.h b/crawl-ref/source/mapdef.h index 3ef19e0eeb..354109e8a1 100644 --- a/crawl-ref/source/mapdef.h +++ b/crawl-ref/source/mapdef.h @@ -258,7 +258,7 @@ std::string parse_weighted_str(const std::string &cspec, T &list); class map_def; class rectangle_iterator; -class keyed_mapspec; +struct keyed_mapspec; class map_lines { public: diff --git a/crawl-ref/source/message.h b/crawl-ref/source/message.h index 7e878fbc28..8af1f17d7e 100644 --- a/crawl-ref/source/message.h +++ b/crawl-ref/source/message.h @@ -87,7 +87,7 @@ namespace msg { public: mpr_stream_buf(msg_channel_type chan); - virtual ~mpr_stream_buf() {}; + virtual ~mpr_stream_buf() {} void set_param(int p); void set_muted(bool m); protected: diff --git a/crawl-ref/source/mgen_enum.h b/crawl-ref/source/mgen_enum.h index 0927e9ae65..d7aee96df8 100644 --- a/crawl-ref/source/mgen_enum.h +++ b/crawl-ref/source/mgen_enum.h @@ -57,6 +57,9 @@ enum band_type BAND_KHUFU, BAND_GOLDEN_EYE, BAND_PIKEL, + BAND_MERFOLK_AQUAMANCER, + BAND_MERFOLK_IMPALER, + BAND_MERFOLK_JAVELINEER, NUM_BANDS // always last }; @@ -99,4 +102,3 @@ enum mgen_flag_type }; #endif - diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 2cc226f127..4bac2e7ff0 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -2696,11 +2696,11 @@ bool scramble(void) return (true); } -bool go_berserk(bool intentional, bool no_clarity) +bool go_berserk(bool intentional) { ASSERT(!crawl_state.arena); - if (!you.can_go_berserk(intentional, no_clarity)) + if (!you.can_go_berserk(intentional)) return (false); if (Tutorial.tutorial_left) diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 3c5cee6cbf..9b0eaa47a7 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -10,10 +10,10 @@ #include "externs.h" struct bolt; -struct dist; +class dist; struct activity_interrupt_data; -bool go_berserk(bool intentional, bool no_clarity = false); +bool go_berserk(bool intentional); void search_around(bool only_adjacent = false); void down_stairs(int old_level, dungeon_feature_type force_stair = DNGN_UNSEEN, diff --git a/crawl-ref/source/mon-abil.cc b/crawl-ref/source/mon-abil.cc index b64ffcf9b4..1c98be0c34 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-abil.h b/crawl-ref/source/mon-abil.h index 47aee1641a..845b114c49 100644 --- a/crawl-ref/source/mon-abil.h +++ b/crawl-ref/source/mon-abil.h @@ -8,7 +8,7 @@ #define MONABIL_H class monsters; -class bolt; +struct bolt; bool mon_special_ability(monsters *monster, bolt & beem); void mon_nearby_ability(monsters *monster); diff --git a/crawl-ref/source/mon-act.cc b/crawl-ref/source/mon-act.cc index 16c025e217..31d1c990a7 100644 --- a/crawl-ref/source/mon-act.cc +++ b/crawl-ref/source/mon-act.cc @@ -332,6 +332,24 @@ static void _maybe_set_patrol_route(monsters *monster) } } +// Keep kraken tentacles from wandering too far away from the boss monster. +static void _kraken_tentacle_movement_clamp(monsters *tentacle) +{ + if (tentacle->type != MONS_KRAKEN_TENTACLE) + return; + + const int kraken_idx = tentacle->number; + ASSERT(!invalid_monster_index(kraken_idx)); + + monsters *kraken = &menv[kraken_idx]; + const int distance_to_head = + grid_distance(tentacle->pos(), kraken->pos()); + // Beyond max distance, the only move the tentacle can make is + // back towards the head. + if (distance_to_head >= KRAKEN_TENTACLE_RANGE) + mmov = (kraken->pos() - tentacle->pos()).sgn(); +} + //--------------------------------------------------------------- // // handle_movement @@ -378,8 +396,7 @@ static void _handle_movement(monsters *monster) delta = monster->target - monster->pos(); // Move the monster. - mmov.x = (delta.x > 0) ? 1 : ((delta.x < 0) ? -1 : 0); - mmov.y = (delta.y > 0) ? 1 : ((delta.y < 0) ? -1 : 0); + mmov = delta.sgn(); if (mons_is_fleeing(monster) && monster->travel_target != MTRAV_WALL && (!monster->friendly() @@ -1122,7 +1139,12 @@ static bool _mons_throw(struct monsters *monster, struct bolt &pbolt, { const mon_attack_def attk = mons_attack_spec(monster, 0); if (attk.type == AT_SHOOT) - ammoDamBonus += random2avg(attk.damage, 2); + { + if (projected == LRET_THROWN && wepClass == OBJ_MISSILES) + ammoHitBonus += random2avg(attk.damage, 2); + else + ammoDamBonus += random2avg(attk.damage, 2); + } } if (projected == LRET_THROWN) @@ -1849,6 +1871,7 @@ static void _handle_monster_move(monsters *monster) { // Calculates mmov based on monster target. _handle_movement(monster); + _kraken_tentacle_movement_clamp(monster); if (mons_is_confused(monster) || monster->type == MONS_AIR_ELEMENTAL diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index 7bbbd77862..fb9b5092d4 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 ); @@ -825,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) @@ -840,6 +855,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; @@ -1662,6 +1679,22 @@ 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: + if (player_in_branch(BRANCH_SHOALS)) + { + 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; + if (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) @@ -1821,7 +1854,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-cast.h b/crawl-ref/source/mon-cast.h index 22629b4f9a..45e87e73c4 100644 --- a/crawl-ref/source/mon-cast.h +++ b/crawl-ref/source/mon-cast.h @@ -10,7 +10,7 @@ #include "enum.h" class monsters; -class bolt; +struct bolt; void init_mons_spells(); bool is_valid_mon_spell(spell_type spell); diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index 5d42867f4d..5bbdd504c7 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -1107,7 +1107,7 @@ static monsterentry mondata[] = { // merfolk ('m') { - MONS_MERFOLK, 'm', LIGHTBLUE, "merfolk", + MONS_MERFOLK, 'm', BLUE, "merfolk", M_WARM_BLOOD | M_SPEAKS, MR_NO_FLAGS, 500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -3, @@ -1119,12 +1119,49 @@ static monsterentry mondata[] = { }, { + MONS_MERFOLK_IMPALER, 'm', LIGHTBLUE, "merfolk impaler", + M_WARM_BLOOD, + MR_NO_FLAGS, + 500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -3, + { {AT_HIT, AF_PLAIN, 34}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { 16, 4, 3, 0 }, + // Impalers prefer light armour, and are dodging experts. + 0, 23, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, + I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, ATTACK_ENERGY(6), + MONUSE_MAGIC_ITEMS, MONEAT_NOTHING, SIZE_MEDIUM +}, + +{ + MONS_MERFOLK_JAVELINEER, 'm', LIGHTGREY, "merfolk javelineer", + M_WARM_BLOOD | M_ARCHER, + MR_NO_FLAGS, + 500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -4, + { {AT_SHOOT, AF_PLAIN, 16}, {AT_HIT, AF_PLAIN, 20}, AT_NO_ATK, AT_NO_ATK }, + { 15, 4, 2, 0 }, + 0, 15, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, + I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, MISSILE_ENERGY(8), + MONUSE_MAGIC_ITEMS, MONEAT_NOTHING, SIZE_MEDIUM +}, + +{ + MONS_MERFOLK_AQUAMANCER, 'm', GREEN, "merfolk aquamancer", + M_WARM_BLOOD | M_SPELLCASTER | M_ACTUAL_SPELLS, + MR_NO_FLAGS, + 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_AQUAMANCER, CE_CONTAMINATED, Z_SMALL, S_SHOUT, + I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, DEFAULT_ENERGY, + MONUSE_MAGIC_ITEMS, MONEAT_NOTHING, SIZE_MEDIUM +}, + +{ MONS_MERMAID, 'm', CYAN, "mermaid", M_SPELLCASTER | M_WARM_BLOOD | M_SPEAKS, MR_NO_FLAGS, 500, 10, MONS_MERMAID, MONS_MERMAID, MH_NATURAL, -5, { {AT_HIT, AF_PLAIN, 10}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, - { 8, 2, 3, 0 }, + { 8, 3, 3, 0 }, 4, 12, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM @@ -1136,7 +1173,7 @@ static monsterentry mondata[] = { MR_NO_FLAGS, 500, 12, MONS_MERMAID, MONS_SIREN, MH_NATURAL, -7, { {AT_HIT, AF_PLAIN, 10}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, - { 8, 2, 3, 0 }, + { 13, 5, 3, 0 }, 4, 12, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_NORMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM @@ -1443,7 +1480,7 @@ static monsterentry mondata[] = { }, { - MONS_SCORPION, 's', LIGHTGREY, "scorpion", + MONS_SCORPION, 's', YELLOW, "scorpion", M_NO_SKELETON, MR_VUL_POISON, 500, 10, MONS_SCORPION, MONS_SCORPION, MH_NATURAL, -3, @@ -2419,7 +2456,7 @@ static monsterentry mondata[] = { M_WARM_BLOOD | M_BATTY, MR_RES_POISON, 1000, 12, MONS_HARPY, MONS_HARPY, MH_NATURAL, -3, - { {AT_CLAW, AF_PLAIN, 10}, {AT_CLAW, AF_STEAL_FOOD, 8}, + { {AT_CLAW, AF_PLAIN, 19}, {AT_CLAW, AF_STEAL_FOOD, 14}, AT_NO_ATK, AT_NO_ATK }, { 7, 3, 5, 0 }, 2, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SCREECH, @@ -3303,7 +3340,7 @@ static monsterentry mondata[] = { M_COLD_BLOOD | M_SPELLCASTER, MR_NO_FLAGS, 1500, 20, MONS_KRAKEN, MONS_KRAKEN, MH_NATURAL, -3, - { {AT_BITE, AF_PLAIN, 15}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { {AT_BITE, AF_PLAIN, 65}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 20, 10, 10, 0 }, 20, 0, MST_KRAKEN, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, @@ -3315,10 +3352,10 @@ static monsterentry mondata[] = { M_COLD_BLOOD | M_NO_EXP_GAIN, MR_RES_ASPHYX, 0, 10, MONS_KRAKEN_TENTACLE, MONS_KRAKEN_TENTACLE, MH_NATURAL, MAG_IMMUNE, - { {AT_TENTACLE_SLAP, AF_PLAIN, 15}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, - { 5, 3, 5, 0 }, + { {AT_TENTACLE_SLAP, AF_PLAIN, 29}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { 12, 3, 2, 0 }, 5, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, - I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, + I_ANIMAL, HT_AMPHIBIOUS_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LARGE }, @@ -4584,12 +4621,12 @@ static monsterentry mondata[] = { }, { - MONS_ILSUIW, 'm', GREEN, "Ilsuiw", + MONS_ILSUIW, 'm', LIGHTGREEN, "Ilsuiw", M_UNIQUE | M_WARM_BLOOD | M_SPELLCASTER | M_ACTUAL_SPELLS | M_SPEAKS, - MR_RES_POISON | MR_RES_COLD, + MR_NO_FLAGS, 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 01edc35951..640a875051 100644 --- a/crawl-ref/source/mon-gear.cc +++ b/crawl-ref/source/mon-gear.cc @@ -12,11 +12,14 @@ #include "mon-gear.h" #include "artefact.h" +#include "colour.h" #include "dungeon.h" #include "env.h" #include "itemprop.h" #include "items.h" #include "makeitem.h" +#include "mgen_enum.h" +#include "mon-place.h" #include "mon-util.h" #include "random.h" #include "spl-book.h" @@ -582,7 +585,64 @@ 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; + item.sub_type = random_choose_weighted(100, WPN_TRIDENT, + 15, WPN_DEMON_TRIDENT, + 0); + if (coinflip()) + level = MAKE_GOOD_ITEM; + else if (coinflip()) + { + // Per dpeg request :) + item.special = SPWPN_REACHING; + item.plus = random_range(-1, 6, 2); + item.plus2 = random_range(-1, 5, 2); + force_item = true; + } + break; + + + case MONS_MERFOLK_AQUAMANCER: + 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_MERFOLK_JAVELINEER: + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_WEAPONS; + item.sub_type = WPN_SPEAR; + if (!one_chance_in(3)) + level = MAKE_GOOD_ITEM; + break; + case MONS_MERFOLK: + if (active_monster_band == BAND_MERFOLK_IMPALER) + { + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_WEAPONS; + item.sub_type = random_choose_weighted(10, WPN_SPEAR, + 10, WPN_TRIDENT, + 5, WPN_HALBERD, + 5, WPN_GLAIVE, + 0); + break; + } if (one_chance_in(3)) { item_race = MAKE_ITEM_NO_RACE; @@ -1028,8 +1088,18 @@ static void _give_ammo(monsters *mon, int level, qty = random_range(4, 7); break; + case MONS_MERFOLK_JAVELINEER: + weap_class = OBJ_MISSILES; + weap_type = MI_JAVELIN; + item_race = MAKE_ITEM_NO_RACE; + qty = random_range(9, 23, 2); + if (one_chance_in(3)) + level = MAKE_GOOD_ITEM; + break; + case MONS_MERFOLK: - if (!one_chance_in(3)) + if (!one_chance_in(3) + || active_monster_band == BAND_MERFOLK_JAVELINEER) { item_race = MAKE_ITEM_NO_RACE; if (coinflip()) @@ -1043,6 +1113,8 @@ static void _give_ammo(monsters *mon, int level, weap_type = MI_JAVELIN; qty = 3 + random2(6); } + if (active_monster_band == BAND_MERFOLK_JAVELINEER) + break; } if (one_chance_in(6) && !mons_summoned) { @@ -1342,6 +1414,22 @@ void give_armour(monsters *mon, int level) break; } + case MONS_MERFOLK_IMPALER: + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_ARMOUR; + item.sub_type = random_choose_weighted(100, ARM_ROBE, + 60, ARM_LEATHER_ARMOUR, + 5, ARM_TROLL_LEATHER_ARMOUR, + 5, ARM_STEAM_DRAGON_ARMOUR, + 0); + break; + + case MONS_MERFOLK_JAVELINEER: + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_ARMOUR; + item.sub_type = ARM_LEATHER_ARMOUR; + break; + case MONS_ANGEL: case MONS_SIGMUND: case MONS_WIGHT: @@ -1448,6 +1536,7 @@ void give_armour(monsters *mon, int level) case MONS_WIZARD: case MONS_ILSUIW: case MONS_MARA: + case MONS_MERFOLK_AQUAMANCER: 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 84d540d8c8..7692517c68 100644 --- a/crawl-ref/source/mon-pick.cc +++ b/crawl-ref/source/mon-pick.cc @@ -1699,15 +1699,12 @@ int mons_shoals_level(int mcls) switch (mcls) { case MONS_BUTTERFLY: - case MONS_PLANT: case MONS_GIANT_BAT: break; case MONS_MERFOLK: case MONS_MERMAID: case MONS_CENTAUR: - case MONS_ETTIN: - case MONS_SHEEP: case MONS_HIPPOGRIFF: mlev++; break; @@ -1721,6 +1718,9 @@ int mons_shoals_level(int mcls) case MONS_CYCLOPS: // will have a sheep band case MONS_SIREN: case MONS_HARPY: + case MONS_MERFOLK_IMPALER: + case MONS_MERFOLK_AQUAMANCER: + case MONS_MERFOLK_JAVELINEER: mlev += 3; break; @@ -1745,11 +1745,6 @@ int mons_shoals_rare(int mcls) { switch (mcls) { - case MONS_PLANT: - return 150; - - case MONS_ETTIN: - case MONS_SHEEP: case MONS_MERFOLK: return 50; @@ -1760,10 +1755,13 @@ int mons_shoals_rare(int mcls) case MONS_GIANT_BAT: case MONS_BUTTERFLY: case MONS_CENTAUR: + case MONS_MERFOLK_IMPALER: + case MONS_MERFOLK_JAVELINEER: return 35; case MONS_SIREN: case MONS_YAKTAUR: + case MONS_MERFOLK_AQUAMANCER: return 25; case MONS_CYCLOPS: diff --git a/crawl-ref/source/mon-place.cc b/crawl-ref/source/mon-place.cc index 27b6c8fe20..dc5c47ba00 100644 --- a/crawl-ref/source/mon-place.cc +++ b/crawl-ref/source/mon-place.cc @@ -42,6 +42,8 @@ #include "travel.h" #include "view.h" +band_type active_monster_band = BAND_NO_BAND; + static std::vector<int> vault_mon_types; static std::vector<int> vault_mon_bases; static std::vector<int> vault_mon_weights; @@ -598,7 +600,7 @@ static monster_type _resolve_monster_type(monster_type mon_type, mon_type = MONS_DANCING_WEAPON; else { - if (you.level_type == LEVEL_PORTAL_VAULT + if (you.level_type == LEVEL_PORTAL_VAULT && vault_mon_types.size() > 0) { int i = choose_random_weighted(vault_mon_weights.begin(), @@ -636,7 +638,7 @@ static monster_type _resolve_monster_type(monster_type mon_type, } else if (you.level_type == LEVEL_PORTAL_VAULT) { - // XXX: We don't have a random monster list here, so pick one + // XXX: We don't have a random monster list here, so pick one // from where we were. place.level_type = LEVEL_DUNGEON; *lev_mons = place.absdepth(); @@ -768,13 +770,14 @@ int place_monster(mgen_data mg, bool force_pos) mg.pos, mg.map_mask, &stair_type, &mg.power); - if (mg.cls == MONS_NO_MONSTER) + if (mg.cls == MONS_NO_MONSTER || mg.cls == MONS_PROGRAM_BUG) return (-1); // (3) Decide on banding (good lord!) int band_size = 1; bool leader = false; monster_type band_monsters[BIG_BAND]; // band monster types + band_type band = BAND_NO_BAND; band_monsters[0] = mg.cls; // The (very) ugly thing band colour. @@ -785,10 +788,8 @@ int place_monster(mgen_data mg, bool force_pos) #ifdef DEBUG_MON_CREATION mpr("Choose band members...", MSGCH_DIAGNOSTICS); #endif - const band_type band = _choose_band(mg.cls, mg.power, band_size, - leader); + band = _choose_band(mg.cls, mg.power, band_size, leader); band_size++; - for (int i = 1; i < band_size; ++i) { band_monsters[i] = _band_member(band, mg.power); @@ -1006,6 +1007,7 @@ int place_monster(mgen_data mg, bool force_pos) band_template.flags |= MG_BAND_MINION; } + unwind_var<band_type> current_band(active_monster_band, band); // (5) For each band monster, loop call to place_monster_aux(). for (int i = 1; i < band_size; i++) { @@ -2110,6 +2112,23 @@ static band_type _choose_band(int mon_type, int power, int &band_size, band_size = 4; break; + case MONS_MERFOLK_AQUAMANCER: + natural_leader = true; + band = BAND_MERFOLK_AQUAMANCER; + band_size = random_range(3, 6); + break; + + case MONS_MERFOLK_JAVELINEER: + natural_leader = true; + band = BAND_MERFOLK_JAVELINEER; + band_size = random_range(3, 5); + break; + + case MONS_MERFOLK_IMPALER: + natural_leader = true; + band = BAND_MERFOLK_IMPALER; + band_size = random_range(3, 5); + break; } // end switch if (band != BAND_NO_BAND && band_size == 0) @@ -2401,7 +2420,12 @@ static monster_type _band_member(band_type band, int power) break; } case BAND_ILSUIW: - mon_type = coinflip()? MONS_MERFOLK : MONS_MERMAID; + mon_type = static_cast<monster_type>( + random_choose_weighted(30, MONS_MERMAID, + 15, MONS_MERFOLK, + 10, MONS_MERFOLK_JAVELINEER, + 10, MONS_MERFOLK_IMPALER, + 0)); break; case BAND_AZRAEL: @@ -2424,6 +2448,18 @@ static monster_type _band_member(band_type band, int power) mon_type = MONS_SLAVE; break; + case BAND_MERFOLK_AQUAMANCER: + mon_type = static_cast<monster_type>( + random_choose_weighted(8, MONS_MERFOLK, + 10, MONS_ICE_BEAST, + 0)); + break; + + case BAND_MERFOLK_IMPALER: + case BAND_MERFOLK_JAVELINEER: + mon_type = MONS_MERFOLK; + break; + default: break; } diff --git a/crawl-ref/source/mon-place.h b/crawl-ref/source/mon-place.h index 3512639462..28dc5e413f 100644 --- a/crawl-ref/source/mon-place.h +++ b/crawl-ref/source/mon-place.h @@ -102,4 +102,7 @@ monsters* get_free_monster(); bool can_place_on_trap(int mon_type, trap_type trap); bool mons_airborne(int mcls, int flies, bool paralysed); +// Active monster band may influence gear generation on band followers. +extern band_type active_monster_band; + #endif // MONPLACE_H diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h index 526d37f378..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, @@ -1319,6 +1319,17 @@ } }, + { MST_MERFOLK_AQUAMANCER, + { + SPELL_PRIMAL_WAVE, + SPELL_BOLT_OF_COLD, + SPELL_THROW_ICICLE, + SPELL_NO_SPELL, + SPELL_NO_SPELL, + SPELL_BLINK + } + }, + { MST_TEST_SPAWNER, { SPELL_SHADOW_CREATURES, diff --git a/crawl-ref/source/mon-stuff.cc b/crawl-ref/source/mon-stuff.cc index 7a85194ea2..ca7b9cf12c 100644 --- a/crawl-ref/source/mon-stuff.cc +++ b/crawl-ref/source/mon-stuff.cc @@ -1323,7 +1323,7 @@ static int _tentacle_too_far(monsters *head, monsters *tentacle) // If this ever changes, we'd need to check if the head and tentacle // are still in the same pool. // XXX: Actually, using Fedhas's Sunlight power you can separate pools... - return grid_distance(head->pos(), tentacle->pos()) > LOS_RADIUS; + return grid_distance(head->pos(), tentacle->pos()) > KRAKEN_TENTACLE_RANGE; } void mons_relocated(monsters *monster) @@ -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 f9d18c97d9..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" @@ -3308,6 +3309,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; @@ -4290,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; @@ -5550,7 +5570,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 +5605,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())) @@ -6014,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 524c48b4d4..b93ed65571 100644 --- a/crawl-ref/source/monster.h +++ b/crawl-ref/source/monster.h @@ -3,6 +3,9 @@ #include "actor.h" +const int KRAKEN_TENTACLE_RANGE = 3; +#define TIDE_CALL_TURN "tide-call-turn" + class mon_enchant { public: @@ -126,7 +129,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); @@ -315,6 +320,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..e490908d1b 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -5980,8 +5980,10 @@ bool player::can_go_berserk() const return (can_go_berserk(false)); } -bool player::can_go_berserk(bool verbose, bool no_clarity) const +bool player::can_go_berserk(bool intentional) const { + const bool verbose = intentional; + if (berserk()) { if (verbose) @@ -6016,7 +6018,7 @@ bool player::can_go_berserk(bool verbose, bool no_clarity) const return (false); } - if (!no_clarity && player_mental_clarity(true)) + if (!intentional && player_mental_clarity(true)) { if (verbose) { @@ -6452,6 +6454,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 +7057,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..1e5c58d47a 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -414,7 +414,7 @@ public: void attacking(actor *other); bool can_go_berserk() const; - bool can_go_berserk(bool verbose, bool no_clarity = false) const; + bool can_go_berserk(bool intentional) const; void go_berserk(bool intentional); bool berserk() const; bool can_mutate() const; @@ -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/rltiles/dc-mon.txt b/crawl-ref/source/rltiles/dc-mon.txt index 208a27627e..e539d95c86 100644 --- a/crawl-ref/source/rltiles/dc-mon.txt +++ b/crawl-ref/source/rltiles/dc-mon.txt @@ -291,7 +291,36 @@ roxanne MONS_ROXANNE ice_statue MONS_ICE_STATUE silver_statue MONS_SILVER_STATUE orange_crystal_statue MONS_ORANGE_CRYSTAL_STATUE -statue_archer MONS_ARCHER_STATUE + +%back statue_base +overlay_axe MONS_STATUE_AXE +overlay_bow MONS_STATUE_ARCHER +overlay_crossbow MONS_STATUE_CROSSBOW +overlay_mace MONS_STATUE_MACE +overlay_mage_hat MONS_STATUE_MAGE +overlay_scythe MONS_STATUE_SCYTHE +overlay_sword MONS_STATUE_SWORD +%back none + +%back light_vine_statue_base +overlay_axe MONS_LIGHT_VINE_STATUE_AXE +overlay_bow MONS_LIGHT_VINE_STATUE_ARCHER +overlay_crossbow MONS_LIGHT_VINE_STATUE_CROSSBOW +overlay_mace MONS_LIGHT_VINE_STATUE_MACE +overlay_mage MONS_LIGHT_VINE_STATUE_MAGE +overlay_scythe MONS_LIGHT_VINE_STATUE_SCYTHE +overlay_sword MONS_LIGHT_VINE_STATUE_SWORD +%back none + +%back dark_vine_statue_base +overlay_axe MONS_DARK_VINE_STATUE_AXE +overlay_bow MONS_DARK_VINE_STATUE_ARCHER +overlay_crossbow MONS_DARK_VINE_STATUE_CROSSBOW +overlay_mace MONS_DARK_VINE_STATUE_MACE +overlay_mage MONS_DARK_VINE_STATUE_MAGE +overlay_scythe MONS_DARK_VINE_STATUE_SCYTHE +overlay_sword MONS_DARK_VINE_STATUE_SWORD +%back none ## Gargoyles ('9') %sdir dc-mon @@ -395,7 +424,9 @@ skeletal_dragon MONS_SKELETAL_DRAGON serpent_of_hell MONS_SERPENT_OF_HELL ## Eyes ('G') +%sdir dc-mon/fungi_plants giant_spore MONS_GIANT_SPORE +%sdir dc-mon giant_eyeball MONS_GIANT_EYEBALL eye_of_draining MONS_EYE_OF_DRAINING giant_orange_brain MONS_GIANT_ORANGE_BRAIN diff --git a/crawl-ref/source/rltiles/dc-mon/giant_spore.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/giant_spore.png Binary files differindex 77c5948737..77c5948737 100644 --- a/crawl-ref/source/rltiles/dc-mon/giant_spore.png +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/giant_spore.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/dark_vine_statue_base.png b/crawl-ref/source/rltiles/dc-mon/statues/dark_vine_statue_base.png Binary files differnew file mode 100644 index 0000000000..5d2a5a1283 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/dark_vine_statue_base.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/light_vine_statue_base.png b/crawl-ref/source/rltiles/dc-mon/statues/light_vine_statue_base.png Binary files differnew file mode 100644 index 0000000000..2d0a4e5ee1 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/light_vine_statue_base.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.png Binary files differnew file mode 100644 index 0000000000..51138c7992 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.png Binary files differnew file mode 100644 index 0000000000..a674ae87dc --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.png Binary files differnew file mode 100644 index 0000000000..d65db57370 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.png Binary files differnew file mode 100644 index 0000000000..7555ff46c1 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.png Binary files differnew file mode 100644 index 0000000000..aa26b5b556 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage_hat.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage_hat.png Binary files differnew file mode 100644 index 0000000000..d9960f0db0 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage_hat.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.png Binary files differnew file mode 100644 index 0000000000..bb2f7df127 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.png Binary files differnew file mode 100644 index 0000000000..6b6e8050de --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.png diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_archer.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_archer.png Binary files differdeleted file mode 100644 index 0358366f7c..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_archer.png +++ /dev/null diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_axe.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_axe.png Binary files differdeleted file mode 100644 index 795928ef22..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_axe.png +++ /dev/null diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.png Binary files differdeleted file mode 100644 index 80fa73380d..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.png +++ /dev/null diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_mace.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_mace.png Binary files differdeleted file mode 100644 index c74efdd79e..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_mace.png +++ /dev/null diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_mage.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_mage.png Binary files differdeleted file mode 100644 index bc709937e2..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_mage.png +++ /dev/null diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.png Binary files differdeleted file mode 100644 index 19ab6410ce..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.png +++ /dev/null diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_sword.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_sword.png Binary files differdeleted file mode 100644 index 6a8c005184..0000000000 --- a/crawl-ref/source/rltiles/dc-mon/statues/statue_sword.png +++ /dev/null diff --git a/crawl-ref/source/shopping.cc b/crawl-ref/source/shopping.cc index 8cb0dfa4be..2b5d582316 100644 --- a/crawl-ref/source/shopping.cc +++ b/crawl-ref/source/shopping.cc @@ -2430,7 +2430,7 @@ void ShoppingList::move_things(const coord_def &_src, const coord_def &_dst) void ShoppingList::forget_pos(const level_pos &pos) { - if (crawl_state.map_stat_gen || crawl_state.test) + if (!crawl_state.need_save) // Shopping list is unitialized and uneeded. return; diff --git a/crawl-ref/source/spells2.h b/crawl-ref/source/spells2.h index 64349079de..83ba485321 100644 --- a/crawl-ref/source/spells2.h +++ b/crawl-ref/source/spells2.h @@ -10,7 +10,7 @@ #include "enum.h" #include "itemprop-enum.h" -struct dist; +class dist; bool brand_weapon(brand_type which_brand, int power); bool brand_ammo(special_missile_type which_brand); @@ -19,7 +19,7 @@ bool burn_freeze(int pow, beam_type flavour, monsters *monster); void corpse_rot(); -struct dist; +class dist; bool vampiric_drain(int pow, const dist &vmove); int detect_creatures(int pow, bool telepathic = false); int detect_items(int pow); diff --git a/crawl-ref/source/spells3.h b/crawl-ref/source/spells3.h index b41a35ae3a..30252963e5 100644 --- a/crawl-ref/source/spells3.h +++ b/crawl-ref/source/spells3.h @@ -10,7 +10,7 @@ #include "itemprop-enum.h" -struct dist; +class dist; struct bolt; bool allow_control_teleport(bool quiet = false); diff --git a/crawl-ref/source/spells4.h b/crawl-ref/source/spells4.h index cd977e3d01..3f6bbbd3ac 100644 --- a/crawl-ref/source/spells4.h +++ b/crawl-ref/source/spells4.h @@ -10,7 +10,7 @@ #include "externs.h" -struct dist; +class dist; struct bolt; bool backlight_monsters(coord_def where, int pow, int garbage); 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 61b544f635..75f92737b3 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -2642,6 +2642,32 @@ }, { + SPELL_PRIMAL_WAVE, "Primal Wave", + SPTYP_CONJURATION | SPTYP_ICE, + SPFLAG_DIR_OR_TARGET, + 6, + 200, + 7, 7, + 0, + NULL, + true, + false +}, + +{ + SPELL_CALL_TIDE, "Call Tide", + SPTYP_TRANSLOCATION, + SPFLAG_MONSTER, + 7, + 0, + -1, -1, + 0, + NULL, + false, + 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: diff --git a/crawl-ref/source/spl-util.h b/crawl-ref/source/spl-util.h index eb44f4db07..a1738c17a9 100644 --- a/crawl-ref/source/spl-util.h +++ b/crawl-ref/source/spl-util.h @@ -32,7 +32,7 @@ enum spschool_flag_type }; struct bolt; -struct dist; +class dist; bool is_valid_spell(spell_type spell); void init_spell_descs(void); diff --git a/crawl-ref/source/store.cc b/crawl-ref/source/store.cc index df8f5b345c..a4afaece9f 100644 --- a/crawl-ref/source/store.cc +++ b/crawl-ref/source/store.cc @@ -951,7 +951,6 @@ const CrawlStoreValue &CrawlStoreValue::operator ///////////////////// // Typecast operators -#ifdef TARGET_COMPILER_VC CrawlStoreValue::operator bool&() { return get_bool(); } CrawlStoreValue::operator char&() { return get_byte(); } CrawlStoreValue::operator short&() { return get_short(); } @@ -964,79 +963,8 @@ CrawlStoreValue::operator CrawlVector&() { return get_vector(); } CrawlStoreValue::operator item_def&() { return get_item(); } CrawlStoreValue::operator level_id&() { return get_level_id(); } CrawlStoreValue::operator level_pos&() { return get_level_pos(); } -CrawlStoreValue::operator monster&() { return get_monster(); } -CrawlStoreValue::operator dlua_chunk&() { return get_dlua_chunk(); } -#else -&CrawlStoreValue::operator bool() -{ - return get_bool(); -} - -&CrawlStoreValue::operator char() -{ - return get_byte(); -} - -&CrawlStoreValue::operator short() -{ - return get_short(); -} - -&CrawlStoreValue::operator float() -{ - return get_float(); -} - -&CrawlStoreValue::operator long() -{ - return get_long(); -} - -&CrawlStoreValue::operator std::string() -{ - return get_string(); -} - -&CrawlStoreValue::operator coord_def() -{ - return get_coord(); -} - -&CrawlStoreValue::operator CrawlHashTable() -{ - return get_table(); -} - -&CrawlStoreValue::operator CrawlVector() -{ - return get_vector(); -} - -&CrawlStoreValue::operator item_def() -{ - return get_item(); -} - -&CrawlStoreValue::operator level_id() -{ - return get_level_id(); -} - -&CrawlStoreValue::operator level_pos() -{ - return get_level_pos(); -} - -&CrawlStoreValue::operator monsters() -{ - return get_monster(); -} - -&CrawlStoreValue::operator dlua_chunk() -{ - return get_lua(); -} -#endif +CrawlStoreValue::operator monsters&() { return get_monster(); } +CrawlStoreValue::operator dlua_chunk&() { return get_lua(); } /////////////////////////// // Const typecast operators diff --git a/crawl-ref/source/store.h b/crawl-ref/source/store.h index 79b9454c97..8a54189cae 100644 --- a/crawl-ref/source/store.h +++ b/crawl-ref/source/store.h @@ -184,7 +184,6 @@ public: const CrawlStoreValue &operator [] (const vec_size &index) const; // Typecast operators -#ifdef TARGET_COMPILER_VC operator bool&(); operator char&(); operator short&(); @@ -199,22 +198,6 @@ public: operator level_pos&(); operator monsters&(); operator dlua_chunk&(); -#else - &operator bool(); - &operator char(); - &operator short(); - &operator long(); - &operator float(); - &operator std::string(); - &operator coord_def(); - &operator CrawlHashTable(); - &operator CrawlVector(); - &operator item_def(); - &operator level_id(); - &operator level_pos(); - &operator monsters(); - &operator dlua_chunk(); -#endif operator bool() const; operator char() const; diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h index 89757eb99d..684df4175d 100644 --- a/crawl-ref/source/stuff.h +++ b/crawl-ref/source/stuff.h @@ -83,11 +83,6 @@ inline bool testbits(unsigned long flags, unsigned long test) return ((flags & test) == test); } -template <typename Z> inline Z sgn(Z x) -{ - return (x < 0 ? -1 : (x > 0 ? 1 : 0)); -} - bool is_trap_square(dungeon_feature_type grid); void zap_los_monsters(bool items_also); 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..33a525cba2 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -25,7 +25,7 @@ bool magic_mapping(int map_radius, int proportion, bool suppress_msg, coord_def origin = coord_def(-1, -1)); void reautomap_level(); -class level_pos; +struct level_pos; void show_map( level_pos &spec_place, bool travel_mode, bool allow_esc = false ); bool is_feature(int feature, const coord_def& where); @@ -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); diff --git a/crawl-ref/source/wiz-mon.cc b/crawl-ref/source/wiz-mon.cc index 632b716b54..e237401b5d 100644 --- a/crawl-ref/source/wiz-mon.cc +++ b/crawl-ref/source/wiz-mon.cc @@ -228,6 +228,11 @@ static bool _sort_monster_list(int a, int b) const monsters* m1 = &menv[a]; const monsters* m2 = &menv[b]; + if (m1->alive() != m2->alive()) + return m1->alive(); + else if (!m1->alive()) + return a < b; + if (m1->type == m2->type) { if (!m1->alive() || !m2->alive()) @@ -263,8 +268,16 @@ void debug_list_monsters() std::string prev_name = ""; int count = 0; - for (monster_iterator mi; mi; ++mi) + for (int i = 0; i < MAX_MONSTERS; ++i) { + const int idx = mon_nums[i]; + if (invalid_monster_index(idx)) + continue; + + const monsters *mi(&menv[idx]); + if (!mi->alive()) + continue; + std::string name = mi->name(DESC_PLAIN, true); if (prev_name != name && count > 0) @@ -282,7 +295,7 @@ void debug_list_monsters() count++; prev_name = name; - int exp = exper_value(*mi); + int exp = exper_value(mi); total_exp += exp; if ((mi->flags & (MF_WAS_NEUTRAL | MF_NO_REWARD)) |