diff options
Diffstat (limited to 'crawl-ref/source')
-rw-r--r-- | crawl-ref/source/dat/levdes.vim | 34 | ||||
-rw-r--r-- | crawl-ref/source/dat/splev.des | 172 | ||||
-rw-r--r-- | crawl-ref/source/dat/vaults.des | 54 | ||||
-rw-r--r-- | crawl-ref/source/libutil.cc | 12 | ||||
-rw-r--r-- | crawl-ref/source/libutil.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 206 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.h | 49 | ||||
-rw-r--r-- | crawl-ref/source/util/levcomp.lpp | 2 | ||||
-rw-r--r-- | crawl-ref/source/util/levcomp.ypp | 15 |
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() ); } ; |