diff options
Diffstat (limited to 'crawl-ref')
154 files changed, 3972 insertions, 938 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.cc b/crawl-ref/source/actor.cc index 3cdeeb9c50..62a8d87cf6 100644 --- a/crawl-ref/source/actor.cc +++ b/crawl-ref/source/actor.cc @@ -161,9 +161,9 @@ void actor::shield_block_succeeded(actor *foe) } } -int actor::body_weight() const +int actor::body_weight(bool base) const { - switch (body_size(PSIZE_BODY)) + switch (body_size(PSIZE_BODY, base)) { case SIZE_TINY: return (150); diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h index b29e826307..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; } @@ -63,7 +67,7 @@ public: virtual size_type body_size(size_part_type psize = PSIZE_TORSO, bool base = false) const = 0; - virtual int body_weight() const; + virtual int body_weight(bool base = false) const; virtual int total_weight() const = 0; virtual int damage_brand(int which_attack = -1) = 0; @@ -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.cc b/crawl-ref/source/artefact.cc index 9b2cc6feed..71ae7b27cd 100644 --- a/crawl-ref/source/artefact.cc +++ b/crawl-ref/source/artefact.cc @@ -1923,7 +1923,7 @@ bool make_item_randart( item_def &item, bool force_mundane ) if (item.flags & ISFLAG_UNRANDART) return (false); - if (item_is_mundane(item) && !one_chance_in(5) && !force_mundane) + if (item.is_mundane() && !one_chance_in(5) && !force_mundane) return (false); ASSERT(!item.props.exists(KNOWN_PROPS_KEY)); 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/attitude-change.cc b/crawl-ref/source/attitude-change.cc index fcfcf2d2a9..ef5ef2fcef 100644 --- a/crawl-ref/source/attitude-change.cc +++ b/crawl-ref/source/attitude-change.cc @@ -864,7 +864,7 @@ void beogh_convert_orc(monsters *orc, bool emergency, // The monster is not really *created* friendly, but should it // become hostile later on, it won't count as a good kill. - orc->flags |= MF_CREATED_FRIENDLY; + orc->flags |= MF_NO_REWARD; // Prevent assertion if the orc was previously worshipping a // different god, rather than already worshipping Beogh or being an diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 9e76ff8e91..41eddbe46b 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, @@ -1931,6 +1948,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() { @@ -1992,8 +2016,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(); @@ -2331,6 +2354,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(), @@ -2942,6 +2976,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)) @@ -3025,6 +3084,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" @@ -3079,7 +3156,7 @@ void bolt::drop_object() return; } - if (!thrown_object_destroyed(item, pos(), false)) + if (!thrown_object_destroyed(item, pos())) { if (item->sub_type == MI_THROWING_NET) { @@ -3093,10 +3170,10 @@ void bolt::drop_object() set_item_stationary(*item); } } + copy_item_to_grid(*item, pos(), 1); } - else if (item->sub_type == MI_LARGE_ROCK - && !feat_destroys_items(grd(pos()))) + else if (item->sub_type == MI_LARGE_ROCK) { // Large rocks mulch to stone. std::string sound_msg = "You hear a cracking sound!"; @@ -3115,6 +3192,10 @@ void bolt::drop_object() copy_item_to_grid(*item, pos(), item->quantity); } + else + { + item_was_destroyed(*item, NON_MONSTER); + } } // Returns true if the beam hits the player, fuzzing the beam if necessary @@ -4328,6 +4409,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 @@ -4694,6 +4778,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.) @@ -5005,21 +5106,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; @@ -5557,6 +5661,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. @@ -5733,7 +5865,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) { @@ -6070,21 +6201,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) { } @@ -6213,6 +6347,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/clua/iter.lua b/crawl-ref/source/dat/clua/iter.lua index b7bf9bc0a0..d9496bdb3c 100644 --- a/crawl-ref/source/dat/clua/iter.lua +++ b/crawl-ref/source/dat/clua/iter.lua @@ -470,3 +470,75 @@ end function iter.slave_iterator (prop, value) return iter.point_iterator:new(dgn.find_marker_positions_by_prop(prop, value)) end + +------------------------------------------------------------------------------- +-- Inventory iterator +------------------------------------------------------------------------------- + +iter.invent_iterator = {} + +function iter.invent_iterator:_new () + local m = {} + setmetatable(m, self) + self.__index = self + return m +end + +function iter.invent_iterator:new (itable, filter, rv_instead) + if itable == nil then + error("itable cannot be nil for invent_iterator") + end + + local mt = iter.invent_iterator:_new() + mt.cur_p = 0 + mt.table = itable + mt.rvi = rv_instead or false + mt.filter = filter or nil + + return mt:iter() +end + +function iter.invent_iterator:next() + local point = nil + local q = 0 + repeat + q = q + 1 + self.cur_p = self.cur_p + 1 + point = self:check_filter(self.table[self.cur_p]) + until point or q == 10 + + return point +end + +function iter.invent_iterator:check_filter(item) + if self.filter ~= nil then + if self.filter(item) then + if self.rvi then + return self.filter(item) + else + return item + end + else + return nil + end + else + return item + end +end + +function iter.invent_iterator:iter () + return function() return self:next() end, nil, nil +end + +-- An easier and more posh way of interfacing with find_marker_positions_by_prop. +function iter.inventory_iterator () + return iter.invent_iterator:new(items.inventory()) +end + +function iter.stash_iterator (point, y) + if y == nil then + return iter.invent_iterator:new(dgn.items_at(point.x, point.y)) + else + return iter.invent_iterator:new(dgn.items_at(point, y)) + end +end diff --git a/crawl-ref/source/dat/database/monspeak.txt b/crawl-ref/source/dat/database/monspeak.txt index a9380051ec..95ef7accc5 100644 --- a/crawl-ref/source/dat/database/monspeak.txt +++ b/crawl-ref/source/dat/database/monspeak.txt @@ -4452,3 +4452,30 @@ launches into embarks on %%%% +############################################################################### +# Speech keys used *specifically* in (portal) vaults. +# +############################################################################### +Cigotuvi_creatures + +VISUAL:@The_monster@ howls in pain. + +VISUAL:@The_monster@ wails in agony. + +VISUAL:@The_monster@ weeps. + +VISUAL:@The_monster@ sobs. + +@The_monster@ screams, "The pain, the pain!" + +@The_monster@ yells, "Please, just kill me!" + +VISUAL:@The_monster@ stares vacantly at hideously deformed hands. + +@The_monster@ asks, "What has happened to me?" + +@The_monster@ asks, "What has been done to me?" + +VISUAL:@The_monster@ prays for aid. + +VISUAL:@The_monster@ begins coughing up blood. 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/descript/spells.txt b/crawl-ref/source/dat/descript/spells.txt index fb0256a781..1543714508 100644 --- a/crawl-ref/source/dat/descript/spells.txt +++ b/crawl-ref/source/dat/descript/spells.txt @@ -280,7 +280,7 @@ This spell will freeze ammunition held by the caster. Such missiles will shatter %%%% Fulsome Distillation -This spell extracts the vile and poisonous essences from a corpse. A rotten corpse may produce a stronger potion. You probably don't want to drink the results. +This spell extracts the vile and poisonous essences from a corpse. You probably don't want to drink the results. The type of potion produced corresponds roughly to the effects of eating said corpse. %%%% Major Healing 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/ossuary.des b/crawl-ref/source/dat/ossuary.des index f0e5e18580..60c46d29dd 100644 --- a/crawl-ref/source/dat/ossuary.des +++ b/crawl-ref/source/dat/ossuary.des @@ -13,7 +13,13 @@ {{ function ossuary_portal(e) - local timeout_turns = crawl.random_range(1500, 2000) + local desc_long = +"You can make out a staircase leading downwards into a small tomb. Sand ".. +"surrounds the staircase and is continuously pouring onto it. Before long the ".. +"staircase will be gone. They say that some distant relatives of the pharaohs ".. +"were entombed here." + + local timeout_turns = crawl.random_range(1500, 2000) local messager = timed_msg { @@ -31,6 +37,7 @@ function ossuary_portal(e) timed_marker { disappear = "The staircase has disappeared completely beneath the sand.", desc = "A sand-covered staircase", + desc_long = desc_long, entity = 'staircase', dst = "ossuary", dstorigin = "in a tomb", @@ -476,11 +483,21 @@ TAGS: ossuary no_item_gen no_monster_gen ITEM: any scroll / any potion MONS: kobold skeleton / goblin skeleton / gnoll skeleton / \ orc skeleton +MONS: mummy # Loot: 10-19 +# Some random content. +SHUFFLE: Bb/Zz, Dd/Ww, Ee/Vv +SUBST: Z=c, z=c, W=c, w=c, V=c, v=c +SUBST: B=X, D=X, E=X +NSUBST: b = 1:= / c +NSUBST: d = 1:= / c +NSUBST: e = 1:= / c +SUBST: X = x2 # Guaranteed 10 items and 10 mummies, two in hidden rooms. # 50/50 chance of loot for each of the eleven rats in the rat room. KITEM: x = any scroll / any potion KMONS: x = mummy +NSUBST: ' = 10:^ / *:. KFEAT: ^ = dart trap / arrow trap KITEM: y = any scroll w:5 / any potion w:5 / nothing w:10 KMONS: y = rat skeleton @@ -489,23 +506,23 @@ KMONS: y = rat skeleton MAP ccc cccxccccc - ccc....+.^c cccccc - cc.1.ccccc.c cc.+^.cc - cc..ccc cc+cc cc..cc..cc - cc.^cc cc...ccccc..cccc.1ccc -ccc cc+cc c+.x.+...1.cc cc...cc -cAccc...cc cc...cccxccc ccc.cc -c..+.^1.+c cc+cc ccc ccc cc+cc -c<ccc...cc cc..cc cccccxccc...c -ccc cc+cc cc^1=cc cc..+.^..+.x.+ - c.cc cc..cc.cc c.1ccccccc...c - c^cc cc..cccc.ccc.cc cc+cc - c..cccc+cc cc.cc+cc ccc - cc1.cc...cccccc=yyyccccc - cc.+..x..+...+yyyyy+.^cc - cccc...cccxcccyyyccc..cc - cc=cc ccc cc+cc cc1.ccccc - cxc ccc cc...=xc + ccc''''+''c cccccc + cc'''ccccc'c cc'+.1cc + cc1'ccc cc+cc cc''cc''cc + cc..cc cc...ccccc''cccc''ccc +ccc cc+cc c+.x.+.1'''cc cc'''cc +cAccc'''cc ccb...cccxccc cecc'cc +c..+..1'+c cBcc+cc ccc cceEec+cc +c<ccc'''cc ccb''cc cccccxccc...ccc +ccc cc+cc cc''=cccccc''+''''+.x.+Xc + c.cc cc1'cc'cdDd.1ccccccc...ccc + c1cc cc..cccc'cdd.cc cc+cc + c''cccc+cc cc'cc+cc cXc + cc''cc...cccccc=yyyccccc ccc + cc'+..x..+'''+yyyyy+.1cc + cccc...cccxcccyyyccc''cc + cc=cc ccc cc+cc cc''ccccc + cxc ccc cc'''+Xc ccc ccccccc ENDMAP 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/dat/vaults.des b/crawl-ref/source/dat/vaults.des index 274a32e4df..18f7bfe94f 100644 --- a/crawl-ref/source/dat/vaults.des +++ b/crawl-ref/source/dat/vaults.des @@ -362,7 +362,9 @@ ENDMAP # example), which will be used to orient that corner towards the middle of # the map. Each quadrant must be capable of generating a rune, which # should be done by placing an 'O' glyph and calling vault8_rune(_G). - +# +# There is a lot of variance among the possible quadrants but we do ensure +# that (exactly) one quadrant with rich loot is generated. ############################################################################### # The main vault for Vault:8. @@ -371,7 +373,7 @@ NAME: vaults_vault PLACE: Vault:8 ORIENT: encompass TAGS: no_rotate no_dump -SUBVAULT: A : vault8_quadrant +SUBVAULT: A : vault8_quadrant_prize SUBVAULT: B : vault8_quadrant SUBVAULT: C : vault8_quadrant SUBVAULT: D : vault8_quadrant @@ -491,7 +493,7 @@ ENDMAP # # about 24 | NAME: vault8_rooms -TAGS: vault8_quadrant uniq_vault8_prize +TAGS: vault8_quadrant_prize SHUFFLE: AC/BD, EG/FH, IKN/MJL SUBST: A=., B=xx=, C=+, D=x, E=+, F=xx=, G=., H=x SUBST: I=., M=xx=, J=x, K=+, N=+, L=xx= @@ -764,7 +766,7 @@ ENDMAP # Vault:8 - Triangles Quadrant (by Mu.) # about 19 | NAME: vault8_triangles -TAGS: vault8_quadrant uniq_vault8_prize +TAGS: vault8_quadrant_prize SUBST: Q = 8 9 .:20 NSUBST: ? = 1:O / *:| SUBST: " = =:1 x:99 @@ -882,7 +884,7 @@ ENDMAP # Vault:8 - Corners Quadrant (by Mu.) # about 25 | NAME: vault8_corners -TAGS: vault8_quadrant uniq_vault8_prize +TAGS: vault8_quadrant_prize SUBST: Q = 8 9 .:10 NSUBST: $ = 1:O / *:$ SUBST: $ = | *:20 $ @@ -919,7 +921,7 @@ ENDMAP # Vault:8 - Flips Quadrant (by Mu.) # about 21 | NAME: vault8_flips -TAGS: vault8_quadrant uniq_vault8_prize +TAGS: vault8_quadrant_prize NSUBST: ; = 3:l / 3:z / 3:a / *:. SUBST: Q = 8 9 SUBST: $ = | * $ @@ -960,7 +962,7 @@ ENDMAP # Vault:8 - Construction Quadrant (by Mu.) # about 21 | NAME: vault8_construction -TAGS: vault8_quadrant uniq_vault8_prize +TAGS: vault8_quadrant_prize SUBST: Q = 8 9 . NSUBST: $ = 1:O / *:$ SUBST: $ = |*$. diff --git a/crawl-ref/source/dat/wizlab.des b/crawl-ref/source/dat/wizlab.des new file mode 100644 index 0000000000..03a12ccc13 --- /dev/null +++ b/crawl-ref/source/dat/wizlab.des @@ -0,0 +1,1696 @@ +### +# +# "Wizard"-themed portals. +# +# Tukima's Studio +# threat: 6 dancing weapons, dancing weapon machine generating up to 5 more +# loot: the weapons, tukima's book and a scroll of acquirement +# xp: 14400 or so +# layout: mirrors; grey and white +# +# Erinya's Formal Garden +# crumbling stone walls. +# threat: centaurs, oklobs +# loot: a staff and four spellbooks +# xp: 10500 or so +# layout: formal (French) garden, flowers, crumbling stone walls +# +# Doroklohe's Tomb +# threat: either Tomb set or demons or golems +# loot: either scrolls or armours or random items +# xp: 10000+ +# layout: lots of tombs with loot and monsters inside +# colour: yellow tombs, rest is dark, floor is randomised +# +# Wucad Mu's Monastery +# threat: "monks", OCS +# loot: books, staff of wucad mu +# xp: 9000+ +# layout: temple-esque! +# colour: dark walls, red and orangge. +# +# Cigotuvi's Fleshworks +# threat: lumps, uglies, abominations +# loot: tmut. potions, books, rMut, ?oSumm, \oDeath +# xp: 16000+ +# layout: organic, irregular, twisty, NO SQUARES, NO HARD CORNERS, +# NOTHING THAT LOOKS PRETTY +# colour: lightred walls, red doors, magenta floor, yellow 'glass' +# +### + +## +# +# PLANNING AREA: +# +# From spells: +# +# Alistair's Brewery: mutated wizards, mephitic cloud generators, +# cupboards full of potions, confused monsters. +# Borgnjor's Mortuary: liches, miasma clouds, potions of decay, slaves that are +# having their life forces drained, vampire (knights, mages). +# Iskenderun's Mystic Bastion: purple draconians, purple wizards with mystic +# blast, naga mages with mystic blast, purple fog and smoke outside windows, +# robes and cloaks of magic resistance as the loot. +# Lee's Rapid Deconstructor: golems and "monk" or otherwise unarmed creatures, +# exploding features and exploding corpses. It's a timed vault, so you have +# to get to the loot quickly and avoid the hordes of monsters. +# Lehudib's Crystal Spire: crystal walls and crystal golems, crystal monsters, +# spear throwers (statues with crystal spear), possibly the unrand as the +# loot. +# Maxwell's Forge: Maxwell employs a bunch of angels to churn out weapons; +# stashes of hammers, flame clouds, other "smithy" styled themes, with black +# smoke. +# Olgreb's Toxic Laboratory: poisonous clouds, a toxic radiance effect, +# poisonous monsters, possibly even a "poison" effect that bypasses rPois, +# potions of poison and potions dropped converting into potions of poison. +# Ozocubu's Refrigerator: simulacra, refrigeration effect, a solid stone room +# inside of an "ice cave", freezing clouds. +# +# From unrands: +# +# Cekugob's Oubliette: a randomised maze (though not a labyrinth) with skeletal +# guards, and with occasionally hidden. Cekugob's amulet as the loot, +# possibly. +# Zonguldrok's Mausoleum: regenerating zombie monsters, sword as the loot. +# Botono's Bayou: A bay-like area with small huts, wizards that cast negative +# related spells, shadow dragons, rebranded swamp worms, wraiths, spectrals. +# Ukta's Hut: phantoms, other "ghosts", ogre mages and ogres, druidic style, +# wooden walls set in a forest (more "earthy" than Erinya's Garden) +# The Alchemist's Tower: gold turned into things, things turned into gold. +# +# Random named Wizard portals. +# +# TO-DO: +# +# We've currently got working vaults for Tukima and Erinya and Doroklohe. +# The flavour messages for the portal vault require flavour, while the actual +# portals themselves could do with an overhaul and some new ones. +# +# *MUST* make these look awesome in tiles, too. +# +## + +{{ +function wizlab_portal (e) + -- Flavour could use some improvement. + local desc_long = "This portal has been left open by a careless wizard." .. + "It might lead to their secret laboratory, but beware!" .. + " Surely it will be guarded by magical traps and " .. + "terrible monsters." + local timeout_turns = crawl.random_range(1200, 1700) + local messager = + timed_msg { + visible = true, + -- $F{xxx} will be substituted with the 'entity' property of the timed + -- marker, or with the desc property (if entity is not set). + messages = time_messages(timeout_turns, + "$F{The} shimmers.", + "$F{The} vibrates and hums gently.", + "$F{The} is slightly opaque.", + "$F{The} is almost invisible now.") + } + + e.lua_marker('O', + timed_marker { + disappear = "The portal disappears completely.", + desc = "magical portal", + desc_long = desc_long, + entity = 'portal', + dst = 'wizlab', + dstname_abbrev = "WizLab", + dstorigin = "in a Wizard's Laboratory", + overmap = "magical portal", + turns = timeout_turns, + floor = "stone_arch", + msg = messager }) + e.kfeat("O = enter_portal_vault") +end + +function wizlab_feat_descs() +end + +dgn.set_lt_callback("wizlab", "wizlab_feat_descs") + +-- Destinations: +function wizlab_setup (e, wizlab_desc) + e.kfeat("< = exit_portal_vault") + -- This is so that the note shows up properly. + dgn.set_level_type_origin("in " .. wizlab_desc) + crawl.mark_milestone("br.enter", "entered " .. wizlab_desc .. ".") + dgn.set_feature_desc_short("gate leading back to the Dungeon", + "portal leading out of here") + if wizlab_desc ~= "Erinya's Formal Garden" then + dgn.set_feature_desc_short("empty arch of ancient stone", + "empty arch of stone") + end +end +}} + +default-depth: Vault:2-7, Crypt, D:22-27 + +############################################################################### +# Portal entrances. +# +# Todo: more, and better. +NAME: enter_wizlab_1 +TAGS: uniq_wizlab +ORIENT: float +: wizlab_portal(_G) +MAP + wwwww + wwwwwww + wwxmxmxww + wwxx...xxww +wwxm..O..mxww + wwxx...xxww + wwxx+xxww + www.www + w.@.w +ENDMAP + +############################################################################### +# Basic Entrance 2 (by Mu.) +# +NAME: enter_wizlab_2 +TAGS: uniq_wizlab no_item_gen no_monster_gen +ORIENT: float +SUBST: x = b +SHUFFLE: tU +: wizlab_portal(_G) +MAP +................. +.xxxxxxxmxxxxxxx. +.xx...x...x...xx. +.n..U...O...U..n. +.xx...x...x...xx. +.xxxxxxx+xxxxxxx. +..xx...x.x...xx.. +..n..t.....t..n.. +..xx...x.x...xx.. +..xxxxxx+xxxxxx.. +................. +......c...c...... +.....ccc.ccc..... +................. +ENDMAP + + +############################################################################### +# Basic Entrance 3 (by Mu.) +# +NAME: enter_wizlab_3 +TAGS: uniq_wizlab no_item_gen no_monster_gen +ORIENT: float +: wizlab_portal(_G) +MARKER: ! = lua:fog_machine { \ + pow_max = 10, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 5, \ + size_buildup_time = 25, cloud_type = "flame" \ + } +MARKER: ? = lua:fog_machine { \ + pow_max = 10, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 5, \ + size_buildup_time = 25, cloud_type = "freezing vapour" \ + } +SUBST: n = cn +COLOUR: . = red / blue +KPROP: .?! = no_rtele_into +MAP + ccccccccc +cc..!.?..cc +c?.......!c +c..nnnnn..c +c..n...n..c +c..n.O.n..c +c.?n...n!.c +c..nn.nn..c +c..nn.nn..c +cc!n.n.n?cc + ccnn.nncc + cc.n.cc + cc.cc + c@c +ENDMAP + +############################################################################### +# Basic Entrance 4 (by Mu.) +# +NAME: enter_wizlab_4 +TAGS: uniq_wizlab no_item_gen no_monster_gen +ORIENT: float +: wizlab_portal(_G) +MAP + xxxxx + w...x + ww.O.x + www...x + www..www +www..www +ww..www +w..www +@.www +@www +ENDMAP + +############################################################################### +# Basic Entrance 5 (by Mu.) +# +NAME: enter_wizlab_5 +TAGS: uniq_wizlab no_item_gen no_monster_gen +ORIENT: float +SUBST: x = b +COLOUR: U = random +COLOUR: W = mutagenic +KFEAT: o = granite_statue +: wizlab_portal(_G) +MAP +xxxxxxxxxxx +xxxx...xxxx +xWWU.O.UWWx +xWxx...xxWx +xWWWW.WWWWx +xxxxW.Wxxxx +xxxxW.Wxxxx +xxxxW.Wxxxx +xxxxW.Wxxxx +xxxxo.oxxxx +xxxx...xxxx + @ +ENDMAP + +############################################################################### +# Basic Entrance 6 (by Mu.) +# +NAME: enter_wizlab_6 +TAGS: uniq_wizlab no_item_gen no_monster_gen +ORIENT: float +SUBST: x = mc. +: wizlab_portal(_G) +MAP +c.c.ccccccc +x.x.x.x..Oc +.x.x.x.x..c +x.x.x.x.x.c +.x.x.x.x.xc +x.x.x.x.x.c +.x.x.x.x.xc +x.x.x.x.x.. +.x.x.x.x.xc +x.x.x.x.x.. +.x.x.x.x.xc +ENDMAP + +# +# +############################################################################### + +default-depth: + +############################################################################### +# And arrivals! +############################################################################### +# Firstly, we have Tukima's Dance Studio. +# +# Todo: clean up the code +NAME: wizlab_tukima +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +MONS: dancing weapon +KFEAT: M = granite_statue +MARKER: M = lua:props_marker {veto_fragmentation="veto", veto_disintegrate="veto", \ + slaved_to="tukima"} +{{ +function tukima_machine (data, triggerable, triggerer, marker, ev) + if triggerer.type ~= "turn" or triggerer.sub_type ~= "countdown" then + return + end + + local x, y = marker:pos() + local you_x, you_y = you.pos() + + if data.weapon_count >= 5 then + if you.see_cell(x, y) then + crawl.mpr("The machine makes a dull noise.") + else + crawl.mpr("You hear a distant, dull noise.") + end + return + end + + if (you_x == x and you_y == y) then return end + if dgn.mons_at(x, y) then return end + + dgn.apply_area_cloud(x, y, 1, 6, 1, 10, "black smoke", "other", -1) + + if (not dgn.create_monster(x, y, "generate_awake dancing weapon")) then + return + end + + data.weapon_count = data.weapon_count + 1 + + if you.see_cell(x, y) then + crawl.mpr("The machine hisses, and spits out a dancing weapon!") + else + crawl.mpr("You hear a distant hissing noise.") + end +end + +local tukima_marker = TriggerableFunction:new { + func = tukima_machine, + repeated = true, + data = {weapon_count=0}, +} + +tukima_marker:add_triggerer(DgnTriggerer:new { + type="turn", + delay_min=500, + delay_max=800 +}) + +lua_marker('?', tukima_marker) + +if crawl.coinflip() then + subst("Y = =") + subst("y = .") + subst("Rr = c") +else + subst("Y = c") + subst("y = m") + subst("R = .") + subst("r = =") +end + +if crawl.coinflip() then + subst("X = =") + subst("x = .") + subst("Ll = c") +else + subst("X = c") + subst("x = m") + subst("L = .") + subst("l = =") +end +}} +SUBST: ? = . +COLOUR: c = darkgrey +COLOUR: = = darkgrey +COLOUR: mMV = silver +KMONS: 8 = ice statue col:silver spells:blink_other;blink_other;blink_other;\ + blink_other;blink_other;major_healing name:strange_machine \ + name_replace name_descriptor hd:13 hp:120 +KITEM: 8 = book of tukima +KMONS: 9 = ice statue col:silver spells:blink_other;blink_other;blink_other;\ + blink_other;blink_other;major_healing name:strange_machine \ + name_replace name_descriptor hd:13 hp:120 +KITEM: 9 = scroll of acquirement +: local mquote = '"The machine had stood there a long time. It was several ' +: .. "hundred feet long and could run on a thimbleful of earth or" +: .. " water. It had been working seven and a half million years." +: .. '"\n -Sweet Their Blood and Sticky, Albert Teichner' +MARKER: 8 = lua:MonPropsMarker:new {description="A glistening silver machine.\n",\ + quote=mquote} +MARKER: 9 = lua:MonPropsMarker:new {description="A glistening silver machine.\n",\ + quote=mquote} +: dgn.set_feature_desc_short("translucent rock wall", +: "crystal clear mirror") +: dgn.set_feature_desc_long("translucent rock wall", +: "A glistening mirror, reflecting everything around it.\n") +: dgn.set_feature_desc_short("granite statue", "strange machine") +: dgn.set_feature_desc_long("granite statue", "A mechanical contraption," .. +: " all hisses and pops.\n") +: dgn.set_feature_desc_short("dry fountain", "glistening fountain") +: dgn.set_feature_desc_long("dry fountain", "A pristine fountain. " .. +: "The only thing it lacks is water.\n") +: dgn.set_feature_desc_short("stone wall", "black marble wall") +: dgn.set_feature_desc_long("stone wall", "The marble glistens and shines. It certainly " .. +: "is beautiful!\n") +: dgn.set_feature_desc_short("Floor", "marble floor") +: dgn.set_feature_desc_long("Floor", "Where the walls are black marble, the floor is " .. +: "a white and silver, with shades of pink here and there.\n") +: dgn.set_feature_quote("A granite statue", mquote) +: wizlab_setup(_G, "Tukima's Studio") +MAP + ccccccc + ccmmmcc + cccccm...mccccc + cc...+.A.<.+...cc + cc..cccm...mccc..cc + cc+cccccm.mccccc+cc + cc.cccccc+cccccc.cc + cc...cccc...cccc...cc + cm...mcm..1..mcm...mc + cm...mcm.....mcm...mc + cm.V.mcm.....mcm.V.mc + cm...mcm.....mcm...mc + cm...mcm..1..mcm...mc + cc...cccc...cccc...cc + ccc.cccccc+cccccc.ccc + cc+ccccm...mcccc+cc + ccm.mccm.....mccm.mcc + cm.1..+...V...+..1.mc + ccXm.mccm.....mccm.mYcc + cccxccmlcccm...mcccrmccycc + ccm.mcccLcccm.mcccRcccm.mcc + ccm...mLLcccm...mcccRRm...mcc + ccm..$..mcccm..?..mcccm..$..mcc + cm..$8$..m+m...M...m+m..$9$..mc + ccm..$..mcccm.....mcccm..$..mcc + ccm...mcc ccm...mcc ccm...mcc + ccmmmcc ccmmmcc ccmmmcc + ccccc ccccc ccccc +ENDMAP + +############################################################################### +# Erinya's Formal Garden +# +# Map was based on this image: http://upload.wikimedia.org/wikipedia/commons/5 +# /58/Plan_du_ch%C3%A2teau_et_des_jardins_de_Clagny_dessin%C3%A9_par_ +# Maraine_XVIIe_si%C3%A8cle.jpg (concat it back together to get the +# full link). +NAME: wizlab_erinya +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +MONS: centaur col:green, centaur warrior col:lightgreen +MONS: oklob plant +MONS: stone golem name:vine_covered name_adjective col:lightgreen \ + tile:mons_vine_golem +MONS: toadstool col:any name:flowers n_rpl n_des n_the +SUBST: y = ct +COLOUR: G = yellow +KFEAT: / = stone_arch +KFEAT: _ = altar_fedhas +MARKER: A = feat:stone_arch +ITEM: any fixed theme book, any fixed level book, any staff +{{ + +function grow (data, triggerable, triggerer, marker, ev) + if triggerer.type ~= "turn" or triggerer.sub_type ~= "countdown" then + return + end + + local count = 0 + local flowdesc = "A rather pretty cluster of flowers." + if you.can_smell then + flowdesc = flowdesc .. " They smell sweet." + end + + if crawl.one_chance_in(10) then + -- Fruit! + for spot in iter.adjacent_iterator() do + if not feat.is_solid(spot:xy()) and not feat.destroys_items(spot:xy()) + and crawl.one_chance_in(3) then + -- Do nothing. + dgn.create_item(spot.x, spot.y, "pear / apple / choko / apricot / orange" + .. "/ banana / strawberry q:4 / lemon / rambutan " + .. "/ grape q:7 / sultana q:9 / lychee") + count = count + 1 + end + end + if count > 0 then + crawl.mpr("Fruit sprouts up around you!") + end + else + -- Flowers! + for spot in iter.adjacent_iterator() do + if not feat.is_solid(spot:xy()) and not feat.destroys_items(spot:xy()) + and not crawl.one_chance_in(3) then + dgn.create_monster(spot.x, spot.y, "toadstool col:any name:flowers " .. + "name_replace name_descriptor name_definite") + dgn.mons_at(spot.x, spot.y).set_prop("description", flowdesc .. "\n") + count = count + 1 + end + end + if count > 0 then + crawl.mpr("Flowers sprout up around you!") + end + end +end + +local grow_marker = TriggerableFunction:new { func = grow, repeated = true } + +grow_marker:add_triggerer(DgnTriggerer:new { type="turn", + delay_min=500, delay_max=800, }) + +lua_marker("_", grow_marker) +}} +: set_border_fill_type("stone_wall") +: wizlab_setup(_G, "Erinya's Formal Garden") +: dgn.set_feature_desc_short("empty arch of ancient stone", +: "rose-covered archway") +: dgn.set_feature_desc_long("empty arch of ancient stone", +: "The roses look beautiful!\n") +: dgn.set_feature_desc_short("granite statue", "vine-covered statue") +: dgn.set_feature_desc_long("granite statue", "It looks almost like a " .. +: "monster made entirely from vines.\n") +: dgn.set_feature_desc_short("stone wall", "crumbing stone wall") +: dgn.set_feature_desc_long("stone wall", "It looks ancient.\n") +: dgn.set_feature_desc_short("Floor", "grass-covered ground") +: dgn.set_feature_desc_long("Floor", "Strewn with leaves and twigs that" .. +: "crunch loudly underfoot, the grass is crisp and fresh.\n") +MAP +ccccccccccccccccccccccccccccccccccccccccccccc +ctttttttttttttttttttttttttttttttttttttttttttc +ctttttttt...........................ttttttttc +ctttt.....tttttttttttt.tttttttttttt.....ttttc +cttt..tt.tttttttttt.......tttttttttt.tt..tttc +ct.1.tGt............<.T.A............tGt.1.tc +cttt..tt.tttttttttt.......tttttttttt.tt..tttc +ctttt.....tttttttttttt.tttttttttttt.....ttttc +ctttttttt...........................ttttttttc +cttttttttttttttttttttt.tttttttttttttttttttttc +ctttttttttyyyttttttty...ytttttttyyytttttttttc +ctttttttty...yttttty.....yttttty...yttttttttc +cttttttt..111.........T.........111..tttttttc +ctttttt...121.tttttc.....cttttt.121...ttttttc +cttttt.tty...yttttttcc=cctttttty...ytt.tttttc +ctttt.tttty.yttttttt.....ttttttty.ytttt.ttttc +cttt.tttttt.tttttttyd232eyttttttt.tttttt.tttc +cttt.tttttt..tttttttydefyttttttt..tttttt.tttc +ctttt.tttttt..tttttttytyttttttt..tttttt.ttttc +cttttt.tttttt..ttttttttttttttt..tttttt.tttttc +ctttttt.tttttt..tttttcccttttt..tttttt.ttttttc +cttttyyy/yyyttt..ttty...yttt..tttyyy/yyyttttc +cttty...1...yttt..ty..1..yt..ttty...1...ytttc +ctty...1T1...yttt....1T1....ttty...1T1...yttc +cttty...1...yttt..ty..2..yt..ttty...1...ytttc +cttttyyy/yyyttt..ttty...yttt..tttyyy/yyyttttc +ctttttt.tttttt..tttttcccttttt..tttttt.ttttttc +cttttt.tttttt..ttttttttttttttt..tttttt.tttttc +ctttt.tttttt..ttttt.wwWww.ttttt..tttttt.ttttc +cttt.tttttt..ttttt.wwG_Gww.ttttt..tttttt.tttc +cttt.ttttt..ttttt....4.4....ttttt..ttttt.tttc +ctttt.ttty/ytttt....cc=cc....tttty/yttt.ttttc +cttttt.ty...yttttttcc...cctttttty...yt.tttttc +ctttttt./.1./.ttttcc..2..cctttt./.1./.ttttttc +cttttttty...yt...../.1T1../....ty...ytttttttc +cttttttttyyyttttttty.....ytttttttyyyttttttttc +ctttttttttttttttttttyytyytttttttttttttttttttc +ccccccccccccccccccccccccccccccccccccccccccccc +ENDMAP + +############################################################################### +# Doroklohe's Forbidden Tomb +# +# It seems as if he practised box casting. I wonder what that's good for. +# Vault by dpeg. Thanks to Enne for box_glyph and to Jude for triggers. +# +# A bleach sun settles in the smog-stained sky +# Dismembered bodies stray in disarray +# Breakfast is served in the Manchester Morgue +# The beginning of a horrifying day +# Impetigo 1992 +NAME: wizlab_doroklohe +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +LROCKCOL: yellow +KFEAT: ! = x +{{ +function make_box(cenx, ceny, gly) + for ox = -1, 1 do + for oy = -1, 1 do + local x = cenx + ox + local y = ceny + oy + if (x >= 1 and y >= 1 and x <= width() and y <= height()) then + if (ox ~= 0 or oy ~= 0) then + mapgrd[x][y] = gly; + end + end + end + end +end + +function box_glyph(findgly, boxgly) + local glyphs = {gly_points(findgly)} + for i = 1, #glyphs, 2 do + -- This is ugly. gly_points should return 1-indexed coordinates. + -- No longer ugly, as mapgrd is 0-indexed, like gly points. + make_box(glyphs[i], glyphs[i+1], boxgly) + end +end +}} +# There are two layouts: round and grid. +# Round has 19 boxes: 12 % and 6 * and 1 | +# Grid has 21 boxes: b-u and | +# Layout for grid can be regular or random. +# +: local layout = crawl.random2(13) +: if layout < 3 then +# Preparation for the rare regular grid layout. +SUBST: b=., c=., d=., e=., f=., g=., h=., i=., j=., k=. +SUBST: l=., m=., n=., o=., p=., q=., r=., s=., t=., u=. +SUBST: B=b, C=c, D=d, E=e, F=f, G=g, H=h, I=i, J=j, K=k +SUBST: L=l, M=m, N=n, O=o, P=p, Q=q, R=r, S=s, T=t, U=u +: end +: if layout > 9 then +# First the round layout... +COLOUR: _ = darkgrey +KFEAT: _ = stone_wall +SUBST: b=., c=., d=., e=., f=., g=., h=., i=., j=., k=. +SUBST: l=., m=., n=., o=., p=., q=., r=., s=., t=., u=. +SUBST: B=., C=., D=., E=., F=., G=., H=., I=., J=., K=. +SUBST: L=., M=., N=., O=., P=., Q=., R=., S=., T=., U=. +: else +# ...and now the grid layout. +SUBST: _ = . +SUBST: *=., %=. +SUBST: B=b, C=c, D=d, E=e, F=f, G=g, H=h, I=i, J=j, K=k +SUBST: L=l, M=m, N=n, O=o, P=p, Q=q, R=r, S=s, T=t, U=u +NSUBST: b = 1:b / *:. +NSUBST: c = 1:c / *:. +NSUBST: d = 1:d / *:. +NSUBST: e = 1:e / *:. +NSUBST: f = 1:f / *:. +NSUBST: g = 1:g / *:. +NSUBST: h = 1:h / *:. +NSUBST: i = 1:i / *:. +NSUBST: j = 1:j / *:. +NSUBST: k = 1:k / *:. +NSUBST: l = 1:l / *:. +NSUBST: m = 1:m / *:. +NSUBST: n = 1:n / *:. +NSUBST: o = 1:o / *:. +NSUBST: p = 1:p / *:. +NSUBST: q = 1:q / *:. +NSUBST: r = 1:r / *:. +NSUBST: s = 1:s / *:. +NSUBST: t = 1:t / *:. +NSUBST: u = 1:u / *:. +# Of the outer boxes, three are damaged. +SHUFFLE: bcdefghijklm +: box_glyph('b', 'X') +: box_glyph('c', 'Y') +: box_glyph('d', 'Z') +SUBST: b = . +SUBST: c = . +SUBST: d = . +NSUBST: X = 1=. / *=!:20 .:1 +NSUBST: Y = 1=. / *=!:30 .:1 +NSUBST: Z = 1=. / *=!:50 .:1 +# For the other boxes, prepare same setup as for the +# round layout (i.e. six *'s) +SHUFFLE: nqru +SUBST: e=%, f=%, g=%, h=%, i=%, j=%, k=%, l=%, m=%, n=% +SUBST: o=*, p=*, q=%, r=*, s=*, t=*, u=* +: end +# Monsters +# 0 Tomb set: zombies, mummies +# 1 Demons (if demons are added or reclassified, please change!) +# 2 Golems +# 3 Holy set (for later) +: local mrnd = crawl.random2(4) +: if mrnd == 0 then +KMONS: % = clay golem / wood golem / toenail golem w:2 +KMONS: * = stone golem / crystal golem / iron golem +KMONS: | = electric golem +: elseif mrnd == 1 then +KMONS: % = blue devil / iron devil / ynoxinul / neqoxec / hellion /\ + / chaos spawn / demonic crawler / hellwing / orange demon /\ + shadow demon / tormentor +KMONS: * = lorocyproca / reaper / soul eater / ice devil / sun demon +KMONS: | = blue death / green death / pit fiend / fiend / shadow fiend /\ + ice fiend / executioner / balrug / cacodemon +: else +KMONS: % = place:D:15 zombie / guardian mummy w:2 +KMONS: * = place:D:27 zombie / greater mummy w:5 +KMONS: | = ancient lich +: end +# Loot. +# 0 = scrolls. 1 = armours. 2 = mixed. +: local lrnd = crawl.random2(3) +: if lrnd == 0 then +KITEM: % = any scroll / any scroll q:2 w:3 / any scroll q:3 w:1 +: kitem("* = " .. dgn.good_scrolls) +KITEM: | = scroll of acquirement / scroll of enchant weapon III q:2 /\ + scroll of blinking q:2, scroll of fog q:2, scroll of holy word q:2 +: elseif lrnd == 1 then +KITEM: % = any armour +KITEM: * = any good_item armour +KITEM: | = cursed robe ego:resistance / cursed gold dragon armour +: else +KITEM: % = any / star_item w:2 +KITEM: * = star_item / superb_item w:2 +KITEM: | = superb_item +: end +# Boxification: +: box_glyph('%', '!') +: box_glyph('*', '!') +: box_glyph('|', '!') +# Intentionally colouring the floor glyphs of the central room only. +# The squares beneath the boxes are to remain uncoloured. +COLOUR: ^ = darkgrey +COLOUR: = = yellow +KFEAT: ^ = stone_wall +COLOUR: . = darkgrey / blue w:3 / cyan w:1 +MARKER: ! = lua:portal_desc {wall_drop=1} +MARKER: = = lua:portal_desc {wall_drop=1} +{{ + +function convert_boxes (data, triggerable, triggerer, marker, ev) + if triggerer.type ~= "turn" or triggerer.sub_type ~= "countdown" then + return + end + + if data.phase == 1 then + data.phase = data.phase + 1 + if you.silenced() then + crawl.mpr("The ground shakes.", "warning") + else + crawl.mpr("There is a faint hissing noise.", "warning") + end + return + elseif data.phase == 2 then + data.phase = data.phase + 1 + if you.silenced() then + crawl.mpr("The ground shakes.", "warning") + else + crawl.mpr("There is a loud grinding noise.", "warning") + end + return + elseif data.phase == 3 then + data.phase = data.phase + 1 + crawl.mpr("The walls fall away. The entombed are set free!", "warning") + for _, i in ipairs(dgn.find_marker_positions_by_prop("wall_drop", 1)) do + dgn.terrain_changed(i.x, i.y, "floor", false, false, false) + dgn.place_cloud(i.x, i.y, "black smoke", 2) + end + return + else + return + end +end + +local box_marker = TriggerableFunction:new {func=convert_boxes, + repeated=true, data={phase=1} } + +box_marker:add_triggerer(DgnTriggerer:new { type="turn", + delay_min=500, delay_max=1000}) + +lua_marker("A", box_marker) +}} +: dgn.set_feature_desc_short("rock wall", "strange rock wall") +: dgn.set_feature_desc_long("rock wall", "It is decorated with " +: .. "images you might see in a tomb.\n") +: dgn.set_feature_desc_short("stone wall", "highly decorated stone wall") +: dgn.set_feature_desc_long("stone wall", "It is covered with disturbing" +: .. ", horrifying images.\n") +: dgn.set_feature_desc_short("Floor", "rough-hewn floor") +: dgn.set_feature_desc_long("Floor", "It is rough and uneven in places.\n") +: wizlab_setup(_G, "Doroklohe's Tomb") +MAP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^________________...........________________^ +^____________...................____________^ +^_________........................._________^ +^_______b....cc......d%d......ee....f_______^ +^_____bB.....cC%.....dDd.....%Ee.....Ff_____^ +^____bb...............................ff____^ +^___.....................................___^ +^__........%......o*.....*p......%........__^ +^__.......nnN....oO.......Pp....Qqq.......__^ +^_........................................._^ +^_...mm...............................gg..._^ +^_...mM..%......*.....|.....*......%..Gg..._^ +^_...mm...............................gg..._^ +^_........................................._^ +^__.......uuU....tT.......Ss....Rrr.......__^ +^__........%......t*.....*s......%........__^ +^___.....................................___^ +^____ll...............................hh____^ +^_____lL.....kK%.....jJj.....%Ii.....Hh_____^ +^_______l....kk......j%j......ii....h_______^ +^_________........................._________^ +^____________...................____________^ +^________________...........________________^ +^^^^^^^^^^^^^^^^^^^^^^.^^^^^^^^^^^^^^^^^^^^^^ + ^____...____^ + ^__.......__^ + ^_...!=!..._^ + ^_.<.=A=.<._^ + ^_...!=!..._^ + ^__.......__^ + ^____...____^ + ^^^^^^^^^^^^^ +ENDMAP + +############################################################################### +# Alistair's Brewery +# +# Todo: work out what happens next??? +NAME: wizlab_alistair +ORIENT: encompass +TAGS: no_item_gen no_monster_gen no_rotate allow_dup +# unfinished +: wizlab_setup(_G, "Alistair's Brewery") +SHUFFLE: CYHB +KFEAT: CYHB = . +KPROP: B = bloody / nothing +KMONS: CYH = swamp drake name:mutated_drake n_des n_rpl col:magenta w:30 \ + spells:mephitic_cloud;mephitic_cloud;blink;mephitic_cloud;\ + mephitic_cloud;blink / wizard name:mutated name_adjective \ + col:lightmagenta w:10 / nothing w:130 +COLOUR: =c = magenta, w = poison, n = blue +LFLOORCOL: white +: dgn.set_feature_desc_short("deep water", "toxic goo") +: dgn.set_feature_desc_long("deep water", "It stinks. Drinking it " +: .. "might seem to be a good idea after a bottle or two.\n") +ITEM: potion of confusion w:20 / any potion +MAP + ccccccc + c.....c + ccccccc..A..ccccccc + cCCCCC+.....cBBBBBc + cCCCCCc..<..cBBBBBc + cCCCCCc.....cBBBBBc + cCCCCCcccncccBBBBBc + cCCCCCc.....+BBBBBc + ccccc+ccc.....ccccc+ccc + c.....c...ccc..<c.....c + c.www.c..ccdcc..c.www.c + c.www.n..cdddc..n.www.c + c.www.c..ccdcc..c.www.c + c.....c...c+c...c.....c + ccc+ccccc.....ccc+ccccc + cYYYYYc.....cHHHHHc + cYYYYYcccncccHHHHHc + cYYYYY+.....cHHHHHc + cYYYYYc.www.cHHHHHc + cYYYYYc.www.+HHHHHc + ccccccc.www.ccccccc + c.....c + ccccccc +ENDMAP + +############################################################################### +# Borgnjor's Mortuary +# +NAME: wizlab_borgnjor +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Borgnjor's Mortuary") +MAP +ENDMAP + +############################################################################## +# Cigotuvi's Fleshworks (by Mu.) +# +# Cigotuvi has mastered the art of manipulating flesh, and his lab is a +# living testament to this fact, seeming like the interior of some +# ghastly beast. The walls and floor are slick and membranous, pulsing to an +# unheard heartbeat and oozing thick, green ichor from every inch. +# +# The bulk of Cigotuvi's Fleshworks is devoted to cells that house test +# subjects in various stages of degeneration. Most are sickly humanoids; the +# others are ugly things, pulsating lumps and abominations. +# +# The central, circular chamber houses Cigotuvi's flesh golem. +# The Eastern half of his lab is dominated by a snaking passageway filled with +# pulsating lumps and the occasional ugly thing. +# The western half of his lab is full of abominations, including the terrible +# "Cigotuvi's Monster". +NAME: wizlab_cigotuvi +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +KPROP: . = w:1 bloody / w:15 nothing +KPROP: ' = bloody / w:5 nothing +KMONS: a = human name:sickly name_adjective tile:mons_deformed_human / \ + human name:monstrous name_adjective tile:mons_deformed_human / \ + human name:deformed name_adjective tile:mons_deformed_human / \ + human name:twisted name_adjective tile:mons_deformed_human / \ + human name:grotesque name_adjective tile:mons_deformed_human / \ + human name:hideous name_adjective tile:mons_deformed_human / \ + human name:febrile name_adjective tile:mons_deformed_human +KMONS: b = elf name:sickly name_adjective tile:mons_deformed_elf / \ + elf name:monstrous name_adjective tile:mons_deformed_elf / \ + elf name:deformed name_adjective tile:mons_deformed_elf / \ + elf name:twisted name_adjective tile:mons_deformed_elf / \ + elf name:grotesque name_adjective tile:mons_deformed_elf / \ + elf name:hideous name_adjective tile:mons_deformed_elf / \ + elf name:febrile name_adjective tile:mons_deformed_elf +KMONS: d = orc name:sickly name_adjective tile:mons_deformed_orc / \ + orc name:monstrous name_adjective tile:mons_deformed_orc / \ + orc name:deformed name_adjective tile:mons_deformed_orc / \ + orc name:twisted name_adjective tile:mons_deformed_orc / \ + orc name:grotesque name_adjective tile:mons_deformed_orc / \ + orc name:hideous name_adjective tile:mons_deformed_orc / \ + orc name:febrile name_adjective tile:mons_deformed_orc +KMONS: u = ugly thing / w:2 very ugly thing +KMONS: J = pulsating lump +KMONS: x = small abomination +KMONS: X = large abomination +KMONS: e = giant eyeball +SUBST: " = J:1 / .:6 +KMONS: 8 = col:red clay golem name:flesh_golem name_replace \ + tile:mons_flesh_golem name_descriptor +KMONS: M = col:mutagenic tentacled monstrosity name:Cigotuvi's_Monster \ + name_replace +MARKER: M = lua:MonPropsMarker:new {description="This terrifying creation " \ + .. "looks to have been constructed from the bodyparts of every monstrous " \ + .. "creature imaginable.\n", quote="\"I beheld the wretch -- the " \ + .. "miserable monster whom I had created. He held up the curtain " \ + .. "of the bed; and his eyes, if eyes they may be called, were " \ + .. "fixed on me. His jaws opened, and he muttered some inarticulate " \ + .. "sounds, while a grin wrinkled his cheeks.\"\n -Frankenstein, Mary Shelley."} +MARKER: 8 = lua:MonPropsMarker:new {description="An animated mound of misshapen flesh.\n"} +: local sickdesc = "This poor creature looks hideously deformed.\n" +MARKER: a = lua:MonPropsMarker:new {description=sickdesc, speech_key="Cigotuvi_creatures"} +MARKER: b = lua:MonPropsMarker:new {description=sickdesc, speech_key="Cigotuvi_creatures"} +MARKER: d = lua:MonPropsMarker:new {description=sickdesc, speech_key="Cigotuvi_creatures"} +MARKER: ! = lua:fog_machine { \ + pow_max = 15, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 20, \ + size_buildup_time = 500, cloud_type = "mutagenic fog" \ + } +MARKER: ? = lua:fog_machine { \ + pow_max = 20, delay_min = 10, delay_max = 40, \ + size = 5, size_buildup_amnt = 10, \ + size_buildup_time = 25, cloud_type = "mutagenic fog" \ + } +LFLOORCOL: magenta +LFLOORTILE: floor_nerves +LROCKTILE: wall_flesh +TILE: c = wall_flesh +TILE: m = wall_transparent_flesh +TILE: + = no_random dngn_fleshy_orifice +COLOUR: m = yellow +COLOUR: c+ = lightred +COLOUR: W = green +: dgn.set_feature_desc_short("Some shallow water", "Viscous fluid") +: dgn.set_feature_desc_long("Some shallow water", "This sticky " +: .. "fluid seems to be secreted by the nearby walls.\n") +: dgn.set_feature_desc_short("stone wall", "sinewy wall") +: dgn.set_feature_desc_long("stone wall", "These walls look" +: .. " strangely organic, but incredibly tough and durable.\n") +: dgn.set_feature_desc_short("translucent rock wall", "thin membrane") +: dgn.set_feature_desc_long("translucent rock wall", "This strange" +: .. " membrane is thin enough to see through.\n") +: dgn.set_feature_desc_short("Floor", "pulsating floor") +: dgn.set_feature_desc_long("Floor", "This slick floor seems to pulse" +: .. " to an unknown beat.\n") +# Changing door descriptions like this currently doesn't work, needs fixing. +: lua_marker('+', props_marker { +: door_description_noun="fleshy orifice", +: door_berserk_verb_open="You part the %s%s", +: door_berserk_adjective="with a squelch!", +: door_berserk_verb_close="You squeeze the %s%s closed", +: door_noisy_verb_open="You part the %s%s with a squelch!", +: door_noisy_verb_close="You squeeze the %s%s closed with a squelch!", +: door_airborne_verb_open="You reach down and part the %s%s.", +: door_airborne_verb_close="You reach down and squeeze the %s%s closed.", +: door_verb_open="You part the %s%s.", +: door_verb_close="You squeeze the %s%s shut." +: }) +KITEM: $ = potion of degeneration / potion of decay / \ + potion of mutation / w:3 potion of strong poison / \ + w:2 potion of poison / w:5 potion of healing / \ + w:3 potion of heal wounds / w:1 potion of cure mutation +KITEM: * = gold +KITEM: % = amulet of resist mutation +KITEM: : = randbook disc:transmutation owner:Cigotuvi / any book / \ + acquire book +KITEM: ^ = book of morphology / randbook disc:transmutation \ + owner:Cigotuvi spell:cigotuvi's_degeneration +KITEM: | = staff of death +KITEM: l = scroll of summoning +NSUBST: L = 1:| / *:l +: wizlab_setup(_G, "Cigotuvi's Fleshworks") +MAP +ccccccccccccccccccccccccccccccccccccccccccccc +ccLLLcccc.cccccccccccAccccccccccccWWWW.u:cccc +c.....c.....ccc...ccc+cccccccccccW......u^ccc +c.X.........cc.u...m...m..ccccccW........u:cc +c.M........cc...b..m.x.m.a.cccccW..........cc +c.X........cc...d..m...m.u..ccccW.........Wcc +c..........cc.cccccm...mc...ccccc........Wccc +c.....ccc+ccccc..cccc+cccccccccccc......Wcccc +ccLLLccc..ccccuuu.cm...m..d..ccccc+cccccccccc +cccccccc..ccc..!...m.x.m.a..cccccc""ccccccccc +cc$$cccc..ccc.uuu..m...m...ccccccc""cc**%**cc +c$$$$cc...cccc....cm...m.cccWccccc""c.e.c.e.c +c$$$$+...cccccc..cccc+cccc...Wcccc""c...c...c +c$$$$c...ccccccccc.m...m.xx...Wccc""cmm+c+mmc +cc$ccccc+cccccccc..m.x.m...u..Wccc""c.......c +ccccccc...cccc...a.m...m....x.cc.c""c.......c +cccc.........c..b..m...mc....cc..c""c...!...c +c.xJ...ccc...cc..cccc+cccc..cc...c""c......cc +c...ccccccc+cccccc.m.....ccccJJJJc""+.....ccc +cc...ccccc'''cccc..m.....uccJJJJJc""ccccccccc +cc...ccc''''''cc.!.m.....<uc.....+"""""""""cc +c..cccc''''''''cc.......cuccc....cc"""""""""c +c..ccc'''''''''cccccc+cccccccccccccccccccc""c +c..cc''''''''''ccccc...ccccccc""""""""""""""c +c..cc''''''''''ccc.......cccc""""""""""""""cc +c..c'''''''''''cc.........ccc""cccccccccccccc +c..c+c+c+c+c+ccc...........cc""""""""""""""cc +c..cXcXcXcXcXccc...........ccc""""""""""""""c +c..cccccccccccc.....c.c.....cccccccccccccc""c +c...xJ........+......?......+"""""ccc"""""""c +c...xJ........c.....c.c.....c""""""c"""""""cc +ccmmmccccmmmcccc...........cccccc""c""ccccccc +c.....cc.....cccWW.......WWcc""""""c""""""ccc +cc.JJccccuuucccccWW..8..WWcc""""""ccc""""""cc +ccJJ.cccc.a.ccccccWWWWWWWccc""ccccccccccc""cc +cc.dccccccuuccccccccWWWccccc"""""""""""""""cc +ccccccccccccccccccccccccccccc"""""""""""""ccc +ccccccccccccccccccccccccccccccccccccccccccccc +ENDMAP + +############################################################################### +# Iskenderun's Mystic Tower +# +NAME: wizlab_iskenderun +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Iskenderun's Mystic Tower") +MAP +ENDMAP + +############################################################################### +# Lee's Rapid Deconstructor +# +NAME: wizlab_lee +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Lee's Rapid Deconstructor") +MAP +ENDMAP + +############################################################################### +# Lehudib's Crystal Spire +# +NAME: wizlab_lehudib +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Lehudib's Crystal Spire") +MAP +ENDMAP + +############################################################################### +# Maxwell's Forge +# +NAME: wizlab_maxwell +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Maxwell's Forge") +MAP +ENDMAP + +############################################################################### +# Olgreb's Toxic Laboratory +# +NAME: wizlab_olgreb +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Olgreb's Toxic Laboratory") +MAP +ENDMAP + +############################################################################### +# Ozocubu's Refrigerator +# +NAME: wizlab_ozocubu +ORIENT: encompass +TAGS: no_item_gen no_monster_gen no_rotate allow_dup +# unfinished +MONS: skeletal warrior patrolling col:lightblue ; any weapon good_item \ + . ice dragon armour +MONS: ice statue spells:ice_storm col:darkgrey name:black name_adjective +MONS: polar bear, white imp / wraith / freezing wraith, ice beast +MONS: ice dragon, nothing +SHUFFLE: BC +: local int = crawl.random2(3) +: if int == 0 then +SUBST: B = 3 +SUBST: C = 4 +: elseif int == 1 then +SUBST: B = 4 +SUBST: C = 5 +: elseif int == 2 then +SUBST: B = 3 +SUBST: C = 5 +: end +LROCKCOL: white +LFLOORCOL: lightblue +LROCKTILE: wall_ice +LFLOORTILE: floor_ice +KFEAT: XQ = rock_wall +KFEAT: R = closed_door +COLOUR: =c = darkgrey +MARKER: U = lua:fog_machine { \ + pow_max = 10, delay_min = 30, delay_max = 40, \ + size = 6, cloud_type = "freezing vapour" \ + } +{{ +-- Map prettyfication +smear_map({iterations=20, smear='x', onto='.xx', boxy=false}) +smear_map({iterations=40, smear='x', onto='.xx', boxy=false}) +--fill_disconnected({fill='x'}) + +function fridge (data, triggerable, triggerer, marker, ev) + if triggerer.type ~= "turn" or triggerer.sub_type ~= "countdown" then + return + end + + local loudlines = {'A voice screams, "Out, out, out!"', + 'There is a horrible grinding noise.', + 'There is a sudden noise, like that of ice cracking.', + 'A voice screams, "Freeze!"'} + + local speechlines = {'You feel a sudden draft.', + 'Snow coalesces in the air in front of you.', + 'You feel a sudden chill.', + 'The air becomes thick with cold.'} + + if not (you.silenced()) then + speechlines = util.append(speechlines, loudlines) + end + crawl.mpr(util.random_from(speechlines), "warning") + spells.refrigeration(crawl.random2avg(40, 6)) +end + +local fridge_marker = TriggerableFunction:new { func = fridge, repeated = true } + +fridge_marker:add_triggerer(DgnTriggerer:new { type="turn", + delay_min=500, delay_max=800, }) + +lua_marker("Q", fridge_marker) +}} +validate {{ return glyphs_connected('A', 'R') and glyphs_connected('A', '<')}} +: wizlab_setup(_G, "Ozocubu's Refrigerator") +MAP + xxxx + xxx..xxx + xxx.....xx + xx........xx xxxx xxx + xx....A.....xx xx..xx xxx.xx xxx + xx...<.......xxx....xxxx....xx xx.xx + xx..........................xxx...xx + xxx................c...........c..xx + xx..............cccccccRccccccc..xx + xx..............cBBBBc.cCCCCc....xx + xx.............cBBBBc.cCCCCc.....xx + xx..............cBBBBc.cCCCCc....xx + xx.............c.cBBBBc.cCCCCc.c...xx + xx.............2ccc=cccc+cccc=cccc.xx + xx.....www.....c...c.......c...c.xx cccc + xxx...wwxww....c.1.c...<...c.1.c.xx cccxxx + xxx.xxxwxx...c...c.......c...c..xxcccxxx$xx + xxxxwwwx...cc+ccccc+ccccc+cc...xccxx$$$$xx + Qxxwwxx...c...c.......c...c..xxcccxx$$$$x + xxxxxwwxx....c.1.c...U...c.1.c.xxccccxxx$$xx + xx.xxwwwxxx..cc...c.......c...ccxccxxxx333xx + xx...x.wwwxxccccc+ccccc+ccccc+cccccxx.....xx + x..6..xwwwwxc.......c.....c.......cx.......xx + xx...xxxwwww+.1.....+..1..+.....1.=.xxx.....xx + xx.xxxxxwxxc.......c.....c.......cxx xxx.xxx + xxxx xxxxxcccccccccccccccccccccccxx xxx +ENDMAP + +############################################################################### +# Cekugob's Oubliette +# +NAME: wizlab_cekugob +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Cekugob's Oubliette") +MAP +ENDMAP + +############################################################################### +# Zonguldrok's Shrine +# +NAME: wizlab_zonguldrok +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Zonguldrok's Shrine") +MAP +ENDMAP + +############################################################################### +# Wucad Mu's Monastery +# +# TODO: Rebrand everything properly! +NAME: wizlab_wucad +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +SUBST: T = t. +KMONS: 8 = orange crystal statue name:Statue_of_Wucad_Mu n_rpl n_the hd:20 hp:300 +KITEM: 8 = quarterstaff unrand:staff_of_wucad_mu, book of enchantments +COLOUR: =c = darkgrey +COLOUR: t = lightred / red w:3 +COLOUR: AG = red +COLOUR: < = lightred +KFEAT: ' = open_door +KFEAT: 1234567 = . +KMONS: 1234567 = nothing +LROCKCOL: darkgrey +ITEM: potion of experience, manual of fighting / manual of armour / manual of \ + spellcasting, manual of staves / manual of fighting / manual of dodging,\ + potion of gain dexterity / potion of gain strength / potion of gain \ + intelligence +LFLOORCOL: white +{{ +local function msgfn (data, triggerable, triggerer, marker, ev) + if data.trig == true then + return + end + + if crawl.one_chance_in(3) then + return + end + + if data.spot == 1 then + crawl.mpr("Strange, shadowy figures dance through the air in front of you.") + data.trig = true + elseif data.spot == 2 then + crawl.mpr("This room is filled with shadowy figures, quietly meditating.") + data.trig = true + elseif data.spot == 3 then + crawl.mpr("Here, spectral monks perform complicated, martial routines; they fade quickly.") + data.trig = true + elseif data.spot == 4 then + if you.silenced() then + return + end + crawl.mpr("Faint laughter comes from somewhere. Too faint to be real.") + data.trig = true + elseif data.spot == 5 then + if you.silenced() then + return + end + crawl.mpr("There is a faint scream of pain from a crouched figure. This too fades quickly.") + data.trig = true + elseif data.spot == 6 then + crawl.mpr("Grey monks gather around the fountain. They do not speak, nor look at each other.") + data.trig = true + elseif data.spot == 7 then + crawl.mpr("A figure sits in silent meditation. It spots you, gestures wildly, and disappears.") + data.trig = true + end +end + +local function trigfn (spot) + return Triggerable.synchronized_markers(function_at_spot(msgfn, + { spot=spot, trig = false}, true, { only_at_slave = true, + listen_to_slaves = true })) +end + +lua_marker('1', trigfn(1)) +lua_marker('2', trigfn(2)) +lua_marker('3', trigfn(3)) +lua_marker('4', trigfn(4)) +lua_marker('5', trigfn(5)) +lua_marker('6', trigfn(6)) +lua_marker('7', trigfn(7)) + +local function summon_monks (data, triggerable, triggerer, marker, ev) + if triggerer.type ~= "turn" or triggerer.sub_type ~= "countdown" then + return + end + + local msp = "human name:monk n_suf col:white hd:10 dur:2 sum:shadow_creatures" .. + " nas:old_memories tile:human_monk seen / rock troll name:monk n_suf".. + " dur:2 seen sum:shadow_creatures nas:old_memories tile:rock_troll_monk" .. + " / iron troll name:monk n_suf col:white dur:2 sum:shadow_creatures " .. + " nas:old_memories seen tile:iron_troll_monk" + + local count = 0 + for point in iter.circle_iterator(3) do + if crawl.one_chance_in(11) then + if feat.is_solid(point.x, point.y) or feat.destroys_items(point.x, point.y) then + else + local mon = dgn.create_monster(point.x, point.y, msp) + mon.set_prop("description", "It seems a flimsy representation of a monk;" + .. " nothing more than a half-formed memory.\n") + count = count + 1 + end + end + end + + crawl.redraw_view() + + if count == 1 then + crawl.mpr("One of the shadowy figures appears more solid!") + elseif count > 1 then + crawl.mpr("Shadows coalesce into solid form.") + end +end + +local summon_marker = TriggerableFunction:new ({ func=summon_monks, + repeated=true }) + +summon_marker:add_triggerer(DgnTriggerer:new {type="turn", delay_min=50, + delay_max=290 }) + +lua_marker('A', summon_marker) + +}} +: wizlab_setup(_G, "Wucad Mu's Monastery") +: set_border_fill_type("trees") +MAP +tttttttttttttttttttttttttttttttttttttttt +tttttttttttttttttttttttttttttttttttttttt +ttttttttttttttTTTTTTTttttttttttttttttttt +ttttttTTTTTTTT.......TTTTTTTTTTTTttttttt +tttttT.........ccccc.............Ttttttt +ttttT.........cc777cc.............Tttttt +tttT.......cccc77777cccc...........Ttttt +ttT.......cc..cc777cc..cc......ccc..Tttt +ttT......cc....cc+cc....cc....cc6cc..Ttt +ttT.....Tc....G.....G....cT..cc6U6cc.Ttt +ttT.....ccG.............Gcc...cc6cc..Ttt +ttT......+.......8.......+.....c+c...Ttt +ttT.....ccG.............Gcc..........Ttt +ttT.....Tc....G.....G....cT..........Ttt +ttT.c..cTcc.............cccT.........Ttt +ttTcc++ccccccc.......ccccccc+cc......Ttt +ttcc5555cccdeccc===cccfgccc444cc.....Ttt +ttc555555c....c.....c....c44444c....Tttt +ttc555555cc.............cc44444cc..ctttt +ttc555555ccccccccccccccccccc+cccc++ccttt +ttcc5555ccc.............ccc222cc3333cctt +tttccccccc...............+22222+33333ctt +ttttttttccc..ccc+++ccc..ccc222cc3333cctt +tttttttttcccccTT111Ttccccccccccccccccttt +ttttttttttttttT11111Tttttttttttttttttttt +tttttttttttT.........TTTTTTttttttttttttt +tttccccctttT.....G........Ttcccccttttttt +ttcc...cctT..............Ttcc...cctttttt +ttc..A..'..................'..<..ctttttt +ttcc...cctT.....TTTT.....Ttcc...cctttttt +tttccccctttT...TTttTT....Tttcccccttttttt +ttttttttttttTTTTttttTTTTTttttttttttttttt +tttttttttttttttttttttttttttttttttttttttt +tttttttttttttttttttttttttttttttttttttttt +ENDMAP + +############################################################################### +# Boton's Bayou +# +NAME: wizlab_botono +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Botono's Bayou") +MAP +ENDMAP + +############################################################################### +# Ukta's Hut +# +NAME: wizlab_ukta +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "Ukta's Hut") +MAP +ENDMAP + +############################################################################### +# The Alchemist's Tower +# +NAME: wizlab_alchemist +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +: wizlab_setup(_G, "The Alchemist's Tower") +MAP +ENDMAP + +############################################################################### +# Random and semi-random Wizard vaults +############################################################################### + +############################################################################### +# Chambers of the Cloud Mage +# +# To-do: make it look as cool in tiles. +NAME: wizlab_cloud +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +KMONS: 1 = col:silver patrolling wizard name:Cloud_Mage name_replace \ + name_definite spells:mephitic_cloud;freezing_cloud;poisonous_cloud;\ + airstrike;blink_range;blink_range hd:20 hp:150 ; \ + robe unrand:robe_of_clouds . quick blade ego:electrocution | \ + quick blade ego:freezing | dagger ego:speed | dagger ego:electrocution \ + | dagger ego:freezing +KMONS: * = w:5 vapour / w:20 air elemental +SUBST: . = .:120 * +MARKER: ! = lua:fog_machine { \ + pow_max = 10, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 5, \ + size_buildup_time = 25, cloud_type = "grey smoke", \ + colour = "white", name = "white fluffiness" \ + } +KITEM: % = w:20 potion of levitation / w:20 potion of water / \ + potion of magic / potion of speed / potion of resistance / \ + gold / potion of agility / potion of brilliance +KITEM: ? = randbook disc:air +KITEM: $ = wand of lightning / staff of air / ring of levitation /\ + gold / scroll of silence / ring of teleport control / \ + amulet of controlled flight / quick blade / ring of evasion /\ + ring of see invisible +KFEAT: # = deep_water +NSUBST: $ = 1:? / *:$ +SUBST: $ = $% +COLOUR: .+<co = white +COLOUR: wW# = silver +: set_border_fill_type('open_sea') +: wizlab_setup(_G, "The Cloud Mage's Chambers") +MAP +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +#####################oooooooooooooooooooooooooooooo################## +###################ooo............................ooo################ +#################ooo...wwwwwwwwwwwwwwwwwwwwwwwwww...ooo############## +################oo...wwwwwwwccccccccccccccccccccwww...oo############# +################o..wwwwwwwccccccccccccccccccccccwwwww..o############# +###############oo.wwwwwwcccccccc..c..c..c..c..ccwwwwww.oo############ +###############o..wwwwwcc.....c.!............!.cwwwwww..o############ +##############oo.wwwwwwcc.$$$.c................cwwwwwww.oo########### +##############o..wwwwwccc.$$$.c................cWWWWWWW..o########### +##############o..wwwwwccc.$$$.+...1............+.........o########### +##############o..wwwwwccc.$$$.c................cWWWWWWW..o########### +##############oo.wwwwwwcc.$$$.c................cwwwwwww.oo########### +###############o..wwwwwcc.....c.!............!.cwwwwww..o############ +###############oo.wwwwwwcccccccc..c..c..c..c..ccwwwwww.oo############ +################o..wwwwwwwccccccccccccccccccccccwwwww..o############# +################oo...wwwwwwwccccccccccccccccccccwww...oo############# +#################oo....wwwwwwwwwwwwwwwwwwwwwwwwww...ooo############## +##################oooo............................ooo################ +#####################ccc.......cccccccccccccccccccc################## +#####################oo.......oo#####ooo############################# +####################oo.......oo#####oo.oo############################ +###################oo.......oo#####oo.!.oo########################### +###################o!.....!ooo####oo.....ooo######################### +###################ooo.......ooo#oo........oo######################## +####################oooo.......ooo..........oo####################### +#######################ooo!.....ooo..........o####################### +########################o.....!ooocc+cc......oo###################### +#######################ooo......occ...cc!.....o###################### +########################o......occ.....cc.....oo##################### +#######################ooo......c.......c......o##################### +#######################o.!.....oc.<.A.<.c......o##################### +#######################o.c......c.......co...!oo##################### +#######################o.co.....cc.....cc......o##################### +#######################c+cccc....cc...cc!.....oo##################### +#######################o....co...!ccccc......oo###################### +#######################o.%%.co.............ooc####################### +#######################o.%%.coo...........ccccccccc################## +#######################o....c#o..........!...+....o################## +#######################oooooc#oo........oooooc.%%.o################## +###############################oo......oo####c.%%.o################## +################################oo.!..oo#####c....o################## +#################################oooooo######cooooo################## +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +##################################################################### +ENDMAP + +############################################################################### +# Random wizlab (death theme) (by Mu.) +# +NAME: wizlab_random1 +ORIENT: encompass +#TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +LFLOORCOL: darkgrey +COLOUR: =c = white +COLOUR: " = red +KMONS: a = slave +KMONS: b = human +KMONS: d = troll +NSUBST: b = 3:d / *:b +KMONS: p = vampire knight / w:5 vampire +KMONS: V = vampire mage +KMONS: L = patrolling lich / w:5 patrolling ancient lich +KMONS: u = patrolling mummy / w:2 patrolling guardian mummy +KMONS: M = patrolling mummy priest / w:2 patrolling greater mummy +MARKER: u = lua:fog_machine { \ + pow_max = 10, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 5, \ + size_buildup_time = 25, cloud_type = "foul pestilence" \ + } +MARKER: M = lua:fog_machine { \ + pow_max = 10, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 5, \ + size_buildup_time = 25, cloud_type = "foul pestilence" \ + } +KITEM: $ = potion of healing / potion of heal wounds / potion of decay / \ + scroll of torment / potion of blood / w:20 gold +KITEM: | = ring of regeneration / ring of life protection / amulet of warding / \ + wand of draining / wand of healing / any book / acquire book / \ + staff of death / potion of healing / potion of heal wounds / \ + potion of decay / potion of blood +SUBST: | = |$ +: wizlab_setup(_G, "Random1") +MAP +cccccccccccccccccccccccccccccccccccccccccccccc +ccc....LccccccccccccucM.cucccccccccccccccccccc +ccc.....c...c$$$cccc.c..c.cccccccccccccc.....c +ccc.....+.<..$$$cccc.c..c.c..................c +ccc.....c...c$$$cccc+c++c+c.cccccccccccc.....c +ccc.....cccccccccccc......c+cccccccccccc.....c +cccc.ccccccccccc...c......c....ccccccccc....Lc +cccc.ccccccc.......+......+........ccccccc+ccc +cccc.ccccc.........c......c......p...cccc...cc +cccc.cccc....nnnnnncccccccc...........ccc.<.cc +cccc.ccc.p..nna.aaccc|||||ccc..........cc...cc +cccc.ccc....n..cccc...nnn...ccccnnnnn..ccc.ccc +cccc.cc....nnacc......nbn......ccaaan...c$$$cc +cccc.cc....n..c..nnn..nnn..nnn..c..an...c$$$cc +cccc.cc....nacc..nbn..."...nbn..cca.n...c$$$cc +cccc..+....n.c...nnn..."...nnn...cccc+cccccccc +ccccccccc+cccc......"..".."......cc......+..uc +ccu..+......c|......."V"V".......|c......ccccc +cccccc......c|.nnn...V"""V...nnn.|c......+..Mc +ccM..+......c|.nbn"""""Y"""""nbn.|c......+...c +cc...+......c|.nnn...V"""V...nnn.|c......ccccc +cccccc......c|.......".".".......|c......+..uc +ccu..+......cc......"..".."......cccc+cccccccc +ccccccccc+cccc...nnn..."...nnn...c.n....+...cc +cc...cc...n.acc..nbn..."...nbn..cc.n....ccc.cc +cc.A.cc...n.a.c..nnn..nnn..nnn..c.an....ccc.cc +cc...cc...na..cc......nbn......cc.nn....ccc.cc +ccc.cccc..nnnnncccc...nnn...cccc.an....cccc.cc +cc...ccc..........ccc.....ccca...nn....cccc.cc +cc.<.cccc...........ccc+ccccnnnnnn..p.ccccc.cc +cc...ccccc....p.....c......c.........cccccc.cc +ccc+cccccccc........c......+.......cccccccc.cc +c.....cccccccccc....c......c...cccccccccccc.cc +c.aaa.ccccccccccccc+c......ccccccccccccc.....c +c..p..ccccccccccccc.c+c++c+ccccc$$$c...c.....c +c..p................c.c..c.ccccc$$$..<.+.....c +c.....ccccccccccccccc.c..c.ccccc$$$c...c.....c +cccccccccccccccccccccuc.McucccccccccccccL....c +cccccccccccccccccccccccccccccccccccccccccccccc +ENDMAP + +############################################################################### +# Halls of the Hellbinder (by Mu.) +# +NAME: wizlab_demon +ORIENT: encompass +TAGS: wizlab no_item_gen no_monster_gen no_rotate allow_dup +LFLOORCOL: red +LROCKCOL: red +COLOUR: c = darkgrey +COLOUR: " = yellow +KPROP: ' = bloody / nothing +KFEAT: _ = altar_makhleb +KPROP: _ = bloody +MARKER: ! = lua:fog_machine { \ + pow_max = 10, delay_min = 10, delay_max = 40, \ + size = 1, size_buildup_amnt = 5, \ + size_buildup_time = 25, cloud_type = "flame" } +KMONS: 2 = w:1 Lorocyproca / w:20 soul eater / w:20 reaper /\ + w:20 ice devil / w:20 sun demon / hellion +KMONS: 3 = blue devil / iron devil / shadow demon / neqoxec /\ + w:5 tormentor / ynoxinul +KMONS: 4 = kobold demonologist / deep elf demonologist +KMONS: 5 = blue death / green death / cacodemon +KMONS: 1 = col:fire patrolling wizard name:Hellbinder name_replace \ + name_definite spells:call_imp;summon_demon;haste;\ + hellfire;blink_away;throw_flame hd:20 hp:150 ; demon blade \ + . robe +KITEM: $ = gold / w:1 scroll of torment / w:1 scroll of summoning +KITEM: | = rod of demonology / ring of fire / ring of protection from fire /\ + amulet of conservation / scroll of torment / scroll of summoning /\ + demon blade w:2 / demon whip w:2 / demon trident w:3 / gold / wand of fire /\ + wand of draining / staff of summoning / any book / any good_item +KITEM: B = randbook disc:summoning +NSUBST: | = 1:B / *:| +: set_random_mon_list([[imp / shadow imp / lemure / manes / quasit / white imp / ]] +: .. [[ufetubus / midge / red devil / rotting devil / hairy devil / ]]) +MAP +cccccccccccccccccccccccccccccccccccccccccccccc +ccccccc.cccccc...................cccccc.cccccc +ccccc.....cccc.cc+ccccc+ccccc+cc.cccc.....cccc +cccc.."""..ccc.c...cccc.cccc...c.ccc..."...ccc +ccc.."..."..cc.+.A.ccc...ccc.<.+.cc.."....."cc +ccc."...3.".cc.c...cc.....cc...c.cc.""".3.".cc +cc.."..3.."....ccccc..c.c..ccccc.....".3."...c +cc..".3..."....cccc..cc.cc..cccc......3."....c +ccc.".....".cc.ccc.....4.....ccc.cc...."""""cc +ccc.."..."..cc.cc..cccc.cccc..cc.cc...".....cc +cccc.."""..ccc.+...............+.ccc.".....ccc +ccccc.....cccc.ccccccccccccccccc.cccc.....cccc +ccccccc.cccccc...................cccccc.cccccc +ccccccc.cccccc.cccccccc.cccccccc.cccccc.cccccc +ccccccc.ccccccc.cccccc.c.cccccc.ccccccc.cccccc +ccccccc.cccccccc.cccc.ccc.cccc.cccccccc.cccccc +ccccccc.ccccccccc.cc.ccccc.cc.ccccccccc.cccccc +ccccccc.cccccccccc..ccccccc..cccccccccc.cccccc +ccccccc.cccc.5...................5.cccc.cccccc +ccccccc.cccc..ccccccccc.ccccccccc..cccc.cccccc +ccccccc.ccccc..cccc$$$c.c$$$cccc..ccccc.cccccc +ccccccc.cccccc..ccc$$$+.+$$$ccc..cccccc.cccccc +ccccccc.cc.cccc..cc$$$c.c$$$cc..cccc.cc.cccccc +ccccccc......ccc..ccccc.ccccc..ccc.."...cccccc +ccccccc..."...ccc..cccc.cccc..ccc.."..".cccccc +cccccc.."..."..ccc..ccc.ccc..ccc.."..""".ccccc +cccccc..."2"...cccc..cc.cc..cccc."2222"..ccccc +ccccc.."2"""2"..cccc.......cccc.".........cccc +cccccc..""2""...ccccc."4".ccccc""""""""""ccccc +cccccc..."."....ccccc.."..ccccc........".ccccc +ccccccc.."""....ccccc.."..ccccc.......".cccccc +cccccccc........ccccc.....ccccc......".ccccccc +cccccccccc......cccc..ccc..cccc......ccccccccc +ccccccccccc.....ccc..ccccc..ccc.....cccccccccc +cccccccccccc.....c...ccccc...c.....ccccccccccc +cccccccccccc.."."..cc.ccc.cc.."."..ccccccccccc +ccccccccccccc.."..cccc.c.cccc.."..cccccccccccc +ccccccccccccc."...ccccc!ccccc...".cccccccccccc +cccccccccccccc.....ccc.c.ccc.....ccccccccccccc +ccccccccccccccc.....c.ccc.c.....cccccccccccccc +ccccccccccccccccc.....ccc.....cccccccccccccccc +ccccccccccccccccccc.........cccccccccccccccccc +ccccccccccccccccccccc.....cccccccccccccccccccc +ccccccccccccccccccccccc+cccccccccccccccccccccc +ccccccccccccccccccccc.....cccccccccccccccccccc +ccccccccccccccccccc..."""...cccccccccccccccccc +ccccccccccccccccc...""...""...cccccccccccccccc +cccccccccccccccc.."".......""..ccccccccccccccc +ccccccccccccccc.."..".....".."..cccccccccccccc +ccccccccccccccc."....lllll....".cccccccccccccc +cccccccccccccc.."...ll"."ll..."..ccccccccccccc +cccccccccccccc.".".ll"..."ll.".".ccccccccccccc +ccccccccccccc.."...."..1.."...."..cccccccccccc +ccccccccccccc.."..."".'''.""..."..cccccccccccc +ccccccccccccc..".."xx"'_'"xx".."..cccccccccccc +cccccccccccc....""..xx"'"xx..""....ccccccccccc +cccccccccccc|..."..".xxxxx.".."...|ccccccccccc +cccccccccccc||..."".........""...||ccccccccccc +cccccccccccc|||.................|||ccccccccccc +cccccccccccccccccccccccccccccccccccccccccccccc +ENDMAP diff --git a/crawl-ref/source/dbg-util.cc b/crawl-ref/source/dbg-util.cc index 57bf3b1b37..84e0f87a0e 100644 --- a/crawl-ref/source/dbg-util.cc +++ b/crawl-ref/source/dbg-util.cc @@ -129,12 +129,15 @@ void debug_dump_levgen() else { const CrawlHashTable &vaults = props[TEMP_VAULTS_KEY].get_table(); - CrawlHashTable::const_iterator i = vaults.begin(); - - for (; i != vaults.end(); ++i) + if (!vaults.empty()) { - mprf(" %s: %s", i->first.c_str(), - i->second.get_string().c_str()); + CrawlHashTable::const_iterator i = vaults.begin(); + + for (; i != vaults.end(); ++i) + { + mprf(" %s: %s", i->first.c_str(), + i->second.get_string().c_str()); + } } } mpr(""); @@ -413,5 +416,3 @@ int debug_cap_stat(int stat) stat > 127 ? 127 : stat); } - - diff --git a/crawl-ref/source/dgn-shoals.cc b/crawl-ref/source/dgn-shoals.cc index 201f3f4690..57025a67d4 100644 --- a/crawl-ref/source/dgn-shoals.cc +++ b/crawl-ref/source/dgn-shoals.cc @@ -1,22 +1,30 @@ #include "AppHdr.h" #include "branch.h" +#include "colour.h" #include "coord.h" #include "coordit.h" #include "dungeon.h" #include "dgn-shoals.h" #include "env.h" +#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"; @@ -37,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 @@ -48,10 +73,17 @@ enum shoals_height_thresholds SHT_SHALLOW_WATER = -14 }; -static double _to_radians(int degrees) +enum tide_direction { - return degrees * M_PI / 180; -} + TIDE_RISING, + TIDE_FALLING +}; + +static tide_direction _shoals_tide_direction; +static monsters *tide_caller = NULL; +static coord_def tide_caller_pos; +static long tide_called_turns = 0L; +static int tide_called_peak = 0; static dungeon_feature_type _shoals_feature_by_height(int height) { @@ -68,6 +100,40 @@ static dungeon_feature_type _shoals_feature_at(const coord_def &c) return _shoals_feature_by_height(height); } +static int _shoals_feature_height(dungeon_feature_type feat) +{ + switch (feat) + { + case DNGN_FLOOR: + return SHT_FLOOR; + case DNGN_SHALLOW_WATER: + return SHT_SHALLOW_WATER; + case DNGN_DEEP_WATER: + return SHT_SHALLOW_WATER - 1; + default: + return 0; + } +} + +// Returns true if the given feature can be affected by Shoals tides. +static bool _shoals_tide_susceptible_feat(dungeon_feature_type feat) +{ + return (feat_is_water(feat) || feat == DNGN_FLOOR); +} + +// Return true if tide effects can propagate through this square. +// NOTE: uses RNG! +static bool _shoals_tide_passable_feat(dungeon_feature_type feat) +{ + return (feat_is_watery(feat) + // The Shoals tide can sometimes lap past the doorways of rooms + // near the water. Note that the actual probability of the tide + // getting through a doorway is this probability * 0.5 -- + // see _shoals_apply_tide. + || (feat == DNGN_OPEN_DOOR && !one_chance_in(3)) + || (feat == DNGN_CLOSED_DOOR && one_chance_in(3))); +} + static void _shoals_init_heights() { env.heightmap.reset(new grid_heightmap); @@ -75,33 +141,13 @@ 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; + return dgn_random_point_from(c, radius, _shoals_margin); } -static coord_def _random_point_from(const coord_def &c, int radius, - int directed_angle = -1) +static coord_def _random_point(int offset = 0) { - const double directed_radians( - directed_angle == -1? 0.0 : _to_radians(directed_angle)); - while (true) { - 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; - } - } -} - -static coord_def _random_point(int offset = 0) { return coord_def(random_range(offset, GXM - offset - 1), random_range(offset, GYM - offset - 1)); } @@ -111,7 +157,8 @@ static void _shoals_island_center(const coord_def &c, int n_perturb, int radius, { for (int i = 0; i < n_perturb; ++i) { coord_def p = _random_point_from(c, random2(1 + radius)); - shoals_heights(p) += random_range(bounce_low, bounce_high); + if (!p.origin()) + shoals_heights(p) += random_range(bounce_low, bounce_high); } } @@ -152,12 +199,12 @@ static void _shoals_build_island() const int addition_offset = random_range(2, 10); coord_def offsetC = _random_point_from(c, addition_offset); - - _shoals_island_center(offsetC, random_range(N_PERTURB_OFFSET_LOW, - N_PERTURB_OFFSET_HIGH), - random_range(PERTURB_OFFSET_RADIUS_LOW, - PERTURB_OFFSET_RADIUS_HIGH), - 25, 35); + if (!offsetC.origin()) + _shoals_island_center(offsetC, random_range(N_PERTURB_OFFSET_LOW, + N_PERTURB_OFFSET_HIGH), + random_range(PERTURB_OFFSET_RADIUS_LOW, + PERTURB_OFFSET_RADIUS_HIGH), + 25, 35); } } @@ -176,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; @@ -353,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 @@ -416,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); @@ -433,20 +814,46 @@ 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(); } -// 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; +// Search the map for vaults and set the terrain heights for features +// in the vault to reasonable levels. +void shoals_postprocess_level() +{ + if (!player_in_branch(BRANCH_SHOALS) || !env.heightmap.get()) + return; -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; + for (rectangle_iterator ri(1); ri; ++ri) + { + const coord_def c(*ri); + if (!(dgn_Map_Mask(c) & MMT_VAULT)) + continue; + + const dungeon_feature_type feat(grd(c)); + if (!_shoals_tide_susceptible_feat(feat)) + continue; + + const dungeon_feature_type expected_feat(_shoals_feature_at(c)); + // It would be nice to do actual height contours within + // vaults, but for now, keep it simple. + if (feat != expected_feat) + shoals_heights(c) = _shoals_feature_height(feat); + } +} 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) @@ -455,12 +862,12 @@ 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; } static coord_def _shoals_escape_place_from(coord_def bad_place, - bool monster_free) + actor *act, item_def *it) { int best_height = -1000; coord_def chosen; @@ -468,8 +875,10 @@ static coord_def _shoals_escape_place_from(coord_def bad_place, { coord_def p(*ai); const dungeon_feature_type feat(grd(p)); - if (!feat_is_solid(feat) && !feat_destroys_items(feat) - && (!monster_free || !actor_at(p))) + if (!feat_has_solid_floor(feat)) + continue; + + if (!act || !actor_at(p)) { if (best_height == -1000 || shoals_heights(p) > best_height) { @@ -487,11 +896,22 @@ static bool _shoals_tide_sweep_items_clear(coord_def c) if (link == NON_ITEM) return true; - const coord_def target(_shoals_escape_place_from(c, false)); - if (target.origin()) - return false; + for (stack_iterator si(c); si; ++si) + { + const coord_def target(_shoals_escape_place_from(c, NULL, &*si)); + // Don't abort tide entry because of items. If we can't sweep the + // item clear here, let dungeon_terrain_changed teleport the item + // to the nearest safe square. + int id = si.link(); + + // Let the tide break up stacks + if (!one_chance_in(2)) + continue; + + if (!target.origin()) + move_item_to_grid(&id, target); + } - move_item_stack_to_grid(c, target); return true; } @@ -513,7 +933,7 @@ static bool _shoals_tide_sweep_actors_clear(coord_def c) if (monster_habitable_grid(mvictim, DNGN_DEEP_WATER)) return true; } - coord_def evacuation_point(_shoals_escape_place_from(c, true)); + coord_def evacuation_point(_shoals_escape_place_from(c, victim, NULL)); // The tide moves on even if we cannot evacuate the tile! if (!evacuation_point.origin()) victim->move_to_pos(evacuation_point); @@ -541,6 +961,19 @@ static void _shoals_apply_tide_feature_at(coord_def c, dungeon_terrain_changed(c, feat, true, false, true); } +// Determines if the tide is rising or falling based on before and +// after features at the same square. +static tide_direction _shoals_feature_tide_height_change( + dungeon_feature_type oldfeat, + dungeon_feature_type newfeat) +{ + const int height_delta = + _shoals_feature_height(newfeat) - _shoals_feature_height(oldfeat); + // If the apparent height of the new feature is greater (floor vs water), + // the tide is receding. + return height_delta < 0? TIDE_RISING : TIDE_FALLING; +} + static void _shoals_apply_tide_at(coord_def c, int tide) { const int effective_height = shoals_heights(c) - tide; @@ -549,10 +982,35 @@ static void _shoals_apply_tide_at(coord_def c, int tide) // Make sure we're not sprouting new walls. if (feat_is_wall(newfeat)) newfeat = DNGN_FLOOR; + const dungeon_feature_type oldfeat = grd(c); + + if (oldfeat == newfeat + || (_shoals_feature_tide_height_change(oldfeat, newfeat) != + _shoals_tide_direction)) + { + return; + } _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]; @@ -575,12 +1033,16 @@ static void _shoals_apply_tide(int tide) for (int i = 0, size = cpage.size(); i < size; ++i) { coord_def c(cpage[i]); - const bool was_wet(feat_is_water(grd(c))); + const bool was_wet(_shoals_tide_passable_feat(grd(c))); seen_points(c) = true; - _shoals_apply_tide_at(c, tide); - - // Only wet squares can propagate the tide onwards. - if (was_wet) + _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 + // effects!) can propagate the tide onwards. If the tide is + // receding and just left the square dry, there's only a chance of + // it continuing past and draining other squares through this one. + if (was_wet && (is_wet || coinflip())) { for (adjacent_iterator ai(c); ai; ++ai) { @@ -590,7 +1052,7 @@ static void _shoals_apply_tide(int tide) if (!seen_points(adj)) { const dungeon_feature_type feat = grd(adj); - if (feat_is_water(feat) || feat == DNGN_FLOOR) + if (_shoals_tide_susceptible_feat(feat)) { npage.push_back(adj); seen_points(adj) = true; @@ -610,13 +1072,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; @@ -628,11 +1099,48 @@ 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; while (turns_elapsed-- > 0) _shoals_run_tide(tide, acc); env.properties[ENVP_SHOALS_TIDE_KEY] = short(tide); env.properties[ENVP_SHOALS_TIDE_VEL] = short(acc); - _shoals_apply_tide(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 aae09ff653..1d14c620f2 100644 --- a/crawl-ref/source/dgn-shoals.h +++ b/crawl-ref/source/dgn-shoals.h @@ -2,6 +2,8 @@ #define DGN_SHOALS_H void prepare_shoals(int level_number); -void shoals_apply_tides(int turns_elapsed); +void shoals_postprocess_level(); +void shoals_apply_tides(int turns_elapsed, bool force = false); +void shoals_release_tide(monsters *caller); #endif diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index 3b0a1262b3..58aac3b22a 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -523,7 +523,7 @@ void full_describe_view() if (unknown_mimic) // It'll be on top. list_items.push_back(get_mimic_item(mon)); - const int oid = igrd(*ri); + const int oid = you.visible_igrd(*ri); if (oid == NON_ITEM) continue; @@ -1498,7 +1498,7 @@ void direction(dist& moves, const targetting_type restricts, { case ATT_FRIENDLY: m->attitude = ATT_GOOD_NEUTRAL; - m->flags &= ~MF_CREATED_FRIENDLY; + m->flags &= ~MF_NO_REWARD; m->flags |= MF_WAS_NEUTRAL; break; case ATT_GOOD_NEUTRAL: @@ -1513,7 +1513,7 @@ void direction(dist& moves, const targetting_type restricts, break; case ATT_HOSTILE: m->attitude = ATT_FRIENDLY; - m->flags |= MF_CREATED_FRIENDLY; + m->flags |= MF_NO_REWARD; break; } @@ -1764,10 +1764,10 @@ std::string get_terse_square_desc(const coord_def &gc) else desc = mons.full_name(DESC_PLAIN, true); } - else if (igrd(gc) != NON_ITEM) + else if (you.visible_igrd(gc) != NON_ITEM) { - if (mitm[igrd(gc)].is_valid()) - desc = mitm[igrd(gc)].name(DESC_PLAIN); + if (mitm[you.visible_igrd(gc)].is_valid()) + desc = mitm[you.visible_igrd(gc)].name(DESC_PLAIN); } else desc = feature_description(gc, false, DESC_PLAIN, false); @@ -1793,7 +1793,7 @@ void get_square_desc(const coord_def &c, describe_info &inf, return; const monsters* mons = monster_at(c); - const int oid = igrd(c); + const int oid = you.visible_igrd(c); if (mons && mons->visible_to(&you)) { @@ -1836,7 +1836,7 @@ void full_describe_square(const coord_def &c) return; const monsters* mons = monster_at(c); - const int oid = igrd(c); + const int oid = you.visible_igrd(c); if (mons && mons->visible_to(&you)) { @@ -3425,7 +3425,7 @@ static void _describe_cell(const coord_def& where, bool in_range) cloud_described = true; } - int targ_item = igrd(where); + int targ_item = you.visible_igrd(where); if (targ_item != NON_ITEM) { diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index 61b09b2717..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" @@ -233,6 +234,8 @@ typedef std::list<coord_def> coord_list; static void _dgn_set_floor_colours(); static bool _fixup_interlevel_connectivity(); +void dgn_postprocess_level(); + ////////////////////////////////////////////////////////////////////////// // Static data @@ -374,6 +377,8 @@ bool builder(int level_number, int level_type) vault_names.push_back(you.level_type_name); } + dgn_postprocess_level(); + dgn_Layout_Type.clear(); Level_Unique_Maps.clear(); Level_Unique_Tags.clear(); @@ -398,6 +403,13 @@ bool builder(int level_number, int level_type) return (false); } +// Should be called after a level is constructed to perform any final +// fixups. +void dgn_postprocess_level() +{ + shoals_postprocess_level(); +} + void level_welcome_messages() { for (int i = 0, size = Level_Vaults.size(); i < size; ++i) @@ -823,7 +835,17 @@ void dgn_register_place(const vault_placement &place, bool register_vault) dgn_register_vault(place.map); if (!place.map.has_tag("layout")) - _mask_vault(place, MMT_VAULT | MMT_NO_DOOR); + { + if (place.map.orient == MAP_ENCOMPASS) + { + for (rectangle_iterator ri(0); ri; ++ri) + dgn_Map_Mask(*ri) |= MMT_VAULT | MMT_NO_DOOR; + } + else + { + _mask_vault(place, MMT_VAULT | MMT_NO_DOOR); + } + } if (place.map.has_tag("no_monster_gen")) _mask_vault(place, MMT_NO_MONS); @@ -876,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; @@ -888,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) @@ -1745,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', " @@ -2224,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 { @@ -2301,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 { @@ -4063,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; @@ -4092,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); @@ -4122,6 +4146,7 @@ bool dgn_place_map(const map_def *mdef, bool clobber, bool make_no_exits, } setup_environment_effects(); + dgn_postprocess_level(); } return (did_map); @@ -4523,6 +4548,11 @@ static void _dgn_give_mon_spec_items(mons_spec &mspec, if (item_made != NON_ITEM && item_made != -1) { item_def &item(mitm[item_made]); + + // Mark items on summoned monsters as such. + if (mspec.abjuration_duration != 0) + item.flags |= ISFLAG_SUMMONED; + if (!mon.pickup_item(item, 0, true)) destroy_item(item_made, true); } @@ -4910,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; @@ -5975,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; } @@ -5989,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; } @@ -6003,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; } @@ -6147,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. @@ -7592,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/effects.cc b/crawl-ref/source/effects.cc index bede6d46c2..a4a765177e 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -2315,14 +2315,23 @@ int acquirement_create_item(object_class_type class_wanted, if (agent > GOD_NO_GOD && agent < NUM_GODS && agent == you.religion) thing.inscription = "god gift"; - move_item_to_grid( &thing_created, pos ); - - // This should never actually be NON_ITEM because of the way - // move_item_to_grid works (doesn't create a new item ever), - // but we're checking it anyways. -- bwr + // Moving this above the move since it might not exist after falling. if (thing_created != NON_ITEM && !quiet) canned_msg(MSG_SOMETHING_APPEARS); + // If a god wants to give you something but the floor doesn't want it, + // it counts as a failed acquirement - no piety, etc cost. + if (feat_destroys_item(grd(pos), thing) && (agent > GOD_NO_GOD) && + (agent < NUM_GODS)) + { + if (agent == GOD_XOM) + simple_god_message(" snickers.", GOD_XOM); + else + return _failed_acquirement(quiet); + } + + move_item_to_grid( &thing_created, pos ); + return (thing_created); } @@ -2380,37 +2389,9 @@ bool acquirement(object_class_type class_wanted, int agent, } } - if (feat_destroys_items(grd(you.pos()))) - { - // How sad (and stupid). - if (!silenced(you.pos()) && !quiet) - mprf(MSGCH_SOUND, feat_item_destruction_message(grd(you.pos()))); + acquirement_create_item(class_wanted, agent, quiet, you.pos(), debug); - if (agent > GOD_NO_GOD && agent < NUM_GODS) - { - if (agent == GOD_XOM) - simple_god_message(" snickers.", GOD_XOM); - else - { - ASSERT(!"God gave gift item while player was on grid which " - "destroys items."); - mprf(MSGCH_ERROR, "%s gave a god gift while you were on " - "terrain which destroys items.", - god_name((god_type) agent).c_str()); - } - } - - *item_index = NON_ITEM; - - // Well, the item may have fallen in the drink, but the intent is - // that acquirement happened. -- bwr - return (true); - } - - *item_index = - acquirement_create_item(class_wanted, agent, quiet, you.pos(), debug); - - return (*item_index != NON_ITEM); + return (true); } bool recharge_wand(int item_slot) @@ -2969,11 +2950,6 @@ static void _hell_effects() } } -static bool _is_floor(const dungeon_feature_type feat) -{ - return (!feat_is_solid(feat) && !feat_destroys_items(feat)); -} - // This function checks whether we can turn a wall into a floor space and // still keep a corridor-like environment. The wall in position x is a // a candidate for switching if it's flanked by floor grids to two sides @@ -2995,8 +2971,8 @@ static bool _feat_is_flanked_by_walls(const coord_def &p) return (false); return (feat_is_wall(grd(adjs[0])) && feat_is_wall(grd(adjs[1])) - && _is_floor(grd(adjs[2])) && _is_floor(grd(adjs[3])) - || _is_floor(grd(adjs[0])) && _is_floor(grd(adjs[1])) + && feat_has_solid_floor(grd(adjs[2])) && feat_has_solid_floor(grd(adjs[3])) + || feat_has_solid_floor(grd(adjs[0])) && feat_has_solid_floor(grd(adjs[1])) && feat_is_wall(grd(adjs[2])) && feat_is_wall(grd(adjs[3]))); } @@ -3101,7 +3077,7 @@ static bool _deadend_check_floor(const coord_def &p) continue; const coord_def a(p.x, p.y+2*i); - if (!in_bounds(a) || _is_floor(grd(a))) + if (!in_bounds(a) || feat_has_solid_floor(grd(a))) continue; for (int j = -1; j <= 1; j++) @@ -3114,7 +3090,7 @@ static bool _deadend_check_floor(const coord_def &p) continue; const coord_def c(p.x+j, p.y+i); - if (_is_floor(grd(c)) && !_is_floor(grd(b))) + if (feat_has_solid_floor(grd(c)) && !feat_has_solid_floor(grd(b))) return (false); } } @@ -3127,7 +3103,7 @@ static bool _deadend_check_floor(const coord_def &p) continue; const coord_def a(p.x+2*i, p.y); - if (!in_bounds(a) || _is_floor(grd(a))) + if (!in_bounds(a) || feat_has_solid_floor(grd(a))) continue; for (int j = -1; j <= 1; j++) @@ -3140,7 +3116,7 @@ static bool _deadend_check_floor(const coord_def &p) continue; const coord_def c(p.x+i, p.y+j); - if (_is_floor(grd(c)) && !_is_floor(grd(b))) + if (feat_has_solid_floor(grd(c)) && !feat_has_solid_floor(grd(b))) return (false); } } @@ -3249,7 +3225,7 @@ void change_labyrinth(bool msg) // Use the adjacent floor grids as source and destination. coord_def src(c.x-1,c.y); coord_def dst(c.x+1,c.y); - if (!_is_floor(grd(src)) || !_is_floor(grd(dst))) + if (!feat_has_solid_floor(grd(src)) || !feat_has_solid_floor(grd(dst))) { src = coord_def(c.x, c.y-1); dst = coord_def(c.x, c.y+1); @@ -3388,7 +3364,7 @@ void change_labyrinth(bool msg) int floor_count = 0; coord_def new_adj(p); for (adjacent_iterator ai(c); ai; ++ai) - if (_is_floor(grd(*ai)) && one_chance_in(++floor_count)) + if (feat_has_solid_floor(grd(*ai)) && one_chance_in(++floor_count)) new_adj = *ai; if (new_adj != p && maybe_bloodify_square(new_adj)) @@ -3436,7 +3412,7 @@ void change_labyrinth(bool msg) if (!in_bounds(p)) continue; - if (_is_floor(grd(p))) + if (feat_has_solid_floor(grd(p))) { // Once a valid grid is found, move all items from the // stack onto it. diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index f720abdfc7..5ea569e163 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, @@ -993,10 +994,10 @@ enum dungeon_feature_type // Highest grid value which can't be reached through. DNGN_MAX_NONREACH = DNGN_CLEAR_PERMAROCK_WALL, - DNGN_TREES, DNGN_OPEN_SEA, // Shoals equivalent for permarock // Can be seen through and reached past. + DNGN_TREES, DNGN_ORCISH_IDOL = 15, DNGN_GRANITE_STATUE = 21, // 21 DNGN_STATUE_RESERVED, @@ -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, @@ -2122,7 +2130,7 @@ enum mon_attitude_type // These are now saved in an unsigned long in the monsters struct. enum monster_flag_type { - MF_CREATED_FRIENDLY = 0x01, // no benefit from killing + MF_NO_REWARD = 0x01, // no benefit from killing MF_JUST_SUMMONED = 0x02, // monster skips next available action MF_TAKING_STAIRS = 0x04, // is following player through stairs MF_INTERESTING = 0x08, // Player finds monster interesting @@ -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, SPELL_IOOD, NUM_SPELLS @@ -3112,6 +3123,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 cf2b98d8d0..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); @@ -536,6 +546,9 @@ public: // Returns true if this item should be preserved as far as possible. bool is_critical() const; + // Returns true if this item should not normally be enchanted. + bool is_mundane() const; + private: std::string name_aux(description_level_type desc, bool terse, bool ident, diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 46901757a6..d6ae6ad37e 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/food.cc b/crawl-ref/source/food.cc index bd2db8d728..d17a4a5371 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -452,7 +452,7 @@ static bool _have_corpses_in_pack(bool remind) bool butchery(int which_corpse) { - if (igrd(you.pos()) == NON_ITEM) + if (you.visible_igrd(you.pos()) == NON_ITEM) { if (!_have_corpses_in_pack(false)) mpr("There isn't anything here!"); @@ -503,7 +503,7 @@ bool butchery(int which_corpse) int num_corpses = 0; int corpse_id = -1; bool prechosen = (which_corpse != -1); - for (stack_iterator si(you.pos()); si; ++si) + for (stack_iterator si(you.pos(), true); si; ++si) { if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY) { @@ -577,7 +577,7 @@ bool butchery(int which_corpse) bool did_weap_swap = false; bool first_corpse = true; int keyin; - for (stack_iterator si(you.pos()); si; ++si) + for (stack_iterator si(you.pos(), true); si; ++si) { if (si->base_type != OBJ_CORPSES || si->sub_type != CORPSE_BODY) continue; @@ -790,7 +790,7 @@ bool eat_food(int slot) if (result != -2) // else skip ahead to inventory { - if (igrd(you.pos()) != NON_ITEM) + if (you.visible_igrd(you.pos()) != NON_ITEM) { result = eat_from_floor(true); if (result == 1) @@ -1186,7 +1186,7 @@ int eat_from_floor(bool skip_chunks) bool found_valid = false; std::vector<item_def *> food_items; - for (stack_iterator si(you.pos()); si; ++si ) + for (stack_iterator si(you.pos(), true); si; ++si ) { if (you.species == SP_VAMPIRE) { @@ -1483,7 +1483,7 @@ int prompt_eat_chunks() // First search the stash on the floor, unless levitating. if (you.flight_mode() != FL_LEVITATE) { - for (stack_iterator si(you.pos()); si; ++si) + for (stack_iterator si(you.pos(), true); si; ++si) { if (you.species == SP_VAMPIRE) { diff --git a/crawl-ref/source/godabil.cc b/crawl-ref/source/godabil.cc index 7c8e6b5a51..68acb0790e 100644 --- a/crawl-ref/source/godabil.cc +++ b/crawl-ref/source/godabil.cc @@ -279,7 +279,7 @@ void yred_make_enslaved_soul(monsters *mon, bool force_hostile, mon->colour = ETC_UNHOLY; - mon->flags |= MF_CREATED_FRIENDLY; + mon->flags |= MF_NO_REWARD; mon->flags |= MF_ENSLAVED_SOUL; if (twisted) @@ -1146,7 +1146,7 @@ bool evolve_flora() current_plant->upgrade_type(current_target.new_type, true, true); current_plant->god = GOD_FEDHAS; current_plant->attitude = ATT_FRIENDLY; - current_plant->flags |= MF_CREATED_FRIENDLY; + current_plant->flags |= MF_NO_REWARD; current_plant->flags |= MF_ATT_CHANGE_ATTEMPT; // Try to remove slowly dying in case we are upgrading a diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index fd01008e25..07aa928eb4 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -3113,7 +3113,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus, pbolt.fire(); // The item can be destroyed before returning. - if (did_return && thrown_object_destroyed(&item, pbolt.target, true)) + if (did_return && thrown_object_destroyed(&item, pbolt.target)) did_return = false; } @@ -3178,8 +3178,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus, return (hit); } -bool thrown_object_destroyed(item_def *item, const coord_def& where, - bool returning) +bool thrown_object_destroyed(item_def *item, const coord_def& where) { ASSERT(item != NULL); @@ -3244,22 +3243,6 @@ bool thrown_object_destroyed(item_def *item, const coord_def& where, // destruction: plus / (1 + plus) chance of survival. bool destroyed = (chance == 0) ? false : (one_chance_in(chance) && one_chance_in(item->plus + 1)); - bool hostile_grid = feat_destroys_items(grd(where)); - - // Non-returning items thrown into item-destroying grids are always - // destroyed. Returning items are only destroyed if they would have - // been randomly destroyed anyway. - if (returning && !destroyed) - hostile_grid = false; - - if (hostile_grid) - { - if (player_can_hear(where)) - mprf(MSGCH_SOUND, feat_item_destruction_message(grd(where))); - - item_was_destroyed(*item, NON_MONSTER); - destroyed = true; - } return destroyed; } diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h index 8df61d07b6..bba83aa3b4 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); @@ -104,8 +104,7 @@ void throw_noise(actor* act, const bolt &pbolt, const item_def &ammo); bool throw_it(bolt &pbolt, int throw_2, bool teleport = false, int acc_bonus = 0, dist *target = NULL); -bool thrown_object_destroyed(item_def *item, const coord_def& where, - bool returning); +bool thrown_object_destroyed(item_def *item, const coord_def& where); void prompt_inscribe_item(); int launcher_shield_slowdown(const item_def &launcher, diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc index ea2f8bd7b3..bf47f3ab96 100644 --- a/crawl-ref/source/itemprop.cc +++ b/crawl-ref/source/itemprop.cc @@ -562,33 +562,6 @@ bool item_ident( const item_def &item, unsigned long flags ) return ((item.flags & flags) == flags); } -// Is item something that no one would usually bother enchanting? -bool item_is_mundane(const item_def &item) -{ - switch (item.base_type) - { - case OBJ_WEAPONS: - if (item.sub_type == WPN_CLUB - || item.sub_type == WPN_GIANT_CLUB - || item.sub_type == WPN_GIANT_SPIKED_CLUB - || item.sub_type == WPN_KNIFE) - { - return (true); - } - break; - - case OBJ_ARMOUR: - if (item.sub_type == ARM_ANIMAL_SKIN) - return (true); - break; - - default: - break; - } - - return (false); -} - void set_ident_flags( item_def &item, unsigned long flags ) { preserve_quiver_slots p; diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h index 99c861da67..5604d352f5 100644 --- a/crawl-ref/source/itemprop.h +++ b/crawl-ref/source/itemprop.h @@ -14,9 +14,6 @@ struct bolt; void init_properties(void); -// Returns true if this item should not normally be enchanted. -bool item_is_mundane(const item_def &item); - // cursed: bool item_known_cursed( const item_def &item ); bool item_known_uncursed( const item_def &item ); diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index 7543da8ea5..fe96b86d1b 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -1171,7 +1171,7 @@ void pickup() return; } - int o = igrd(you.pos()); + int o = you.visible_igrd(you.pos()); const int num_nonsquelched = _count_nonsquelched_items(o); if (o == NON_ITEM) @@ -1719,7 +1719,10 @@ void mark_items_non_pickup_at(const coord_def &pos) // // Done this way in the hopes that it will be obvious from // calling code that "obj" is possibly modified. -bool move_item_to_grid( int *const obj, const coord_def& p ) +// +// Returns false on error or level full - cases where you +// keep the item. +bool move_item_to_grid( int *const obj, const coord_def& p, bool silent ) { ASSERT(in_bounds(p)); @@ -1730,6 +1733,15 @@ bool move_item_to_grid( int *const obj, const coord_def& p ) item_def& item(mitm[ob]); + if (feat_destroys_item(grd(p), mitm[ob], !silenced(p) && !silent)) + { + item_was_destroyed(item, NON_MONSTER); + destroy_item(ob); + ob = NON_ITEM; + + return (true); + } + // If it's a stackable type... if (is_stackable_item( item )) { @@ -1761,7 +1773,7 @@ bool move_item_to_grid( int *const obj, const coord_def& p ) while (item.quantity > 1) { // If we can't copy the items out, we lose the surplus. - if (copy_item_to_grid(item, p, 1, false)) + if (copy_item_to_grid(item, p, 1, false, true)) --item.quantity; else item.quantity = 1; @@ -1809,15 +1821,22 @@ void move_item_stack_to_grid( const coord_def& from, const coord_def& to ) } -// Returns quantity dropped. +// Returns false iff no items could be dropped. bool copy_item_to_grid( const item_def &item, const coord_def& p, - int quant_drop, bool mark_dropped ) + int quant_drop, bool mark_dropped, bool silent ) { ASSERT(in_bounds(p)); if (quant_drop == 0) return (false); + if (feat_destroys_item(grd(p), item, !silenced(p) && !silent)) + { + item_was_destroyed(item, NON_MONSTER); + + return (true); + } + // default quant_drop == -1 => drop all if (quant_drop < 0) quant_drop = item.quantity; @@ -1864,7 +1883,7 @@ bool copy_item_to_grid( const item_def &item, const coord_def& p, origin_set_unknown(new_item); } - move_item_to_grid( &new_item_idx, p ); + move_item_to_grid( &new_item_idx, p, true ); if (is_blood_potion(item) && item.quantity != quant_drop) // partial drop only { @@ -1966,9 +1985,8 @@ bool drop_item( int item_dropped, int quant_drop, bool try_offer ) const dungeon_feature_type my_grid = grd(you.pos()); - if (!feat_destroys_items(my_grid) - && !copy_item_to_grid( you.inv[item_dropped], - you.pos(), quant_drop, true )) + if (!copy_item_to_grid( you.inv[item_dropped], + you.pos(), quant_drop, true, true )) { mpr("Too many items on this level, not dropping the item."); return (false); @@ -1977,13 +1995,15 @@ bool drop_item( int item_dropped, int quant_drop, bool try_offer ) mprf("You drop %s.", quant_name(you.inv[item_dropped], quant_drop, DESC_NOCAP_A).c_str()); - if (feat_destroys_items(my_grid)) - { - if (!silenced(you.pos())) - mprf(MSGCH_SOUND, feat_item_destruction_message(my_grid)); + bool quiet = silenced(you.pos()); - item_was_destroyed(you.inv[item_dropped], NON_MONSTER); - } + // If you drop an item in as a merfolk, it is below the water line and + // makes no noise falling. + if (you.swimming()) + quiet = true; + + if (feat_destroys_item(my_grid, you.inv[item_dropped], !quiet)) + ; else if (strstr(you.inv[item_dropped].inscription.c_str(), "=s") != 0) StashTrack.add_stash(); @@ -2558,7 +2578,7 @@ static void _do_autopickup() return; } - int o = igrd(you.pos()); + int o = you.visible_igrd(you.pos()); std::string pickup_warning; while (o != NON_ITEM) @@ -2934,6 +2954,33 @@ bool item_def::is_critical() const && plus != RUNE_ABYSSAL); } +// Is item something that no one would usually bother enchanting? +bool item_def::is_mundane() const +{ + switch (base_type) + { + case OBJ_WEAPONS: + if (sub_type == WPN_CLUB + || sub_type == WPN_GIANT_CLUB + || sub_type == WPN_GIANT_SPIKED_CLUB + || sub_type == WPN_KNIFE) + { + return (true); + } + break; + + case OBJ_ARMOUR: + if (sub_type == ARM_ANIMAL_SKIN) + return (true); + break; + + default: + break; + } + + return (false); +} + static void _rune_from_specs(const char* _specs, item_def &item) { char specs[80]; diff --git a/crawl-ref/source/items.h b/crawl-ref/source/items.h index 4ffa1c9c30..8d82197764 100644 --- a/crawl-ref/source/items.h +++ b/crawl-ref/source/items.h @@ -34,7 +34,8 @@ bool dec_mitm_item_quantity(int obj, int amount); void inc_inv_item_quantity(int obj, int amount, bool suppress_burden = false); void inc_mitm_item_quantity(int obj, int amount); -bool move_item_to_grid( int *const obj, const coord_def& p ); +bool move_item_to_grid( int *const obj, const coord_def& p, + bool silent = false ); void move_item_stack_to_grid( const coord_def& from, const coord_def& to ); void note_inscribe_item(item_def &item); int move_item_to_player(int obj, int quant_got, bool quiet = false, @@ -77,7 +78,8 @@ void item_list_on_square( std::vector<const item_def*>& items, bool copy_item_to_grid( const item_def &item, const coord_def& p, int quant_drop = -1, // item.quantity by default - bool mark_dropped = false); + bool mark_dropped = false, + bool silent = false ); bool move_top_item( const coord_def &src, const coord_def &dest ); diff --git a/crawl-ref/source/l_feat.cc b/crawl-ref/source/l_feat.cc index 00529c11dc..baeaac20fd 100644 --- a/crawl-ref/source/l_feat.cc +++ b/crawl-ref/source/l_feat.cc @@ -31,10 +31,9 @@ return (1); \ } -FEATF(_feat_destroys_items, feat_destroys_items) - FEATF(_feat_is_wall, feat_is_wall) FEATF(_feat_is_solid, feat_is_solid) +FEATF(_feat_has_solid_floor, feat_has_solid_floor) FEATF(_feat_is_opaque, feat_is_opaque) FEATF(_feat_is_door, feat_is_door) FEATF(_feat_is_closed_door, feat_is_closed_door) @@ -60,9 +59,9 @@ FEATF(_feat_is_critical, is_critical_feature) const struct luaL_reg feat_dlib[] = { -{ "destroys_items", _feat_destroys_items }, { "is_wall", _feat_is_wall }, { "is_solid", _feat_is_solid }, +{ "has_solid_floor", _feat_has_solid_floor }, { "is_opaque", _feat_is_opaque }, { "is_door", _feat_is_door }, { "is_closed_door", _feat_is_closed_door }, diff --git a/crawl-ref/source/l_item.cc b/crawl-ref/source/l_item.cc index 80bbc88df9..ecd343be2a 100644 --- a/crawl-ref/source/l_item.cc +++ b/crawl-ref/source/l_item.cc @@ -48,7 +48,7 @@ void lua_set_exclusive_item(const item_def *item) void lua_push_floor_items(lua_State *ls) { - lua_push_items(ls, igrd(you.pos())); + lua_push_items(ls, you.visible_igrd(you.pos())); } void lua_push_inv_items(lua_State *ls = NULL) @@ -686,6 +686,53 @@ static int l_item_do_destroy(lua_State *ls) return (1); } +static int l_item_do_dec_quantity(lua_State *ls) +{ + ASSERT_DLUA; + + LUA_ITEM(item, 1); + if (!item || !item->is_valid()) + { + lua_pushboolean(ls, false); + return (1); + } + + // The quantity to reduce by. + int quantity = luaL_checkint(ls, 2); + + bool destroyed = false; + + if (in_inventory(*item)) + destroyed = dec_inv_item_quantity(item->link, quantity); + else + destroyed = dec_mitm_item_quantity(item->index(), quantity); + + lua_pushboolean(ls, destroyed); + return (1); +} + +static int l_item_do_inc_quantity(lua_State *ls) +{ + ASSERT_DLUA; + + LUA_ITEM(item, 1); + if (!item || !item->is_valid()) + { + lua_pushboolean(ls, false); + return (1); + } + + // The quantity to increase by. + int quantity = luaL_checkint(ls, 2); + + if (in_inventory(*item)) + inc_inv_item_quantity(item->link, quantity); + else + inc_mitm_item_quantity(item->index(), quantity); + + return (0); +} + static const struct luaL_reg item_lib[] = { { "artefact", l_item_artefact }, @@ -717,6 +764,8 @@ static const struct luaL_reg item_lib[] = { "dropped", l_item_dropped }, { "can_cut_meat", l_item_can_cut_meat }, { "destroy", l_item_do_destroy }, + { "dec_quantity", l_item_do_dec_quantity }, + { "inc_quantity", l_item_do_inc_quantity }, { NULL, NULL }, }; diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc index ce7fcbb429..bd3440d0fb 100644 --- a/crawl-ref/source/libutil.cc +++ b/crawl-ref/source/libutil.cc @@ -69,6 +69,10 @@ description_level_type description_type_by_name(const char *desc) return DESC_INVENTORY; else if (!strcmp("none", desc)) return DESC_NONE; + else if (!strcmp("base", desc)) + return DESC_BASENAME; + else if (!strcmp("qual", desc)) + return DESC_QUALNAME; return DESC_PLAIN; } @@ -139,12 +143,15 @@ std::string strip_filename_unsafe_chars(const std::string &s) std::string vmake_stringf(const char* s, va_list args) { char buf1[400]; + va_list orig_args; + va_copy(orig_args, args); size_t len = vsnprintf(buf1, sizeof buf1, s, args); if (len < sizeof buf1) return (buf1); char *buf2 = (char*)malloc(len + 1); - vsnprintf(buf2, len + 1, s, args); + vsnprintf(buf2, len + 1, s, orig_args); + va_end(orig_args); std::string ret(buf2); free(buf2); diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc index 7f3ccb2592..8b675c2770 100644 --- a/crawl-ref/source/main.cc +++ b/crawl-ref/source/main.cc @@ -4631,7 +4631,7 @@ static void _compile_time_asserts() COMPILE_CHECK(SP_VAMPIRE == 30 , c3); COMPILE_CHECK(SPELL_DEBUGGING_RAY == 103 , c4); COMPILE_CHECK(SPELL_RETURNING_AMMUNITION == 162 , c5); - COMPILE_CHECK(NUM_SPELLS == 216 , c6); + COMPILE_CHECK(NUM_SPELLS == 218 , 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/makefile.obj b/crawl-ref/source/makefile.obj index 3c2ca7a271..a6eb79d4c3 100644 --- a/crawl-ref/source/makefile.obj +++ b/crawl-ref/source/makefile.obj @@ -102,6 +102,7 @@ menu.o \ message.o \ mgen_data.o \ misc.o \ +mislead.o \ mon-abil.o \ mon-act.o \ mon-behv.o \ diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc index 977198929d..442b500112 100644 --- a/crawl-ref/source/makeitem.cc +++ b/crawl-ref/source/makeitem.cc @@ -1699,7 +1699,7 @@ brand_ok: } else if ((force_good || is_demonic(item) || forced_ego || x_chance_in_y(51 + item_level, 200)) - && (!item_is_mundane(item) || force_good)) + && (!item.is_mundane() || force_good)) { // Make a better item (possibly ego). if (!no_brand) @@ -2399,7 +2399,7 @@ static void _generate_armour_item(item_def& item, bool allow_uniques, } else if ((force_good || forced_ego || item.sub_type == ARM_WIZARD_HAT || x_chance_in_y(51 + item_level, 250)) - && (!item_is_mundane(item) || force_good)) + && (!item.is_mundane() || force_good)) { // Make a good item... item.plus += random2(3); @@ -3233,7 +3233,7 @@ static bool _weapon_is_visibly_special(const item_def &item) if (visibly_branded || is_artefact(item)) return (true); - if (item_is_mundane(item)) + if (item.is_mundane()) return (false); if ((item.plus || item.plus2) @@ -3256,7 +3256,7 @@ static bool _armour_is_visibly_special(const item_def &item) if (visibly_branded || is_artefact(item)) return (true); - if (item_is_mundane(item)) + if (item.is_mundane()) return (false); if (item.plus && !one_chance_in(3)) 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/maps.cc b/crawl-ref/source/maps.cc index 07da121e02..072b7c8557 100644 --- a/crawl-ref/source/maps.cc +++ b/crawl-ref/source/maps.cc @@ -337,7 +337,7 @@ static bool _safe_vault_place(const map_def &map, if (lines[dp.y][dp.x] == ' ') continue; - if (dgn_Map_Mask[cp.x][cp.y] == MMT_VAULT) + if (dgn_Map_Mask[cp.x][cp.y] & MMT_VAULT) return (false); const dungeon_feature_type dfeat = grd(cp); diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index 6a3d36c487..a6adba6847 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -413,12 +413,17 @@ int channel_to_colour( msg_channel_type channel, int param ) static void do_message_print( msg_channel_type channel, int param, const char *format, va_list argp ) { - // Is this limit intentional? char buff[200]; - vsnprintf( buff, sizeof( buff ), format, argp ); - buff[199] = 0; - - mpr(buff, channel, param); + size_t len = vsnprintf( buff, sizeof( buff ), format, argp ); + if (len < sizeof( buff )) { + mpr( buff, channel, param ); + } + else { + char *heapbuf = (char*)malloc( len + 1 ); + vsnprintf( heapbuf, len + 1, format, argp ); + mpr( heapbuf, channel, param ); + free( heapbuf ); + } } void mprf( msg_channel_type channel, int param, const char *format, ... ) 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 b5c9e6bb40..b9d9fdabb3 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -1041,23 +1041,18 @@ void split_potions_into_decay( int obj, int amount, bool need_msg ) } } - // Only bother creating a distinct stack of potions - // if it won't get destroyed right away. - if (!feat_destroys_items(grd(you.pos()))) - { - item_def potion2; - potion2.base_type = OBJ_POTIONS; - potion2.sub_type = POT_DECAY; - // Keep description as it was. - potion2.plus = potion.plus; - potion2.quantity = amount; - potion2.colour = potion.colour; - potion2.plus2 = 0; - potion2.flags = 0; - potion2.special = 0; - - copy_item_to_grid(potion2, you.pos()); - } + item_def potion2; + potion2.base_type = OBJ_POTIONS; + potion2.sub_type = POT_DECAY; + // Keep description as it was. + potion2.plus = potion.plus; + potion2.quantity = amount; + potion2.colour = potion.colour; + potion2.plus2 = 0; + potion2.flags = 0; + potion2.special = 0; + + copy_item_to_grid(potion2, you.pos()); // Is decreased even if the decay stack goes splat. dec_inv_item_quantity(obj, amount); @@ -2696,11 +2691,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) @@ -3219,7 +3214,7 @@ bool stop_attack_prompt(const monsters *mon, bool beam_attack, const bool isUnchivalric = is_unchivalric_attack(&you, mon); const bool isHoly = mon->is_holy() && (mon->attitude != ATT_HOSTILE - || testbits(mon->flags, MF_CREATED_FRIENDLY) + || testbits(mon->flags, MF_NO_REWARD) || testbits(mon->flags, MF_WAS_NEUTRAL)); if (isFriendly) 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/mislead.cc b/crawl-ref/source/mislead.cc new file mode 100644 index 0000000000..64c574d97a --- /dev/null +++ b/crawl-ref/source/mislead.cc @@ -0,0 +1,127 @@ +/* File: mislead.cc + * Summary: Handling of the Mislead spell and stats + */ + +#include "AppHdr.h" +#include "mislead.h" + +#include "enum.h" +#include "env.h" +#include "message.h" +#include "monster.h" +#include "mon-iter.h" +#include "mon-util.h" +#include "view.h" +#include "random.h" +#include "tutorial.h" +#include "xom.h" + +bool unsuitable_misled_monster(monster_type mons) +{ + return (mons_is_unique(mons) || mons_is_mimic(mons) + || mons_class_is_stationary(mons) || mons_genus(mons) == MONS_DRACONIAN + || mons == MONS_DANCING_WEAPON || mons == MONS_UGLY_THING + || mons == MONS_VERY_UGLY_THING || mons == MONS_ZOMBIE_SMALL + || mons == MONS_ZOMBIE_LARGE || mons == MONS_SKELETON_SMALL + || mons == MONS_SKELETON_LARGE || mons == MONS_SIMULACRUM_SMALL + || mons == MONS_SIMULACRUM_LARGE || mons == MONS_SPECTRAL_THING + || mons == MONS_SLIME_CREATURE || mons == MONS_BALLISTOMYCETE + || mons == MONS_HYDRA || mons == MONS_PLAYER_GHOST + || mons == MONS_SHAPESHIFTER || mons == MONS_PANDEMONIUM_DEMON + || mons == MONS_KILLER_KLOWN || mons == MONS_KRAKEN + || mons == MONS_KRAKEN_TENTACLE + || mons == MONS_GLOWING_SHAPESHIFTER + || mons == MONS_GIANT_BAT); +} + +monster_type get_misled_monster (monsters *monster) +{ + monster_type mons = random_monster_at_grid(monster->pos()); + if (unsuitable_misled_monster(mons)) + mons = random_monster_at_grid(monster->pos()); + + if (unsuitable_misled_monster(mons)) + return (MONS_GIANT_BAT); + + return mons; +} + +bool update_mislead_monster(monsters* monster) +{ + // Don't affect uniques, named monsters, and monsters with special tiles. + if (mons_is_unique(monster->type) || !monster->mname.empty() + || monster->props.exists("monster_tile") + || monster->props.exists("mislead_as")) + { + return (false); + } + + short misled_as = get_misled_monster(monster); + monster->props["mislead_as"] = misled_as; + + if (misled_as == MONS_GIANT_BAT) + return (false); + + return (true); +} + +int update_mislead_monsters(monsters* caster) +{ + int count = 0; + + for (monster_iterator mi; mi; ++mi) + if (*mi != caster && update_mislead_monster(*mi)) + count++; + + return count; +} + +void mons_cast_mislead(monsters *monster) +{ + // This really only affects the player; it causes confusion when cast on + // non-player foes, but that is dealt with inside ye-great-Switch-of-Doom. + if (monster->foe != MHITYOU) + return; + + // Prevents Mislead spam by Mara and co. {due} + if (you.duration[DUR_MISLED] > 10 && one_chance_in(3)) + return; + + if (wearing_amulet(AMU_CLARITY)) + { + mpr("Your vision blurs momentarily."); + return; + } + + update_mislead_monsters(monster); + + const int old_value = you.duration[DUR_MISLED]; + you.increase_duration(DUR_MISLED, monster->hit_dice * 12 / 3, 50); + if (you.duration[DUR_MISLED] > old_value) + { + you.check_awaken(500); + + if (old_value <= 0) + { + mpr("But for a moment, strange images dance in front of your eyes.", MSGCH_WARN); +#ifdef USE_TILE + tiles.add_overlay(you.pos(), tileidx_zap(MAGENTA)); + update_screen(); +#else + flash_view(MAGENTA); +#endif + more(); + } + else + mpr("You are even more misled!", MSGCH_WARN); + + learned_something_new(TUT_YOU_ENCHANTED); + + xom_is_stimulated((you.duration[DUR_MISLED] - old_value) + / BASELINE_DELAY); + } + + return; +} + + diff --git a/crawl-ref/source/mislead.h b/crawl-ref/source/mislead.h new file mode 100644 index 0000000000..540ffea9b1 --- /dev/null +++ b/crawl-ref/source/mislead.h @@ -0,0 +1,8 @@ +/* File: mislead.h + * Summary: Handling of the Mislead spell and stats + */ + +bool unsuitable_misled_monster(monster_type mons); +monster_type get_misled_monster (monsters *monster); +int update_mislead_monsters(monsters* caster); +bool update_mislead_monster(monsters* monster); diff --git a/crawl-ref/source/mon-abil.cc b/crawl-ref/source/mon-abil.cc index 3811310e65..0927248da1 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!"); @@ -1500,7 +1490,5 @@ void activate_ballistomycetes( monsters * monster, const coord_def & origin) } if (you.see_cell(origin) && found_others) - { mprf("You feel the ballistomycetes will spawn a replacement spore."); - } } 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 62df9c963b..8112618b0f 100644 --- a/crawl-ref/source/mon-act.cc +++ b/crawl-ref/source/mon-act.cc @@ -333,6 +333,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 @@ -379,8 +397,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() @@ -1123,7 +1140,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) @@ -1392,7 +1414,7 @@ static bool _mons_throw(struct monsters *monster, struct bolt &pbolt, pbolt.fire(); // The item can be destroyed before returning. - if (really_returns && thrown_object_destroyed(&item, pbolt.target, true)) + if (really_returns && thrown_object_destroyed(&item, pbolt.target)) { really_returns = false; } @@ -1858,6 +1880,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 @@ -2169,6 +2192,7 @@ static bool _monster_eat_item(monsters *monster, bool nearby) bool death_ooze_ate_good = false; bool death_ooze_ate_corpse = false; + // Jellies can swim, so don't check water for (stack_iterator si(monster->pos()); si && eaten < max_eat && hps_changed < 50; ++si) { @@ -2452,6 +2476,14 @@ static bool _handle_pickup(monsters *monster) if (monster->asleep() || monster->submerged()) return (false); + // Hack - Harpies fly over water, but we don't have a general + // system for monster igrd yet. Flying intelligent monsters + // (kenku!) would also count here. + dungeon_feature_type feat = grd(monster->pos()); + + if ((feat == DNGN_LAVA || feat == DNGN_DEEP_WATER) && !monster->flight_mode()) + return (false); + const bool nearby = mons_near(monster); int count_pickup = 0; @@ -2770,7 +2802,7 @@ static bool _mon_can_move_to_pos(const monsters *monster, // The kraken is so large it cannot enter shallow water. // Its tentacles can, and will, though. - if (monster->type == MONS_KRAKEN && target_grid == DNGN_SHALLOW_WATER) + if (mons_base_type(monster) == MONS_KRAKEN && target_grid == DNGN_SHALLOW_WATER) return (false); // Effectively slows down monster movement across water. diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index f236c31c16..f844fe7218 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -25,6 +25,7 @@ #include "mon-project.h" #include "terrain.h" #include "tutorial.h" +#include "mislead.h" #include "mgen_data.h" #include "coord.h" #include "mon-speak.h" @@ -217,6 +218,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 +363,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 ); @@ -830,6 +845,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) @@ -845,6 +861,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; @@ -1570,108 +1588,6 @@ int _count_mara_fakes() return count; } -bool _unsuitable_misled_monster(monster_type mons) -{ - return (mons_is_unique(mons) || mons_is_mimic(mons) - || mons_class_is_stationary(mons) || mons_genus(mons) == MONS_DRACONIAN - || mons == MONS_DANCING_WEAPON || mons == MONS_UGLY_THING - || mons == MONS_VERY_UGLY_THING || mons == MONS_ZOMBIE_SMALL - || mons == MONS_ZOMBIE_LARGE || mons == MONS_SKELETON_SMALL - || mons == MONS_SKELETON_LARGE || mons == MONS_SIMULACRUM_SMALL - || mons == MONS_SIMULACRUM_LARGE || mons == MONS_SPECTRAL_THING - || mons == MONS_SLIME_CREATURE || mons == MONS_BALLISTOMYCETE - || mons == MONS_HYDRA || mons == MONS_PLAYER_GHOST - || mons == MONS_SHAPESHIFTER || mons == MONS_PANDEMONIUM_DEMON - || mons == MONS_KILLER_KLOWN || mons == MONS_KRAKEN - || mons == MONS_KRAKEN_TENTACLE - || mons == MONS_GLOWING_SHAPESHIFTER); -} - -monster_type _get_misled_monster (monsters *monster) -{ - monster_type mons = random_monster_at_grid(monster->pos()); - if (_unsuitable_misled_monster(mons)) - mons = random_monster_at_grid(monster->pos()); - - if (_unsuitable_misled_monster(mons)) - return (MONS_GIANT_BAT); - - return mons; -} - -int _update_mislead_monsters(monsters* monster) -{ - int count = 0; - - for (monster_iterator mi; mi; ++mi) - { - if (*mi == monster) - continue; - - // Don't affect uniques, named monsters, and monsters with special tiles. - if (mons_is_unique(mi->type) || !mi->mname.empty() - || mi->props.exists("monster_tile") || mi->props.exists("mislead_as")) - { - continue; - } - else - { - mi->props["mislead_as"] = short(_get_misled_monster(*mi)); - count++; - } - } - - return count; -} - -void mons_cast_mislead(monsters *monster) -{ - // This really only affects the player; it causes confusion when cast on - // non-player foes, but that is dealt with inside ye-great-Switch-of-Doom. - if (monster->foe != MHITYOU) - return; - - // Prevents Mislead spam by Mara and co. {due} - if (you.duration[DUR_MISLED] > 10 && one_chance_in(3)) - return; - - if (wearing_amulet(AMU_CLARITY)) - { - mpr("Your vision blurs momentarily."); - return; - } - - _update_mislead_monsters(monster); - - const int old_value = you.duration[DUR_MISLED]; - you.increase_duration(DUR_MISLED, monster->hit_dice * 12 / 3, 50); - if (you.duration[DUR_MISLED] > old_value) - { - you.check_awaken(500); - - if (old_value <= 0) - { - mpr("But for a moment, strange images dance in front of your eyes.", MSGCH_WARN); -#ifdef USE_TILE - tiles.add_overlay(you.pos(), tileidx_zap(MAGENTA)); - update_screen(); -#else - flash_view(MAGENTA); -#endif - more(); - } - else - mpr("You are even more misled!", MSGCH_WARN); - - learned_something_new(TUT_YOU_ENCHANTED); - - xom_is_stimulated((you.duration[DUR_MISLED] - old_value) - / BASELINE_DELAY); - } - - return; -} - bool _find_players_ghost () { bool found = false; @@ -1769,6 +1685,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) @@ -1892,15 +1824,21 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast, // Tentacles aren't really summoned (controlled by spell_cast // being passed to summon_type), so I'm not sure what the // abjuration value (3) is doing there. (jpeg) - if (create_monster( + int tentacle = create_monster( mgen_data(MONS_KRAKEN_TENTACLE, SAME_ATTITUDE(monster), monster, 3, spell_cast, monster->pos(), monster->foe, 0, god, MONS_NO_MONSTER, kraken_index, monster->colour, you.your_level, PROX_CLOSE_TO_PLAYER, - you.level_type)) == -1) + you.level_type)); + + if (tentacle < 0) { sumcount2--; } + else if (monster->holiness() == MH_UNDEAD) + { + menv[tentacle].flags |= MF_HONORARY_UNDEAD; + } } if (sumcount2 == 1) mpr("A tentacle rises from the water!"); @@ -1928,7 +1866,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 ec6b912be0..7523989417 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, @@ -2436,7 +2473,7 @@ static monsterentry mondata[] = { { {AT_HIT, AF_COLD, 5}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 5, 3, 5, 0 }, 5, 10, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, - I_ANIMAL, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY, + I_ANIMAL, HT_AMPHIBIOUS_LAND, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LARGE }, @@ -3239,10 +3276,10 @@ static monsterentry mondata[] = { MONS_BIG_FISH, ';', LIGHTGREEN, "big fish", M_COLD_BLOOD, MR_NO_FLAGS, - 0, 10, MONS_BIG_FISH, MONS_BIG_FISH, MH_NATURAL, -3, + 300, 10, MONS_BIG_FISH, MONS_BIG_FISH, MH_NATURAL, -3, { {AT_BITE, AF_PLAIN, 8}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 4, 3, 5, 0 }, - 1, 12, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, + 1, 12, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_SMALL }, @@ -3251,10 +3288,10 @@ static monsterentry mondata[] = { MONS_GIANT_GOLDFISH, ';', LIGHTRED, "giant goldfish", M_COLD_BLOOD, MR_NO_FLAGS, - 0, 10, MONS_BIG_FISH, MONS_GIANT_GOLDFISH, MH_NATURAL, -3, + 500, 10, MONS_BIG_FISH, MONS_GIANT_GOLDFISH, MH_NATURAL, -3, { {AT_BITE, AF_PLAIN, 15}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 7, 3, 5, 0 }, - 5, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, + 5, 7, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LITTLE }, @@ -3263,10 +3300,10 @@ static monsterentry mondata[] = { MONS_ELECTRIC_EEL, ';', LIGHTBLUE, "electric eel", M_COLD_BLOOD | M_SPECIAL_ABILITY, MR_RES_ELEC, - 0, 10, MONS_ELECTRIC_EEL, MONS_ELECTRIC_EEL, MH_NATURAL, -3, + 700, 10, MONS_ELECTRIC_EEL, MONS_ELECTRIC_EEL, MH_NATURAL, -3, { AT_NO_ATK, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 3, 3, 5, 0 }, - 1, 15, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, + 1, 15, MST_NO_SPELLS, CE_CLEAN, Z_SMALL, S_SILENT, I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_SMALL }, @@ -3275,11 +3312,11 @@ static monsterentry mondata[] = { MONS_JELLYFISH, 'J', CYAN, "jellyfish", M_NO_FLAGS, MR_RES_POISON, - 0, 10, MONS_JELLYFISH, MONS_JELLYFISH, MH_NATURAL, -3, + 1000, 10, MONS_JELLYFISH, MONS_JELLYFISH, MH_NATURAL, -3, { {AT_STING, AF_POISON_STR, 1}, {AT_HIT, AF_PLAIN, 1}, AT_NO_ATK, AT_NO_ATK }, { 4, 3, 5, 0 }, - 0, 5, MST_NO_SPELLS, CE_NOCORPSE, Z_SMALL, S_SILENT, + 0, 5, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, I_PLANT, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LITTLE }, @@ -3289,10 +3326,10 @@ static monsterentry mondata[] = { MONS_SHARK, ';', WHITE, "shark", M_COLD_BLOOD | M_BLOOD_SCENT, MR_NO_FLAGS, - 0, 12, MONS_SHARK, MONS_SHARK, MH_NATURAL, -3, + 2000, 12, MONS_SHARK, MONS_SHARK, MH_NATURAL, -3, { {AT_BITE, AF_PLAIN, 15}, {AT_BITE, AF_PLAIN, 8}, AT_NO_ATK, AT_NO_ATK }, { 7, 3, 5, 0 }, - 9, 5, MST_NO_SPELLS, CE_NOCORPSE, Z_BIG, S_SILENT, + 9, 5, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SILENT, I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LARGE }, @@ -3302,10 +3339,10 @@ static monsterentry mondata[] = { MONS_KRAKEN, 'X', BLACK, "kraken", 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 }, + 3500, 20, MONS_KRAKEN, MONS_KRAKEN, MH_NATURAL, -3, + { {AT_BITE, AF_PLAIN, 50}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 20, 10, 10, 0 }, - 20, 0, MST_KRAKEN, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, + 20, 0, MST_KRAKEN, CE_POISONOUS, Z_BIG, S_SILENT, I_ANIMAL, HT_WATER, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_HUGE }, @@ -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 }, @@ -4597,12 +4634,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-pathfind.cc b/crawl-ref/source/mon-pathfind.cc index 173d643020..68215b5fca 100644 --- a/crawl-ref/source/mon-pathfind.cc +++ b/crawl-ref/source/mon-pathfind.cc @@ -392,7 +392,7 @@ bool monster_pathfind::traversable(const coord_def p) if (mons) return mons_traversable(p); - return (!feat_is_solid(grd(p)) && !feat_destroys_items(grd(p))); + return feat_has_solid_floor(grd(p)); } // Checks whether a given monster can pass over a certain position, respecting 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 9faa371cb8..10dd3c615c 100644 --- a/crawl-ref/source/mon-place.cc +++ b/crawl-ref/source/mon-place.cc @@ -24,6 +24,7 @@ #include "lev-pand.h" #include "makeitem.h" #include "message.h" +#include "mislead.h" #include "mon-behv.h" #include "mon-gear.h" #include "mon-iter.h" @@ -41,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; @@ -597,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(), @@ -635,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(); @@ -767,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. @@ -784,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); @@ -1005,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++) { @@ -1035,7 +1038,7 @@ int place_monster(mgen_data mg, bool force_pos) // Don't give XP for the slaves to discourage hunting. Pikel // has an artificially large XP modifier to compensate for // this. - menv[band_id].flags |= MF_CREATED_FRIENDLY; + menv[band_id].flags |= MF_NO_REWARD; menv[band_id].props["pikel_band"] = true; } } @@ -1242,6 +1245,9 @@ static int _place_monster_aux(const mgen_data &mg, mon->add_ench(ENCH_SLOWLY_DYING); } + if (!crawl_state.arena && you.misled()) + update_mislead_monster(mon); + if (monster_can_submerge(mon, grd(fpos)) && !one_chance_in(5)) mon->add_ench(ENCH_SUBMERGED); @@ -1675,7 +1681,9 @@ static void _define_zombie(int mid, monster_type ztype, monster_type cs, define_monster(mid); // Turn off all spellcasting flags. - menv[mid].flags &= ~MF_SPELLCASTER & ~MF_ACTUAL_SPELLS & ~MF_PRIEST; + // Hack - kraken get to keep their spell-like ability. + if (menv[mid].base_monster != MONS_KRAKEN) + menv[mid].flags &= ~MF_SPELLCASTER & ~MF_ACTUAL_SPELLS & ~MF_PRIEST; menv[mid].hit_points = hit_points(menv[mid].hit_dice, 6, 5); menv[mid].max_hit_points = menv[mid].hit_points; @@ -2106,6 +2114,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) @@ -2397,7 +2422,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: @@ -2420,6 +2450,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; } @@ -2574,7 +2616,7 @@ int mons_place(mgen_data mg) if (mg.behaviour > NUM_BEHAVIOURS) { if (mg.behaviour == BEH_FRIENDLY) - creation->flags |= MF_CREATED_FRIENDLY; + creation->flags |= MF_NO_REWARD; if (mg.behaviour == BEH_NEUTRAL || mg.behaviour == BEH_GOOD_NEUTRAL || mg.behaviour == BEH_STRICT_NEUTRAL) 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 4eae124ef4..12f8c137ad 100644 --- a/crawl-ref/source/mon-stuff.cc +++ b/crawl-ref/source/mon-stuff.cc @@ -255,10 +255,6 @@ bool curse_an_item( bool decay_potions, bool quiet ) void monster_drop_ething(monsters *monster, bool mark_item_origins, int owner_id) { - const bool hostile_grid = feat_destroys_items(grd(monster->pos())); - - bool destroyed = false; - // Drop weapons & missiles last (ie on top) so others pick up. for (int i = NUM_MONSTER_SLOTS - 1; i >= 0; i--) { @@ -268,30 +264,26 @@ void monster_drop_ething(monsters *monster, bool mark_item_origins, { const bool summoned_item = testbits(mitm[item].flags, ISFLAG_SUMMONED); - if (hostile_grid || summoned_item) + if (summoned_item) { item_was_destroyed(mitm[item], monster->mindex()); destroy_item( item ); - if (!summoned_item) - destroyed = true; } else { if (monster->friendly() && mitm[item].is_valid()) mitm[item].flags |= ISFLAG_DROPPED_BY_ALLY; - move_item_to_grid(&item, monster->pos()); - if (mark_item_origins && mitm[item].is_valid()) origin_set_monster(mitm[item], monster); + + // If a monster is swimming, the items are ALREADY underwater + move_item_to_grid(&item, monster->pos(), monster->swimming()); } monster->inv[i] = NON_ITEM; } } - - if (destroyed) - mprf(MSGCH_SOUND, feat_item_destruction_message(grd(monster->pos()))); } monster_type fill_out_corpse(const monsters* monster, item_def& corpse, @@ -421,14 +413,6 @@ bool explode_corpse(item_def& corpse, const coord_def& where) --nchunks; - if (feat_destroys_items(grd(cp))) - { - if (!silenced(cp)) - mprf(MSGCH_SOUND, feat_item_destruction_message(grd(cp))); - - continue; - } - dprf("Success"); copy_item_to_grid(corpse, cp); @@ -483,16 +467,8 @@ int place_monster_corpse(const monsters *monster, bool silent, return (-1); } - if (feat_destroys_items(grd(monster->pos()))) - { - item_was_destroyed(corpse); - destroy_item(o); - return (-1); - } + move_item_to_grid(&o, monster->pos(), !monster->swimming()); - // Don't care if 'o' is changed, and it shouldn't be (corpses don't - // stack). - move_item_to_grid(&o, monster->pos()); if (you.see_cell(monster->pos())) { if (force && !silent) @@ -507,10 +483,12 @@ int place_monster_corpse(const monsters *monster, bool silent, } const bool poison = (mons_corpse_effect(corpse_class) == CE_POISONOUS && player_res_poison() <= 0); - tutorial_dissection_reminder(!poison); + + if (o != NON_ITEM) + tutorial_dissection_reminder(!poison); } - return (o); + return (o == NON_ITEM ? -1 : o); } static void _tutorial_inspect_kill() @@ -595,7 +573,7 @@ static void _give_adjusted_experience(monsters *monster, killer_type killer, const int experience = exper_value(monster); const bool created_friendly = - testbits(monster->flags, MF_CREATED_FRIENDLY); + testbits(monster->flags, MF_NO_REWARD); const bool was_neutral = testbits(monster->flags, MF_WAS_NEUTRAL); const bool no_xp = monster->has_ench(ENCH_ABJ) || !experience; const bool already_got_half_xp = testbits(monster->flags, MF_GOT_HALF_XP); @@ -1323,12 +1301,12 @@ 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) { - if (monster->type == MONS_KRAKEN) + if (mons_base_type(monster) == MONS_KRAKEN) { int headnum = monster->mindex(); @@ -1422,6 +1400,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 )); @@ -1616,7 +1597,7 @@ int monster_die(monsters *monster, killer_type killer, && monster->visible_to(&you); const bool exploded = monster->flags & MF_EXPLODE_KILL; - const bool created_friendly = testbits(monster->flags, MF_CREATED_FRIENDLY); + const bool created_friendly = testbits(monster->flags, MF_NO_REWARD); bool anon = (killer_index == ANON_FRIENDLY_MONSTER); const mon_holy_type targ_holy = monster->holiness(); @@ -2117,7 +2098,7 @@ int monster_die(monsters *monster, killer_type killer, // he goes away. pikel_band_neutralise(); } - else if (monster->type == MONS_KRAKEN) + else if (mons_base_type(monster) == MONS_KRAKEN) { if (_destroy_tentacles(monster) && !in_transit) { diff --git a/crawl-ref/source/mon-transit.cc b/crawl-ref/source/mon-transit.cc index 49a1e37ba0..6db3c417ca 100644 --- a/crawl-ref/source/mon-transit.cc +++ b/crawl-ref/source/mon-transit.cc @@ -214,7 +214,7 @@ void place_transiting_items() pos, true); // List of items we couldn't place. - if (!copy_item_to_grid(*item, where_to_go)) + if (!copy_item_to_grid(*item, where_to_go, 1, false, true)) keep.push_back(*item); } diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 4d861d9de0..82dce8d5a0 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -927,6 +927,11 @@ bool mons_is_zombified(const monsters *mon) return (mons_class_is_zombified(mon->type)); } +monster_type mons_base_type(const monsters *mon) +{ + return mons_is_zombified(mon) ? mon->base_monster : mon->type; +} + bool mons_class_can_be_zombified(int mc) { int ms = mons_species(mc); @@ -2396,6 +2401,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/mon-util.h b/crawl-ref/source/mon-util.h index ce186ddf40..931c817748 100644 --- a/crawl-ref/source/mon-util.h +++ b/crawl-ref/source/mon-util.h @@ -557,6 +557,7 @@ bool mons_can_regenerate(const monsters *mon); int mons_zombie_size(int mc); monster_type mons_zombie_base(const monsters *mon); bool mons_class_is_zombified(int mc); +monster_type mons_base_type(const monsters *mon); bool mons_is_zombified(const monsters *monster); bool mons_class_can_be_zombified(int mc); bool mons_can_be_zombified(const monsters *mon); diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc index 5285da7795..1e88b10f33 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" @@ -187,7 +188,7 @@ static bool _player_near_water() bool monsters::wants_submerge() const { // Krakens never retreat when food (the player) is in range. - if (type == MONS_KRAKEN) + if (mons_base_type(this) == MONS_KRAKEN) if (_player_near_water()) return (false); @@ -316,7 +317,7 @@ size_type monsters::body_size(size_part_type /* psize */, bool /* base */) const return (ret); } -int monsters::body_weight() const +int monsters::body_weight(bool /*base*/) const { int mclass = type; @@ -1153,12 +1154,8 @@ bool monsters::drop_item(int eslot, int near) was_unequipped = true; } - bool on_floor = true; - if (pitem->flags & ISFLAG_SUMMONED) { - on_floor = false; - if (need_message(near)) mprf("%s %s as %s drops %s!", pitem->name(DESC_CAP_THE).c_str(), @@ -1169,38 +1166,30 @@ bool monsters::drop_item(int eslot, int near) item_was_destroyed(*pitem, mindex()); destroy_item(item_index); } - else if (!move_item_to_grid(&item_index, pos())) - { - // Re-equip item if we somehow failed to drop it. - if (was_unequipped) - equip(*pitem, eslot, near); - - return (false); - } - - // move_item_to_grid could change item_index, so - // update pitem. - pitem = &mitm[item_index]; - - if (on_floor) + else { - if (friendly()) - pitem->flags |= ISFLAG_DROPPED_BY_ALLY; - if (need_message(near)) { mprf("%s drops %s.", name(DESC_CAP_THE).c_str(), pitem->name(DESC_NOCAP_A).c_str()); } - dungeon_feature_type feat = grd(pos()); - if (feat_destroys_items(feat)) + if (!move_item_to_grid(&item_index, pos(), swimming())) { - if ( player_can_hear(pos()) ) - mprf(MSGCH_SOUND, feat_item_destruction_message(feat)); + // Re-equip item if we somehow failed to drop it. + if (was_unequipped) + equip(*pitem, eslot, near); - item_was_destroyed(*pitem, mindex()); - unlink_item(item_index); + return (false); + } + + if (friendly() && item_index != NON_ITEM) + { + // move_item_to_grid could change item_index, so + // update pitem. + pitem = &mitm[item_index]; + + pitem->flags |= ISFLAG_DROPPED_BY_ALLY; } } @@ -3308,6 +3297,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 +4294,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 +5558,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 +5593,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())) @@ -5955,7 +5965,7 @@ void monsters::react_to_damage(int damage, beam_type flavour, kill_category whos if (nmons != -1 && nmons != NON_MONSTER) { // Don't allow milking the royal jelly. - menv[nmons].flags |= MF_CREATED_FRIENDLY; + menv[nmons].flags |= MF_NO_REWARD; spawned++; } } @@ -5984,7 +5994,7 @@ void monsters::react_to_damage(int damage, beam_type flavour, kill_category whos else if (type == MONS_KRAKEN_TENTACLE && flavour != BEAM_TORMENT_DAMAGE) { if (!invalid_monster_index(number) - && menv[number].type == MONS_KRAKEN) + && mons_base_type(&menv[number]) == MONS_KRAKEN) { menv[number].hurt(&you, damage, flavour); @@ -6014,7 +6024,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 400ad01769..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); @@ -221,7 +226,7 @@ public: bool can_pass_through_feat(dungeon_feature_type grid) const; bool is_habitable_feat(dungeon_feature_type actual_grid) const; size_type body_size(size_part_type psize = PSIZE_TORSO, bool base = false) const; - int body_weight() const; + int body_weight(bool base = false) const; int total_weight() const; int damage_brand(int which_attack = -1); int damage_type(int which_attack = -1); @@ -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..2813101bbe 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); @@ -544,6 +551,10 @@ bool expose_items_to_element(beam_type flavour, const coord_def& where, if (target_class == OBJ_UNASSIGNED) return (false); + // Beams fly *over* water and lava. + if (grd(where) == DNGN_LAVA || grd(where) == DNGN_DEEP_WATER) + return (false); + for (stack_iterator si(where); si; ++si) { if (!si->is_valid()) diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 04c1498297..564bf18de1 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -2379,7 +2379,11 @@ int player_sust_abil(bool calc_unid) int carrying_capacity(burden_state_type bs) { - int cap = (2 * you.body_weight()) + (you.strength * 300) + // Yuck. We need this for gameplay - it nerfs small forms too much + // otherwise - but there's no good way to rationalize here... --sorear + int used_weight = std::max(you.body_weight(), you.body_weight(true)); + + int cap = (2 * used_weight) + (you.strength * 300) + (you.airborne() ? 1000 : 0); // We are nice to the lighter species in that strength adds absolutely // instead of relatively to body weight. --dpeg @@ -5545,6 +5549,17 @@ bool player::can_swim() const return (species == SP_MERFOLK && merfolk_change_is_safe(true)); } +int player::visible_igrd(const coord_def &where) const +{ + if (grd(where) == DNGN_LAVA + || (grd(where) == DNGN_DEEP_WATER && species != SP_MERFOLK)) + { + return (NON_ITEM); + } + + return igrd(where); +} + bool player::swimming() const { return in_water() && can_swim(); @@ -5593,9 +5608,12 @@ size_type player::body_size(size_part_type psize, bool base) const } } -int player::body_weight() const +int player::body_weight(bool base) const { - int weight = actor::body_weight(); + int weight = actor::body_weight(base); + + if (base) + return (weight); switch (attribute[ATTR_TRANSFORMATION]) { @@ -5973,8 +5991,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) @@ -6009,7 +6029,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) { @@ -6445,6 +6465,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. @@ -7042,6 +7068,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 a02e5f5c4c..9c64409023 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -323,6 +323,7 @@ public: bool in_water() const; bool can_swim() const; + int visible_igrd(const coord_def&) const; bool is_levitating() const; bool cannot_speak() const; bool invisible() const; @@ -383,7 +384,7 @@ public: bool can_pass_through_feat(dungeon_feature_type grid) const; bool is_habitable_feat(dungeon_feature_type actual_grid) const; size_type body_size(size_part_type psize = PSIZE_TORSO, bool base = false) const; - int body_weight() const; + int body_weight(bool base = false) const; int total_weight() const; int damage_brand(int which_attack = -1); int damage_type(int which_attack = -1); @@ -414,7 +415,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 +465,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 +515,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/religion.cc b/crawl-ref/source/religion.cc index 3e5d0ec767..99c2375cc0 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -1175,7 +1175,6 @@ static bool _need_missile_gift() const item_def *launcher = _find_missile_launcher(best_missile_skill); return (you.piety > 80 && random2( you.piety ) > 70 - && !feat_destroys_items( grd(you.pos()) ) && one_chance_in(8) && you.skills[ best_missile_skill ] >= 8 && (launcher || best_missile_skill == SK_DARTS)); @@ -1263,9 +1262,6 @@ static void _show_pure_deck_chances() static void _give_nemelex_gift() { - if (feat_destroys_items(grd(you.pos()))) - return; - // Nemelex will give at least one gift early. if (!you.num_gifts[GOD_NEMELEX_XOBEH] && x_chance_in_y(you.piety + 1, piety_breakpoint(1)) @@ -1289,14 +1285,16 @@ static void _give_nemelex_gift() #if DEBUG_GIFTS || DEBUG_CARDS _show_pure_deck_chances(); #endif - _update_sacrifice_weights(choice); - int thing_created = items( 1, OBJ_MISCELLANY, gift_type, true, 1, MAKE_ITEM_RANDOM_RACE, 0, 0, GOD_NEMELEX_XOBEH ); + move_item_to_grid(&thing_created, you.pos(), true); + if (thing_created != NON_ITEM) { + _update_sacrifice_weights(choice); + // Piety|Common | Rare |Legendary // -------------------------------- // 0: 95.00%, 5.00%, 0.00% @@ -1329,8 +1327,6 @@ static void _give_nemelex_gift() deck.colour = deck_rarity_to_color(rarity); deck.inscription = "god gift"; - move_item_to_grid(&thing_created, you.pos()); - simple_god_message(" grants you a gift!"); more(); canned_msg(MSG_SOMETHING_APPEARS); @@ -2046,7 +2042,6 @@ static void _do_god_gift(bool prayed_for) case GOD_TROG: if (you.piety > 130 && random2(you.piety) > 120 - && !feat_destroys_items(grd(you.pos())) && one_chance_in(4)) { if (you.religion == GOD_TROG @@ -2058,7 +2053,8 @@ static void _do_god_gift(bool prayed_for) { success = acquirement(OBJ_ARMOUR, you.religion); // Okawaru charges extra for armour acquirements. - _inc_gift_timeout(30 + random2avg(15, 2)); + if (success) + _inc_gift_timeout(30 + random2avg(15, 2)); } if (success) @@ -2194,8 +2190,7 @@ static void _do_god_gift(bool prayed_for) } } - if (gift != NUM_BOOKS - && !feat_destroys_items(grd(you.pos()))) + if (gift != NUM_BOOKS) { if (gift == OBJ_RANDOM) { @@ -2216,7 +2211,7 @@ static void _do_god_gift(bool prayed_for) // reason. mark_had_book(gift); - move_item_to_grid( &thing_created, you.pos() ); + move_item_to_grid( &thing_created, you.pos(), true ); if (thing_created != NON_ITEM) { @@ -2271,7 +2266,7 @@ static bool _confirm_pray_sacrifice(god_type god) return (false); } - for (stack_iterator si(you.pos()); si; ++si) + for (stack_iterator si(you.pos(), true); si; ++si) { if (_god_likes_item(god, *si) && (_is_risky_sacrifice(*si) @@ -2683,7 +2678,7 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known, } if (thing_done == DID_ATTACK_HOLY && victim - && !testbits(victim->flags, MF_CREATED_FRIENDLY) + && !testbits(victim->flags, MF_NO_REWARD) && !testbits(victim->flags, MF_WAS_NEUTRAL)) { break; @@ -3029,7 +3024,7 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known, case GOD_SHINING_ONE: case GOD_ELYVILON: if (victim - && !testbits(victim->flags, MF_CREATED_FRIENDLY) + && !testbits(victim->flags, MF_NO_REWARD) && !testbits(victim->flags, MF_WAS_NEUTRAL)) { break; @@ -3097,7 +3092,7 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known, case GOD_SHINING_ONE: case GOD_ELYVILON: if (victim - && !testbits(victim->flags, MF_CREATED_FRIENDLY) + && !testbits(victim->flags, MF_NO_REWARD) && !testbits(victim->flags, MF_WAS_NEUTRAL)) { break; @@ -3816,7 +3811,7 @@ bool ely_destroy_weapons() god_acting gdact; bool success = false; - for (stack_iterator si(you.pos()); si; ++si) + for (stack_iterator si(you.pos(), true); si; ++si) { item_def& item(*si); if (item.base_type != OBJ_WEAPONS @@ -4699,7 +4694,8 @@ void offer_items() int i = igrd(you.pos()); - if (!god_likes_items(you.religion) && i != NON_ITEM) + if (!god_likes_items(you.religion) && i != NON_ITEM + && you.visible_igrd(you.pos()) != NON_ITEM) { simple_god_message(" doesn't care about such mundane gifts.", you.religion); @@ -4859,9 +4855,9 @@ void offer_items() if (num_sacced > 0 && you.religion == GOD_KIKUBAAQUDGHA) { - simple_god_message(" torments the living!"); + simple_god_message(" torments the living!"); torment(TORMENT_KIKUBAAQUDGHA, you.pos()); - you.piety -= 8 + random2(4); // costs 8 - 12 piety + lose_piety(random_range(8, 12)); } // Explanatory messages if nothing the god likes is sacrificed. diff --git a/crawl-ref/source/rltiles/UNUSED/other/transparent_flesh.png b/crawl-ref/source/rltiles/UNUSED/other/transparent_flesh.png Binary files differnew file mode 100644 index 0000000000..2437acebb2 --- /dev/null +++ b/crawl-ref/source/rltiles/UNUSED/other/transparent_flesh.png diff --git a/crawl-ref/source/rltiles/dc-dngn.txt b/crawl-ref/source/rltiles/dc-dngn.txt index 53a5f1270a..f306cfd048 100644 --- a/crawl-ref/source/rltiles/dc-dngn.txt +++ b/crawl-ref/source/rltiles/dc-dngn.txt @@ -418,6 +418,12 @@ wall/wall_flesh3 wall/wall_flesh4 wall/wall_flesh5 wall/wall_flesh6 +wall/transparent_flesh1 WALL_TRANSPARENT_FLESH +wall/transparent_flesh2 +wall/transparent_flesh3 +wall/transparent_flesh4 +wall/transparent_flesh5 +wall/transparent_flesh6 wall/wall_vines0 WALL_VINES wall/wall_vines1 @@ -875,6 +881,9 @@ wall/dngn_green_crystal_wall DNGN_GREEN_CRYSTAL_WALL DNGN_CRYSTAL DNGN_CRYSTAL_G %repeat DNGN_CRYSTAL DNGN_CRYSTAL_WHITE %resetcol +wall/tree1 DNGN_TREE +wall/tree2 + ## doors dngn_detected_secret_door DNGN_DETECTED_SECRET_DOOR dngn_closed_door DNGN_CLOSED_DOOR @@ -886,6 +895,14 @@ gate_open_left DNGN_GATE_OPEN_LEFT gate_open_middle DNGN_GATE_OPEN_MIDDLE gate_open_right DNGN_GATE_OPEN_RIGHT +## Alternate doors for vaults, etc. Should be in the order listed above for +## doors which also provide gateways; otherwise detected, closed, open, or +## closed, open. +%sdir dc-dngn/gateways +fleshy_orifice_closed DNGN_FLESHY_ORIFICE +fleshy_orifice_open +%sdir dc-dngn + dngn_orcish_idol DNGN_ORCISH_IDOL dngn_granite_statue DNGN_GRANITE_STATUE diff --git a/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_closed.png b/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_closed.png Binary files differnew file mode 100644 index 0000000000..fcf1893994 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_closed.png diff --git a/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_open.png b/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_open.png Binary files differnew file mode 100644 index 0000000000..6ce8d1f29e --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_open.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.png Binary files differnew file mode 100644 index 0000000000..664f187d47 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.png Binary files differnew file mode 100644 index 0000000000..a602714196 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.png Binary files differnew file mode 100644 index 0000000000..9a12e349bd --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.png Binary files differnew file mode 100644 index 0000000000..712bc83793 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.png Binary files differnew file mode 100644 index 0000000000..74fd01c736 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.png Binary files differnew file mode 100644 index 0000000000..b9da8247fb --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/tree1.png b/crawl-ref/source/rltiles/dc-dngn/wall/tree1.png Binary files differnew file mode 100644 index 0000000000..6f80075b4d --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/tree1.png diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/tree2.png b/crawl-ref/source/rltiles/dc-dngn/wall/tree2.png Binary files differnew file mode 100644 index 0000000000..a8835226a0 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-dngn/wall/tree2.png diff --git a/crawl-ref/source/rltiles/dc-misc.txt b/crawl-ref/source/rltiles/dc-misc.txt index 653b614435..10bc4fc714 100644 --- a/crawl-ref/source/rltiles/dc-misc.txt +++ b/crawl-ref/source/rltiles/dc-misc.txt @@ -22,6 +22,12 @@ cloud_poison2 CLOUD_POISON_2 cloud_black_smoke CLOUD_BLACK_SMOKE cloud_blue_smoke CLOUD_BLUE_SMOKE cloud_grey_smoke CLOUD_GREY_SMOKE + +%variation CLOUD_GREY_SMOKE white +%lum 0 30 +%repeat CLOUD_GREY_SMOKE CLOUD_WHITE_SMOKE +%resetcol + cloud_miasma CLOUD_MIASMA cloud_tloc_energy CLOUD_TLOC_ENERGY cloud_mutagenic_small1 CLOUD_MUTAGENIC_0 diff --git a/crawl-ref/source/rltiles/dc-mon.txt b/crawl-ref/source/rltiles/dc-mon.txt index 49e246efe8..06220dd2f5 100644 --- a/crawl-ref/source/rltiles/dc-mon.txt +++ b/crawl-ref/source/rltiles/dc-mon.txt @@ -280,6 +280,10 @@ iron_golem MONS_IRON_GOLEM stone_golem MONS_STONE_GOLEM toenail_golem MONS_TOENAIL_GOLEM wood_golem MONS_WOOD_GOLEM +# For the Cigotuvi WizLab +flesh_golem MONS_FLESH_GOLEM +# For the Erinya WizLab +vine-covered_golem MONS_VINE_GOLEM ## Statues (also '8') %sdir dc-mon/unique @@ -288,7 +292,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 @@ -337,6 +370,10 @@ shapeshifter MONS_SHAPESHIFTER glowing_shapeshifter MONS_GLOWING_SHAPESHIFTER killer_klown MONS_KILLER_KLOWN slave MONS_SLAVE +## From the Wucad Mu wizlab +human_monk_ghost MONS_HUMAN_MONK +## From the Cigotuvi wizlab +deformed_human MONS_DEFORMED_HUMAN ## Angels ('A') angel MONS_ANGEL @@ -388,7 +425,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 @@ -466,6 +505,7 @@ two_headed_ogre MONS_TWO_HEADED_OGRE ogre_mage MONS_OGRE_MAGE ## Plants ('P') +%sdir dc-mon/fungi_plants plant MONS_PLANT plant_crypt MONS_WITHERED_PLANT oklob_plant MONS_OKLOB_PLANT @@ -479,6 +519,9 @@ troll MONS_TROLL rock_troll MONS_ROCK_TROLL iron_troll MONS_IRON_TROLL deep_troll MONS_DEEP_TROLL +## From the Wucad Mu wizard laboratory +rock_troll_monk_ghost MONS_ROCK_TROLL_MONK +iron_troll_monk_ghost MONS_IRON_TROLL_MONK ## Vampires ('V') vampire MONS_VAMPIRE @@ -529,10 +572,15 @@ deep_elf_priest MONS_DEEP_ELF_PRIEST deep_elf_high_priest MONS_DEEP_ELF_HIGH_PRIEST deep_elf_blademaster MONS_DEEP_ELF_BLADEMASTER deep_elf_master_archer MONS_DEEP_ELF_MASTER_ARCHER +## From the Cigotuvi wizlab +deformed_elf MONS_DEFORMED_ELF ## Fungi ('f') +%sdir dc-mon/fungi_plants fungus MONS_TOADSTOOL fungus MONS_FUNGUS +ballistomycete MONS_BALLISTOMYCETE_INACTIVE +active_ballistomycete MONS_BALLISTOMYCETE_ACTIVE wandering_mushroom MONS_WANDERING_MUSHROOM ## Goblins ('g') @@ -577,6 +625,7 @@ orc_knight MONS_ORC_KNIGHT orc_warlord MONS_ORC_WARLORD orc_sorcerer MONS_ORC_SORCERER orc_high_priest MONS_ORC_HIGH_PRIEST +deformed_orc MONS_DEFORMED_ORC ## Ghosts ('p') %rim 0 diff --git a/crawl-ref/source/rltiles/dc-mon/deformed_elf.png b/crawl-ref/source/rltiles/dc-mon/deformed_elf.png Binary files differnew file mode 100644 index 0000000000..df09452448 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/deformed_elf.png diff --git a/crawl-ref/source/rltiles/dc-mon/deformed_human.png b/crawl-ref/source/rltiles/dc-mon/deformed_human.png Binary files differnew file mode 100644 index 0000000000..78d8225146 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/deformed_human.png diff --git a/crawl-ref/source/rltiles/dc-mon/deformed_orc.png b/crawl-ref/source/rltiles/dc-mon/deformed_orc.png Binary files differnew file mode 100644 index 0000000000..4d62be9385 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/deformed_orc.png diff --git a/crawl-ref/source/rltiles/dc-mon/flesh_golem.png b/crawl-ref/source/rltiles/dc-mon/flesh_golem.png Binary files differnew file mode 100644 index 0000000000..d763855dd6 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/flesh_golem.png diff --git a/crawl-ref/source/rltiles/dc-mon/fungi_plants/active_ballistomycete.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/active_ballistomycete.png Binary files differnew file mode 100644 index 0000000000..bd01f0470c --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/active_ballistomycete.png diff --git a/crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.png Binary files differnew file mode 100644 index 0000000000..69bff99afd --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.png diff --git a/crawl-ref/source/rltiles/dc-mon/fungus.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/fungus.png Binary files differindex 75d0cd66ed..75d0cd66ed 100644 --- a/crawl-ref/source/rltiles/dc-mon/fungus.png +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/fungus.png 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/oklob_plant.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/oklob_plant.png Binary files differindex 4f0f0da6b2..4f0f0da6b2 100644 --- a/crawl-ref/source/rltiles/dc-mon/oklob_plant.png +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/oklob_plant.png diff --git a/crawl-ref/source/rltiles/dc-mon/plant.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant.png Binary files differindex b3124b5cae..b3124b5cae 100644 --- a/crawl-ref/source/rltiles/dc-mon/plant.png +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant.png diff --git a/crawl-ref/source/rltiles/dc-mon/plant_crypt.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant_crypt.png Binary files differindex f17a501d4f..f17a501d4f 100644 --- a/crawl-ref/source/rltiles/dc-mon/plant_crypt.png +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant_crypt.png diff --git a/crawl-ref/source/rltiles/dc-mon/wandering_mushroom.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/wandering_mushroom.png Binary files differindex e8cff68b06..e8cff68b06 100644 --- a/crawl-ref/source/rltiles/dc-mon/wandering_mushroom.png +++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/wandering_mushroom.png diff --git a/crawl-ref/source/rltiles/dc-mon/human_monk_ghost.png b/crawl-ref/source/rltiles/dc-mon/human_monk_ghost.png Binary files differnew file mode 100644 index 0000000000..911281db34 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/human_monk_ghost.png diff --git a/crawl-ref/source/rltiles/dc-mon/iron_troll_monk_ghost.png b/crawl-ref/source/rltiles/dc-mon/iron_troll_monk_ghost.png Binary files differnew file mode 100644 index 0000000000..c6e65c7c35 --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/iron_troll_monk_ghost.png diff --git a/crawl-ref/source/rltiles/dc-mon/rock_troll_monk_ghost.png b/crawl-ref/source/rltiles/dc-mon/rock_troll_monk_ghost.png Binary files differnew file mode 100644 index 0000000000..9a40dccc8a --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/rock_troll_monk_ghost.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/rltiles/dc-mon/vine-covered_golem.png b/crawl-ref/source/rltiles/dc-mon/vine-covered_golem.png Binary files differnew file mode 100644 index 0000000000..d8f604671b --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/vine-covered_golem.png 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/show.cc b/crawl-ref/source/show.cc index c37901d7e0..0148d92438 100644 --- a/crawl-ref/source/show.cc +++ b/crawl-ref/source/show.cc @@ -224,8 +224,8 @@ void show_def::_update_item_at(const coord_def &gp, const coord_def &ep) const monsters* m = monster_at(gp); if (m && mons_is_unknown_mimic(m)) eitem = &get_mimic_item(m); - else if (igrd(gp) != NON_ITEM) - eitem = &mitm[igrd(gp)]; + else if (you.visible_igrd(gp) != NON_ITEM) + eitem = &mitm[you.visible_igrd(gp)]; else return; @@ -234,17 +234,20 @@ void show_def::_update_item_at(const coord_def &gp, const coord_def &ep) glyph g = get_item_glyph(eitem); const dungeon_feature_type feat = grd(gp); + if (Options.feature_item_brand && is_critical_feature(feat)) ecol |= COLFLAG_FEATURE_ITEM; else if (Options.trap_item_brand && feat_is_trap(feat)) ecol |= COLFLAG_TRAP_ITEM; else { - const unsigned short gcol = env.grid_colours(gp); - ecol = (feat == DNGN_SHALLOW_WATER) ? - (gcol != BLACK ? gcol : CYAN) : g.col; + ecol = g.col; + + if (feat_is_water(feat)) + ecol = _feat_colour(gp, feat); + // monster(mimic)-owned items have link = NON_ITEM+1+midx - if (eitem->link > NON_ITEM && igrd(gp) != NON_ITEM) + if (eitem->link > NON_ITEM && you.visible_igrd(gp) != NON_ITEM) ecol |= COLFLAG_ITEM_HEAP; else if (eitem->link < NON_ITEM && !crawl_state.arena) ecol |= COLFLAG_ITEM_HEAP; @@ -253,7 +256,7 @@ void show_def::_update_item_at(const coord_def &gp, const coord_def &ep) } #ifdef USE_TILE - int idx = igrd(gp); + int idx = you.visible_igrd(gp); if (idx != NON_ITEM) { if (feat_is_stair(feat)) diff --git a/crawl-ref/source/showsymb.cc b/crawl-ref/source/showsymb.cc index 6f3c77fea6..6019065d7e 100644 --- a/crawl-ref/source/showsymb.cc +++ b/crawl-ref/source/showsymb.cc @@ -100,7 +100,7 @@ static int _get_mons_colour(const monsters *mons) col |= COLFLAG_FEATURE_ITEM; } else if (Options.heap_brand != CHATTR_NORMAL - && igrd(mons->pos()) != NON_ITEM + && you.visible_igrd(mons->pos()) != NON_ITEM && !crawl_state.arena) { col |= COLFLAG_ITEM_HEAP; 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.cc b/crawl-ref/source/spells4.cc index 6fbb55f399..d4c78afc48 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -1177,14 +1177,12 @@ bool cast_evaporate(int pow, bolt& beem, int pot_idx) // Producing helpful potions would break game balance here... // and producing more than one potion from a corpse, or not // using up the corpse might also lead to game balance problems. - bwr -void cast_fulsome_distillation(int pow) +void cast_fulsome_distillation(int /*pow*/) { - pow = std::min(50, pow); - int corpse = -1; // Search items at the player's location for corpses. - for (stack_iterator si(you.pos()); si; ++si) + for (stack_iterator si(you.pos(), true); si; ++si) { if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY) { @@ -1205,96 +1203,63 @@ void cast_fulsome_distillation(int pow) return; } - const bool rotten = food_is_rotten(mitm[corpse]); - const bool big_monster = (mons_type_hit_dice(mitm[corpse].plus) >= 5); - const bool power_up = (rotten && big_monster); - potion_type pot_type = POT_WATER; - switch (mitm[corpse].plus) + switch (mons_corpse_effect(mitm[corpse].plus)) { - case MONS_GIANT_BAT: // extracting batty behaviour : 1 - case MONS_GIANT_BLOWFLY: // extracting batty behaviour : 5 - pot_type = POT_CONFUSION; + case CE_CLEAN: + pot_type = POT_WATER; break; - case MONS_RED_WASP: // paralysis attack : 8 - case MONS_YELLOW_WASP: // paralysis attack : 4 - pot_type = POT_PARALYSIS; + case CE_CONTAMINATED: + pot_type = (mons_weight(mitm[corpse].plus) >= 900) + ? POT_DEGENERATION : POT_CONFUSION; break; - case MONS_SNAKE: // clean meat, but poisonous attack : 2 - case MONS_GIANT_ANT: // clean meat, but poisonous attack : 3 - pot_type = (power_up ? POT_POISON : POT_CONFUSION); + case CE_POISONOUS: + pot_type = POT_POISON; break; - case MONS_ORANGE_RAT: // poisonous meat, but draining attack : 3 - pot_type = (power_up ? POT_DECAY : POT_POISON); + case CE_MUTAGEN_RANDOM: + case CE_MUTAGEN_GOOD: // unused + case CE_RANDOM: // unused + pot_type = POT_MUTATION; break; - case MONS_SPINY_WORM: // 12 - pot_type = (power_up ? POT_DECAY : POT_STRONG_POISON); + case CE_MUTAGEN_BAD: // unused + case CE_ROTTEN: // actually this only occurs via mangling + case CE_HCL: // necrophage + pot_type = POT_DECAY; break; + case CE_NOCORPSE: // shouldn't occur default: - switch (mons_corpse_effect(mitm[corpse].plus)) - { - case CE_CLEAN: - pot_type = (power_up ? POT_CONFUSION : POT_WATER); - break; - - case CE_CONTAMINATED: - pot_type = (power_up ? POT_DEGENERATION : POT_POISON); - break; - - case CE_POISONOUS: - pot_type = (power_up ? POT_STRONG_POISON : POT_POISON); - break; + break; + } - case CE_MUTAGEN_RANDOM: - case CE_MUTAGEN_GOOD: // unused - case CE_RANDOM: // unused - pot_type = POT_MUTATION; - break; + switch (mitm[corpse].plus) + { + case MONS_RED_WASP: // paralysis attack + pot_type = POT_PARALYSIS; + break; - case CE_MUTAGEN_BAD: // unused - case CE_ROTTEN: // actually this only occurs via mangling - case CE_HCL: // necrophage - pot_type = (power_up ? POT_DECAY : POT_STRONG_POISON); - break; + case MONS_YELLOW_WASP: // slowing attack + pot_type = POT_SLOWING; + break; - case CE_NOCORPSE: // shouldn't occur - default: - break; - } + default: break; } - // If not powerful enough, we downgrade the potion. - if (random2(50) > pow + 10 * rotten) + struct monsterentry* smc = get_monster_data(mitm[corpse].plus); + + for (int nattk = 0; nattk < 4; ++nattk) { - switch (pot_type) + if (smc->attack[nattk].flavour == AF_POISON_MEDIUM || + smc->attack[nattk].flavour == AF_POISON_STRONG || + smc->attack[nattk].flavour == AF_POISON_STR) { - case POT_DECAY: - case POT_DEGENERATION: - case POT_STRONG_POISON: - pot_type = POT_POISON; - break; - - case POT_MUTATION: - case POT_POISON: - pot_type = POT_CONFUSION; - break; - - case POT_PARALYSIS: - pot_type = POT_SLOWING; - break; - - case POT_CONFUSION: - case POT_SLOWING: - default: - pot_type = POT_WATER; - break; + pot_type = POT_STRONG_POISON; } } @@ -1310,6 +1275,8 @@ void cast_fulsome_distillation(int pow) mitm[corpse].inscription.clear(); item_colour(mitm[corpse]); // sets special as well + set_ident_type(mitm[corpse], ID_KNOWN_TYPE); + mprf("You extract %s from the corpse.", mitm[corpse].name(DESC_NOCAP_A).c_str()); @@ -1509,7 +1476,7 @@ bool cast_fragmentation(int pow, const dist& spd) goto all_done; } - for (stack_iterator si(spd.target); si; ++si) + for (stack_iterator si(spd.target, true); si; ++si) { if (si->base_type == OBJ_CORPSES) { @@ -1747,16 +1714,18 @@ bool cast_portal_projectile(int pow) bool cast_apportation(int pow, const coord_def& where) { - // Protect the player from destroying the item. - if (feat_destroys_items(grd(you.pos()))) + if (you.trans_wall_blocking(where)) { - mpr( "That would be silly while over this terrain!" ); + mpr("A translucent wall is in the way."); return (false); } - if (you.trans_wall_blocking(where)) + // Letting mostly-melee characters spam apport after every Shoals + // fight seems like it has too much grinding potential. We could + // weaken this for high power. + if (grd(where) == DNGN_DEEP_WATER || grd(where) == DNGN_LAVA) { - mpr("A translucent wall is in the way."); + mpr("The density of the terrain blocks your spell."); return (false); } @@ -1783,6 +1752,13 @@ bool cast_apportation(int pow, const coord_def& where) item_def& item = mitm[item_idx]; + // Protect the player from destroying the item. + if (feat_destroys_item(grd(you.pos()), item)) + { + mpr( "That would be silly while over this terrain!" ); + return (false); + } + // Mass of one unit. const int unit_mass = item_mass(item); const int max_mass = pow * 30 + random2(pow * 20); 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 21f7c7f290..10c4e7cea9 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -1059,7 +1059,7 @@ static void _try_monster_cast(spell_type spell, int powc, mon->type = MONS_HUMAN; mon->behaviour = BEH_SEEK; mon->attitude = ATT_FRIENDLY; - mon->flags = (MF_CREATED_FRIENDLY | MF_JUST_SUMMONED | MF_SEEN + mon->flags = (MF_NO_REWARD | MF_JUST_SUMMONED | MF_SEEN | MF_WAS_IN_VIEW | MF_HARD_RESET); mon->hit_points = you.hp; mon->hit_dice = you.experience_level; @@ -1140,6 +1140,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(). @@ -1420,6 +1421,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 d1f567d942..9de226af2e 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -927,7 +927,7 @@ SPTYP_TRANSMUTATION | SPTYP_NECROMANCY, SPFLAG_NONE, 1, - 50, + 0, -1, -1, 0, NULL, @@ -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_IOOD, "Orb of Destruction", SPTYP_CONJURATION, SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, 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/stash.cc b/crawl-ref/source/stash.cc index 147dbc67d8..f40dc4005e 100644 --- a/crawl-ref/source/stash.cc +++ b/crawl-ref/source/stash.cc @@ -326,7 +326,7 @@ static bool _grid_has_mimic_item(const coord_def& pos) static bool _grid_has_perceived_item(const coord_def& pos) { - return (igrd(pos) != NON_ITEM || _grid_has_mimic_item(pos)); + return (you.visible_igrd(pos) != NON_ITEM || _grid_has_mimic_item(pos)); } static bool _grid_has_perceived_multiple_items(const coord_def& pos) @@ -336,7 +336,7 @@ static bool _grid_has_perceived_multiple_items(const coord_def& pos) if (_grid_has_mimic_item(pos)) ++count; - for (stack_iterator si(pos); si && count < 2; ++si) + for (stack_iterator si(pos, true); si && count < 2; ++si) ++count; return (count > 1); @@ -361,7 +361,7 @@ void Stash::update() items.clear(); // Now, grab all items on that square and fill our vector - for (stack_iterator si(p); si; ++si) + for (stack_iterator si(p, true); si; ++si) if (!is_filtered(*si)) add_item(*si); @@ -384,7 +384,7 @@ void Stash::update() pitem = &get_mimic_item(monster_at(p)); else { - pitem = &mitm[igrd(p)]; + pitem = &mitm[you.visible_igrd(p)]; tutorial_first_item(*pitem); } 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.cc b/crawl-ref/source/stuff.cc index ccff7125aa..9094d9b214 100644 --- a/crawl-ref/source/stuff.cc +++ b/crawl-ref/source/stuff.cc @@ -65,9 +65,9 @@ #include "tutorial.h" #include "view.h" -stack_iterator::stack_iterator(const coord_def& pos) +stack_iterator::stack_iterator(const coord_def& pos, bool accesible) { - cur_link = igrd(pos); + cur_link = accesible ? you.visible_igrd(pos) : igrd(pos); if ( cur_link != NON_ITEM ) next_link = mitm[cur_link].link; else diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h index 89757eb99d..8d0e288db6 100644 --- a/crawl-ref/source/stuff.h +++ b/crawl-ref/source/stuff.h @@ -24,7 +24,7 @@ class stack_iterator : public std::iterator<std::forward_iterator_tag, item_def> { public: - explicit stack_iterator( const coord_def& pos ); + explicit stack_iterator( const coord_def& pos, bool accessible = false ); explicit stack_iterator( int start_link ); operator bool() const; @@ -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/tags.cc b/crawl-ref/source/tags.cc index 6d12c323ab..82a2f462ab 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -2426,7 +2426,7 @@ void tag_missing_level_attitude() if (menv[i].type < 0) continue; - is_friendly = testbits(menv[i].flags, MF_CREATED_FRIENDLY); + is_friendly = testbits(menv[i].flags, MF_NO_REWARD); menv[i].foe = MHITNOT; diff --git a/crawl-ref/source/terrain.cc b/crawl-ref/source/terrain.cc index 9b2fe96deb..b917b87b50 100644 --- a/crawl-ref/source/terrain.cc +++ b/crawl-ref/source/terrain.cc @@ -321,6 +321,12 @@ bool cell_is_solid(const coord_def &c) return (feat_is_solid(grd(c))); } +bool feat_has_solid_floor(dungeon_feature_type feat) +{ + return (!feat_is_solid(feat) && feat != DNGN_DEEP_WATER && + feat != DNGN_LAVA); +} + bool feat_is_door(dungeon_feature_type feat) { return (feat == DNGN_CLOSED_DOOR || feat == DNGN_DETECTED_SECRET_DOOR @@ -376,11 +382,6 @@ bool feat_is_watery(dungeon_feature_type feat) return (feat_is_water(feat) || feat == DNGN_FOUNTAIN_BLUE); } -bool feat_destroys_items(dungeon_feature_type feat) -{ - return (feat == DNGN_LAVA || feat == DNGN_DEEP_WATER); -} - // Returns GOD_NO_GOD if feat is not an altar, otherwise returns the // GOD_* type. god_type feat_altar_god(dungeon_feature_type feat) @@ -543,16 +544,31 @@ dungeon_feature_type grid_secret_door_appearance(const coord_def &where) : ret); } -const char *feat_item_destruction_message(dungeon_feature_type feat) +bool feat_destroys_item(dungeon_feature_type feat, const item_def &item, + bool noisy) { - return (feat == DNGN_DEEP_WATER ? "You hear a splash." : - feat == DNGN_LAVA ? "You hear a sizzling splash." - : "You hear a crunching noise."); + switch (feat) + { + case DNGN_SHALLOW_WATER: + case DNGN_DEEP_WATER: + if (noisy) + mprf(MSGCH_SOUND, "You hear a splash."); + return (false); + + case DNGN_LAVA: + if (noisy) + mprf(MSGCH_SOUND, "You hear a sizzling splash."); + return (item.base_type == OBJ_SCROLLS); + + default: + return (false); + } } static coord_def _dgn_find_nearest_square( const coord_def &pos, - bool (*acceptable)(const coord_def &), + void *thing, + bool (*acceptable)(const coord_def &, void *thing), bool (*traversable)(const coord_def &) = NULL) { memset(travel_point_distance, 0, sizeof(travel_distance_grid_t)); @@ -568,7 +584,7 @@ static coord_def _dgn_find_nearest_square( { const coord_def &p = *i; - if (p != pos && acceptable(p)) + if (p != pos && acceptable(p, thing)) return (p); travel_point_distance[p.x][p.y] = 1; @@ -597,16 +613,17 @@ static coord_def _dgn_find_nearest_square( return (unfound); } -static bool _item_safe_square(const coord_def &pos) +static bool _item_safe_square(const coord_def &pos, void *item) { const dungeon_feature_type feat = grd(pos); - return (feat_is_traversable(feat) && !feat_destroys_items(feat)); + return (feat_is_traversable(feat) && + !feat_destroys_item(feat, *static_cast<item_def *>(item))); } // Moves an item on the floor to the nearest adjacent floor-space. static bool _dgn_shift_item(const coord_def &pos, item_def &item) { - const coord_def np = _dgn_find_nearest_square(pos, _item_safe_square); + const coord_def np = _dgn_find_nearest_square(pos, &item, _item_safe_square); if (in_bounds(np) && np != pos) { int index = item.index(); @@ -622,7 +639,7 @@ bool is_critical_feature(dungeon_feature_type feat) || feat_altar_god(feat) != GOD_NO_GOD); } -static bool _is_feature_shift_target(const coord_def &pos) +static bool _is_feature_shift_target(const coord_def &pos, void*) { return (grd(pos) == DNGN_FLOOR && !dungeon_events.has_listeners_at(pos)); } @@ -634,7 +651,7 @@ static bool _dgn_shift_feature(const coord_def &pos) return (false); const coord_def dest = - _dgn_find_nearest_square(pos, _is_feature_shift_target); + _dgn_find_nearest_square(pos, NULL, _is_feature_shift_target); if (in_bounds(dest) && dest != pos) { @@ -654,27 +671,27 @@ static bool _dgn_shift_feature(const coord_def &pos) static void _dgn_check_terrain_items(const coord_def &pos, bool preserve_items) { const dungeon_feature_type feat = grd(pos); - if (feat_is_solid(feat) || feat_destroys_items(feat)) + + int item = igrd(pos); + bool did_destroy = false; + while (item != NON_ITEM) { - int item = igrd(pos); - bool did_destroy = false; - while (item != NON_ITEM) - { - const int curr = item; - item = mitm[item].link; + const int curr = item; + item = mitm[item].link; - // Game-critical item. - if (preserve_items || mitm[curr].is_critical()) - _dgn_shift_item(pos, mitm[curr]); - else - { - item_was_destroyed(mitm[curr]); - destroy_item(curr); - did_destroy = true; - } + if (!feat_is_solid(feat) && !feat_destroys_item(feat, mitm[curr])) + continue; + + // Game-critical item. + if (preserve_items || mitm[curr].is_critical()) + _dgn_shift_item(pos, mitm[curr]); + else + { + feat_destroys_item(feat, mitm[curr], true); + item_was_destroyed(mitm[curr]); + destroy_item(curr); + did_destroy = true; } - if (did_destroy && player_can_hear(pos)) - mprf(MSGCH_SOUND, feat_item_destruction_message(feat)); } } @@ -704,7 +721,7 @@ static void _dgn_check_terrain_blood(const coord_def &pos, else { if (feat_is_solid(old_feat) != feat_is_solid(new_feat) - || feat_is_water(new_feat) || feat_destroys_items(new_feat) + || feat_is_water(new_feat) || new_feat == DNGN_LAVA || is_critical_feature(new_feat)) { env.pgrid(pos) &= ~(FPROP_BLOODY); diff --git a/crawl-ref/source/terrain.h b/crawl-ref/source/terrain.h index f464d29929..73c5c5ee5e 100644 --- a/crawl-ref/source/terrain.h +++ b/crawl-ref/source/terrain.h @@ -24,6 +24,7 @@ bool cell_is_solid(const coord_def &c); bool feat_is_wall(dungeon_feature_type feat); bool feat_is_opaque(dungeon_feature_type feat); bool feat_is_solid(dungeon_feature_type feat); +bool feat_has_solid_floor(dungeon_feature_type feat); bool feat_is_door(dungeon_feature_type feat); bool feat_is_closed_door(dungeon_feature_type feat); bool feat_is_secret_door(dungeon_feature_type feat); @@ -63,9 +64,7 @@ void find_connected_range(coord_def d, dungeon_feature_type ft_min, void get_door_description(int door_size, const char** adjective, const char** noun); dungeon_feature_type grid_secret_door_appearance(const coord_def &where); dungeon_feature_type grid_appearance(const coord_def &gc); -bool feat_destroys_items(dungeon_feature_type feat); - -const char *feat_item_destruction_message( dungeon_feature_type feat ); +bool feat_destroys_item(dungeon_feature_type feat, const item_def &item, bool noisy = false); // Terrain changed under 'pos', perform necessary effects. void dungeon_terrain_changed(const coord_def &pos, diff --git a/crawl-ref/source/tilepick.cc b/crawl-ref/source/tilepick.cc index 4a0a8b2fb1..4d40a20c0c 100644 --- a/crawl-ref/source/tilepick.cc +++ b/crawl-ref/source/tilepick.cc @@ -199,7 +199,9 @@ int tileidx_monster_base(const monsters *mon, bool detected) // fungi ('f') case MONS_BALLISTOMYCETE: - return TILEP_MONS_FUNGUS; + if (!detected && mon->has_ench(ENCH_SPORE_PRODUCTION)) + return TILEP_MONS_BALLISTOMYCETE_ACTIVE; + return TILEP_MONS_BALLISTOMYCETE_INACTIVE; case MONS_TOADSTOOL: return TILEP_MONS_TOADSTOOL; case MONS_FUNGUS: @@ -2474,6 +2476,8 @@ int tileidx_feature(dungeon_feature_type feat, int gx, int gy) return TILE_DNGN_ORCISH_IDOL; case DNGN_WAX_WALL: return TILE_DNGN_WAX_WALL; + case DNGN_TREES: + return TILE_DNGN_TREE; case DNGN_GRANITE_STATUE: return TILE_DNGN_GRANITE_STATUE; case DNGN_LAVA: @@ -4794,7 +4798,7 @@ void tile_place_monster(int gx, int gy, int idx, bool foreground, bool detected) if (!mons_is_known_mimic(mon)) { // If necessary add item brand. - if (igrd(gc) != NON_ITEM) + if (you.visible_igrd(gc) != NON_ITEM) { if (foreground) t |= TILE_FLAG_S_UNDER; @@ -4814,7 +4818,7 @@ void tile_place_monster(int gx, int gy, int idx, bool foreground, bool detected) else if (menv[idx].holiness() == MH_PLANT) { // If necessary add item brand. - if (igrd(gc) != NON_ITEM) + if (you.visible_igrd(gc) != NON_ITEM) { if (foreground) t |= TILE_FLAG_S_UNDER; diff --git a/crawl-ref/source/tilereg.cc b/crawl-ref/source/tilereg.cc index 4490e3a59a..65383f46b4 100644 --- a/crawl-ref/source/tilereg.cc +++ b/crawl-ref/source/tilereg.cc @@ -1745,7 +1745,7 @@ bool DungeonRegion::update_tip_text(std::string& tip) tip += get_class_abbrev(you.char_class); tip += ")"; - if (igrd(m_cursor[CURSOR_MOUSE]) != NON_ITEM) + if (you.visible_igrd(m_cursor[CURSOR_MOUSE]) != NON_ITEM) tip += "\n[L-Click] Pick up items (g)"; const dungeon_feature_type feat = grd(m_cursor[CURSOR_MOUSE]); diff --git a/crawl-ref/source/tilesdl.cc b/crawl-ref/source/tilesdl.cc index 50dbb83f58..62ac431970 100644 --- a/crawl-ref/source/tilesdl.cc +++ b/crawl-ref/source/tilesdl.cc @@ -1550,7 +1550,7 @@ void TilesFramework::update_inventory() memset(inv_shown, 0, sizeof(inv_shown)); int num_ground = 0; - for (int i = igrd(you.pos()); i != NON_ITEM; i = mitm[i].link) + for (int i = you.visible_igrd(you.pos()); i != NON_ITEM; i = mitm[i].link) num_ground++; // If the inventory is full, show at least one row of the ground. @@ -1676,7 +1676,7 @@ void TilesFramework::update_inventory() type = (object_class_type)(find - obj_syms); } - for (int i = igrd(you.pos()); i != NON_ITEM; i = mitm[i].link) + for (int i = you.visible_igrd(you.pos()); i != NON_ITEM; i = mitm[i].link) { if ((int)inv.size() >= mx * my) break; diff --git a/crawl-ref/source/traps.cc b/crawl-ref/source/traps.cc index 9083576b6e..b7ca9f2dec 100644 --- a/crawl-ref/source/traps.cc +++ b/crawl-ref/source/traps.cc @@ -686,18 +686,18 @@ int trap_def::max_damage(const actor& act) // players -- this choice prevents traps from easily killing // large monsters fairly deep within the dungeon. if (act.atype() == ACT_MONSTER) - level = 0; + level = 0; switch (this->type) { - case TRAP_NEEDLE: return 0; + case TRAP_NEEDLE: return 0; case TRAP_DART: return 4 + level/2; case TRAP_ARROW: return 7 + level; case TRAP_SPEAR: return 10 + level; case TRAP_BOLT: return 13 + level; case TRAP_AXE: return 15 + level; - default: return 0; - case TRAP_BLADE: return (level ? level*2 : 10) + 28; + case TRAP_BLADE: return (level ? 2*level : 10) + 28; + default: return 0; } return (0); @@ -762,7 +762,7 @@ static bool _disarm_is_deadly(trap_def& trap) if (trap.type == TRAP_NEEDLE && you.res_poison() <= 0) dam += 15; // arbitrary - return you.hp <= dam; + return (you.hp <= dam); } // where *must* point to a valid, discovered trap. diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index 28b593d97a..296e39318a 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -550,8 +550,8 @@ inline static void _check_interesting_square(int x, int y, ed.found_item(pos, get_mimic_item(mons)); } - if (igrd(pos) != NON_ITEM) - ed.found_item( pos, mitm[ igrd(pos) ] ); + if (you.visible_igrd(pos) != NON_ITEM) + ed.found_item( pos, mitm[ you.visible_igrd(pos) ] ); } ed.found_feature( pos, grd(pos) ); diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc index 69aeb760a2..e0e7d53b72 100644 --- a/crawl-ref/source/tutorial.cc +++ b/crawl-ref/source/tutorial.cc @@ -1887,7 +1887,7 @@ void learned_something_new(tutorial_event_type seen_what, coord_def gc) text << "Ah, a corpse!"; else { - int i = igrd(gc); + int i = you.visible_igrd(gc); while (i != NON_ITEM) { if (mitm[i].base_type == OBJ_CORPSES) @@ -4680,7 +4680,7 @@ void tutorial_observe_cell(const coord_def& gc) else if (grd(gc) == DNGN_ENTER_PORTAL_VAULT) learned_something_new(TUT_SEEN_PORTAL, gc); - const int it = igrd(gc); + const int it = you.visible_igrd(gc); if (it != NON_ITEM) { const item_def& item(mitm[it]); 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-item.cc b/crawl-ref/source/wiz-item.cc index 6fa4b411b0..56b1b9bba0 100644 --- a/crawl-ref/source/wiz-item.cc +++ b/crawl-ref/source/wiz-item.cc @@ -50,6 +50,9 @@ static void _make_all_books() move_item_to_grid(&thing, you.pos()); + if (thing == NON_ITEM) + continue; + item_def book(mitm[thing]); mark_had_book(book); @@ -819,13 +822,6 @@ void wizard_list_items() //--------------------------------------------------------------- static void _debug_acquirement_stats(FILE *ostat) { - if (feat_destroys_items(grd(you.pos()))) - { - mpr("You must stand on a square which doesn't destroy items " - "in order to do this."); - return; - } - int p = get_item_slot(11); if (p == NON_ITEM) { diff --git a/crawl-ref/source/wiz-mon.cc b/crawl-ref/source/wiz-mon.cc index d7270d082a..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,10 +295,10 @@ 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_CREATED_FRIENDLY)) + if ((mi->flags & (MF_WAS_NEUTRAL | MF_NO_REWARD)) || mi->has_ench(ENCH_ABJ)) { continue; diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc index 4f01f1145a..40a19123cb 100644 --- a/crawl-ref/source/xom.cc +++ b/crawl-ref/source/xom.cc @@ -659,11 +659,8 @@ static void _xom_make_item(object_class_type base, int subtype, int power) items(true, base, subtype, true, power, MAKE_ITEM_RANDOM_RACE, 0, 0, GOD_XOM); - if (feat_destroys_items(grd(you.pos()))) + if (feat_destroys_item(grd(you.pos()), mitm[thing_created], !silenced(you.pos()))) { - if (!silenced(you.pos())) - mprf(MSGCH_SOUND, feat_item_destruction_message(grd(you.pos()))); - simple_god_message(" snickers.", GOD_XOM); destroy_item(thing_created, true); thing_created = NON_ITEM; @@ -682,9 +679,9 @@ static void _xom_make_item(object_class_type base, int subtype, int power) mitm[thing_created].name(DESC_PLAIN).c_str()); take_note(Note(NOTE_XOM_EFFECT, you.piety, -1, gift_buf), true); - move_item_to_grid(&thing_created, you.pos()); mitm[thing_created].inscription = "god gift"; canned_msg(MSG_SOMETHING_APPEARS); + move_item_to_grid(&thing_created, you.pos()); stop_running(); } @@ -2425,7 +2422,7 @@ static void _xom_zero_miscast() } } - if (!feat_destroys_items(feat) && !feat_is_solid(feat) + if (feat_has_solid_floor(feat) && inv_items.size() > 0) { int idx = inv_items[random2(inv_items.size())]; |