summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/dat/levdes.vim34
-rw-r--r--crawl-ref/source/dat/splev.des172
-rw-r--r--crawl-ref/source/dat/vaults.des54
-rw-r--r--crawl-ref/source/libutil.cc12
-rw-r--r--crawl-ref/source/libutil.h2
-rw-r--r--crawl-ref/source/mapdef.cc206
-rw-r--r--crawl-ref/source/mapdef.h49
-rw-r--r--crawl-ref/source/util/levcomp.lpp2
-rw-r--r--crawl-ref/source/util/levcomp.ypp15
9 files changed, 381 insertions, 165 deletions
diff --git a/crawl-ref/source/dat/levdes.vim b/crawl-ref/source/dat/levdes.vim
index 9350a904bb..8e8b573af9 100644
--- a/crawl-ref/source/dat/levdes.vim
+++ b/crawl-ref/source/dat/levdes.vim
@@ -1,10 +1,9 @@
-" levdes.vim:
-"
-" Basic Vim syntax highlighting for Dungeon Crawl Stone Soup level design
-" (.des) files.
-"
-" This syntax highlighting script is distributed under the terms of the
-" Crawl Public License. See licence.txt for details.
+" Vim syntax file
+" Language: Dungeon Crawl level design (.des) files.
+" Maintainer: Darshan Shaligram <scintilla@gmail.com>
+" Last Change: 2007 Feb 20
+" Remark: Basic Vim syntax highlighting for Dungeon Crawl Stone Soup
+" level design (.des) files.
if version < 600
syntax clear
@@ -17,8 +16,15 @@ syn case match
setlocal iskeyword+=:
setlocal iskeyword+=-
-syn keyword desDeclarator NAME: ORIENT: DEPTH: PLACE: MONS: FLAGS: SYMBOL: default-depth: TAGS: CHANCE: ITEM: SHUFFLE:
-syn keyword desOrientation encompass north south east west northeast northwest southeast southwest float
+syn region desSubst start=/^SUBST:\s*/ end=/$/ contains=desSubstDec,desSubstArg,desSubstSep,@desMapElements keepend
+
+syn region desShuffle start=/^SHUFFLE:\s*/ end=/$/ contains=desShuffleDec,desMapFrag keepend
+
+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 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*#.*$"
@@ -33,7 +39,7 @@ syn match desMapWaxWall /a/ contained
syn match desMapMonst /[0-9]/ contained
syn match desMapGold /\$/ contained
syn match desMapLava /l/ contained
-syn match desMapWater /w/ contained
+syn match desMapWater /[wW]/ contained
syn match desMapEntry /@/ contained
syn match desMapTrap /\^/ contained
@@ -49,12 +55,20 @@ syn cluster desMapElements add=desMapEntry,desMapWaxWall
syn cluster desMapElements add=desMapRune,desMapOrb,desMapValuable
+syn match desSubstArg /\S/ contained nextgroup=desSubstSep skipwhite
+syn match desSubstSep /[:=]/ contained nextgroup=desMapFrag skipwhite
+syn region desMapFrag start=/./ end=/$/ contains=@desMapElements contained
+
syn region desMap start=/^\s*\<MAP\>\s*$/ end=/^\s*\<ENDMAP\>\s*$/ contains=@desMapElements keepend
hi link desDeclarator Statement
+hi link desSubstDec Statement
+hi link desShuffleDec Statement
hi link desMapBookend Statement
hi link desComment Comment
hi link desMap String
+hi link desSubstArg String
+hi link desSubstSep Type
hi link desOrientation Type
hi desMapWall guifg=darkgray term=bold gui=bold ctermfg=brown
diff --git a/crawl-ref/source/dat/splev.des b/crawl-ref/source/dat/splev.des
index 99bc3911ae..abc7c12c95 100644
--- a/crawl-ref/source/dat/splev.des
+++ b/crawl-ref/source/dat/splev.des
@@ -17,8 +17,7 @@
# @ - entry point - must be on outside and, except in ORIENT:float layouts,
# must always be on a particular side or sides - see templates
# W - shallow water
-# w - deep water - Entry vault makers, note that this may receive water
-# creatures!
+# w - deep water - Entry vault makers: this may receive water creatures!
# l - lava - Entry vault makers, note that this may receive lava creatures!
# (Use the no_monster_gen tag to prevent both of these effects.)
# >< - extra stairs - you can leave level by these but will never be placed o
@@ -32,8 +31,7 @@
# ^ - random trap
# ~ - random trap suitable for the branch and depth the map is being used.
#
-# A - Vestibule gateway (opened by Horn). Can also be put on other levels for
-# colour, where it won't do anything.
+# A - Vestibule gateway (opened by Horn).
# B - Altar. These are assigned specific types (eg of Zin etc) in dungeon.cc,
# in order.
# C - Random Altar.
@@ -44,7 +42,7 @@
# T - Water fountain
# U - Magic fountain
# V - Permanently dry fountain
-# #
+#
# $ - gold
# % - normal item
# * - higher level item (good)
@@ -60,13 +58,20 @@
# 9 - +5 depth monster
# 8 - (+2) * 2 depth monster (aargh!). Can get golden dragons and titans
# this way.
-# 1-7 - monster array monster. See section below on MONS: arrays for more inf
+# 1-7 - monster array monster. See section below on MONS: arrays for more
+# information
+#
+# A vault always has its body between MAP and ENDMAP commands. Furthermore,
+# several other additional commands are possible (some of them mandatory).
#
-# ORIENT: LINES:
-# Some kind of orient: line is mandatory, unless you want the vault to be a
-# minivault, which is usually not what you want. Valid orient: lines are:
-# float: The dungeon builder will put your vault wherever it wants to.
-# some_direction: The vault will lie along the mentioned side of the map:
+# 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.
+# "some_direction": The vault will lie along the mentioned side of the map:
# xxxxxxxxxx xxxxxxxxxxxxx
# xORIENT:Nx xORIENT:NW|..
# x.VAULT..x x.VAULT...|..
@@ -74,43 +79,48 @@
# xrest....x xrest........
# x...of...x x.....of.....
# x...levelx x.......level
-# ...which brings us to padding. With any some_direction orientation, you nee
+# ...which brings us to padding. With any some_direction orientation, you need
# 6 layers of x-padding along any level-edge that the vault borders. For
# instance, if your map is ORIENT: north, you must have a 6 deep border of
# rock wall (or any other kind of wall) along the northern, eastern, and
# western edges of the map.
-# encompass: the vault completely occupies the entire level. Padding is needed
-# on all 4 sides.
+# "encompass": the vault completely occupies the entire level. Padding is
+# needed on all 4 sides.
#
-# "ITEM:" LINES:
+# 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 the
-# vault definition, the second will be used for "e"s, etc. Positions are
-# comma-separated. Positions can contain multiple possibilities, one of which
-# the builder will choose randomly. Separate such multiple possibilities usin
-# a slash. Note that "nothing" (without the quotes) is a valid possibility.
+# 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
+# the vault definition, the second will be used for "e"s, etc. Positions are
+# comma-separated; several ITEM: lines are possible as well.
+# Positions can contain multiple possibilities, one of which the builder will
+# choose randomly. Separate such multiple possibilities using a slash. Note
+# that "nothing" (without the quotes) is a valid possibility. The random
+# choice is done for each individual occurence of the letter.
# You can also give possibilities a "weight," which affects their chance of
# being picked. The default weight is 10. The chance to pick a possibility is
# [possibility's weight: / sum of all weight:s in that array position]
-# Modifiers: "good_item" makes the builder try to make the item a good one.
-# "any" by itself lets it plop down any object -- and you can combine "any"
-# with "good_item." "any" plus an item class gives a random item of that clas
-# (e.g. "any book").
+# Modifiers:
+# "good_item" makes the builder try to make the item a good one.
+# "any" by itself gives random choice; you can combine "any" with "good_item."
+# "any book", "any misc" etc. gives a random item of that class.
#
# Limitations: You can't affect stack quantity for stackable items, nor can you
# affect curse status nor item race, nor can you give specific egos, nor can
# give fixedarts. You also can't lay down corpses, skeletons, or chunks.
#
-# "MONS:" LINES:
+# 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"
# in the vault definition, the second for "2," etc. Note that if, for
# example, you place a 3 on the map, but your MONS: line has no third
# position, the 3 will be filled with RANDOM_MONSTER.
+# Individual monsters may be prefixed with the "generate_awake" (without
+# the quotes). Use this sparingly.
+# Note that 8, 9, 0 also place monsters (see the table above).
#
-# "CHANCE:" LINES
+# 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
@@ -120,56 +130,94 @@
# almost guarantee that a vault will be picked, and thus are great
# for testing. !!!
#
-# TAGS
+# 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
-# the vault's creation. Highly advised for entry vaults with
-# a player-hostile geography, MUST-HAVE for those with water
-# or lava.
-# no_pool_fixup: prevents water squares next to land from being randomly conv
-# from deep water (the default) to shallow.
+# "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
+# the vault's creation. Highly advised for entry vaults with
+# a player-hostile geography, MUST-HAVE for those with water
+# or lava.
+# "no_pool_fixup": prevents water squares next to land from being randomly
+# converted from deep water (the default) to shallow.
+# "branch_entry" eg. "orc_entry", "lair_entry" etc. If chosen, these maps
+# will contain the stairs for that branch. Use "O" to place
+# the stairs. Branch entries should go to splev.des.
#
# 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!"
-# no_hmirror: Like no_rotate, but for horizontal mirroring.
-# no_vmirror: Like no_rotate, but for vertical mirroring.
+# "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!"
+# "no_hmirror": Like no_rotate, but for horizontal mirroring.
+# "no_vmirror": Like no_rotate, but for vertical mirroring.
#
-# note that a lot of the vaults are in there mainly to add some
+# 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",
+# as well as swapping all "3" with "w" (or any other of the 23
+# possibilities).
+# SHUFFLE: 12/3w (i.e. list of slash-separated blocks of same size)
+# will either do nothing or swap all "1" with "3" and then also
+# swap "2" with "w" everywhere.
+# Several SHUFFLE: lines can be used, and the shuffles will be applied in
+# 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
+# will replace occurrences of ? with one of TUV.
+# SUBST: ? = T U V
+# does the same thing - whitespace is not significant.
+# SUBST: ? = T:20 U V
+# makes T twice as likely to be used as U or V (the default weight
+# is 10). Note that there has to be at least one space after T:20
+# and that whitespace in T:20 is not permitted.
+# SUBST: ? : TUV
+# replaces occurrences of ? with one of TUV, and guarantees that all
+# occurrences of ? will get the same replacement symbol.
+# The placeholder symbol can be any non-space, printable character apart from
+# : and =. The replacement symbols can be any non-space printable character,
+# including : and = ("SUBST: ? = +=:123def" is valid).
+#
+# SUBST: lines can safely replace symbols with themselves:
+#
+# SUBST: w = wW
+#
+# Multiple SUBST: lines can be used, and will be applied in order.
+# Substitutions are performed after any declared shuffles.
+#
+# 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)
-# that form the border have a rock wall padding at least 6 deep. For instance,
-# if your map is ORIENT: north, you must have a 6 deep border of rock wall (or
-# any other kind of wall) along the northern, eastern, and western edges of the
-# map. If you're doing a fullscreen map (encompass), you must pad all around
-# the map with 6 layers of wall. For ORIENT: encompass maps, you don't need to
-# explicitly include the padding provided you make the map small enough that
-# the padding can be provided automatically.
+# forming the border have a rock wall padding at least 6 deep. For instance,
+# if your map is ORIENT: north, you must have a 6 deep border of rock wall
+# (or any other kind of wall) along the northern, eastern, and western edges
+# of the map. If you're doing a fullscreen map (encompass), you must pad all
+# around the map with 6 layers of wall. For ORIENT: encompass maps, you
+# don't need to explicitly include the padding provided you make the map
+# small enough that the padding can be provided automatically.
#
-# You do not have to place all of the stairs unless the level is full
-# screen, in which case you must place all except the extra stairs (>
-# and <). The <> stairs can be put anywhere and in any quantities but
-# do not have to be there. Any of the other stairs which are not
-# present in the vault will be randomly placed outside it. Also
-# generally try to avoid rooms with no exit.
+# You do not have to place all of the stairs unless the level is full screen,
+# in which case you must place all except the extra stairs (> and <). The <>
+# stairs can be put anywhere and in any quantities but do not have to be
+# there. Any of the other stairs which are not present in the vault will be
+# randomly placed outside it. Also generally try to avoid rooms with no exit.
#
-# You can use the templates below to build vaults. The entry point
-# '@' must be present (except full-screen vaults where it must not
-# and orient:float maps, where it is optional - the builder will
-# randomly convert '.' spaces on edges to entry points if needed) and
-# be on an edge of the vault.
+# You can use the templates below to build vaults. The entry point '@' must
+# be present (except full-screen vaults where it must not and orient:float
+# maps, where it is optional - the builder will randomly convert '.' spaces
+# on edges to entry points if needed) and be on an edge of the vault.
#
# Minivaults are handled very differently from regular vaults and special
-# levels. They're placed *after* normal map generation, whereas normal vaults
-# are placed before generating the rest of the level. There's no way to
-# guarantee generation of a minivault on a particular level, whereas vaults
-# can be forced to appear using a PLACE: attribute.
+# levels. They're placed *after* normal map generation, whereas normal
+# vaults are placed before generating the rest of the level. There's no way
+# to guarantee generation of a minivault on a particular level, whereas
+# vaults can be forced to appear using a PLACE: attribute.
#
# I think that's all. Have fun!
#
diff --git a/crawl-ref/source/dat/vaults.des b/crawl-ref/source/dat/vaults.des
index 70d4176fe6..0339e79efb 100644
--- a/crawl-ref/source/dat/vaults.des
+++ b/crawl-ref/source/dat/vaults.des
@@ -86,12 +86,12 @@
# "encompass": the vault completely occupies the entire level. Padding is
# needed on all 4 sides.
#
-# ITEM
+# 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
# the vault definition, the second will be used for "e"s, etc. Positions are
-# comma-separated; several ITEM lines are possible as well.
+# comma-separated; several ITEM: lines are possible as well.
# Positions can contain multiple possibilities, one of which the builder will
# choose randomly. Separate such multiple possibilities using a slash. Note
# that "nothing" (without the quotes) is a valid possibility. The random
@@ -108,7 +108,7 @@
# affect curse status nor item race, nor can you give specific egos, nor can
# give fixedarts. You also can't lay down corpses, skeletons, or chunks.
#
-# MONS
+# 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"
@@ -119,7 +119,7 @@
# the quotes). Use this sparingly.
# Note that 8, 9, 0 also place monsters (see the table above).
#
-# CHANCE
+# 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
@@ -129,7 +129,7 @@
# almost guarantee that a vault will be picked, and thus are great
# for testing. !!!
#
-# TAGS
+# 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
@@ -149,16 +149,42 @@
# "no_hmirror": Like no_rotate, but for horizontal mirroring.
# "no_vmirror": Like no_rotate, but for vertical mirroring.
#
-# SHUFFLE
-# This allows to randomly permute glyphs on the map. There are two ways:
+# 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",
# as well as swapping all "3" with "w" (or any other of the 23
# possibilities).
-# SHUFFLE: 12,3w (i.e. list of comma-separated blocks of same size)
+# SHUFFLE: 12/3w (i.e. list of slash-separated blocks of same size)
# will either do nothing or swap all "1" with "3" and then also
# swap "2" with "w" everywhere.
-# Several SHUFFLE lines are possible.
+# Several SHUFFLE: lines can be used, and the shuffles will be applied in
+# 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
+# will replace occurrences of ? with one of TUV.
+# SUBST: ? = T U V
+# does the same thing - whitespace is not significant.
+# SUBST: ? = T:20 U V
+# makes T twice as likely to be used as U or V (the default weight
+# is 10). Note that there has to be at least one space after T:20
+# and that whitespace in T:20 is not permitted.
+# SUBST: ? : TUV
+# replaces occurrences of ? with one of TUV, and guarantees that all
+# occurrences of ? will get the same replacement symbol.
+# The placeholder symbol can be any non-space, printable character apart from
+# : and =. The replacement symbols can be any non-space printable character,
+# including : and = ("SUBST: ? = +=:123def" is valid).
+#
+# SUBST: lines can safely replace symbols with themselves:
+#
+# SUBST: w = wW
+#
+# Multiple SUBST: lines can be used, and will be applied in order.
+# Substitutions are performed after any declared shuffles.
#
# 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
@@ -3277,7 +3303,7 @@ ENDMAP
NAME: david_entry_061_a_altar
TAGS: entry no_monster_gen
ORIENT: northwest
-SYMBOL: l.
+SUBST: ?=l.
CHANCE: 6
SHUFFLE: Cc
MAP
@@ -3494,9 +3520,11 @@ ORIENT: float
FLAGS: no_rotate
SHUFFLE: {[(
SHUFFLE: def
+
# A test run with 50 tries had precisely one level without shallow water
# access to surroundings. This is intended and I like the low chance for this.
-# Using SYMBOL: wW this chance could bew decreased even more.
+# Using SUBST: w=wW this chance could be decreased even more.
+
MAP
.............................
.............................
@@ -5826,7 +5854,7 @@ ENDMAP
###################################
# Solitary fountain
NAME: solitary_fountain
-SYMBOL: TUV
+SUBST: ?=TUV
MAP
.....
@@ -5840,7 +5868,7 @@ ENDMAP
# Fountainhead
NAME: fountainhead
-SYMBOL: TUV
+SUBST: ?=TUV
MAP
..............
diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc
index d380b92419..5246c2704d 100644
--- a/crawl-ref/source/libutil.cc
+++ b/crawl-ref/source/libutil.cc
@@ -193,9 +193,11 @@ void lowercase(std::string &s)
s[i] = tolower(s[i]);
}
+// Replaces all occurrences of any of the characters in tofind with the
+// replacement string.
std::string replace_all_of(std::string s,
- const std::string &tofind,
- const std::string &replacement)
+ const std::string &tofind,
+ const std::string &replacement)
{
std::string::size_type start = 0;
std::string::size_type found;
@@ -349,6 +351,12 @@ int cancelable_get_line( char *buf, int len, int maxcol,
return reader.read_line();
}
+std::string trimmed_string( std::string s )
+{
+ trim_string(s);
+ return (s);
+}
+
// also used with macros
std::string & trim_string( std::string &str )
{
diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h
index 5ad02e504e..f9cec51893 100644
--- a/crawl-ref/source/libutil.h
+++ b/crawl-ref/source/libutil.h
@@ -67,6 +67,8 @@ int cancelable_get_line( char *buf,
int (*keyproc)(int &c) = NULL );
std::string & trim_string( std::string &str );
+std::string trimmed_string( std::string s );
+
std::vector<std::string> split_string(
const char *sep,
std::string s,
diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc
index 2ddacf61f9..dbc066a8a5 100644
--- a/crawl-ref/source/mapdef.cc
+++ b/crawl-ref/source/mapdef.cc
@@ -142,6 +142,111 @@ void map_lines::add_line(const std::string &s)
map_width = s.length();
}
+std::string map_lines::clean_shuffle(std::string s)
+{
+ return replace_all_of(s, " \t", "");
+}
+
+std::string map_lines::check_block_shuffle(const std::string &s)
+{
+ const std::vector<std::string> segs = split_string("/", s);
+ const unsigned seglen = segs[0].length();
+
+ for (int i = 1, size = segs.size(); i < size; ++i)
+ {
+ if (seglen != segs[i].length())
+ return ("block shuffle segment length mismatch");
+ }
+
+ return ("");
+}
+
+std::string map_lines::check_shuffle(std::string &s)
+{
+ if (s.find(',') != std::string::npos)
+ return ("use / for block shuffle, or multiple SHUFFLE: lines");
+
+ s = clean_shuffle(s);
+
+ if (s.find('/') != std::string::npos)
+ return check_block_shuffle(s);
+
+ return ("");
+}
+
+std::string map_lines::parse_glyph_replacements(std::string s,
+ glyph_replacements_t &gly)
+{
+ s = replace_all_of(s, "\t", " ");
+ std::vector<std::string> segs = split_string(" ", s);
+
+ for (int i = 0, size = segs.size(); i < size; ++i)
+ {
+ const std::string &is = segs[i];
+ if (is.length() > 2 && is[1] == ':')
+ {
+ const int glych = is[0];
+ int weight = atoi( is.substr(2).c_str() );
+ if (weight < 1)
+ weight = 10;
+
+ gly.push_back( glyph_weighted_replacement_t(glych, weight) );
+ }
+ else
+ {
+ for (int c = 0, cs = is.length(); c < cs; ++c)
+ gly.push_back( glyph_weighted_replacement_t(is[c], 10) );
+ }
+ }
+
+ return ("");
+}
+
+std::string map_lines::add_subst(const std::string &sub)
+{
+ std::string s = trimmed_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 :");
+
+ 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());
+
+ glyph_replacements_t repl;
+ std::string err = parse_glyph_replacements(substitute, repl);
+ if (!err.empty())
+ return (err);
+
+ substitutions.push_back(
+ subst_spec( what_to_subst[0], fixed, repl ) );
+
+ return ("");
+}
+
+std::string map_lines::add_shuffle(const std::string &raws)
+{
+ std::string s = raws;
+ const std::string err = check_shuffle(s);
+
+ if (err.empty())
+ shuffles.push_back(s);
+
+ return (err);
+}
+
int map_lines::width() const
{
return map_width;
@@ -209,24 +314,26 @@ bool map_lines::solid_borders(map_section_type border)
void map_lines::clear()
{
+ substitutions.clear();
+ shuffles.clear();
lines.clear();
map_width = 0;
}
-void map_lines::resolve(std::string &s, const std::string &fill)
+void map_lines::subst(std::string &s, subst_spec &spec)
{
- std::string::size_type pos;
- while ((pos = s.find('?')) != std::string::npos)
- s[pos] = fill[ random2(fill.length()) ];
+ std::string::size_type pos = 0;
+ while ((pos = s.find(spec.key(), pos)) != std::string::npos)
+ s[pos++] = spec.value();
}
-void map_lines::resolve(const std::string &fillins)
+void map_lines::subst()
{
- if (fillins.empty() || fillins.find('?') != std::string::npos)
- return;
-
- for (int i = 0, size = lines.size(); i < size; ++i)
- resolve(lines[i], fillins);
+ for (int i = 0, size = substitutions.size(); i < size; ++i)
+ {
+ for (int y = 0, ysize = lines.size(); y < ysize; ++y)
+ subst(lines[y], substitutions[i]);
+ }
}
std::string map_lines::block_shuffle(const std::string &s)
@@ -285,7 +392,7 @@ void map_lines::resolve_shuffle(const std::string &shufflage)
}
}
-void map_lines::resolve_shuffles(const std::vector<std::string> &shuffles)
+void map_lines::resolve_shuffles()
{
for (int i = 0, size = shuffles.size(); i < size; ++i)
resolve_shuffle( shuffles[i] );
@@ -376,7 +483,6 @@ void map_def::init()
tags.clear();
place.clear();
items.clear();
- shuffles.clear();
depth.reset();
orient = MAP_NONE;
@@ -387,55 +493,10 @@ void map_def::init()
flags = MAPF_MIRROR_VERTICAL | MAPF_MIRROR_HORIZONTAL
| MAPF_ROTATE;
- random_symbols.clear();
-
map.clear();
mons.clear();
}
-std::string map_def::clean_shuffle(std::string s)
-{
- return replace_all_of(s, " \t", "");
-}
-
-std::string map_def::check_block_shuffle(const std::string &s)
-{
- const std::vector<std::string> segs = split_string("/", s);
- const unsigned seglen = segs[0].length();
-
- for (int i = 1, size = segs.size(); i < size; ++i)
- {
- if (seglen != segs[i].length())
- return ("block shuffle segment length mismatch");
- }
-
- return ("");
-}
-
-std::string map_def::check_shuffle(std::string &s)
-{
- if (s.find(',') != std::string::npos)
- return ("use / for block shuffle, or multiple SHUFFLE: lines");
-
- s = clean_shuffle(s);
-
- if (s.find('/') != std::string::npos)
- return check_block_shuffle(s);
-
- return ("");
-}
-
-std::string map_def::add_shuffle(const std::string &raws)
-{
- std::string s = raws;
- const std::string err = check_shuffle(s);
-
- if (err.empty())
- shuffles.push_back(s);
-
- return (err);
-}
-
bool map_def::is_minivault() const
{
return (orient == MAP_NONE);
@@ -645,8 +706,8 @@ void map_def::normalise()
void map_def::resolve()
{
- map.resolve( random_symbols );
- map.resolve_shuffles( shuffles );
+ map.resolve_shuffles();
+ map.subst();
}
void map_def::fixup()
@@ -1014,3 +1075,30 @@ item_list::item_spec_slot item_list::parse_item_spec(std::string spec)
return (list);
}
+
+/////////////////////////////////////////////////////////////////////////
+// subst_spec
+
+subst_spec::subst_spec(int torepl, bool dofix, const glyph_replacements_t &g)
+ : foo(torepl), fix(dofix), frozen_value(0), repl(g)
+{
+}
+
+int subst_spec::value()
+{
+ if (frozen_value)
+ return (frozen_value);
+
+ int cumulative = 0;
+ int chosen = 0;
+ for (int i = 0, size = repl.size(); i < size; ++i)
+ {
+ if (random2(cumulative += repl[i].second) < repl[i].second)
+ chosen = repl[i].first;
+ }
+
+ if (fix)
+ frozen_value = chosen;
+
+ return (chosen);
+}
diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h
index b02c08a86a..51857597a5 100644
--- a/crawl-ref/source/mapdef.h
+++ b/crawl-ref/source/mapdef.h
@@ -40,12 +40,38 @@ public:
int span() const;
};
+typedef std::pair<int,int> glyph_weighted_replacement_t;
+typedef std::vector<glyph_weighted_replacement_t> glyph_replacements_t;
+
+class subst_spec
+{
+public:
+ subst_spec(int torepl, bool fix, const glyph_replacements_t &repls);
+
+ int key() const
+ {
+ return (foo);
+ }
+
+ int value();
+
+private:
+ int foo; // The thing to replace.
+ bool fix; // If true, the first replacement fixes the value.
+ int frozen_value;
+
+ glyph_replacements_t repl;
+};
+
class map_lines
{
public:
map_lines();
void add_line(const std::string &s);
+ std::string add_subst(const std::string &st);
+ std::string add_shuffle(const std::string &s);
+
void set_orientation(const std::string &s);
int width() const;
@@ -56,8 +82,8 @@ public:
bool solid_borders(map_section_type border);
- void resolve(const std::string &fillins);
- void resolve_shuffles(const std::vector<std::string> &shuffles);
+ void subst();
+ void resolve_shuffles();
// Make all lines the same length.
void normalise(char fillc = 'x');
@@ -73,12 +99,19 @@ public:
private:
void resolve_shuffle(const std::string &shuffle);
- void resolve(std::string &s, const std::string &fill);
+ void subst(std::string &s, subst_spec &spec);
void check_borders();
std::string shuffle(std::string s);
std::string block_shuffle(const std::string &s);
+ std::string check_shuffle(std::string &s);
+ std::string check_block_shuffle(const std::string &s);
+ std::string clean_shuffle(std::string s);
+ std::string parse_glyph_replacements(std::string s,
+ glyph_replacements_t &gly);
private:
+ std::vector<subst_spec> substitutions;
+ std::vector<std::string> shuffles;
std::vector<std::string> lines;
int map_width;
bool solid_north, solid_east, solid_south, solid_west;
@@ -211,9 +244,6 @@ public:
mons_list mons;
item_list items;
- std::string random_symbols;
- std::vector<std::string> shuffles;
-
public:
void init();
void hmirror();
@@ -223,8 +253,6 @@ public:
void resolve();
void fixup();
- std::string add_shuffle(const std::string &s);
-
bool can_dock(map_section_type) const;
coord_def dock_pos(map_section_type) const;
coord_def float_dock();
@@ -233,11 +261,6 @@ public:
bool is_minivault() const;
bool has_tag(const std::string &tag) const;
-
-private:
- std::string check_shuffle(std::string &s);
- std::string check_block_shuffle(const std::string &s);
- std::string clean_shuffle(std::string s);
};
class monster_chance
diff --git a/crawl-ref/source/util/levcomp.lpp b/crawl-ref/source/util/levcomp.lpp
index faf61d7f7c..c10e719a7f 100644
--- a/crawl-ref/source/util/levcomp.lpp
+++ b/crawl-ref/source/util/levcomp.lpp
@@ -121,7 +121,7 @@ PLACE: { BEGIN(ARGUMENT); return PLACE; }
CHANCE: return CHANCE;
FLAGS: return FLAGS;
TAGS: { BEGIN(KEYWORDS); return TAGS; }
-SYMBOL: { BEGIN(ARGUMENT); return SYMBOL; }
+SUBST: { BEGIN(ARGUMENT); return SUBST; }
MONS: { BEGIN(MNAME); return MONS; }
ITEM: { BEGIN(ITEM_LIST); return ITEM; }
SHUFFLE: { BEGIN(ARGUMENT); return SHUFFLE; }
diff --git a/crawl-ref/source/util/levcomp.ypp b/crawl-ref/source/util/levcomp.ypp
index 62817052a9..658e6736cf 100644
--- a/crawl-ref/source/util/levcomp.ypp
+++ b/crawl-ref/source/util/levcomp.ypp
@@ -25,7 +25,7 @@ void yyerror(const char *e)
}
%token <i> BRANCHDEF BRANCH DESC DEFAULT
-%token <i> DEFAULT_DEPTH SYMBOL TAGS SHUFFLE
+%token <i> DEFAULT_DEPTH SHUFFLE SUBST TAGS
%token <i> NAME DEPTH ORIENT PLACE CHANCE FLAGS MONS ITEM
%token <i> ROOT_DEPTH ENTRY_MSG EXIT_MSG
%token <i> ROCK_COLOUR FLOOR_COLOUR
@@ -136,7 +136,7 @@ metaline : place
shuffle : SHUFFLE { }
| SHUFFLE STRING
{
- std::string err = lc_map.add_shuffle($2);
+ std::string err = lc_map.map.add_shuffle($2);
if (!err.empty())
yyerror(
make_stringf(
@@ -156,10 +156,15 @@ tagstrings : /* empty */
}
;
-symbol : SYMBOL {}
- | SYMBOL STRING
+symbol : SUBST {}
+ | SUBST STRING
{
- lc_map.random_symbols = $2;
+ std::string err = lc_map.map.add_subst($2);
+ if (!err.empty())
+ yyerror(
+ make_stringf(
+ "Bad SUBST argument: '%s' (%s)",
+ $2, err.c_str() ).c_str() );
}
;