summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/dat/levdes.vim2
-rw-r--r--crawl-ref/source/dat/splev.des64
-rw-r--r--crawl-ref/source/dat/vaults.des62
-rw-r--r--crawl-ref/source/debug.cc27
-rw-r--r--crawl-ref/source/describe.cc20
-rw-r--r--crawl-ref/source/describe.h1
-rw-r--r--crawl-ref/source/dungeon.cc126
-rw-r--r--crawl-ref/source/enum.h1
-rw-r--r--crawl-ref/source/mapdef.cc269
-rw-r--r--crawl-ref/source/mapdef.h76
-rw-r--r--crawl-ref/source/misc.cc28
-rw-r--r--crawl-ref/source/misc.h2
-rw-r--r--crawl-ref/source/util/levcomp.lpp4
-rw-r--r--crawl-ref/source/util/levcomp.ypp35
14 files changed, 627 insertions, 90 deletions
diff --git a/crawl-ref/source/dat/levdes.vim b/crawl-ref/source/dat/levdes.vim
index 8e8b573af9..59cf33b6d0 100644
--- a/crawl-ref/source/dat/levdes.vim
+++ b/crawl-ref/source/dat/levdes.vim
@@ -23,7 +23,7 @@ syn region desShuffle start=/^SHUFFLE:\s*/ end=/$/ contains=desShuffleDec,desMap
syn keyword desSubstDec SUBST: contained
syn keyword desShuffleDec SHUFFLE: contained
-syn keyword desDeclarator NAME: ORIENT: DEPTH: PLACE: MONS: FLAGS: default-depth: TAGS: CHANCE: ITEM:
+syn keyword desDeclarator NAME: ORIENT: DEPTH: PLACE: MONS: FLAGS: default-depth: TAGS: CHANCE: ITEM: KFEAT: KMONS: KITEM:
syn keyword desOrientation encompass north south east west northeast northwest southeast southwest float no_hmirror no_vmirror no_rotate entry pan no_pool_fixup no_monster_gen generate_awake
syn match desComment "^\s*#.*$"
diff --git a/crawl-ref/source/dat/splev.des b/crawl-ref/source/dat/splev.des
index 5353ac308c..b0778ab487 100644
--- a/crawl-ref/source/dat/splev.des
+++ b/crawl-ref/source/dat/splev.des
@@ -4,7 +4,8 @@
# If you want to define random vaults and minivaults, they should go
# to vaults.des.
#
-# key:
+# Key:
+# ----
# x - DNGN_ROCK_WALL
# X - DNGN_PERMAROCK_WALL -> should always be undiggable! -- bwr
# c - DNGN_STONE_WALL
@@ -64,10 +65,12 @@
# A vault always has its body between MAP and ENDMAP commands. Furthermore,
# several other additional commands are possible (some of them mandatory).
#
-# NAME
+# NAME:
+# ----
# Each vault/level/map must have a unique name. Underscores and digits are ok.
#
# ORIENT: (north | northwest | ... | float | encompass)
+# ------
# Some kind of ORIENT: line is mandatory, unless you want the vault to be a
# minivault, which is usually not what you want. Valid values are:
# "float": The dungeon builder will put your vault wherever it wants to.
@@ -88,6 +91,7 @@
# needed on all 4 sides.
#
# ITEM:
+# ----
# These are used to help place specified items at specific places within a
# vault. They create an array with up to 8 positions. What's in the first
# position in the array will be used when the dungeon builder sees a "d" in
@@ -110,6 +114,7 @@
# give fixedarts. You also can't lay down corpses, skeletons, or chunks.
#
# MONS:
+# ----
# These are used to help place specific monsters at specific places in a
# vault. They create an array with up to 7 positions. What's in the first
# position in the array will be used when the dungeon builder sees a "1"
@@ -121,6 +126,7 @@
# Note that 8, 9, 0 also place monsters (see the table above).
#
# CHANCE:
+# ------
# For entry vaults and any other vaults randomly picked from among a set,
# this type of line affects the likelihood of the given vault being picked in
# a given game. The default CHANCE: is 10. The likelihood of a vault getting
@@ -131,6 +137,7 @@
# for testing. !!!
#
# TAGS:
+# ----
# Tags go an a TAGS: line and are space-separated. Valid tags are:
# "entry": this tag MUST be there for a vault to be pickable as an entry vault.
# "no_monster_gen": this tag prevents monster generation at the time of
@@ -143,7 +150,8 @@
# will contain the stairs for that branch. Use "O" to place
# the stairs. Branch entries should go to splev.des.
#
-# FLAGS
+# FLAGS:
+# -----
# Flags go on a FLAGS: line and are space-separated. Valid flags are:
# "no_rotate": Normally, the dungeon builder can, at its whim, rotate your
# vault. This flag tells it, "hey, don't do that to my vault!"
@@ -151,6 +159,7 @@
# "no_vmirror": Like no_rotate, but for vertical mirroring.
#
# SHUFFLE:
+# -------
# This allows you to randomly permute glyphs on the map. There are two ways:
# SHUFFLE: 123w (i.e. list of glyphs, NOT comma-separated)
# could, for example, swap all occurences of "1" with "2",
@@ -163,6 +172,7 @@
# order.
#
# SUBST:
+# ------
# The SUBST: directive allows you to specify a placeholder symbol that is
# replaced with a random glyph from a set. For instance:
# SUBST: ? = TUV
@@ -184,14 +194,60 @@
#
# SUBST: w = wW
#
-# Multiple SUBST: lines can be used, and will be applied in order.
+# Multiple SUBST: lines can be used, and will be applied in order.
# Substitutions are performed after any declared shuffles.
#
+# KFEAT:
+# -----
+# The KFEAT: directive allows you to specify a placeholder symbol that is
+# replaced with another symbol, named feature, trap, or shop. Eg.:
+#
+# KFEAT: Z = C / needle trap / antique armour shop / altar of Zin
+#
+# Replaces occurrences of Z with C (random altar), a needle trap, an
+# antique armour shop, or an altar of Zin. Different instances of Z may
+# receive different replacements. To force a single replacement for all Z,
+# use:
+#
+# KFEAT: Z : C / needle trap / antique armour shop
+#
+# You'll notice that 'Z' is the symbol of the Orb of Zot. Kxxx directives
+# allow you to assign arbitrary definitions to any symbol.
+#
+# The placeholder used by KFEAT can be shared by KITEM and KMONS. See below.
+# If the placeholder is shared, all defined Kxxxx operations for the
+# placeholder are performed.
+#
+# KMONS:
+# -----
+# KMONS: allows you to specify a placeholder symbol that indicates the
+# position of a monster (or monsters).
+#
+# KMONS: ? = orc priest / deep elf priest
+#
+# Using KMONS: allows you to exceed the 7 slot limit for monsters. It is also
+# useful if you want to place a monster on a non-floor square (used in
+# association with a KFEAT:). Eg:
+#
+# KFEAT: Z = W
+# KMONS: Z = rat
+#
+# (Places a rat on a shallow water square for all occurrences of Z.)
+#
+# KITEM:
+# -----
+# KITEM: places the specified item at all occurrences of the placeholder. It
+# can be combined with KFEAT and KMONS for the same placeholder. Eg:
+#
+# KITEM: ? = potion of healing / potion of restore abilities
+#
+#
# Note that a lot of the vaults are in there mainly to add some
# interest to the scenery, and are not the lethal treasure-fests you
# find in Angband (not that there's anything wrong with that)
#
# Guidelines for creating new vault maps:
+# ---------------------------------------
#
# If your map is not a minivault or a floating vault, make sure the side(s)
# forming the border have a rock wall padding at least 6 deep. For instance,
diff --git a/crawl-ref/source/dat/vaults.des b/crawl-ref/source/dat/vaults.des
index dec39d1a79..c13a8e1ec1 100644
--- a/crawl-ref/source/dat/vaults.des
+++ b/crawl-ref/source/dat/vaults.des
@@ -4,7 +4,8 @@
# Special levels (including V:8!) should go to splev.des. Do NOT put special
# levels here, or they could be selected when the game seeks random vaults.
#
-# key:
+# Key:
+# ----
# x - DNGN_ROCK_WALL
# X - DNGN_PERMAROCK_WALL -> should always be undiggable! -- bwr
# c - DNGN_STONE_WALL
@@ -64,10 +65,12 @@
# A vault always has its body between MAP and ENDMAP commands. Furthermore,
# several other additional commands are possible (some of them mandatory).
#
-# NAME
+# NAME:
+# ----
# Each vault/level/map must have a unique name. Underscores and digits are ok.
#
# ORIENT: (north | northwest | ... | float | encompass)
+# ------
# Some kind of ORIENT: line is mandatory, unless you want the vault to be a
# minivault, which is usually not what you want. Valid values are:
# "float": The dungeon builder will put your vault wherever it wants to.
@@ -88,6 +91,7 @@
# needed on all 4 sides.
#
# ITEM:
+# ----
# These are used to help place specified items at specific places within a
# vault. They create an array with up to 8 positions. What's in the first
# position in the array will be used when the dungeon builder sees a "d" in
@@ -110,6 +114,7 @@
# give fixedarts. You also can't lay down corpses, skeletons, or chunks.
#
# MONS:
+# ----
# These are used to help place specific monsters at specific places in a
# vault. They create an array with up to 7 positions. What's in the first
# position in the array will be used when the dungeon builder sees a "1"
@@ -121,6 +126,7 @@
# Note that 8, 9, 0 also place monsters (see the table above).
#
# CHANCE:
+# ------
# For entry vaults and any other vaults randomly picked from among a set,
# this type of line affects the likelihood of the given vault being picked in
# a given game. The default CHANCE: is 10. The likelihood of a vault getting
@@ -131,6 +137,7 @@
# for testing. !!!
#
# TAGS:
+# ----
# Tags go an a TAGS: line and are space-separated. Valid tags are:
# "entry": this tag MUST be there for a vault to be pickable as an entry vault.
# "no_monster_gen": this tag prevents monster generation at the time of
@@ -143,7 +150,8 @@
# will contain the stairs for that branch. Use "O" to place
# the stairs. Branch entries should go to splev.des.
#
-# FLAGS
+# FLAGS:
+# -----
# Flags go on a FLAGS: line and are space-separated. Valid flags are:
# "no_rotate": Normally, the dungeon builder can, at its whim, rotate your
# vault. This flag tells it, "hey, don't do that to my vault!"
@@ -151,6 +159,7 @@
# "no_vmirror": Like no_rotate, but for vertical mirroring.
#
# SHUFFLE:
+# -------
# This allows you to randomly permute glyphs on the map. There are two ways:
# SHUFFLE: 123w (i.e. list of glyphs, NOT comma-separated)
# could, for example, swap all occurences of "1" with "2",
@@ -163,6 +172,7 @@
# order.
#
# SUBST:
+# ------
# The SUBST: directive allows you to specify a placeholder symbol that is
# replaced with a random glyph from a set. For instance:
# SUBST: ? = TUV
@@ -187,11 +197,57 @@
# Multiple SUBST: lines can be used, and will be applied in order.
# Substitutions are performed after any declared shuffles.
#
+# KFEAT:
+# -----
+# The KFEAT: directive allows you to specify a placeholder symbol that is
+# replaced with another symbol, named feature, trap, or shop. Eg.:
+#
+# KFEAT: Z = C / needle trap / antique armour shop / altar of Zin
+#
+# Replaces occurrences of Z with C (random altar), a needle trap, an
+# antique armour shop, or an altar of Zin. Different instances of Z may
+# receive different replacements. To force a single replacement for all Z,
+# use:
+#
+# KFEAT: Z : C / needle trap / antique armour shop
+#
+# You'll notice that 'Z' is the symbol of the Orb of Zot. Kxxx directives
+# allow you to assign arbitrary definitions to any symbol.
+#
+# The placeholder used by KFEAT can be shared by KITEM and KMONS. See below.
+# If the placeholder is shared, all defined Kxxxx operations for the
+# placeholder are performed.
+#
+# KMONS:
+# -----
+# KMONS: allows you to specify a placeholder symbol that indicates the
+# position of a monster (or monsters).
+#
+# KMONS: ? = orc priest / deep elf priest
+#
+# Using KMONS: allows you to exceed the 7 slot limit for monsters. It is also
+# useful if you want to place a monster on a non-floor square (used in
+# association with a KFEAT:). Eg:
+#
+# KFEAT: Z = W
+# KMONS: Z = rat
+#
+# (Places a rat on a shallow water square for all occurrences of Z.)
+#
+# KITEM:
+# -----
+# KITEM: places the specified item at all occurrences of the placeholder. It
+# can be combined with KFEAT and KMONS for the same placeholder. Eg:
+#
+# KITEM: ? = potion of healing / potion of restore abilities
+#
+#
# Note that a lot of the vaults are in there mainly to add some
# interest to the scenery, and are not the lethal treasure-fests you
# find in Angband (not that there's anything wrong with that)
#
# Guidelines for creating new vault maps:
+# ---------------------------------------
#
# If your map is not a minivault or a floating vault, make sure the side(s)
# forming the border have a rock wall padding at least 6 deep. For instance,
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index d836f0a3ec..aaef55481c 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -2195,21 +2195,6 @@ void debug_make_trap()
trap_name(trap));
}
-static const char *shop_types[] = {
- "weapon",
- "armour",
- "antique weapon",
- "antique armour",
- "antiques",
- "jewellery",
- "wand",
- "book",
- "food",
- "distillery",
- "scroll",
- "general"
-};
-
void debug_make_shop()
{
char requested_shop[80];
@@ -2245,16 +2230,10 @@ void debug_make_shop()
return;
strlwr(requested_shop);
- for (unsigned i = 0; i < sizeof(shop_types) / sizeof (*shop_types); ++i)
- {
- if (strstr(requested_shop, shop_types[i]))
- {
- new_shop_type = i;
- break;
- }
- }
+ std::string s = replace_all_of(requested_shop, "*", "");
+ new_shop_type = str_to_shoptype(s);
- if (new_shop_type == SHOP_UNASSIGNED)
+ if (new_shop_type == SHOP_UNASSIGNED || new_shop_type == -1)
{
mprf("Bad shop type: \"%s\"", requested_shop);
return;
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 3929106b4e..20a66edbce 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -332,11 +332,31 @@ static const char *trap_names[] =
const char *trap_name(trap_type trap)
{
+ ASSERT(NUM_TRAPS == sizeof(trap_names) / sizeof(*trap_names));
+
if (trap >= TRAP_DART && trap < NUM_TRAPS)
return trap_names[ static_cast<int>( trap ) ];
return (NULL);
}
+int str_to_trap(const std::string &s)
+{
+ ASSERT(NUM_TRAPS == sizeof(trap_names) / sizeof(*trap_names));
+
+ if (s == "random")
+ return (TRAP_RANDOM);
+ else if (s == "suitable")
+ return (TRAP_INDEPTH);
+
+ for (int i = 0; i < NUM_TRAPS; ++i)
+ {
+ if (trap_names[i] == s)
+ return (i);
+ }
+
+ return (-1);
+}
+
//---------------------------------------------------------------
//
// describe_demon
diff --git a/crawl-ref/source/describe.h b/crawl-ref/source/describe.h
index 9128c27555..32868ddaf7 100644
--- a/crawl-ref/source/describe.h
+++ b/crawl-ref/source/describe.h
@@ -65,5 +65,6 @@ void describe_spell(int spelled);
std::string ghost_description(bool concise = false);
const char *trap_name(trap_type trap);
+int str_to_trap(const std::string &s);
#endif
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 4a9063bea6..7c325b7c21 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -162,8 +162,8 @@ static void build_minivaults(int level_number, int force_vault);
static int vault_grid( vault_placement &,
int level_number, int vx, int vy, int altar_count,
FixedVector < char, 7 > &acq_item_class,
- char vgrid, std::vector<coord_def> &targets,
- int &num_runes, int rune_subst = -1);
+ int vgrid, std::vector<coord_def> &targets,
+ int &num_runes, int rune_subst = -1, bool foll = false);
// ALTAR FUNCTIONS
static int pick_an_altar(void);
@@ -5972,6 +5972,27 @@ static bool build_vaults(int level_number, int force_vault, int rune_subst,
return (true);
} // end build_vaults()
+static void dngn_place_item_explicit(const item_spec &spec,
+ int x, int y, int level)
+{
+ // Dummy object?
+ if (spec.base_type == OBJ_UNASSIGNED)
+ return;
+
+ if (spec.level >= 0)
+ level = spec.level;
+
+ const int item_made =
+ items( spec.allow_uniques, spec.base_type, spec.sub_type, true,
+ level, spec.race );
+
+ if (item_made != NON_ITEM && item_made != -1)
+ {
+ mitm[item_made].x = x;
+ mitm[item_made].y = y;
+ }
+}
+
static void dngn_place_item_explicit(int index, int x, int y,
vault_placement &place,
int level)
@@ -5988,22 +6009,30 @@ static void dngn_place_item_explicit(int index, int x, int y,
}
const item_spec spec = sitems.get_item(index);
+ dngn_place_item_explicit(spec, x, y, level);
+}
- // Dummy object?
- if (spec.base_type == OBJ_UNASSIGNED)
- return;
-
- if (spec.level >= 0)
- level = spec.level;
-
- const int item_made =
- items( spec.allow_uniques, spec.base_type, spec.sub_type, true,
- level, spec.race );
-
- if (item_made != NON_ITEM)
+static void dngn_make_monster(
+ const mons_spec &monster_type_thing,
+ int monster_level,
+ int vx, int vy)
+{
+ if (monster_type_thing.mid != -1)
{
- mitm[item_made].x = x;
- mitm[item_made].y = y;
+ const int mid = monster_type_thing.mid;
+ int not_used;
+
+ if (mid != RANDOM_MONSTER && mid < NUM_MONSTERS)
+ {
+ const int habitat = monster_habitat(mid);
+ if (habitat != DNGN_FLOOR)
+ grd[vx][vy] = habitat;
+ }
+
+ place_monster( not_used, mid, monster_level,
+ monster_type_thing.generate_awake?
+ BEH_WANDER : BEH_SLEEP,
+ MHITNOT, true, vx, vy, false );
}
}
@@ -6015,16 +6044,57 @@ static int vault_grid( vault_placement &place,
int vx, int vy,
int altar_count,
FixedVector < char, 7 > &acq_item_class,
- char vgrid,
+ int vgrid,
std::vector<coord_def> &targets,
int &num_runes,
- int rune_subst )
+ int rune_subst,
+ bool following )
{
int not_used;
+ keyed_mapspec *mapsp = following? NULL : place.map.mapspec_for_key(vgrid);
+ if (mapsp)
+ {
+ const feature_spec f = mapsp->get_feat();
+ if (f.feat >= 0)
+ {
+ grd[vx][vy] = f.feat;
+ vgrid = -1;
+ }
+ else if (f.glyph >= 0)
+ {
+ altar_count = vault_grid( place, level_number, vx, vy,
+ altar_count, acq_item_class,
+ f.glyph, targets, num_runes,
+ rune_subst, true );
+ }
+ else if (f.shop >= 0)
+ place_spec_shop(level_number, vx, vy, f.shop);
+ else if (f.trap >= 0)
+ {
+ const int trap =
+ f.trap == TRAP_INDEPTH? random_trap_for_level(level_number)
+ : f.trap;
+
+ place_specific_trap(vx, vy, trap);
+ }
+ else
+ grd[vx][vy] = DNGN_FLOOR;
+
+ mons_spec mons = mapsp->get_mons();
+ dngn_make_monster(mons, level_number, vx, vy);
+
+ item_spec item = mapsp->get_item();
+ dngn_place_item_explicit(item, vx, vy, level_number);
+
+ return (altar_count);
+ }
+
// first, set base tile for grids {dlb}:
const int grid =
- grd[vx][vy] = ((vgrid == 'x') ? DNGN_ROCK_WALL :
+ grd[vx][vy] =
+ ((vgrid == -1) ? grd[vx][vy] :
+ (vgrid == 'x') ? DNGN_ROCK_WALL :
(vgrid == 'X') ? DNGN_PERMAROCK_WALL :
(vgrid == 'c') ? DNGN_STONE_WALL :
(vgrid == 'v') ? DNGN_METAL_WALL :
@@ -6210,22 +6280,8 @@ static int vault_grid( vault_placement &place,
if (vgrid != '8' && vgrid != '9' && vgrid != '0')
monster_type_thing = place.map.mons.get_monster(vgrid - '1');
- if (monster_type_thing.mid != -1)
- {
- const int mid = monster_type_thing.mid;
-
- if (mid != RANDOM_MONSTER && mid < NUM_MONSTERS)
- {
- const int habitat = monster_habitat(mid);
- if (habitat != DNGN_FLOOR)
- grd[vx][vy] = habitat;
- }
-
- place_monster( not_used, mid, monster_level,
- monster_type_thing.generate_awake?
- BEH_WANDER : BEH_SLEEP,
- MHITNOT, true, vx, vy, false );
- }
+ dngn_make_monster(monster_type_thing, monster_level,
+ vx, vy);
}
// again, this seems odd, given that this is just one of many
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 9495c20fb1..5b5da96304 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -3677,6 +3677,7 @@ enum trap_type // env.trap_type[]
TRAP_NEEDLE,
NUM_TRAPS, // must remain last 'regular' member {dlb}
TRAP_UNASSIGNED = 100, // keep set at 100 for now {dlb}
+ TRAP_INDEPTH = 253, // Level-appropriate trap.
TRAP_NONTELEPORT = 254,
TRAP_RANDOM = 255 // set at 255 to avoid potential conflicts {dlb}
};
diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc
index dbc066a8a5..f247685c90 100644
--- a/crawl-ref/source/mapdef.cc
+++ b/crawl-ref/source/mapdef.cc
@@ -3,10 +3,13 @@
#include <cctype>
#include "AppHdr.h"
+#include "describe.h"
+#include "direct.h"
#include "invent.h"
#include "items.h"
#include "libutil.h"
#include "mapdef.h"
+#include "misc.h"
#include "monplace.h"
#include "mon-util.h"
#include "stuff.h"
@@ -80,6 +83,33 @@ int strip_number_tag(std::string &s, const std::string &tagprefix)
return atoi(argument.c_str());
}
+static std::string split_key_item(const std::string &s,
+ int *key,
+ int *separator,
+ std::string *arg)
+{
+ std::string::size_type
+ norm = s.find("="),
+ fixe = s.find(":");
+
+ const std::string::size_type sep = norm < fixe? norm : fixe;
+ if (sep == std::string::npos)
+ return ("malformed declaration - must use = or :");
+
+ std::string what_to_subst = trimmed_string(s.substr(0, sep));
+ std::string substitute = trimmed_string(s.substr(sep + 1));
+
+ if (what_to_subst.length() != 1)
+ return make_stringf("selector '%s' must be exactly one character",
+ what_to_subst.c_str());
+
+ *key = what_to_subst[0];
+ *arg = substitute;
+ *separator = s[sep];
+
+ return ("");
+}
+
///////////////////////////////////////////////
// level_range
//
@@ -209,29 +239,21 @@ std::string map_lines::add_subst(const std::string &sub)
if (s.empty())
return ("");
- std::string::size_type
- norm = s.find("="),
- fixe = s.find(":");
-
- const std::string::size_type sep = norm < fixe? norm : fixe;
- if (sep == std::string::npos)
- return ("malformed SUBST declaration - must use = or :");
+ int sep = 0;
+ int key = 0;
+ std::string substitute;
- const bool fixed = (sep == fixe);
- std::string what_to_subst = trimmed_string(sub.substr(0, sep));
- std::string substitute = trimmed_string(sub.substr(sep + 1));
-
- if (what_to_subst.length() != 1)
- return make_stringf("selector '%s' must be exactly one character",
- what_to_subst.c_str());
+ std::string err = split_key_item(sub, &key, &sep, &substitute);
+ if (!err.empty())
+ return (err);
glyph_replacements_t repl;
- std::string err = parse_glyph_replacements(substitute, repl);
+ err = parse_glyph_replacements(substitute, repl);
if (!err.empty())
return (err);
substitutions.push_back(
- subst_spec( what_to_subst[0], fixed, repl ) );
+ subst_spec( key, sep == ':', repl ) );
return ("");
}
@@ -483,6 +505,7 @@ void map_def::init()
tags.clear();
place.clear();
items.clear();
+ keyspecs.clear();
depth.reset();
orient = MAP_NONE;
@@ -721,6 +744,43 @@ bool map_def::has_tag(const std::string &tagwanted) const
&& tags.find(" " + tagwanted + " ") != std::string::npos;
}
+keyed_mapspec *map_def::mapspec_for_key(int key)
+{
+ keyed_specs::iterator i = keyspecs.find(key);
+ return i != keyspecs.end()? &i->second : NULL;
+}
+
+std::string map_def::add_key_field(
+ const std::string &s,
+ std::string (keyed_mapspec::*set_field)(const std::string &s, bool fixed))
+{
+ int key = 0;
+ int separator = 0;
+ std::string arg;
+
+ std::string err = split_key_item(s, &key, &separator, &arg);
+ if (!err.empty())
+ return (err);
+
+ keyed_mapspec &km = keyspecs[key];
+ return ((km.*set_field)(arg, separator == ':'));
+}
+
+std::string map_def::add_key_item(const std::string &s)
+{
+ return add_key_field(s, &keyed_mapspec::set_item);
+}
+
+std::string map_def::add_key_feat(const std::string &s)
+{
+ return add_key_field(s, &keyed_mapspec::set_feat);
+}
+
+std::string map_def::add_key_mons(const std::string &s)
+{
+ return add_key_field(s, &keyed_mapspec::set_mons);
+}
+
///////////////////////////////////////////////////////////////////
// mons_list
//
@@ -817,7 +877,7 @@ mons_list::mons_spec_slot mons_list::parse_mons_spec(std::string spec)
return (slot);
}
-std::string mons_list::add_mons(const std::string &s)
+std::string mons_list::add_mons(const std::string &s, bool fix)
{
error.clear();
@@ -825,6 +885,9 @@ std::string mons_list::add_mons(const std::string &s)
if (!error.empty())
return (error);
+ if (fix)
+ slotmons.fix_slot = true;
+
mons.push_back( slotmons );
return (error);
@@ -929,13 +992,17 @@ item_spec item_list::get_item(int index)
return (pick_item(items[index]));
}
-std::string item_list::add_item(const std::string &spec)
+std::string item_list::add_item(const std::string &spec, bool fix)
{
error.clear();
item_spec_slot sp = parse_item_spec(spec);
if (error.empty())
+ {
+ if (fix)
+ sp.fix_slot = true;
items.push_back(sp);
+ }
return (error);
}
@@ -1102,3 +1169,169 @@ int subst_spec::value()
return (chosen);
}
+
+//////////////////////////////////////////////////////////////////////////
+// keyed_mapspec
+
+keyed_mapspec::keyed_mapspec()
+ : feat(), item(), mons()
+{
+}
+
+std::string keyed_mapspec::set_feat(const std::string &s, bool fix)
+{
+ err.clear();
+ parse_features(s);
+ feat.fix_slot = fix;
+ return (err);
+}
+
+void keyed_mapspec::parse_features(const std::string &s)
+{
+ feat.feats.clear();
+ std::vector<std::string> specs = split_string("/", s);
+ for (int i = 0, size = specs.size(); i < size; ++i)
+ {
+ const std::string &spec = specs[i];
+
+ feature_spec_list feats = parse_feature(spec);
+ if (!err.empty())
+ return;
+ feat.feats.insert( feat.feats.end(),
+ feats.begin(),
+ feats.end() );
+ }
+}
+
+feature_spec keyed_mapspec::parse_trap(std::string s, int weight)
+{
+ strip_tag(s, "trap");
+ trim_string(s);
+ lowercase(s);
+
+ const int trap = str_to_trap(s);
+ if (trap == -1)
+ err = make_stringf("bad trap name: '%s'", s.c_str());
+
+ feature_spec fspec(-1, weight);
+ fspec.trap = trap;
+ return (fspec);
+}
+
+feature_spec keyed_mapspec::parse_shop(std::string s, int weight)
+{
+ strip_tag(s, "shop");
+ trim_string(s);
+ lowercase(s);
+
+ const int shop = str_to_shoptype(s);
+ if (shop == -1)
+ err = make_stringf("bad shop type: '%s'", s.c_str());
+
+ feature_spec fspec(-1, weight);
+ fspec.shop = shop;
+ return (fspec);
+}
+
+feature_spec_list keyed_mapspec::parse_feature(const std::string &str)
+{
+ std::string s = str;
+ int weight = strip_number_tag(s, "weight:");
+ if (weight == TAG_UNFOUND || weight <= 0)
+ weight = 10;
+ trim_string(s);
+
+ feature_spec_list list;
+ if (s.length() == 1)
+ {
+ feature_spec fsp(-1, weight);
+ fsp.glyph = s[0];
+ list.push_back( fsp );
+ return (list);
+ }
+
+ if (s.find("trap") != std::string::npos)
+ {
+ list.push_back( parse_trap(s, weight) );
+ return (list);
+ }
+
+ if (s.find("shop") != std::string::npos
+ || s.find("store") != std::string::npos)
+ {
+ list.push_back( parse_shop(s, weight) );
+ return (list);
+ }
+
+ std::vector<dungeon_feature_type> feats = features_by_desc(s);
+ for (int i = 0, size = feats.size(); i < size; ++i)
+ list.push_back( feature_spec(feats[i], weight) );
+
+ if (feats.empty())
+ err = make_stringf("no features matching \"%s\"",
+ str.c_str());
+
+ return (list);
+}
+
+std::string keyed_mapspec::set_mons(const std::string &s, bool fix)
+{
+ err.clear();
+ mons.clear();
+
+ return (mons.add_mons(s, fix));
+}
+
+std::string keyed_mapspec::set_item(const std::string &s, bool fix)
+{
+ err.clear();
+ item.clear();
+
+ return (item.add_item(s, fix));
+}
+
+feature_spec keyed_mapspec::get_feat()
+{
+ return feat.get_feat();
+}
+
+mons_spec keyed_mapspec::get_mons()
+{
+ return (mons.size()? mons.get_monster(0) : mons_spec(-1));
+}
+
+item_spec keyed_mapspec::get_item()
+{
+ if (item.size())
+ return item.get_item(0);
+
+ item_spec spec;
+ spec.base_type = OBJ_UNASSIGNED;
+ return (spec);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// feature_slot
+
+feature_slot::feature_slot() : feats(), fix_slot(false)
+{
+}
+
+feature_spec feature_slot::get_feat()
+{
+ int tweight = 0;
+ feature_spec chosen_feat = feature_spec(DNGN_FLOOR);
+ for (int i = 0, size = feats.size(); i < size; ++i)
+ {
+ const feature_spec &feat = feats[i];
+ if (random2(tweight += feat.genweight) < feat.genweight)
+ chosen_feat = feat;
+ }
+
+ if (fix_slot)
+ {
+ feats.clear();
+ feats.push_back( chosen_feat );
+ }
+ return (chosen_feat);
+}
diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h
index 51857597a5..278bf6bbed 100644
--- a/crawl-ref/source/mapdef.h
+++ b/crawl-ref/source/mapdef.h
@@ -142,7 +142,7 @@ public:
mons_spec get_monster(int index);
// Returns an error string if the monster is unrecognised.
- std::string add_mons(const std::string &s);
+ std::string add_mons(const std::string &s, bool fix_slot = false);
size_t size() const { return mons.size(); }
@@ -202,7 +202,7 @@ public:
item_spec get_item(int index);
size_t size() const { return items.size(); }
- std::string add_item(const std::string &spec);
+ std::string add_item(const std::string &spec, bool fix = false);
private:
struct item_spec_slot
@@ -228,6 +228,61 @@ private:
std::string error;
};
+struct feature_spec
+{
+ int genweight;
+ int feat;
+ int shop;
+ int trap;
+ int glyph;
+
+ feature_spec(int f, int wt = 10)
+ : genweight(wt), feat(f), shop(-1),
+ trap(-1), glyph(-1)
+ { }
+ feature_spec() : genweight(0), feat(0), shop(-1), trap(-1), glyph(-1) { }
+};
+
+typedef std::vector<feature_spec> feature_spec_list;
+struct feature_slot
+{
+ feature_spec_list feats;
+ bool fix_slot;
+
+ feature_slot();
+ feature_spec get_feat();
+};
+
+struct keyed_mapspec
+{
+public:
+ feature_slot feat;
+ item_list item;
+ mons_list mons;
+
+public:
+ keyed_mapspec();
+
+ std::string set_feat(const std::string &s, bool fix);
+ std::string set_mons(const std::string &s, bool fix);
+ std::string set_item(const std::string &s, bool fix);
+
+ feature_spec get_feat();
+ mons_spec get_mons();
+ item_spec get_item();
+
+private:
+ std::string err;
+
+private:
+ void parse_features(const std::string &);
+ feature_spec_list parse_feature(const std::string &s);
+ feature_spec parse_shop(std::string s, int weight);
+ feature_spec parse_trap(std::string s, int weight);
+};
+
+typedef std::map<int, keyed_mapspec> keyed_specs;
+
// Not providing a constructor to make life easy for C-style initialisation.
class map_def
{
@@ -244,6 +299,8 @@ public:
mons_list mons;
item_list items;
+ keyed_specs keyspecs;
+
public:
void init();
void hmirror();
@@ -253,6 +310,12 @@ public:
void resolve();
void fixup();
+ keyed_mapspec *mapspec_for_key(int key);
+
+ std::string add_key_item(const std::string &s);
+ std::string add_key_mons(const std::string &s);
+ std::string add_key_feat(const std::string &s);
+
bool can_dock(map_section_type) const;
coord_def dock_pos(map_section_type) const;
coord_def float_dock();
@@ -261,6 +324,12 @@ public:
bool is_minivault() const;
bool has_tag(const std::string &tag) const;
+
+private:
+ std::string add_key_field(
+ const std::string &s,
+ std::string (keyed_mapspec::*set_field)(
+ const std::string &s, bool fixed));
};
class monster_chance
@@ -279,8 +348,7 @@ public:
// Can be empty, in which case the default colours are applied.
std::string floor_colour, rock_colour;
-
- std::string tags;
+ std::string tags;
// The probability of requesting a random vault.
int p_vault;
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 94542fff02..8e0845748b 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -2171,3 +2171,31 @@ void run_environment_effects()
}
}
}
+
+static const char *shop_types[] = {
+ "weapon",
+ "armour",
+ "antique weapon",
+ "antique armour",
+ "antiques",
+ "jewellery",
+ "wand",
+ "book",
+ "food",
+ "distillery",
+ "scroll",
+ "general"
+};
+
+int str_to_shoptype(const std::string &s)
+{
+ if (s == "random")
+ return (SHOP_RANDOM);
+
+ for (unsigned i = 0; i < sizeof(shop_types) / sizeof (*shop_types); ++i)
+ {
+ if (s == shop_types[i])
+ return (i);
+ }
+ return (-1);
+}
diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h
index b4fbfeb902..cc0b1e0fc7 100644
--- a/crawl-ref/source/misc.h
+++ b/crawl-ref/source/misc.h
@@ -193,4 +193,6 @@ int absdungeon_depth(unsigned char branch, int subdepth);
// Abyss and Pan.
void set_colours_from_monsters();
+int str_to_shoptype(const std::string &s);
+
#endif
diff --git a/crawl-ref/source/util/levcomp.lpp b/crawl-ref/source/util/levcomp.lpp
index 9441ca0306..cb04657c0d 100644
--- a/crawl-ref/source/util/levcomp.lpp
+++ b/crawl-ref/source/util/levcomp.lpp
@@ -126,6 +126,10 @@ MONS: { BEGIN(MNAME); return MONS; }
ITEM: { BEGIN(ITEM_LIST); return ITEM; }
SHUFFLE: { BEGIN(ARGUMENT); return SHUFFLE; }
+KFEAT: { BEGIN(ARGUMENT); return KFEAT; }
+KITEM: { BEGIN(ARGUMENT); return KITEM; }
+KMONS: { BEGIN(ARGUMENT); return KMONS; }
+
BRANCHDEF: return BRANCH;
DEFAULT return DEFAULT;
DESC: return DESC;
diff --git a/crawl-ref/source/util/levcomp.ypp b/crawl-ref/source/util/levcomp.ypp
index 4adbdfb486..b6ee732330 100644
--- a/crawl-ref/source/util/levcomp.ypp
+++ b/crawl-ref/source/util/levcomp.ypp
@@ -41,7 +41,7 @@ void yyerror(const char *e)
}
%token <i> BRANCHDEF BRANCH DESC DEFAULT
-%token <i> DEFAULT_DEPTH SHUFFLE SUBST TAGS
+%token <i> DEFAULT_DEPTH SHUFFLE SUBST TAGS KFEAT KITEM KMONS
%token <i> NAME DEPTH ORIENT PLACE CHANCE FLAGS MONS ITEM
%token <i> ROOT_DEPTH ENTRY_MSG EXIT_MSG
%token <i> ROCK_COLOUR FLOOR_COLOUR
@@ -162,8 +162,41 @@ metaline : place
| symbol
| tags
| shuffle
+ | kfeat
+ | kitem
+ | kmons
;
+kfeat : KFEAT { }
+ | KFEAT STRING
+ {
+ std::string err = lc_map.add_key_feat($2);
+ if (!err.empty())
+ yyerror(
+ make_stringf("Bad arg to KFEAT: '%s' (%s)",
+ $2, err.c_str()).c_str());
+ }
+
+kmons : KMONS { }
+ | KMONS STRING
+ {
+ std::string err = lc_map.add_key_mons($2);
+ if (!err.empty())
+ yyerror(
+ make_stringf("Bad arg to KMONS: '%s' (%s)",
+ $2, err.c_str()).c_str());
+ }
+
+kitem : KITEM { }
+ | KITEM STRING
+ {
+ std::string err = lc_map.add_key_item($2);
+ if (!err.empty())
+ yyerror(
+ make_stringf("Bad arg to KITEM: '%s' (%s)",
+ $2, err.c_str()).c_str());
+ }
+
shuffle : SHUFFLE { }
| SHUFFLE STRING
{