diff options
-rw-r--r-- | crawl-ref/docs/level-design.txt | 193 | ||||
-rw-r--r-- | crawl-ref/source/dat/vaults.des | 41 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.cc | 48 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.h | 6 |
5 files changed, 200 insertions, 92 deletions
diff --git a/crawl-ref/docs/level-design.txt b/crawl-ref/docs/level-design.txt index 5bb33ad73e..c3723ab71e 100644 --- a/crawl-ref/docs/level-design.txt +++ b/crawl-ref/docs/level-design.txt @@ -19,16 +19,18 @@ are in use: splev.des: * branch endings (like Elf:7, Vaults:8) * premade level maps (like the Ecumenical Temples and Geryon) * Pan lairs of named demons - * branch entries (these can ornament the stairs for a branch) vaults.des: * random vaults (Crawl sometimes chooses a special level when making up a new level; these are often challenging and sometimes contain loot) - * entry vaults (each game - except turorials - uses one of - these premade maps for the vicinity of the entrance) * minivaults * Pan minivaults + entry.des * entry vaults (each game - but not tutorial games - uses one of + these premade maps for the vicinity of the entrance) + + ebranch.des * branch entries (these can ornament the stairs for a branch) + tricky.des: * a few entry vaults which are harder in some regard (use of special items, or being puzzles) @@ -79,6 +81,10 @@ Random minivaults are small maps that are placed onto a level that the dungeon builder has already constructed fully otherwise (the level may include other vaults). +Minivaults are distinguished from normal vaults solely by the absence +of an ORIENT: declaration. Any map without a specified ORIENT: is a +minivault. + B. Sample Map --------------- @@ -103,7 +109,7 @@ FLAGS: no_rotate SHUFFLE: de SUBST: 1=12. MONS: butterfly, plant -ITEM: stone, w:10 any_good book / w:90 nothing +ITEM: stone, w:10 any book / w:90 nothing # actual map below: MAP xx1@2xx @@ -119,11 +125,11 @@ ENDMAP "CHANCE: 5" makes it appear less often (default is 10). "TAGS: temple_entry" turns the 'O' on the map into stairs to the Temple. "FLAGS: no_rotate" forbids rotation (but mirroring is still allowed). -"SHUFFLE: dw" may replace all 'd' with 'w' in the map. +"SHUFFLE: de" may replace all 'd' with 'e' in the map. "SUBST: 1=12." may replace each '1' with either '1' or '2' or '.'. "MONS: butterfly, plant" turns all '1' into butterflies, and '2' into plants. "ITEM: stone" turns all 'd' into stones -"ITEM: w:10 any book / w:100 nothing" turns all 'e' into a book +"ITEM: w:10 any book / w:90 nothing" turns all 'e' into a book (with 10% chance) or creates no object (with 90% chance). The symbols on the map: @@ -156,25 +162,25 @@ Terrain = - secret door (DNGN_SECRET_DOOR) W - shallow water - w - deep water - can be turned into shallow water; prevent this with the - no_pool_fixup TAG. Also, this may receive water creatures! For entry + w - deep water - can be randomly turned into shallow water by the + level-builder; you can prevent this conversion with the no_pool_fixup TAG. + Also, water may automatically receive water creatures! For entry vaults, avoid this with the no_monster_gen TAG. l - lava - again, use the no_monster_gen TAG for entry vaults! Features -------- - @ - entry point - must be on outside and, except in ORIENT:float layouts, - must always be on a particular side or sides - see templates. If you use - ORIENT: float, and do not use any @, the dungeon builder will connect at - least one floorspace on the edge of your map to the rest of the level; if - there is no floorspace on the edge of your map, it will be isolated. - }{ - stairs 82/86 - You must be able to reach these from each other. The + @ - entry point - must be on outside edge. If you use ORIENT: float, and do + not use any @, the dungeon builder will connect at least one floorspace on + the edge of your map to the rest of the level; if there is no floorspace + on the edge of your map, it will be isolated. + }{ - Stone stairs - You must be able to reach these from each other. The { upstair is also the stair on which the player will enter the dungeon for entry vaults. - )( - stairs 83/87 - ][ - stairs 84/88 + )( - Stone stairs, set 2. + ][ - Stone stairs, set 3. - >< - extra rock stairs - you can leave level by these but will rarely be + >< - extra rock stairs - you can leave the level by these but will rarely be placed on them from another level I - orcish idol (does nothing) @@ -196,15 +202,15 @@ Features U - Magic fountain V - Permanently dry fountain -# Note: Due to the level maker having seen incremental improvements over the -# years, there are some inconsistencies. For examples, dangerous statues -# (orange, silver ice) are now genuine monsters. In particular, the 'G' and 'S' -# glyphs could be dispensed with (but many older vaults use them, of course) - -# especially as there's no glyph for ice statues. -# -# Also, the most of the other feature glyphs can be replaced with KFEAT: -# lines. The same goes for some item glyphs ('O', 'P', 'R', 'Z') which could -# be replaced by KITEM: lines. +Note: Due to the level maker having seen incremental improvements over +the years, there are some inconsistencies. For examples, dangerous +statues (orange, silver ice) are now genuine monsters. In particular, +the 'H' and 'S' glyphs could be dispensed with (but many older vaults +use them, of course) - especially as there's no glyph for ice statues. + +Also, the most of the other feature glyphs can be replaced with KFEAT: +lines. The same goes for some item glyphs ('R', 'Z') which +could be replaced by KITEM: lines. Items ----- @@ -237,7 +243,7 @@ NAME: a_string ORIENT: (float |encompass | north | northwest | ... | southeast) 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: + be a minivault. Valid values are: * "float": The dungeon builder puts your vault wherever it wants to. * "some_direction": The vault lies along that side of the map: xxxxxxxxxx xxxxxxxxxxxxx @@ -255,10 +261,51 @@ ORIENT: (float |encompass | north | northwest | ... | southeast) * "encompass": the vault completely occupies the entire level. Padding is needed on all 4 sides. + ORIENT: float vaults need no padding and give a lot of + flexibility to the dungeon generator; float should generally + be preferred to other ORIENT: settings for new vaults. + + DEPTH: For random vaults and minivaults, this gives the range where the vault - may be placed in the main dungeon. E.g. - DEPTH: 7-20 + may be placed in the dungeon. Branch entry vaults can also be + constrained by depth. E.g. + + DEPTH: 7-20 + A simple DEPTH: declaration that does not specify a branch + applies to all branches. A map declared with depth 7-20 could + be used in the Lair, for instance. (Lair:1 will be treated as + a depth of 12 if the Lair entrance is on D:11.) + + You can constrain a map by branch: + + DEPTH: Lair:7-9 + + (Anywhere between levels 7-9 of the Lair, inclusive.) + + You can apply multiple constraints (with multiple DEPTH: lines, if + necessary, or on one line, comma-separated): + + DEPTH: 7-20, !12-14 + + (Anywhere in the dungeon between depths 7-20, but not on levels + 12-14.) + + DEPTH: 7-20, !Orc + + (Anywhere in the dungeon between depths 7-20, but never in the Orcish + Mines.) + + DEPTH: Lair:* + + (Anywhere in the Lair. Can also be expressed as "DEPTH: Lair".) + + Maps that do not specify a DEPTH: attribute will inherit their depth + constraints from the closest preceding default-depth: line. If there + is no preceding default-depth directive in the .des file, the map will + have no DEPTH: constraint. Note that maps without a DEPTH: constraint + cannot be selected as random vaults or minivaults. + CHANCE: (number with 10 being default) For entry vaults and any other vaults randomly picked from among a set, this type of line affects the likelihood of the given vault @@ -281,6 +328,10 @@ PLACE: Used to specify certain special levels. Existing special levels are: TAGS: generate_awake, no_item_gen, no_monster_gen, no_pool_fixup, orc_entry Tags go an a TAGS: line and are space-separated. Valid tags are: + * "dummy": this tag indicates that the vault is a stub; if the dungeon + builder picks a dummy vault, it pretends no vault was + selected. Dummies are used to reduce the probability + of other vaults at the same depth / place. * "entry": this tag MUST be there for a vault to be pickable as an entry vault. * "generate_awake": Monsters placed (using MONS, KMONS) in this @@ -292,14 +343,14 @@ TAGS: generate_awake, no_item_gen, no_monster_gen, no_pool_fixup, orc_entry a player-hostile geography, MUST-HAVE for those with water/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. Currently used: - temple_entry, orc_entry, vault_entry, lair_entry, hive_entry. + * "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. - As long as a branch has very few entries, a dummy one is a must. + Use "O" to place the stairs. Branch entries should go to + ebranch.des. If a branch has very few entries, a dummy entry is + advisable to make sure the player doesn't get bored of the same + few entries recycled ad nauseam. * "mnoleg" or the name of some other pandemonium lord. This makes - the map eligible for said pan lord's lair (and it can't appear - anywhere else). + the map eligible for said pan lord's lair. FLAGS: no_rotate, no_hmirror, no_vmirror Flags go on a FLAGS: line and are space-separated. Valid flags are: @@ -425,30 +476,44 @@ KFEAT: Z = C / needle trap / antique armour shop / altar of Zin lines accept weights as for MONS or ITEM. KMONS: ? = orc priest / w:3 deep elf priest + KMONS: allows you to specify a placeholder symbol that indicates the position of a monster (or monsters). 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:). Fr example, - KFEAT: Z = W - KMONS: Z = rat + square (used in association with a KFEAT:). For example, + KFEAT: Z = W + KMONS: Z = rat places a rat on a shallow water square for all occurrences of Z. + KMONS: also allows you to specify alternative monsters if the + primary monster you want to place is unavailable (because it + is a unique that was already generated). For instance, if you want + to generate one of Terence, Michael or Erica or a generic human + (whoever is available, in that order, you can use): + KMONS: n = Terence, Michael, Erica, human + Or if you want to pick randomly: + KMONS: n = Terence / Michael / Erica, human + KITEM: ? = potion of healing / potion of restore abilities KITEM: places the specified item at all occurrences of the placeholder. It can be combined with KFEAT: and KMONS: lines for the same placeholder. - For items like gold or fountains, you have to use the description - of items instead of their shortcuts. For example, - KITEM: none / gold + For items like gold, you have to use the description of items + instead of their shortcuts. For example, + KITEM: ? = nothing / gold works but the following does not: - KITEM: none / $ + KITEM: ? = nothing / $ + + KITEM: allows you to place multiple items on the same square: + KITEM: ? = bread ration, potion of water, potion of porridge E. Hints for level makers ---------------------------- * Technical stuff: + 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, if your map is ORIENT: north, you must have a 6 deep border of @@ -464,16 +529,38 @@ E. Hints for level makers rooms with no exit (use at least > or < to make it possible for players to get away). - The entry point '@' must be present (except full-screen vaults where it - must not). For ORIENT: float maps, all @ will be connected to floors in - the rest of the map. Make sure that no part of your entry level can be - cut off! If no @ is present, the level builder will put one on a random - floor space '.' at the circumference. (Sometimes this may be used for - good effect. When you give no @'s with this feature in mind, please make - a comment stating this - else somebody may just add @'s later on :) + Minivaults should generally be completely surrounded by one-space of + floor for accessibility. Minivaults are not handled like regular + vaults in terms of entry-points: the entry-point glyph @ is ignored + for minivaults. In general, at least one square of a minivault will + be reachable from the rest of the level, but there is no way to + predict which one. If your minivault is intended to be accessible, + surround it with floor. + + The entry point '@' must be present for all vaults (except + full-screen vaults and minivaults where it must not, and floating + vaults where it is optional). All @ will be connected to floor space + in the rest of the map (multiple @ close together may merge into the + same exit corridor). Make sure that no part of your entry level can + be cut off! If no @ is present in a floating vault (and there are no + doors on the edge of the map, see below), the level builder will put + one or more on a random floor space '.' at the circumference. Note + that it is not possible to predict which floor spaces the level + builder will choose to connect; if you need predictability, use + explicit @ exits on the edge. + + The level-builder will also implicitly treat doors and secret doors + on the edge of a map as explicit exits (the same as using @) and + connect them to the rest of the level. + + Not using @ and allowing the level-builder to pick exits is + acceptable in floating vaults, but when you use no @'s with this + feature in mind, please add comments stating this - else somebody + may just add @'s later on. :) - Note that non-rectangular maps will be padded with rock walls for the - smallest rectangle containing them. Unfortunately. + Non-rectangular maps will be padded (to the right) with rock walls + (or with floor spaces for minivaults) for the smallest rectangle + containing them. Unfortunately. Entry levels should be rather small. Their intention is to provide some atmosphere for the starting room, not to get a grip on the whole of D:1. @@ -522,7 +609,9 @@ E. Hints for level makers new branch ends, you have to resort to wizard mode and use the &~ command to go directly to the place where the map is used, say Snake:5. You may want to use a high CHANCE: again, if the map has alternatives (like Snake:5, or - Coc:7). + Coc:7). Minivaults can also be tested by adding a PLACE: to the definition, + which makes it very likely that the minivault will appear in the chosen + level. If the .des file syntax is incorrect, Crawl will tell you on which line of which des file it found the syntax error, making for easier debugging. diff --git a/crawl-ref/source/dat/vaults.des b/crawl-ref/source/dat/vaults.des index c53d0bb780..56699ad926 100644 --- a/crawl-ref/source/dat/vaults.des +++ b/crawl-ref/source/dat/vaults.des @@ -1282,7 +1282,7 @@ ENDMAP # attribute makes the map a normal vault, and mayhem can result. # Default depth for minivaults. -default-depth: 8-27, !Orc, !Lair +default-depth: 8-27, !Orc, !Lair, !Swamp, !Shoal ############################################################################## # Minivault balancer @@ -1299,7 +1299,7 @@ ENDMAP # Greed's colour NAME: david_greed -DEPTH: 10-26, !Orc, !Lair +DEPTH: 10-26, !Orc, !Lair, !Swamp, !Shoal ITEM: nothing / any good_item MONS: silver statue / orange crystal statue / ice statue SHUFFLE: d| @@ -1324,7 +1324,7 @@ ENDMAP # Defended altar NAME: david_defended_altar -DEPTH: 7-20 +DEPTH: 7-20, !Lair, !Swamp, !Shoal ITEM: nothing SHUFFLE: 1I/2T MONS: orc priest, deep elf priest / w:30 nothing @@ -1487,7 +1487,7 @@ ENDMAP ################################### # Evil zoo NAME: minivault_9 -DEPTH: 15-27 +DEPTH: 15-27, !Orc, !Lair, !Swamp, !Shoal MAP ............ .==========. @@ -1948,7 +1948,7 @@ ENDMAP # Dispersion (David) # NAME: dispersion -DEPTH: 13-26 +DEPTH: 13-26, !Orc, !Lair, !Swamp, !Shoal SUBST: Y = 89 KITEM: 8 = % / * / w:1 | KITEM: 9 = % / * / w:1 | @@ -1977,15 +1977,17 @@ NAME: fake_naga_vault # Intentionally moved this deeper because explore will gladly run into the # room, and being hit by multiple mimics is un-fun. -DEPTH: 15-26 +DEPTH: 15-26, !Orc, !Lair, !Swamp, !Shoal MONS: mimic, guardian naga MAP -xxxxxxxxx -x1111111x -x1111111x -x1111111x -x11.2.11x -xxxx+xxxx +........... +.xxxxxxxxx. +.x1111111x. +.x1111111x. +.x1111111x. +.x11.2.11x. +.xxxx+xxxx. +........... ENDMAP ############################################################################## @@ -1997,10 +1999,12 @@ TAGS: no_monster_gen ITEM: potion of water w:20 / bread ration / meat ration / arrow / bolt / spear / any potion w:5 SUBST: ? = += MAP -x?xx -xddx -xddx -xxxx +...... +.x?xx. +.xddx. +.xddx. +.xxxx. +...... ENDMAP ############################################################################## @@ -2223,7 +2227,6 @@ x ENDMAP ################################### -# minvault intended for Orc Mines # pond with fungi NAME: mines1_lemuel MONS: fungus, wandering mushroom @@ -2246,7 +2249,6 @@ MAP ENDMAP ################################### -# minvault intended for Orc Mines # hidden treasure chamber NAME: mines2_lemuel SUBST: ? = x. @@ -2261,7 +2263,6 @@ MAP ENDMAP ################################### -# minvault intended for Orc Mines # treasure chamber with oklob pant NAME: mines3_lemuel SUBST: ? = +=. @@ -2285,7 +2286,6 @@ MAP ENDMAP ################################### -# minvault intended for Orc Mines # mage tower -- either orc with assistants, or ogre with guards NAME: mines4_lemuel SUBST: ? = += @@ -2309,7 +2309,6 @@ MAP ENDMAP ################################### -# minvault intended for Orc Mines # warg stables, with goblin stableboy NAME: mines5_lemuel SUBST: ?: wx diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index c2b3c8aebd..b590650b36 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -3183,7 +3183,8 @@ static void dngn_place_item_explicit(int index, int x, int y, dngn_place_item_explicit(spec, x, y, level); } -static void dngn_place_monster( +static bool dngn_place_monster( + const vault_placement &place, const mons_spec &monster_type_thing, int monster_level, int vx, int vy) @@ -3191,6 +3192,10 @@ static void dngn_place_monster( if (monster_type_thing.mid != -1) { const int mid = monster_type_thing.mid; + const bool generate_awake = + monster_type_thing.generate_awake + || place.map.has_tag("generate_awake"); + int not_used; const int mlev = monster_type_thing.mlevel; @@ -3207,18 +3212,35 @@ static void dngn_place_monster( if (mid != RANDOM_MONSTER && mid < NUM_MONSTERS) { if (mons_is_unique(mid) && you.unique_creatures[mid]) - return; + return (false); 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 ); + + return (place_monster( not_used, mid, monster_level, + generate_awake? BEH_WANDER : BEH_SLEEP, + MHITNOT, true, vx, vy, false )); } + return (false); +} + +static bool dngn_place_one_monster( + const vault_placement &place, + mons_list &mons, + int monster_level, + int vx, int vy) +{ + for (int i = 0, size = mons.size(); i < size; ++i) + { + if (dngn_place_monster(place, mons.get_monster(i), + monster_level, vx, vy)) + { + return (true); + } + } + return (false); } static monster_type random_evil_statue() @@ -3277,10 +3299,8 @@ static int vault_grid( vault_placement &place, else grd[vx][vy] = DNGN_FLOOR; - mons_spec mons = mapsp->get_mons(); - if (place.map.has_tag("generate_awake")) - mons.generate_awake = true; - dngn_place_monster(mons, level_number, vx, vy); + mons_list &mons = mapsp->get_monsters(); + dngn_place_one_monster(place, mons, level_number, vx, vy); item_list &items = mapsp->get_items(); dngn_place_multiple_items(items, vx, vy, level_number); @@ -3487,11 +3507,7 @@ 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 (place.map.has_tag("generate_awake")) - monster_type_thing.generate_awake = true; - - dngn_place_monster(monster_type_thing, monster_level, - vx, vy); + dngn_place_monster(place, 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/mapdef.cc b/crawl-ref/source/mapdef.cc index 2420d3ba9e..67a4257f47 100644 --- a/crawl-ref/source/mapdef.cc +++ b/crawl-ref/source/mapdef.cc @@ -1531,9 +1531,9 @@ feature_spec keyed_mapspec::get_feat() return feat.get_feat(key_glyph); } -mons_spec keyed_mapspec::get_mons() +mons_list &keyed_mapspec::get_monsters() { - return (mons.size()? mons.get_monster(0) : mons_spec(-1)); + return (mons); } item_list &keyed_mapspec::get_items() diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h index 3d5866af96..1f6cc05fa4 100644 --- a/crawl-ref/source/mapdef.h +++ b/crawl-ref/source/mapdef.h @@ -56,6 +56,10 @@ public: std::string str_depth_range() const; operator raw_range () const; + operator std::string () const + { + return describe(); + } }; typedef std::pair<int,int> glyph_weighted_replacement_t; @@ -331,7 +335,7 @@ public: std::string set_item(const std::string &s, bool fix); feature_spec get_feat(); - mons_spec get_mons(); + mons_list &get_monsters(); item_list &get_items(); private: |