diff options
-rw-r--r-- | crawl-ref/source/dat/levdes.vim | 2 | ||||
-rw-r--r-- | crawl-ref/source/dat/splev.des | 64 | ||||
-rw-r--r-- | crawl-ref/source/dat/vaults.des | 62 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 27 | ||||
-rw-r--r-- | crawl-ref/source/describe.cc | 20 | ||||
-rw-r--r-- | crawl-ref/source/describe.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.cc | 126 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 269 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.h | 76 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 28 | ||||
-rw-r--r-- | crawl-ref/source/misc.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/util/levcomp.lpp | 4 | ||||
-rw-r--r-- | crawl-ref/source/util/levcomp.ypp | 35 |
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 { |