summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Borowski <kilobyte@angband.pl>2009-12-30 14:31:12 +0100
committerAdam Borowski <kilobyte@angband.pl>2009-12-30 14:31:12 +0100
commitd8190f4c633d2c4065e92c61894f85f506d5246a (patch)
tree887402a9869486e5a23386bc41dec9fae1987d39
parent1d0cbf5f8be4dcb3d78167d3a1f29691779298f8 (diff)
parent30ad58ea18d6e4e2132d235e60f66f11131e17dc (diff)
downloadcrawl-ref-d8190f4c633d2c4065e92c61894f85f506d5246a.tar.gz
crawl-ref-d8190f4c633d2c4065e92c61894f85f506d5246a.zip
Merge branch 'master' into iood
-rw-r--r--crawl-ref/settings/052_monster_glyphs.txt31
-rw-r--r--crawl-ref/source/abl-show.cc9
-rw-r--r--crawl-ref/source/actor.cc4
-rw-r--r--crawl-ref/source/actor.h7
-rw-r--r--crawl-ref/source/arena.h2
-rw-r--r--crawl-ref/source/artefact.cc2
-rw-r--r--crawl-ref/source/artefact.h2
-rw-r--r--crawl-ref/source/attitude-change.cc2
-rw-r--r--crawl-ref/source/beam.cc201
-rw-r--r--crawl-ref/source/beam.h8
-rw-r--r--crawl-ref/source/dat/clua/iter.lua72
-rw-r--r--crawl-ref/source/dat/database/monspeak.txt27
-rw-r--r--crawl-ref/source/dat/descript/items.txt3
-rw-r--r--crawl-ref/source/dat/descript/monsters.txt14
-rw-r--r--crawl-ref/source/dat/descript/spells.txt2
-rw-r--r--crawl-ref/source/dat/float.des24
-rw-r--r--crawl-ref/source/dat/mini.des2
-rw-r--r--crawl-ref/source/dat/ossuary.des53
-rw-r--r--crawl-ref/source/dat/shoals.des51
-rw-r--r--crawl-ref/source/dat/vaults.des16
-rw-r--r--crawl-ref/source/dat/wizlab.des1696
-rw-r--r--crawl-ref/source/dbg-util.cc15
-rw-r--r--crawl-ref/source/dgn-shoals.cc644
-rw-r--r--crawl-ref/source/dgn-shoals.h4
-rw-r--r--crawl-ref/source/directn.cc18
-rw-r--r--crawl-ref/source/dungeon.cc110
-rw-r--r--crawl-ref/source/dungeon.h12
-rw-r--r--crawl-ref/source/effects.cc74
-rw-r--r--crawl-ref/source/enum.h32
-rw-r--r--crawl-ref/source/externs.h13
-rw-r--r--crawl-ref/source/fight.cc12
-rw-r--r--crawl-ref/source/files.cc7
-rw-r--r--crawl-ref/source/food.cc12
-rw-r--r--crawl-ref/source/godabil.cc4
-rw-r--r--crawl-ref/source/item_use.cc21
-rw-r--r--crawl-ref/source/item_use.h5
-rw-r--r--crawl-ref/source/itemprop.cc27
-rw-r--r--crawl-ref/source/itemprop.h3
-rw-r--r--crawl-ref/source/items.cc79
-rw-r--r--crawl-ref/source/items.h6
-rw-r--r--crawl-ref/source/l_feat.cc5
-rw-r--r--crawl-ref/source/l_item.cc51
-rw-r--r--crawl-ref/source/libutil.cc9
-rw-r--r--crawl-ref/source/main.cc2
-rw-r--r--crawl-ref/source/makefile.obj1
-rw-r--r--crawl-ref/source/makeitem.cc8
-rw-r--r--crawl-ref/source/mapdef.h2
-rw-r--r--crawl-ref/source/maps.cc2
-rw-r--r--crawl-ref/source/message.cc15
-rw-r--r--crawl-ref/source/message.h2
-rw-r--r--crawl-ref/source/mgen_enum.h4
-rw-r--r--crawl-ref/source/misc.cc35
-rw-r--r--crawl-ref/source/misc.h4
-rw-r--r--crawl-ref/source/mislead.cc127
-rw-r--r--crawl-ref/source/mislead.h8
-rw-r--r--crawl-ref/source/mon-abil.cc14
-rw-r--r--crawl-ref/source/mon-abil.h2
-rw-r--r--crawl-ref/source/mon-act.cc42
-rw-r--r--crawl-ref/source/mon-cast.cc148
-rw-r--r--crawl-ref/source/mon-cast.h2
-rw-r--r--crawl-ref/source/mon-data.h87
-rw-r--r--crawl-ref/source/mon-gear.cc91
-rw-r--r--crawl-ref/source/mon-pathfind.cc2
-rw-r--r--crawl-ref/source/mon-pick.cc14
-rw-r--r--crawl-ref/source/mon-place.cc62
-rw-r--r--crawl-ref/source/mon-place.h3
-rw-r--r--crawl-ref/source/mon-spll.h15
-rw-r--r--crawl-ref/source/mon-stuff.cc53
-rw-r--r--crawl-ref/source/mon-transit.cc2
-rw-r--r--crawl-ref/source/mon-util.cc12
-rw-r--r--crawl-ref/source/mon-util.h1
-rw-r--r--crawl-ref/source/monster.cc78
-rw-r--r--crawl-ref/source/monster.h10
-rw-r--r--crawl-ref/source/ouch.cc11
-rw-r--r--crawl-ref/source/player.cc43
-rw-r--r--crawl-ref/source/player.h10
-rw-r--r--crawl-ref/source/religion.cc38
-rw-r--r--crawl-ref/source/rltiles/UNUSED/other/transparent_flesh.pngbin0 -> 1205 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn.txt17
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_closed.pngbin0 -> 1251 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_open.pngbin0 -> 1874 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.pngbin0 -> 4496 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.pngbin0 -> 4641 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.pngbin0 -> 4314 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.pngbin0 -> 4602 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.pngbin0 -> 4535 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.pngbin0 -> 4668 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/tree1.pngbin0 -> 824 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/wall/tree2.pngbin0 -> 872 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-misc.txt6
-rw-r--r--crawl-ref/source/rltiles/dc-mon.txt51
-rw-r--r--crawl-ref/source/rltiles/dc-mon/deformed_elf.pngbin0 -> 1282 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/deformed_human.pngbin0 -> 1363 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/deformed_orc.pngbin0 -> 1166 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/flesh_golem.pngbin0 -> 2405 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/active_ballistomycete.pngbin0 -> 3597 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.pngbin0 -> 3523 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/fungus.png (renamed from crawl-ref/source/rltiles/dc-mon/fungus.png)bin828 -> 828 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/giant_spore.png (renamed from crawl-ref/source/rltiles/dc-mon/giant_spore.png)bin602 -> 602 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/oklob_plant.png (renamed from crawl-ref/source/rltiles/dc-mon/oklob_plant.png)bin1037 -> 1037 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/plant.png (renamed from crawl-ref/source/rltiles/dc-mon/plant.png)bin673 -> 673 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/plant_crypt.png (renamed from crawl-ref/source/rltiles/dc-mon/plant_crypt.png)bin1539 -> 1539 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/fungi_plants/wandering_mushroom.png (renamed from crawl-ref/source/rltiles/dc-mon/wandering_mushroom.png)bin879 -> 879 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/human_monk_ghost.pngbin0 -> 596 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/iron_troll_monk_ghost.pngbin0 -> 1126 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/rock_troll_monk_ghost.pngbin0 -> 1024 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/dark_vine_statue_base.pngbin0 -> 3821 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/light_vine_statue_base.pngbin0 -> 3870 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.pngbin0 -> 992 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.pngbin0 -> 946 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.pngbin0 -> 1058 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.pngbin0 -> 946 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.pngbin0 -> 939 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_mage_hat.pngbin0 -> 975 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.pngbin0 -> 950 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.pngbin0 -> 966 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_archer.pngbin996 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_axe.pngbin1026 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.pngbin1104 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_mace.pngbin979 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_mage.pngbin975 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.pngbin1008 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/statues/statue_sword.pngbin993 -> 0 bytes
-rw-r--r--crawl-ref/source/rltiles/dc-mon/vine-covered_golem.pngbin0 -> 4523 bytes
-rw-r--r--crawl-ref/source/shopping.cc2
-rw-r--r--crawl-ref/source/show.cc17
-rw-r--r--crawl-ref/source/showsymb.cc2
-rw-r--r--crawl-ref/source/spells2.h4
-rw-r--r--crawl-ref/source/spells3.h2
-rw-r--r--crawl-ref/source/spells4.cc132
-rw-r--r--crawl-ref/source/spells4.h2
-rw-r--r--crawl-ref/source/spl-cast.cc8
-rw-r--r--crawl-ref/source/spl-data.h28
-rw-r--r--crawl-ref/source/spl-util.cc2
-rw-r--r--crawl-ref/source/spl-util.h2
-rw-r--r--crawl-ref/source/stash.cc8
-rw-r--r--crawl-ref/source/store.cc76
-rw-r--r--crawl-ref/source/store.h17
-rw-r--r--crawl-ref/source/stuff.cc4
-rw-r--r--crawl-ref/source/stuff.h7
-rw-r--r--crawl-ref/source/tags.cc2
-rw-r--r--crawl-ref/source/terrain.cc87
-rw-r--r--crawl-ref/source/terrain.h5
-rw-r--r--crawl-ref/source/tilepick.cc10
-rw-r--r--crawl-ref/source/tilereg.cc2
-rw-r--r--crawl-ref/source/tilesdl.cc4
-rw-r--r--crawl-ref/source/traps.cc10
-rw-r--r--crawl-ref/source/travel.cc4
-rw-r--r--crawl-ref/source/tutorial.cc4
-rw-r--r--crawl-ref/source/view.cc13
-rw-r--r--crawl-ref/source/view.h3
-rw-r--r--crawl-ref/source/wiz-item.cc10
-rw-r--r--crawl-ref/source/wiz-mon.cc19
-rw-r--r--crawl-ref/source/xom.cc9
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 &region_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 &region_map,
+ const std::vector<weighted_region> &regions)
+{
+ 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
new file mode 100644
index 0000000000..2437acebb2
--- /dev/null
+++ b/crawl-ref/source/rltiles/UNUSED/other/transparent_flesh.png
Binary files differ
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
new file mode 100644
index 0000000000..fcf1893994
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_closed.png
Binary files differ
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
new file mode 100644
index 0000000000..6ce8d1f29e
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/gateways/fleshy_orifice_open.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.png
new file mode 100644
index 0000000000..664f187d47
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh1.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.png
new file mode 100644
index 0000000000..a602714196
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh2.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.png
new file mode 100644
index 0000000000..9a12e349bd
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh3.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.png
new file mode 100644
index 0000000000..712bc83793
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh4.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.png
new file mode 100644
index 0000000000..74fd01c736
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh5.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.png b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.png
new file mode 100644
index 0000000000..b9da8247fb
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/transparent_flesh6.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/tree1.png b/crawl-ref/source/rltiles/dc-dngn/wall/tree1.png
new file mode 100644
index 0000000000..6f80075b4d
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/tree1.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-dngn/wall/tree2.png b/crawl-ref/source/rltiles/dc-dngn/wall/tree2.png
new file mode 100644
index 0000000000..a8835226a0
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/wall/tree2.png
Binary files differ
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
new file mode 100644
index 0000000000..df09452448
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/deformed_elf.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/deformed_human.png b/crawl-ref/source/rltiles/dc-mon/deformed_human.png
new file mode 100644
index 0000000000..78d8225146
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/deformed_human.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/deformed_orc.png b/crawl-ref/source/rltiles/dc-mon/deformed_orc.png
new file mode 100644
index 0000000000..4d62be9385
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/deformed_orc.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/flesh_golem.png b/crawl-ref/source/rltiles/dc-mon/flesh_golem.png
new file mode 100644
index 0000000000..d763855dd6
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/flesh_golem.png
Binary files differ
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
new file mode 100644
index 0000000000..bd01f0470c
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/active_ballistomycete.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.png
new file mode 100644
index 0000000000..69bff99afd
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/ballistomycete.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/fungus.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/fungus.png
index 75d0cd66ed..75d0cd66ed 100644
--- a/crawl-ref/source/rltiles/dc-mon/fungus.png
+++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/fungus.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/giant_spore.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/giant_spore.png
index 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
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/oklob_plant.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/oklob_plant.png
index 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
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/plant.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant.png
index b3124b5cae..b3124b5cae 100644
--- a/crawl-ref/source/rltiles/dc-mon/plant.png
+++ b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/plant_crypt.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/plant_crypt.png
index 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
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/wandering_mushroom.png b/crawl-ref/source/rltiles/dc-mon/fungi_plants/wandering_mushroom.png
index 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
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/human_monk_ghost.png b/crawl-ref/source/rltiles/dc-mon/human_monk_ghost.png
new file mode 100644
index 0000000000..911281db34
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/human_monk_ghost.png
Binary files differ
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
new file mode 100644
index 0000000000..c6e65c7c35
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/iron_troll_monk_ghost.png
Binary files differ
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
new file mode 100644
index 0000000000..9a40dccc8a
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/rock_troll_monk_ghost.png
Binary files differ
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
new file mode 100644
index 0000000000..5d2a5a1283
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/dark_vine_statue_base.png
Binary files differ
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
new file mode 100644
index 0000000000..2d0a4e5ee1
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/light_vine_statue_base.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.png
new file mode 100644
index 0000000000..51138c7992
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_axe.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.png
new file mode 100644
index 0000000000..a674ae87dc
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_bow.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.png
new file mode 100644
index 0000000000..d65db57370
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_crossbow.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.png
new file mode 100644
index 0000000000..7555ff46c1
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mace.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.png
new file mode 100644
index 0000000000..aa26b5b556
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage.png
Binary files differ
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
new file mode 100644
index 0000000000..d9960f0db0
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_mage_hat.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.png
new file mode 100644
index 0000000000..bb2f7df127
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_scythe.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.png b/crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.png
new file mode 100644
index 0000000000..6b6e8050de
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/statues/overlay_sword.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_archer.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_archer.png
deleted file mode 100644
index 0358366f7c..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_archer.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_axe.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_axe.png
deleted file mode 100644
index 795928ef22..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_axe.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.png
deleted file mode 100644
index 80fa73380d..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_crossbow.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_mace.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_mace.png
deleted file mode 100644
index c74efdd79e..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_mace.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_mage.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_mage.png
deleted file mode 100644
index bc709937e2..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_mage.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.png
deleted file mode 100644
index 19ab6410ce..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_scythe.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/statues/statue_sword.png b/crawl-ref/source/rltiles/dc-mon/statues/statue_sword.png
deleted file mode 100644
index 6a8c005184..0000000000
--- a/crawl-ref/source/rltiles/dc-mon/statues/statue_sword.png
+++ /dev/null
Binary files differ
diff --git a/crawl-ref/source/rltiles/dc-mon/vine-covered_golem.png b/crawl-ref/source/rltiles/dc-mon/vine-covered_golem.png
new file mode 100644
index 0000000000..d8f604671b
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/vine-covered_golem.png
Binary files differ
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())];