summaryrefslogtreecommitdiffstats
path: root/crawl-ref/docs
diff options
context:
space:
mode:
authorDavid Ploog <dploog@users.sourceforge.net>2009-11-08 06:23:50 +0100
committerDavid Ploog <dploog@users.sourceforge.net>2009-11-08 06:25:44 +0100
commit227c7f40fc0ba8f2a15af767c30976d14f9e4ad6 (patch)
tree8ce072cc6e07182053059f44aed1c586f3fbd648 /crawl-ref/docs
parent1c0a611a9e00fc789a8e71f1423e48bec7dd5020 (diff)
downloadcrawl-ref-227c7f40fc0ba8f2a15af767c30976d14f9e4ad6.tar.gz
crawl-ref-227c7f40fc0ba8f2a15af767c30976d14f9e4ad6.zip
Split up level_design.txt into smaller files.
Namely introduction.txt, syntax.txt, advanced.txt all inside of docs/develop/levels.
Diffstat (limited to 'crawl-ref/docs')
-rw-r--r--crawl-ref/docs/develop/levels/advanced.txt894
-rw-r--r--crawl-ref/docs/develop/levels/introduction.txt349
-rw-r--r--crawl-ref/docs/develop/levels/syntax.txt (renamed from crawl-ref/docs/develop/level_design.txt)1170
3 files changed, 1257 insertions, 1156 deletions
diff --git a/crawl-ref/docs/develop/levels/advanced.txt b/crawl-ref/docs/develop/levels/advanced.txt
new file mode 100644
index 0000000000..361116cc6c
--- /dev/null
+++ b/crawl-ref/docs/develop/levels/advanced.txt
@@ -0,0 +1,894 @@
+-----------------------------------------------
+How to make levels for Dungeon Crawl Stone Soup
+-----------------------------------------------
+
+Part III: Advanced Methods
+ ================
+
+Contents: I. Conditionalising levels
+ J. Validating levels
+ K. Portal vaults
+ L. Lua reference
+ M. Feature names
+ N. Map statistics
+ O. Map generation
+
+This document describes the advanced features of vault making. This includes
+usage of lua and how to create portal vaults.
+
+
+I. Conditionalising levels
+=============================
+
+Crawl translates level (.des) files into Lua code chunks and runs these chunks
+to produce the final level that is generated. While you don't need to use Lua
+for most levels, using Lua allows you to conditionalise or randomise levels
+with greater control.
+
+Let's take a simple example of randomisation:
+
+NAME: random_test
+# Put it on D:1 so it's easy to test.
+PLACE: D:1
+ORIENT: float
+MAP
+xxxxxxxxxxxxxxxxxxx
+x........{........x
+xxxAxxxxxBxxxxxCxxx
+xxx.xxxxx.xxxxx.xxx
+xxx@xxxxx@xxxxx@xxx
+ENDMAP
+
+Now let's say you want A, B, and C to be randomly rock or floor, but B should
+be floor if both A and C are rock. Here's one way to do it (add these lines
+to the map definition):
+
+: local asolid, csolid
+: if crawl.random2(2) == 0 then
+: asolid = true
+: subst("A = x")
+: else
+: subst("A = .")
+: end
+: if crawl.random2(2) == 0 then
+: csolid = true
+: subst("C = x")
+: else
+: subst("C = .")
+: end
+: if asolid and csolid then
+: subst("B = .")
+: else
+: subst("B = .x")
+: end
+
+This code uses crawl.random2(N) which returns a number from 0 to N-1
+(in this case, returns 0 or 1). So we give A a 50% chance of being
+rock, and the same for C. If we made both A and C rock, we force B to
+be floor, otherwise we use a subst that gives B the same 50% chance of
+being rock.
+
+You can conditionalise on various factors, such as player experience
+level:
+
+NAME: condition_002
+DEPTH: 1-27
+ORIENT: float
+: if you.xl() > 18 then
+MONS: greater mummy
+: else
+MONS: deep elf priest / deep elf sorcerer / deep elf demonologist
+: end
+MAP
+xxxxxx
+x1...x
+x1...+
+x1...x
+xxxxxx
+ENDMAP
+
+Or based on where the map is being generated:
+
+NAME: condition_003
+DEPTH: Elf:*, Orc:*
+ORIENT: float
+: if you.branch() == "Orc" then
+MONS: orc priest, orc high priest
+: else
+MONS: deep elf priest, deep elf high priest
+: end
+MAP
+xxxxxx
+x1...x
+x2...+
+x1...x
+xxxxxx
+ENDMAP
+
+When conditionalising maps, remember that your Lua code executes in
+two contexts:
+
+1) An initial compilation phase before the game starts.
+2) The actual mapgen phase when the dungeon builder is at work.
+
+In context (1), you will not get useful answers from the Crawl Lua API
+in general, because the game hasn't started. This is generally
+ignorable (as in the case above) because the compilation phase just
+checks the syntax of your Lua code. If you conditionalise your map,
+however, you may run into compile failures. Take this variant, which
+(incorrectly) attempts to conditionalise the map:
+
+NAME: condition_004
+DEPTH: Elf:*, Orc:*
+ORIENT: float
+: if you.branch() == "Orc" then
+MONS: orc priest, orc high priest
+MAP
+xxxxxx
+x1...x
+x2.I.+
+x1...x
+xxxxxx
+ENDMAP
+: elseif you.branch() == "Elf" then
+MONS: deep elf priest, deep elf high priest
+MAP
+xxxxxx
+x1...x
+x2.U.+
+x1...x
+xxxxxx
+ENDMAP
+: end
+
+This map will break the compile with the cryptic message "Must define
+map." (to compound the confusion, the line number for this error will
+be the first line number of the map following the buggy map).
+
+This error is because although the map is Elf or Orc only, at compile
+time, the branch is *neither* Elf nor Orc, so the level-compiler
+thinks you've neglected to define a map.
+
+Lua code can detect the compile phase using crawl.game_started() which
+returns true only when the player has started a game (and will return
+false when the map is being initially compiled).
+
+For more details on the available Lua API and syntax, see the Lua
+reference section.
+
+
+J. Validating levels
+=======================
+
+If you have a map with lots of transforms (SUBST and SHUFFLE), and
+want to guarantee that the map is sane after the transforms, you can
+use a validation hook.
+
+To take a very contrived example:
+
+NAME: contrived_001
+PLACE: D:2
+ORIENT: float
+TAGS: no_pool_fixup
+SUBST: .=.w
+SUBST: c=x.
+MAP
+xxxxxx
+x{.+.c
+x..+>x
+xxxxxx
+ENDMAP
+
+This map has a chance of leaving the player stuck on the upstair
+without access to the rest of the level if the two floor squares near
+the doors are substituted with deep water (from the SUBST line), or
+the 'c' glyph is substituted with rock. Since a cut-off vault is
+uncool, you can force connectedness with the rest of the level:
+
+validate {{ return has_exit_from_glyph('{') }}
+
+The has_exit_from_glyph() function returns true if it is possible to
+leave the vault (without digging, etc.) from the position of the {
+glyph. (This takes things like the merfolk ability to swim into
+account, so a merfolk character may see deep water between the stair
+and door.)
+
+The validate Lua returns false (or nil) to indicate that the map is
+invalid, which will force the dungeon builder to reapply transforms
+(SUBST and SHUFFLE) and validate the map again. If the map fails
+validation enough times, the dungeon builder will discard the entire
+level and retry (this may cause a different map to be selected,
+bypassing the buggy map).
+
+Going back to the example, if you just want to ensure that the player
+can reach the > downstair, you can use:
+
+validate {{ return glyphs_connected('{', '>') }}
+
+NOTE: You cannot use the colon-prefixed syntax for validation Lua. If
+you have a big block of code, use the multiline syntax:
+
+validate {{
+ -- This level is always cool.
+ crawl.mpr("This level is guaranteed perfect!")
+ return true
+}}
+
+
+K. Portal Vaults
+==================
+
+Portal vaults are vaults accessed by portals in the dungeon (labyrinths
+and bazaars are special cases of portal vaults). You can create custom
+portal vaults in the following steps (no compilation is necessary):
+
+* Create a new file name.des in the dat/ folder. Rules:
+ The "name" should be descriptive of the vault you're adding.
+ The "name" should not exceed eight letters.
+ The ending must be "des".
+* Add "name.des" to the list of local files in dat/clua/loadmaps.lua.
+* "name.des" should contain a comment at the top, explaining flavour and
+ gameplay goals of the portal vault (and perhaps additional ideas etc.)
+* Define at least one vault containing the portal (see below).
+* Define at least one destination map (see below).
+* Add a short in-game description for the string "desc" (see below) to
+ dat/descript/features.txt.
+
+Before going into the details of portal vault creation, some words about
+their uses: Portal vaults are different from branches in that they are
+not guaranteed. Also, there is only one go at a portal vault - if you
+leave, it's gone for good. You can apply special rules to a portal vault,
+like enforcing maprot.
+
+Portal vaults can be particulary thematic, using specialised monster
+sets, fitting loot, coloured dungeon features etc. Avoid death traps; it
+is no fun to enter a vault, being unable to leave and be killed outright.
+In order to provide fun and reduce spoiler effects, randomise. For portal
+vaults, it is desirable to have several different layouts (ideally each
+of the maps has some randomisation on its own). Often, it is a good idea
+to skew the map distribution: e.g. with four destination vaults, weights
+like 40,30,20,10 might be more interesting than 25,25,25,25.
+
+In order to test a portal vault, you can either use PLACE: D:2 for an
+entry vault, or use the wizard mode command &L for conjuring up the entry.
+
+Define a vault to hold the portal itself
+----------------------------------------
+
+# Bare-bones portal vault entry
+NAME: portal_generic_entry
+TAGS: allow_dup
+ORIENT: float
+MARKER: O = lua:one_way_stair { desc = "A portal to places unknown", \
+ dst = "generic_portal" }
+KFEAT: O = enter_portal_vault
+MAP
+O
+ENDMAP
+
+Portal entries must contain a portal vault entry (enter_portal_vault).
+This feature must always have a marker that provides the portal with a
+description ("A portal to places unknown") and a destination
+("generic_portal").
+
+In case you want to make sure that the portal vault entry is only used
+once, you add a TAGS: uniq_BAR line. It should be noted that the label
+BAR may *not* end in _entry (otherwise the level builder assumes that
+the vault is a branch entry).
+
+If you want the place name displayed while in the vault to be different
+than the destination name, then you can give one_way_stair() a "dstname"
+parameter. If you want the place origin for items in a character
+dump to be different than the default you can give one_way_stair a
+"dstorigin" parameter (i.e., dstname = "garden", dstorigin = "in the gardens").
+If you want the place name abbreviation used when displaying notes to be
+different than than the default you can use the "dstname_abbrev" parameter.
+
+You can dynamically change the origin string using the lua function
+dgn.set_level_type_origin(), and dynamically change the place name
+abbreviation with dgn.set_set_level_name_abbrev().
+
+Known portal vault entries will be displayed on the overmap. By default
+the name shown on the overmap will be the "dstname" parameter, or if
+that isn't present the "dst" paremeter. It can be set to something else
+with the "overmap" parameter. A note can be made to accompany the
+portal's position on the overmap with the "overmap_note" parameter.
+
+Bones files for characters killed in the portal vault will normally
+use an extension derived from the first three letters of the 'dst'
+property. You can override this by setting the 'dstext' property to
+your preferred extension.
+
+This will produce a portal, but attempting to use it will trigger an
+ASSERT since there's no map for the destination. So we create a
+destination map like so:
+
+Define a destination map
+------------------------
+
+NAME: portal_generic_generic
+# Tag must match dst value of portal in entry.
+TAGS: generic_portal allow_dup
+ORIENT: encompass
+MONS: ancient lich
+KFEAT: > = exit_portal_vault
+MAP
+xxxxxxxxxxx
+x111111111x
+x1A111111>x
+x111111111x
+xxxxxxxxxxx
+ENDMAP
+
+Note that the entry point into the map will be a stone arch. You must
+provide an exit to the dungeon explicitly (KFEAT: > = exit_portal_vault)
+or the player will not be able to leave.
+
+Stairs will not work right in portal vaults, do not use them.
+
+You can use multiple maps with the destination tag (generic_portal),
+and the dungeon builder will pick one at random.
+
+The MARKER parameters
+---------------------
+
+The lines
+ MARKER: O = lua:one_way_stair { desc = "A portal to places unknown", \
+ dst = "generic_portal" }
+ KFEAT: O = enter_portal_vault
+ensure that an 'O' glyph will be turned into a portal. Upon leaving the portal
+vault, you will be placed on its entry which has been turned into a floor. You
+can turn it into something different (usually an empty stone arch), by adding
+ floor = 'stone_arch'
+to the lua:one_way_stair parameters.
+
+Note that the desc string is what you will see upon examining the portal.
+The dst string is used for Crawl's right hand stat area; it will show
+ Place: generic portal
+in the above example. Here is a lost of the parameters that can be used
+within one_way_stair (taken from icecave.des):
+ desc = "A frozen archway", # description of the portal before entry
+ dst = "ice_cave", # label used for maps and entry vaults
+ dstname = "Ice Cave", # used for PLACE: on the main screen
+ dstname_abbrev = "IceCv", # used in the notes
+ dstorigin = "in an ice cave", # appendix for items picked up there
+ overmap = "frozen archway", # used on the overmap (X)
+ floor = "stone_arch" # feature left after escaping the portal
+
+The dst string is also used to link the destination maps to the entry maps.
+In case dstname is missing, dst will be used.
+
+You can replace lua:one_way_stair by lua:timed_marker in order to make timed
+portal vaults (which will disappear after some time). bazaar.des and lab.des
+contain examples. For timed portals, you may want to add messages to the file
+dat/clua/lm_tmsg.lua.
+
+Using lua functions as shortcuts
+--------------------------------
+
+If you are making several entry and destination vaults, you will note a
+lot of duplicated header statements. This can be lessened using lua.
+Define a lua block right at the top (after your comments) as follows:
+
+{{
+function generic_portal(e)
+ e.marker([[O = lua:one_way_stair { desc = "A portal to places unknown",
+ dst = "generic_portal",
+ floor = "stone_arch" }]])
+ e.kfeat("O = enter_portal_vault")
+ e.colour("O = magenta")
+end
+}}
+
+Instead of the MARKER and KFEAT lines introduced above you now just use
+ :generic_portal(_G)
+and the resulting portal glyphs will even be magenta!
+
+Defining a random monster set
+-----------------------------
+
+Portal vaults require a defined random monster set to make the Shadow
+Creatures spell work. This is done by calling dgn.set_random_mon_list()
+manually. Here's an example from ice_cave_small_02 in icecave.des:
+ : dgn.set_random_mon_list("ice beast w:90 / ice dragon / nothing")
+You can use "nothing" to have the spell fail sometimes.
+
+If you are using the same random monster list in several destination maps,
+you can define a lua block and call it from the destination map definition.
+This example is from sewer.des:
+
+{{
+function sewer_random_monster_list(e)
+ e.set_random_mon_list("giant bat w:20 / giant newt w:20 / small snake / \
+ ooze / worm / snake / giant mosquito w:15")
+end
+}}
+
+You can then use this line in the map definition to execute the lua block:
+ : sewer_random_monster_list(_G)
+
+You can also set env.spawn_random_rate() to have monsters generated from the
+list during play.
+
+Milestones for portal vaults
+----------------------------
+
+This example is from icecave.des, defined in the lua preludes:
+
+{{
+function ice_cave_milestone(e)
+ crawl.mark_milestone("br.enter", "entered an Ice Cave.")
+end
+}}
+
+The function is called from each of the destination map definitions:
+
+: ice_cave_milestone(_G)
+
+This marks down entering the portal vault in the milestones. When the portal
+is entered, the destination map is chosen and the call to crawl.mark_milestone
+is executed along with the rest of the map definition.
+
+Adding milestones in a .des does have the slight catch of creating multiple
+milestones if the map fails validation for some reason, so it's best used
+in maps that will never fail validation.
+
+
+L. Lua reference
+===================
+
+How maps are processed
+----------------------
+
+Under the hood, Crawl translates everything in a .des file to Lua. You
+don't need to know what the underlying Lua looks like to design
+levels, but it helps.
+
+Crawl uses Lua 5.1 from http://www.lua.org (the site has information
+on the Lua language). Let's examine how Crawl converts a map
+definition into Lua code with an example map:
+
+NAME: statue_in_pool
+TAGS: no_rotate no_pool_fixup
+: if you.absdepth() < 7 then
+MONS: plant
+: else
+MONS: oklob plant
+: end
+MAP
+1...1
+.www.
+.wGw.
+.www.
+1...1
+ENDMAP
+
+Crawl will convert this map into the following Lua code, wrapped in an
+anonymous function (this is called a Lua chunk):
+
+function ()
+ tags("no_rotate")
+ tags("no_pool_fixup")
+ if you.absdepth() < 7 then
+ mons("plant")
+ else
+ mons("oklob plant")
+ end
+ map(".....")
+ map(".www.")
+ map(".wGw.")
+ map(".www.")
+ map(".....")
+end
+
+If your level defines prelude or validation Lua code, such code is
+extracted into separate prelude and validation chunks. The prelude and
+validation chunks are empty unless specified.
+
+Apart from the special NAME map header, every map header translates to
+a Lua function with the same name in lowercase. For instance, KFEAT:
+<xyz> is translated into kfeat("<xyz>").
+
+If you have a space or comma separated list (such as TAGS, MONS, ITEM,
+etc.), then each space/comma separated item is passed into a separate
+call to the corresponding Lua function. For instance:
+
+TAGS: no_rotate no_pool_fixup
+->
+tags("no_rotate")
+tags("no_pool_fixup")
+
+MONS: orc, gnoll
+->
+mons("orc")
+mons("gnoll")
+
+Knowing what the generated Lua looks like under the hood is useful
+because it allows you to extract repeated boilerplate in similar
+vaults into a Lua function in the .des file's prelude. For instance,
+if you were planning to write a whole slew of vaults featuring statues
+in water guarded by plants, you could extract the common code into the
+top of the .des file as:
+
+# This block has to be placed before any other vault in the .des file.
+{{
+function statue_pool_map(e)
+ e.tags("no_rotate")
+ e.tags("no_pool_fixup")
+ if you.absdepth() < 7 then
+ e.mons("plant")
+ else
+ e.mons("oklob plant")
+ end
+end
+}}
+
+NAME: statue_in_pool
+# Pass in the Lua environment global _G to the prelude function.
+: statue_pool_map(_G)
+MAP
+1...1
+.www.
+.wGw.
+.www.
+1...1
+ENDMAP
+
+You can also use arbitrary Lua directly in vault definitions, which is
+handy when randomizing things:
+
+NAME: statue_in_pool
+: local plant_weight = crawl.random_range(1,10)
+: mons("plant w:" .. plant_weight ..
+: " / oklob plant w:" .. (10 - plant_weight))
+MAP
+1...1
+.www.
+.wGw.
+.www.
+1...1
+ENDMAP
+
+How Lua chunks are associated with a C++ map object
+---------------------------------------------------
+
+A map's Lua chunk consists of calls to functions such as tags(),
+mons(), etc. These functions are defined in the dgn table (see the Lua
+API reference below), and they expect to act on an instance of Crawl's
+C++ mapdef object. Given:
+
+tags("no_rotate")
+
+the actual Lua call needs to be:
+
+dgn.tags(<map>, "no_rotate")
+
+Where <map> is the C++ map object to which the tag should be added.
+Since calling dgn.<foo>(<map>, <xxx>) is tedious, dat/clua/dungeon.lua
+wraps the Lua chunk for the map into an environment that defines
+wrappers for all the functions in 'dgn' as:
+
+ function <xyz>(...)
+ dgn.<xyz>(<map>, ...)
+ end
+
+i.e. for every function <xyz> in the 'dgn' table, we define a new
+function <xyz> that just calls dgn.<xyz>() with the current map as the
+first parameter, and the other parameters as passed in. Thus Lua code
+that you write as:
+
+tags("no_rotate")
+
+is translated to the correct dgn.tags(<map>, "no_rotate").
+
+While this is done automatically for map code, if you need to call Lua
+code that was not defined in the scope of the map, as in the example
+statue_pool_map() function, you need to pass in the map environment to
+that function if you want it to modify the map. Thus the call to
+statue_pool_map looks like:
+
+: statue_pool_map(_G)
+
+Steps involved in processing .des files
+---------------------------------------
+
+* Level files are compiled into a series of Lua chunks. Each map can
+ have one or more Lua chunks associated with it: the prelude, the
+ body, and a validation chunk. The body is mandatory, but validation
+ and prelude chunks are necessary only if your map needs validation
+ or fancy selection criteria.
+
+* When first compiling a .des file, Crawl compiles each map's Lua
+ chunks, then compiles and runs the prelude, body and validation
+ immediately to verify that the Lua code is not broken. Lua errors at
+ this stage will cause Crawl to exit with an error message (hopefully
+ relevant). Note that the validation Lua chunk's return code is
+ completely ignored at this stage - it is only run to check for
+ syntax errors in the code.
+
+* When a new game is started, Crawl will run the Lua preludes for all
+ maps (most maps should have no prelude - map preludes slow the game
+ down). At this point, preludes can change the map's placement or
+ availability.
+
+* When the dungeon builder selects a map (based on TAGS, DEPTH,
+ PLACE), it re-runs the map prelude and the map body, applies
+ transforms (SUBST, SHUFFLE) if any, then calls the map's validation
+ Lua. If the map passes validation, the dungeon builder continues
+ with level-generation; otherwise, it restarts from the map prelude.
+
+The global prelude
+------------------
+
+Every .des file can have (at the start of the file) Lua code that is
+not associated with any specific map, but with all maps in the file.
+This is called the global prelude. The global prelude is run before
+running any other Lua code in the file, once during compilation, and
+once at start of game.
+
+You can use the global prelude to define functions and set up globals
+that the rest of the maps in the .des file use. If you have a lot of
+common code, you should probably add it to dungeon.lua instead.
+
+Syntax for using Lua in .des files
+----------------------------------
+
+* Colon-prefixed lines are individual Lua lines, extending to the end
+ of the line. E.g.
+
+ : crawl.mpr("Hello")
+
+ Colon-prefixed lines are always in the main Lua chunk, unless they occur
+ before any map definitions, in which case they go to the global prelude.
+
+* Lua blocks for the main (body) Lua
+ lua {{ <code> }}
+ or
+ lua {{
+ <code>
+ }}
+ The "lua" word is optional; you can also use:
+ {{ <code> }}
+ and
+ {{
+ <code>
+ }}
+
+NOTE: Colon-prefixed lines, or lua {{ }} blocks defined before any
+map's NAME: directive will add the Lua code to the global prelude.
+
+* Lua blocks for the prelude:
+ prelude {{ <code> }}
+ or
+ prelude {{
+ <code>
+ }}
+
+* Lua blocks for the validate chunk:
+ validate {{ <code> }}
+ or
+ validate {{
+ <code>
+ }}
+
+Debugging Lua
+-------------
+
+Since Lua action happens in the guts of Crawl, it can be hard to tell
+what's going on. Lua debugging involves the time-honoured method of
+peppering your code with print statements:
+
+* Use error() or print() for compile-time work (i.e. when Crawl reads
+ the .des file). Note that print() just writes to the terminal and
+ keeps going, while error() forces Crawl to exit immediately (at
+ compile time; errors during level-generation are handled
+ differently).
+
+* Use crawl.mpr() for output when the game has started (at
+ level-generation time).
+
+It's very important that your finished level never croaks during
+level-generation. A Lua error at this stage is considered a validation
+failure.
+
+Special dungeon-related Lua marker properties
+---------------------------------------------
+
+There are several properties a Lua marker can have which will affect the
+dungeon cell which they are on:
+
+* connected_exclude: Consider the cell to be separate from neighboring
+ cells with identical or similar features. Currently only useful
+ for preventing adjacent doors from grouping together into a gate,
+ forcing them to open and close as separate doors. See the Evil
+ Zoo (minivault_9) in dat/mini.des for an example.
+
+* door_description_suffix: A string to append to the description of
+ any door the marker is on. This should be used for doors
+ rather than the feature_description property since it elemintates
+ the need to track if the door is opened or closed, plus it will
+ have no effect on secret doors which have yet to be detected.
+
+* feature_description: What to use as the short description of the
+ cell's feature.
+
+* feature_description_long: What to use as the long description of the
+ cell's feature.
+
+* stop_explore: If set to anything, and placed on a cell with a statue
+ or orcish idol, will cause auto-explore to stop with the message
+ "Found <whatever>."
+
+* stop_explore_msg: Like stop_explore, but when auto-explore is stopped
+ the content of the property will be printed out as a message.
+
+* veto_disintegrate: If this property is set to "veto" then the cell
+ will be immune to disintegration.
+
+* veto_fragmentation: If this proprety is set to "veto" then the cell
+ will be unaffected by fragmentation (Lee's Rapid Deconstruction
+ spell).
+
+* veto_shatter: If this property is set to "veto" then the cell will
+ be unaffected by the Shatter spell.
+
+Lua API reference
+-----------------
+a. The Map.
+b. Global game state.
+c. Character information.
+
+
+Lua API - the Map
+-----------------
+
+Lua functions dealing with the map are mostly grouped under the "dgn"
+module. For convenience, .des file Lua chunks are run in an environment
+such that function calls written as:
+
+ fn(x, y, ...)
+
+are translated to
+
+ dgn.fn(map, x, y, ...)
+
+where "map" is the reference to the map that the currently executing
+Lua chunk belongs to. This is only for Lua chunks that belong to a
+map, Lua code in the global prelude does not get this treatment
+(because the global prelude is not associated with any map).
+
+Functions in the dgn module:
+
+default_depth, name, depth, place, tags, tags_remove, chance, weight,
+orient, shuffle, shuffle_remove, subst, subst_remove, map, mons, item,
+kfeat, kitem, kmons, grid, points_connected, gly_point, gly_points,
+original_map, glyphs_connected, orig_glyphs_connected, orig_gly_point,
+orig_gly_points, load_des_file, feature_number, feature_name,
+dgn_event_type, register_listener, remove_listener, remove_marker,
+num_matching_markers, feature_desc, feature_desc_at, item_from_index,
+mons_from_index, change_level_flags, change_branch_flags,
+set_random_mon_list
+
+
+Additionally, the dgn module provides a global "mapgrd" variable that
+can access the current map glyphs. The top left symbol in the map
+can be assigned like this:
+
+ mapgrd[1][1] = 'x'
+
+The bottom right symbol can be assigned like this:
+
+ mapgrd[width()][height()] = "."
+
+
+Lua API - global game state
+---------------------------
+
+The "crawl" module provides functions that describe the game state or
+provide utility methods.
+
+mpr, mesclr, random2, coinflip, one_chance_in, redraw_screen,
+input_line, c_input_line, getch, kbhit, flush_input, sendkeys,
+playsound, runmacro, bindkey, setopt, msgch_num, msgch_name, regex,
+message_filter, trim, split, game_started, err_trace, args,
+mark_milestone
+
+
+Lua API - character information
+-------------------------------
+
+The "you" module provides functions that describe the player character.
+
+turn_is_over, spells, abilities, name, race, class, god, hp, mp,
+hunger, strength, intelligence, dexterity, xl, exp, res_poison,
+res_fire, res_cold, res_draining, res_shock, res_statdrain,
+res_mutation, res_slowing, gourmand, levitating, flying, transform,
+stop_activity, floor_items, where, branch, subdepth, absdepth
+
+
+M. Feature Names
+==================
+
+These are the feature names usable in MARKER declarations:
+
+unseen, rock_wall, stone_wall, closed_door, metal_wall, secret_door,
+green_crystal_wall, orcish_idol, wax_wall, permarock_wall,
+silver_statue, granite_statue, orange_crystal_statue,
+statue_reserved_1, statue_reserved_2, lava, deep_water, shallow_water,
+water_stuck, floor, exit_hell, enter_hell, open_door, trap_mechanical,
+trap_magical, trap_iii, undiscovered_trap, enter_shop,
+enter_labyrinth, stone_stairs_down_i, stone_stairs_down_ii,
+stone_stairs_down_iii, escape_hatch_down, stone_stairs_up_i,
+stone_stairs_up_ii, stone_stairs_up_iii, escape_hatch_up, enter_dis,
+enter_gehenna, enter_cocytus, enter_tartarus, enter_abyss, exit_abyss,
+stone_arch, enter_pandemonium, exit_pandemonium, transit_pandemonium,
+builder_special_wall, builder_special_floor, enter_orcish_mines,
+enter_hive, enter_lair, enter_slime_pits, enter_vaults, enter_crypt,
+enter_hall_of_blades, enter_zot, enter_temple, enter_snake_pit,
+enter_elven_halls, enter_tomb, enter_swamp, enter_shoals,
+enter_reserved_2, enter_reserved_3, enter_reserved_4,
+return_from_orcish_mines, return_from_hive, return_from_lair,
+return_from_slime_pits, return_from_vaults, return_from_crypt,
+return_from_hall_of_blades, return_from_zot, return_from_temple,
+return_from_snake_pit, return_from_elven_halls, return_from_tomb,
+return_from_swamp, return_from_shoals, return_reserved_2,
+return_reserved_3, return_reserved_4, enter_portal_vault,
+exit_portal_vault, altar_zin, altar_shining_one, altar_kikubaaqudgha,
+altar_yredelemnul, altar_xom, altar_vehumet, altar_okawaru,
+altar_makhleb, altar_sif_muna, altar_trog, altar_nemelex_xobeh,
+altar_elyvilon, altar_lugonu, altar_beogh, fountain_blue,
+fountain_sparkling, fountain_blood, dry_fountain_blue,
+dry_fountain_sparkling, dry_fountain_blood, permadry_fountain
+
+
+N. Map Statistics
+===================
+
+Full-debug Crawl builds (this does not include normal Crawl builds
+that have wizard-mode - you must build Crawl with "make debug", not
+"make wizard") can produce map generation statistics. To generate
+statistics, run crawl from the command-line as:
+
+crawl -mapstat
+
+This will generate 100 Crawl dungeons and report on the maps used in a
+file named "mapgen.log" in the working directory.
+
+You can change the number of dungeons to generate:
+
+crawl -mapstat 10
+
+Will generate 10 dungeons. If you merely want statistics on the
+probabilities of the random map on each level, use:
+
+crawl -mapstat 1
+
+
+O. Map Generation
+===================
+
+Full-debug Crawl builds (see above for more information) include a test for
+generating specific vaults and outputting a copy of the map to a text file for
+inspection. This is most useful for portal and other encompass vaults which
+use randomisation heavily.
+
+To use the test, you must edit source/test/vault_generation.lua. Fill in the
+following variables:
+
+ * map_to_test: The exact name of the vault you want to generate.
+ * checks: How many times to generate the vault. Default value is 10.
+ * output_to: The basic filename to output the results of generation to. This
+ will have ".<iteration>" appended. For example, "volcano.map" will
+ result in files named "volcano.map.1", "volcano.map.2", etc.
+ * need_to_load_des: If the file is not included in one of the .des files that
+ are listed in source/dat/clua/loadmaps.lua, this should be set to true,
+ and the following variable should be set.
+ * des_file: The name of the file to load. The file should be located in the
+ source/dat folder.
+
+Once you have saved your changes, run crawl:
+
+ crawl -test vault_generation
+
+Once all of the tests have been finished successfully you should find the
+relevant files in your working directory.
diff --git a/crawl-ref/docs/develop/levels/introduction.txt b/crawl-ref/docs/develop/levels/introduction.txt
new file mode 100644
index 0000000000..1066c11583
--- /dev/null
+++ b/crawl-ref/docs/develop/levels/introduction.txt
@@ -0,0 +1,349 @@
+-----------------------------------------------
+How to make levels for Dungeon Crawl Stone Soup
+-----------------------------------------------
+
+Part I: INTRODUCTION
+ ============
+
+Contents: A. Introduction
+ B. Sample map
+ C. The .des files
+ D. Kinds of vaults
+ E. Hints for level makers
+ F. Principles of vault making
+
+This document explains some basics of vault making. For actual vault work,
+you will need to look at syntax.txt. However, by simply looking at the
+example provided here and looking through entry.des, you should be able to
+make changes or small vaults on your own.
+
+
+A. Introduction
+=================
+
+A game like Crawl generally uses random content. This also applies to maps,
+which are made in various ways, but all with a large random component.
+However, for several reasons, it is also useful to have handmade maps: they
+can provide challenges random levels would rarely come up with; they can be
+especially thematic, for example on branch ends. Simple pre-made pieces of
+maps are called "vaults", these can provide challenge, loot, flavour or
+just make the rather big levels of Crawl more interesting.
+
+These vaults come in all sizes, from mere 3x3 to level encompassing maps.
+Section B contains an example vault (not to be used somewhere), giving you
+an idea of how vaults are defined.
+
+Since predefined and fixed content clashes with the philosophy of randomly
+generated content, vaults can be randomised in many ways. The other way to
+help lessen the effect of deja vu is by having lots of vaults. Therefore,
+new vaults are always appreciated.
+
+
+B. Sample Map
+===============
+
+Before going into the technical details of the level-file syntax, it might be
+a good idea to look at an example - a branch entry for the Ecumenical Temple -
+to see what a map definition looks like.
+
+# name below:
+NAME: useless_temple_entry
+# header section below:
+ORIENT: float # "ORIENT: float" tells the level builder that
+ # this entry can be anywhere on the level.
+TAGS: temple_entry # "TAGS: temple_entry" turns the 'O' on the
+MONS: butterfly, plant # map into stairs to the Temple.
+ITEM: stone
+# actual map below: # The symbols on the map:
+MAP # x - rock wall
+xx.d.xx # . - floor
+x..1..x # @ - entry point (
+@d2O2d. # O - stairs to the Temple
+x..1..x # 1 - first monster from list (here butterfly)
+xx.d.xx # 2 - second monster from list (here plant)
+ENDMAP # d - first item from the list (here stones)
+
+Every map consists of a name, a header and the actual map (the order is not
+important as long as the name comes first, but try to stick to this order for
+consistency).
+
+Lines starting with # are comments. The keywords available are explained
+in detail in the syntax.txt file.
+
+
+C. The .des files
+===================
+
+All fixed level information resides in various .des files to be found in the
+dat directory. If you are interested in adding some vaults, say, start with
+the existing ones and modify them. If you are new to vault making, look at
+
+ entry.des - entry vaults (each game - but not tutorial games - uses one of
+ these premade maps for the vicinity of the entrance)
+ float.des - floating vaults
+ large.des - all regular vaults which have ORIENT:encompass/northwest etc
+ mini.des - minivaults (no ORIENT line at all)
+
+The following contain vaults for special branches:
+
+ crypt.des - random vaults for Crypt, the Crypt:5 branch ends, and the
+ predefined maps for Tomb:1, Tomb:2 and Tomb:3
+ elf.des - arrival and random vaults for Elf, and Elf:7 branch ends
+ hells.des - hell entrances, Geryon's vestibule, Coc:7, Tar:7, Dis:7, Geh:7
+ hive.des - hive entrances, random hive vaults, Hive:2
+ lair.des - lair entrances, random vaults for lair proper and sub-branches,
+ and the branch ends: Shoals:5, Swamp:5, Snake:5, Slime:6
+ orc.des - orcish mine entrances, orc only vaults
+ pan.des - vaults of the Pan demon lords, Pan minivaults
+ temple.des - Ecumenical Temples, and Temple entrances
+ vaults.des - entrances for the Vaults, Vaults-specific vaults, the
+ Hall of Blades, and Vaults:8
+ zot.des - vaults for the Zot branch and the maps for Zot:5
+
+There are a number of portal vaults. The following files contain a lot more
+technology. Look into them if you want to make portal vaults of your own, or
+just see lua applied.
+
+ altar.des - minivaults containing altars
+ bailey.des - the bailey portal vault
+ bazaar.des - entrances to bazaar portal vaults, and bazaars proper
+ icecave.des - the ice cave portal vault
+ lab.des - labyrinths exits and flavour vaults
+ ossuary.des - the ossuary portal vault
+ sewer.des - the sewer portal vault
+ shrine.des - the shrine portal vault
+ volcano.des - the volcano portal vault
+ ziggurat.des - the ziggurat entry vaults, pillars and arenas
+
+The following .des files are rather special. Look, don't touch.
+
+ arena.des - arenas for the arena mode
+ didact.des - lua and vaults for syntax checking - not used in-game
+ dummy.des - global dummy balancers
+ layout.des - level layout code that has been moved from dungeon.cc and
+ rewritten in Lua.
+ rooms.des - special monster rooms, such as orc, kobold and jelly rooms
+
+
+D. Kinds of Vaults
+====================
+
+The different kinds of vaults used by Crawl are described below.
+
+Entry vault:
+ A map designed for D:1, which (usually) contains the primary upstair {
+and is always tagged "entry". A player starting a new game will usually land
+in an entry vault.
+
+Branch entry vault, or branch portal vault:
+ A map containing the entry to a branch - either a branch stair (such as
+the stair to the Orcish Mines), or a branch portal (a portal to Hell, say).
+Always tagged "<branchname>_entry".
+
+Special level:
+ A map for a location of significance in the game, such as the Ecumenical
+Temple, or the end of branches such as level 5 of the Snake Pit (Snake:5).
+Special level maps usually have a PLACE: attribute.
+
+Random vaults:
+ Random vaults may be randomly generated at any level in the dungeon.
+Random vault maps are selected by the dungeon builder based on their DEPTH:
+attributes.
+
+Random minivaults:
+ 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 by the absence of an
+ORIENT: declaration. Any map without a specified ORIENT: is a
+minivault. Minivaults are handled like floating vaults (ORIENT: float
+vaults) in most respects. The differences are:
+
+1. Floating vaults may be placed before the rest of the level layout
+ is generated, and the rest of the level may be built around the floating
+ vault. This is never the case for minivaults.
+
+2. Floating vaults may be placed anywhere in the map, including places
+ completely separated from the rest of the level by rock. The
+ dungeon builder will then connect the exits from the floating vault
+ to the rest of the level, usually producing obvious "passages" from
+ the floating vault to the main body of the level.
+
+ In contrast, minivaults are placed such that at least one square of
+ the minivault overlaps with an existing part of the level, and are
+ thus more likely to look like part of the level. Unlike floating
+ vaults, the dungeon builder assumes that any one square of overlap
+ is enough to connect the minivault to the rest of the level and
+ makes no effort to connect exits from the minivault to the level.
+ You can ask the dungeon builder to connect exits from your
+ minivault with the "mini_float" tag.
+
+
+E. Hints for level makers
+===========================
+
+Stairs
+------
+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 (these
+are glyphs > and < and produce escape hatches). 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 > or < to
+make it possible for players to get away, although there are other,
+more elaborate options like teleporation traps, etc.). However, unless
+needed for the purpose, escape hatches are generally not interesting.
+If you do use them, > is almost always better than <.
+
+Entry points
+------------
+Minivaults can use explicit @ exits, or be completely surrounded by
+one space of floor for accessibility. Alternatively, you can request
+that the dungeon builder pick appropriate exits as it does for
+floating vaults by using the "mini_float" tag.
+
+The entry point '@' must be present for all vaults (except for
+full-screen vaults where it must not, and for floating vaults and
+minivaults 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 use one or more random floor spaces '.' or doors at the
+circumference as exits. Note that it is not possible to predict
+which 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. :)
+
+Minivaults vs. random vaults
+----------------------------
+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, although
+using a PLACE: attribute makes the dungeon builder try very hard to place
+the minivault on the specified level. Regular vaults can always be forced to
+appear using a PLACE: attribute.
+
+Technically, you make a minivault like a normal floating vault but
+without an ORIENT: line. Note that minivaults used to be exclusively of
+size 12x12 but this restriction is gone. Still, the smaller the better.
+
+Where possible, use minivaults instead of regular vaults, because the dungeon
+builder has greater freedom with the rest of the level layout when using
+minivaults.
+
+levdes.vim
+----------
+If you use vim, the levdes.vim syntax highlighting script (provided
+in the dat directory) can make level-editing far more pleasant by colouring
+different features in maps and syntax-highlighting .des-file syntax. vim is
+available for nearly all operating systems, including Windows.
+
+
+F. Principles of vault making
+===============================
+
+Small is good!
+--------------
+This applies mostly to entry vaults and minivaults. Especially entry vaults
+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. Minivaults should
+be rather small, as well, in order to increase the chances they may actually
+be chosen during level generation.
+
+Randomise!
+----------
+The level making syntax is very supportive for making a single map appear
+in many versions. Use the SHUFFLE: and SUBST: and NSUBST: directives and
+look at the existing entry vaults. Besides reducing tedium, this avoids
+giving veterans a spoiled edge. As an example, if a secret chamber with
+loot is always at the same place, it's a no-brainer for those who know.
+The same goes for traps. This is much less so if there are several places
+for the chamber (or trap) and there's even a chance it doesn't exist.
+
+You can also use WEIGHT to create modified versions of the same map. In
+order to do this, make several maps and endow each with a chance such
+that the sum of chances add up to 10.
+
+Randomisation does not just apply to layout: you could also have
+different monster population sets (for example make a branch end skewed
+for either melee or ranged opponents), or perhaps couple difficulty to
+loot.
+
+Not too much loot!
+------------------
+For example, entry vaults should in general have very little loot - in
+particular no good_xxx or '*' items lest they might give incentive for
+start-scumming. For random vaults, there needn't be loot at all and, in
+any case, there shouldn't be too much of it. Compare with the branch ends
+rich in treasure (Tomb:3, Cocytus etc.) to get a feeling for this.
+
+It is very easy to fall into the power spiral trap: making newer vaults
+harder than the old ones, but handing out more loot. Be strong, don't
+give in to that temptation.
+
+Have a theme.
+-------------
+It is often worthwhile (for me at least) to have a theme in mind before
+making the actual level. For entry vaults, something simple like 'forest'
+or 'fortress' may be enough. For later (or larger) maps, try to think of
+distinguishing features your map may have. Being cool can be good enough,
+but possessing some gameplay value (for example by being easier for
+particular skills/capabilities like ranged attacks or Traps & Doors or
+necromancy) is even better.
+
+Testing your maps.
+------------------
+This is easy for entry vaults. Temporarily introducing either
+ WEIGHT: 50000 or
+ PLACE: D:1
+will make your entry appear almost always. For other vaults, you can
+for the moment declare them as entry vaults with a huge WEIGHT: as
+above (and preferably in wizard mode).
+
+In wizmode, you can use the following commands
+ &L "map name" (creates that vault on the current level)
+ &P "map name" (for playtesting portal vaults)
+ &~ (for jumping to a branch end).
+For both &L and &P, you don't need to specify the full name of the vault,
+a substring which uniquely determines the vault is enough. If a branch end
+has several alternatives, you may want to use a high WEIGHT: again.
+
+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.
+
+Be fair!
+--------
+Crawl is hard but try to balance your monsters. While it is true that Orc:1
+can show an orcish knight, this is very rare. Hence it's probably a bad idea
+to use orcish knights for an entry to the Orcish Mines.
+
+Phrased more generally, do not use OOD (out-of-depth) monsters unless you
+really know what you want.
+
+Be especially fair when creating entry vaults. If your entry is too hard,
+it might get just trashed. Keep in mind that your vault will be played
+very very often, so even small chances of something stupid happening
+(like creation of a really nasty monster) will kick in often enough.
+
+Glass, colours, names: use sparingly!
+-------------------------------------
+It can be very tempting to use the cool new features all over the place:
+transparent walls for looking through, colouring walls and floor in exotic
+ways, giving monsters special names.
+Each of these have their uses, but ask yourself each time if the vaults
+really benefits from this. If all vaults use colours, actual flavour has
+been lost.
diff --git a/crawl-ref/docs/develop/level_design.txt b/crawl-ref/docs/develop/levels/syntax.txt
index 08edd20a1a..bd3e8b5000 100644
--- a/crawl-ref/docs/develop/level_design.txt
+++ b/crawl-ref/docs/develop/levels/syntax.txt
@@ -2,152 +2,19 @@
How to make levels for Dungeon Crawl Stone Soup
-----------------------------------------------
-Contents: A. Introduction
- B. Sample map
- C. Map symbols
- D. Header information
- E. Conditionalising levels
- F. Validating levels
- G. Hints for level makers
- H. Portal vaults
- I. Lua reference
- J. Feature names
- K. Map statistics
- L. Map generation
-
-
-A. Introduction
-=================
-
-All fixed level information resides in various .des files to be found in the
-dat directory. If you are interested in adding some vaults, say, start with
-the existing ones and modify them. Currently, the following .des files are in
-use:
-
- altar.des - minivaults containing altars
- arena.des - arenas for the arena mode
- bailey.des - the bailey portal vault
- bazaar.des - entrances to bazaar portal vaults, and bazaars proper
- crypt.des - random vaults for Crypt, the Crypt:5 branch ends, and the
- predefined maps for Tomb:1, Tomb:2 and Tomb:3
- didact.des - lua and vaults for syntax checking - not used in-game
- dummy.des - global dummy balancers
- elf.des - arrival and random vaults for Elf, and Elf:7 branch ends
- entry.des - entry vaults (each game - but not tutorial games - uses one of
- these premade maps for the vicinity of the entrance)
- float.des - floating vaults
- hells.des - hell entrances, Geryon's vestibule, Coc:7, Tar:7, Dis:7, Geh:7
- hive.des - hive entrances, random hive vaults, Hive:2
- icecave.des - the ice cave portal vault
- lab.des - labyrinths exits and flavour vaults
- lair.des - lair entrances, random vaults for lair proper and sub-branches,
- and the branch ends: Shoals:5, Swamp:5, Snake:5, Slime:6
- large.des - all regular vaults which have ORIENT:encompass/northwest etc
- layout.des - level layout code that has been moved from dungeon.cc and
- rewritten in Lua.
- mini.des - minivaults (no ORIENT line at all)
- orc.des - orcish mine entrances, orc only vaults
- ossuary.des - the ossuary portal vault
- pan.des - vaults of the Pan demon lords, Pan minivaults
- rooms.des - special monster rooms, such as orc, kobold and jelly rooms
- sewer.des - the sewer portal vault
- shrine.des - the shrine portal vault
- temple.des - Ecumenical Temples, and Temple entrances
- vaults.des - entrances for the Vaults, Vaults-specific vaults, the
- Hall of Blades, and Vaults:8
- volcano.des - the volcano portal vault
- ziggurat.des - the ziggurat entry vaults, pillars and arenas
- zot.des - vaults for the Zot branch and the maps for Zot:5
-
-Kinds of Vaults
----------------
-The different kinds of vaults used by Crawl are described briefly below.
-
-Entry vault:
- A map designed for D:1, which (usually) contains the primary upstair {
-and is always tagged "entry". A player starting a new game will usually land
-in an entry vault.
-
-Branch entry vault, or branch portal vault:
- A map containing the entry to a branch - either a branch stair (such as
-the stair to the Orcish Mines), or a branch portal (a portal to Hell, say).
-Always tagged "<branchname>_entry".
-
-Special level:
- A map for a location of significance in the game, such as the Ecumenical
-Temple, or the end of branches such as level 5 of the Snake Pit (Snake:5).
-Special level maps usually have a PLACE: attribute.
-
-Random vaults:
- Random vaults may be randomly generated at any level in the dungeon.
-Random vault maps are selected by the dungeon builder based on their DEPTH:
-attributes.
-
-Random minivaults:
- 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 by the absence of an
-ORIENT: declaration. Any map without a specified ORIENT: is a
-minivault. Minivaults are handled like floating vaults (ORIENT: float
-vaults) in most respects. The differences are:
-
-1. Floating vaults may be placed before the rest of the level layout
- is generated, and the rest of the level may be built around the floating
- vault. This is never the case for minivaults.
-
-2. Floating vaults may be placed anywhere in the map, including places
- completely separated from the rest of the level by rock. The
- dungeon builder will then connect the exits from the floating vault
- to the rest of the level, usually producing obvious "passages" from
- the floating vault to the main body of the level.
-
- In contrast, minivaults are placed such that at least one square of
- the minivault overlaps with an existing part of the level, and are
- thus more likely to look like part of the level. Unlike floating
- vaults, the dungeon builder assumes that any one square of overlap
- is enough to connect the minivault to the rest of the level and
- makes no effort to connect exits from the minivault to the level.
- You can ask the dungeon builder to connect exits from your
- minivault with the "mini_float" tag.
-
-
-
-B. Sample Map
-===============
-
-Before going into the technical details of the level-file syntax, let's look
-at an example - a branch entry for the Ecumenical Temple - to see what a map
-definition looks like.
-
-# name below:
-NAME: useless_temple_entry
-# header section below:
-ORIENT: float # "ORIENT: float" tells the level builder that
- # this entry can be anywhere on the level.
-TAGS: temple_entry # "TAGS: temple_entry" turns the 'O' on the
-MONS: butterfly, plant # map into stairs to the Temple.
-ITEM: stone
-# actual map below: # The symbols on the map:
-MAP # x - rock wall
-xx.d.xx # . - floor
-x..1..x # @ - entry point (
-@d2O2d. # O - stairs to the Temple
-x..1..x # 1 - first monster from list (here butterfly)
-xx.d.xx # 2 - second monster from list (here plant)
-ENDMAP # d - first item from the list (here stones)
-
-Every map consists of a name, a header and the actual map (the order is not
-important as long as the name comes first, but try to stick to this order for
-consistency).
-
-Lines starting with # are comments. The keywords available are explained
-in detail in the following sections.
-
-
-C. Map symbols
-================
+Part II: SYNTAX
+ ======
+
+Contents: G. Glyphs
+ H. Header information
+
+This document contains the syntax needed for making maps and vaults. It
+does not say anything about principles of vault making; for this, see
+introduction.txt. For more technical aspects, including tests, lua and
+portal vaults, refer to advanced.txt.
+
+G. Glyphs
+===========
Terrain
-------
@@ -238,7 +105,7 @@ Monsters
information
-D. Header information
+H. Header information
=======================
(All declarations apart from NAME: are translated to Lua function calls
@@ -1162,1012 +1029,3 @@ entire group of glyphs being assigned. For example:
NSUBST: A = 1:. / *:x
NSUBST: B = 1:. / *:x
NSUBST: C = 1:. / *:x
-
-E. Conditionalising levels
-=============================
-
-Crawl translates level (.des) files into Lua code chunks and runs these chunks
-to produce the final level that is generated. While you don't need to use Lua
-for most levels, using Lua allows you to conditionalise or randomise levels
-with greater control.
-
-Let's take a simple example of randomisation:
-
-NAME: random_test
-# Put it on D:1 so it's easy to test.
-PLACE: D:1
-ORIENT: float
-MAP
-xxxxxxxxxxxxxxxxxxx
-x........{........x
-xxxAxxxxxBxxxxxCxxx
-xxx.xxxxx.xxxxx.xxx
-xxx@xxxxx@xxxxx@xxx
-ENDMAP
-
-Now let's say you want A, B, and C to be randomly rock or floor, but B should
-be floor if both A and C are rock. Here's one way to do it (add these lines
-to the map definition):
-
-: local asolid, csolid
-: if crawl.random2(2) == 0 then
-: asolid = true
-: subst("A = x")
-: else
-: subst("A = .")
-: end
-: if crawl.random2(2) == 0 then
-: csolid = true
-: subst("C = x")
-: else
-: subst("C = .")
-: end
-: if asolid and csolid then
-: subst("B = .")
-: else
-: subst("B = .x")
-: end
-
-This code uses crawl.random2(N) which returns a number from 0 to N-1
-(in this case, returns 0 or 1). So we give A a 50% chance of being
-rock, and the same for C. If we made both A and C rock, we force B to
-be floor, otherwise we use a subst that gives B the same 50% chance of
-being rock.
-
-You can conditionalise on various factors, such as player experience
-level:
-
-NAME: condition_002
-DEPTH: 1-27
-ORIENT: float
-: if you.xl() > 18 then
-MONS: greater mummy
-: else
-MONS: deep elf priest / deep elf sorcerer / deep elf demonologist
-: end
-MAP
-xxxxxx
-x1...x
-x1...+
-x1...x
-xxxxxx
-ENDMAP
-
-Or based on where the map is being generated:
-
-NAME: condition_003
-DEPTH: Elf:*, Orc:*
-ORIENT: float
-: if you.branch() == "Orc" then
-MONS: orc priest, orc high priest
-: else
-MONS: deep elf priest, deep elf high priest
-: end
-MAP
-xxxxxx
-x1...x
-x2...+
-x1...x
-xxxxxx
-ENDMAP
-
-When conditionalising maps, remember that your Lua code executes in
-two contexts:
-
-1) An initial compilation phase before the game starts.
-2) The actual mapgen phase when the dungeon builder is at work.
-
-In context (1), you will not get useful answers from the Crawl Lua API
-in general, because the game hasn't started. This is generally
-ignorable (as in the case above) because the compilation phase just
-checks the syntax of your Lua code. If you conditionalise your map,
-however, you may run into compile failures. Take this variant, which
-(incorrectly) attempts to conditionalise the map:
-
-NAME: condition_004
-DEPTH: Elf:*, Orc:*
-ORIENT: float
-: if you.branch() == "Orc" then
-MONS: orc priest, orc high priest
-MAP
-xxxxxx
-x1...x
-x2.I.+
-x1...x
-xxxxxx
-ENDMAP
-: elseif you.branch() == "Elf" then
-MONS: deep elf priest, deep elf high priest
-MAP
-xxxxxx
-x1...x
-x2.U.+
-x1...x
-xxxxxx
-ENDMAP
-: end
-
-This map will break the compile with the cryptic message "Must define
-map." (to compound the confusion, the line number for this error will
-be the first line number of the map following the buggy map).
-
-This error is because although the map is Elf or Orc only, at compile
-time, the branch is *neither* Elf nor Orc, so the level-compiler
-thinks you've neglected to define a map.
-
-Lua code can detect the compile phase using crawl.game_started() which
-returns true only when the player has started a game (and will return
-false when the map is being initially compiled).
-
-For more details on the available Lua API and syntax, see the Lua
-reference section.
-
-
-F. Validating levels
-=======================
-
-If you have a map with lots of transforms (SUBST and SHUFFLE), and
-want to guarantee that the map is sane after the transforms, you can
-use a validation hook.
-
-To take a very contrived example:
-
-NAME: contrived_001
-PLACE: D:2
-ORIENT: float
-TAGS: no_pool_fixup
-SUBST: .=.w
-SUBST: c=x.
-MAP
-xxxxxx
-x{.+.c
-x..+>x
-xxxxxx
-ENDMAP
-
-This map has a chance of leaving the player stuck on the upstair
-without access to the rest of the level if the two floor squares near
-the doors are substituted with deep water (from the SUBST line), or
-the 'c' glyph is substituted with rock. Since a cut-off vault is
-uncool, you can force connectedness with the rest of the level:
-
-validate {{ return has_exit_from_glyph('{') }}
-
-The has_exit_from_glyph() function returns true if it is possible to
-leave the vault (without digging, etc.) from the position of the {
-glyph. (This takes things like the merfolk ability to swim into
-account, so a merfolk character may see deep water between the stair
-and door.)
-
-The validate Lua returns false (or nil) to indicate that the map is
-invalid, which will force the dungeon builder to reapply transforms
-(SUBST and SHUFFLE) and validate the map again. If the map fails
-validation enough times, the dungeon builder will discard the entire
-level and retry (this may cause a different map to be selected,
-bypassing the buggy map).
-
-Going back to the example, if you just want to ensure that the player
-can reach the > downstair, you can use:
-
-validate {{ return glyphs_connected('{', '>') }}
-
-NOTE: You cannot use the colon-prefixed syntax for validation Lua. If
-you have a big block of code, use the multiline syntax:
-
-validate {{
- -- This level is always cool.
- crawl.mpr("This level is guaranteed perfect!")
- return true
-}}
-
-
-G. Hints for level makers
-============================
-
-* Technical stuff:
-
- 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 (use at least > or < to make it possible for players
- to get away).
-
- Minivaults can use explicit @ exits, or be completely surrounded by
- one space of floor for accessibility. Alternatively, you can request
- that the dungeon builder pick appropriate exits as it does for
- floating vaults by using the "mini_float" tag.
-
- The entry point '@' must be present for all vaults (except
- full-screen vaults where it must not, and floating vaults and
- minivaults 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 use one or more random floor spaces '.' or doors at the
- circumference as exits. Note that it is not possible to predict
- which 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. :)
-
- 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.
- Minivaults should be rather small, as well, in order to increase the
- chances they may actually be chosen during level generation.
-
-* Randomise!
- The level making syntax is very supportive for making a single map appear
- in many versions. Use the SHUFFLE: and SUBST: and NSUBST: directives and
- look at the existing entry vaults. Besides reducing tedium, this avoids
- giving veterans a spoiled edge. As an example, if a secret chamber with
- loot is always at the same place, it's a no-brainer for those who know.
- The same goes for traps. This is much less so if there are several places
- for the chamber (or trap) and there's even a chance it doesn't exist.
-
- You can also use WEIGHT to create modified versions of the same map. In
- order to do this, make several maps and endow each with a chance such
- that the sum of chances add up to 10.
-
- Randomisation does not just apply to layout: you could also have
- different monster population sets (for example make a branch end skewed
- for either melee or ranged opponents), or perhaps couple difficulty to
- loot.
-
-* Not too much loot.
- For example, entry vaults should in general have very little loot - in
- particular no good_xxx or '*' items lest they might give incentive for
- start-scumming. For random vaults, there needn't be loot at all and, in
- any case, there shouldn't be too much of it. Compare with the branch ends
- rich in treasure (Tomb:3, Cocytus etc.) to get a feeling for this.
-
-* Have a theme.
- It is often worthwhile (for me at least) to have a theme in mind before
- making the actual level. For entry vaults, something simple like 'forest'
- or 'fortress' may be enough. For later (or larger) maps, try to think of
- distinguishing features your map may have. Being cool can be good enough,
- but possessing some gameplay value (for example by being easier for
- particular skills/capabilities like ranged attacks or Traps & Doors or
- necromancy) is even better.
-
-* Testing your maps.
- This is easy for entry vaults. Temporarily introducing a WEIGHT: 50000
- will make your entry appear almost always. For other vaults, you can
- for the moment declare them as entry vaults with a huge WEIGHT: as
- above (and preferably in wizard mode). For more intricate things like
- 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 WEIGHT: again, if the map has alternatives (like Snake:5, or
- 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.
-
- Vaults can be conjured up in wizard mode using the &L command. You don't
- need to specify the full name of the vault, a substring which uniquely
- determines the vault is enough. You can playtest portal vaults using the &P
- wizard command. Branch ends can be conveniently tested with the &~ command.
-
- 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.
-
-* Be fair!
- Crawl is hard but try to balance your monsters. While it is true that Orc:1
- can show an orcish knight, this is very rare. Hence it's probably a bad idea
- to use orcish knights for an entry to the Orcish Mines.
-
- Phrased more generally, do not use OOD (out-of-depth) monsters unless you
- really know what you want.
-
- Be especially fair when creating entry vaults. If your entry is too hard,
- it might get just trashed. Keep in mind that your vault will be played
- very very often, so even small chances of something stupid happening
- (like creation of a really nasty monster) will kick in often enough.
-
-* Minivaults vs. random vaults.
- 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, although
- using a PLACE: attribute makes the dungeon builder try very hard to place
- the minivault on the specified level. Regular vaults can always be forced to
- appear using a PLACE: attribute.
-
- Technically, you make a minivault like a normal floating vault but
- without an ORIENT: line. Note that minivaults used to be exclusively of
- size 12x12 but this restriction is gone. Still, the smaller the better.
-
- Where possible, use minivaults instead of regular vaults, because the dungeon
- builder has greater freedom with the rest of the level layout when using
- minivaults.
-
-* levdes.vim.
- If you use vim, the levdes.vim syntax highlighting script (provided
- in the dat directory) can make level-editing far more pleasant by colouring
- different features in maps and syntax-highlighting .des-file syntax. vim is
- available for nearly all operating systems, including Windows.
-
-
-H. Portal Vaults
-==================
-
-Portal vaults are vaults accessed by portals in the dungeon (labyrinths
-and bazaars are special cases of portal vaults). You can create custom
-portal vaults in the following steps (no compilation is necessary):
-
-* Create a new file name.des in the dat/ folder. Rules:
- The "name" should be descriptive of the vault you're adding.
- The "name" should not exceed eight letters.
- The ending must be "des".
-* Add "name.des" to the list of local files in dat/clua/loadmaps.lua.
-* "name.des" should contain a comment at the top, explaining flavour and
- gameplay goals of the portal vault (and perhaps additional ideas etc.)
-* Define at least one vault containing the portal (see below).
-* Define at least one destination map (see below).
-* Add a short in-game description for the string "desc" (see below) to
- dat/descript/features.txt.
-
-Before going into the details of portal vault creation, some words about
-their uses: Portal vaults are different from branches in that they are
-not guaranteed. Also, there is only one go at a portal vault - if you
-leave, it's gone for good. You can apply special rules to a portal vault,
-like enforcing maprot.
-
-Portal vaults can be particulary thematic, using specialised monster
-sets, fitting loot, coloured dungeon features etc. Avoid death traps; it
-is no fun to enter a vault, being unable to leave and be killed outright.
-In order to provide fun and reduce spoiler effects, randomise. For portal
-vaults, it is desirable to have several different layouts (ideally each
-of the maps has some randomisation on its own). Often, it is a good idea
-to skew the map distribution: e.g. with four destination vaults, weights
-like 40,30,20,10 might be more interesting than 25,25,25,25.
-
-In order to test a portal vault, you can either use PLACE: D:2 for an
-entry vault, or use the wizard mode command &L for conjuring up the entry.
-
-Define a vault to hold the portal itself
-----------------------------------------
-
-# Bare-bones portal vault entry
-NAME: portal_generic_entry
-TAGS: allow_dup
-ORIENT: float
-MARKER: O = lua:one_way_stair { desc = "A portal to places unknown", \
- dst = "generic_portal" }
-KFEAT: O = enter_portal_vault
-MAP
-O
-ENDMAP
-
-Portal entries must contain a portal vault entry (enter_portal_vault).
-This feature must always have a marker that provides the portal with a
-description ("A portal to places unknown") and a destination
-("generic_portal").
-
-In case you want to make sure that the portal vault entry is only used
-once, you add a TAGS: uniq_BAR line. It should be noted that the label
-BAR may *not* end in _entry (otherwise the level builder assumes that
-the vault is a branch entry).
-
-If you want the place name displayed while in the vault to be different
-than the destination name, then you can give one_way_stair() a "dstname"
-parameter. If you want the place origin for items in a character
-dump to be different than the default you can give one_way_stair a
-"dstorigin" parameter (i.e., dstname = "garden", dstorigin = "in the gardens").
-If you want the place name abbreviation used when displaying notes to be
-different than than the default you can use the "dstname_abbrev" parameter.
-
-You can dynamically change the origin string using the lua function
-dgn.set_level_type_origin(), and dynamically change the place name
-abbreviation with dgn.set_set_level_name_abbrev().
-
-Known portal vault entries will be displayed on the overmap. By default
-the name shown on the overmap will be the "dstname" parameter, or if
-that isn't present the "dst" paremeter. It can be set to something else
-with the "overmap" parameter. A note can be made to accompany the
-portal's position on the overmap with the "overmap_note" parameter.
-
-Bones files for characters killed in the portal vault will normally
-use an extension derived from the first three letters of the 'dst'
-property. You can override this by setting the 'dstext' property to
-your preferred extension.
-
-This will produce a portal, but attempting to use it will trigger an
-ASSERT since there's no map for the destination. So we create a
-destination map like so:
-
-Define a destination map
-------------------------
-
-NAME: portal_generic_generic
-# Tag must match dst value of portal in entry.
-TAGS: generic_portal allow_dup
-ORIENT: encompass
-MONS: ancient lich
-KFEAT: > = exit_portal_vault
-MAP
-xxxxxxxxxxx
-x111111111x
-x1A111111>x
-x111111111x
-xxxxxxxxxxx
-ENDMAP
-
-Note that the entry point into the map will be a stone arch. You must
-provide an exit to the dungeon explicitly (KFEAT: > = exit_portal_vault)
-or the player will not be able to leave.
-
-Stairs will not work right in portal vaults, do not use them.
-
-You can use multiple maps with the destination tag (generic_portal),
-and the dungeon builder will pick one at random.
-
-The MARKER parameters
----------------------
-
-The lines
- MARKER: O = lua:one_way_stair { desc = "A portal to places unknown", \
- dst = "generic_portal" }
- KFEAT: O = enter_portal_vault
-ensure that an 'O' glyph will be turned into a portal. Upon leaving the portal
-vault, you will be placed on its entry which has been turned into a floor. You
-can turn it into something different (usually an empty stone arch), by adding
- floor = 'stone_arch'
-to the lua:one_way_stair parameters.
-
-Note that the desc string is what you will see upon examining the portal.
-The dst string is used for Crawl's right hand stat area; it will show
- Place: generic portal
-in the above example. Here is a lost of the parameters that can be used
-within one_way_stair (taken from icecave.des):
- desc = "A frozen archway", # description of the portal before entry
- dst = "ice_cave", # label used for maps and entry vaults
- dstname = "Ice Cave", # used for PLACE: on the main screen
- dstname_abbrev = "IceCv", # used in the notes
- dstorigin = "in an ice cave", # appendix for items picked up there
- overmap = "frozen archway", # used on the overmap (X)
- floor = "stone_arch" # feature left after escaping the portal
-
-The dst string is also used to link the destination maps to the entry maps.
-In case dstname is missing, dst will be used.
-
-You can replace lua:one_way_stair by lua:timed_marker in order to make timed
-portal vaults (which will disappear after some time). bazaar.des and lab.des
-contain examples. For timed portals, you may want to add messages to the file
-dat/clua/lm_tmsg.lua.
-
-Using lua functions as shortcuts
---------------------------------
-
-If you are making several entry and destination vaults, you will note a
-lot of duplicated header statements. This can be lessened using lua.
-Define a lua block right at the top (after your comments) as follows:
-
-{{
-function generic_portal(e)
- e.marker([[O = lua:one_way_stair { desc = "A portal to places unknown",
- dst = "generic_portal",
- floor = "stone_arch" }]])
- e.kfeat("O = enter_portal_vault")
- e.colour("O = magenta")
-end
-}}
-
-Instead of the MARKER and KFEAT lines introduced above you now just use
- :generic_portal(_G)
-and the resulting portal glyphs will even be magenta!
-
-Defining a random monster set
------------------------------
-
-Portal vaults require a defined random monster set to make the Shadow
-Creatures spell work. This is done by calling dgn.set_random_mon_list()
-manually. Here's an example from ice_cave_small_02 in icecave.des:
- : dgn.set_random_mon_list("ice beast w:90 / ice dragon / nothing")
-You can use "nothing" to have the spell fail sometimes.
-
-If you are using the same random monster list in several destination maps,
-you can define a lua block and call it from the destination map definition.
-This example is from sewer.des:
-
-{{
-function sewer_random_monster_list(e)
- e.set_random_mon_list("giant bat w:20 / giant newt w:20 / small snake / \
- ooze / worm / snake / giant mosquito w:15")
-end
-}}
-
-You can then use this line in the map definition to execute the lua block:
- : sewer_random_monster_list(_G)
-
-You can also set env.spawn_random_rate() to have monsters generated from the
-list during play.
-
-Milestones for portal vaults
-----------------------------
-
-This example is from icecave.des, defined in the lua preludes:
-
-{{
-function ice_cave_milestone(e)
- crawl.mark_milestone("br.enter", "entered an Ice Cave.")
-end
-}}
-
-The function is called from each of the destination map definitions:
-
-: ice_cave_milestone(_G)
-
-This marks down entering the portal vault in the milestones. When the portal
-is entered, the destination map is chosen and the call to crawl.mark_milestone
-is executed along with the rest of the map definition.
-
-Adding milestones in a .des does have the slight catch of creating multiple
-milestones if the map fails validation for some reason, so it's best used
-in maps that will never fail validation.
-
-I. Lua reference
-===================
-
-How maps are processed
-----------------------
-
-Under the hood, Crawl translates everything in a .des file to Lua. You
-don't need to know what the underlying Lua looks like to design
-levels, but it helps.
-
-Crawl uses Lua 5.1 from http://www.lua.org (the site has information
-on the Lua language). Let's examine how Crawl converts a map
-definition into Lua code with an example map:
-
-NAME: statue_in_pool
-TAGS: no_rotate no_pool_fixup
-: if you.absdepth() < 7 then
-MONS: plant
-: else
-MONS: oklob plant
-: end
-MAP
-1...1
-.www.
-.wGw.
-.www.
-1...1
-ENDMAP
-
-Crawl will convert this map into the following Lua code, wrapped in an
-anonymous function (this is called a Lua chunk):
-
-function ()
- tags("no_rotate")
- tags("no_pool_fixup")
- if you.absdepth() < 7 then
- mons("plant")
- else
- mons("oklob plant")
- end
- map(".....")
- map(".www.")
- map(".wGw.")
- map(".www.")
- map(".....")
-end
-
-If your level defines prelude or validation Lua code, such code is
-extracted into separate prelude and validation chunks. The prelude and
-validation chunks are empty unless specified.
-
-Apart from the special NAME map header, every map header translates to
-a Lua function with the same name in lowercase. For instance, KFEAT:
-<xyz> is translated into kfeat("<xyz>").
-
-If you have a space or comma separated list (such as TAGS, MONS, ITEM,
-etc.), then each space/comma separated item is passed into a separate
-call to the corresponding Lua function. For instance:
-
-TAGS: no_rotate no_pool_fixup
-->
-tags("no_rotate")
-tags("no_pool_fixup")
-
-MONS: orc, gnoll
-->
-mons("orc")
-mons("gnoll")
-
-Knowing what the generated Lua looks like under the hood is useful
-because it allows you to extract repeated boilerplate in similar
-vaults into a Lua function in the .des file's prelude. For instance,
-if you were planning to write a whole slew of vaults featuring statues
-in water guarded by plants, you could extract the common code into the
-top of the .des file as:
-
-# This block has to be placed before any other vault in the .des file.
-{{
-function statue_pool_map(e)
- e.tags("no_rotate")
- e.tags("no_pool_fixup")
- if you.absdepth() < 7 then
- e.mons("plant")
- else
- e.mons("oklob plant")
- end
-end
-}}
-
-NAME: statue_in_pool
-# Pass in the Lua environment global _G to the prelude function.
-: statue_pool_map(_G)
-MAP
-1...1
-.www.
-.wGw.
-.www.
-1...1
-ENDMAP
-
-You can also use arbitrary Lua directly in vault definitions, which is
-handy when randomizing things:
-
-NAME: statue_in_pool
-: local plant_weight = crawl.random_range(1,10)
-: mons("plant w:" .. plant_weight ..
-: " / oklob plant w:" .. (10 - plant_weight))
-MAP
-1...1
-.www.
-.wGw.
-.www.
-1...1
-ENDMAP
-
-How Lua chunks are associated with a C++ map object
----------------------------------------------------
-
-A map's Lua chunk consists of calls to functions such as tags(),
-mons(), etc. These functions are defined in the dgn table (see the Lua
-API reference below), and they expect to act on an instance of Crawl's
-C++ mapdef object. Given:
-
-tags("no_rotate")
-
-the actual Lua call needs to be:
-
-dgn.tags(<map>, "no_rotate")
-
-Where <map> is the C++ map object to which the tag should be added.
-Since calling dgn.<foo>(<map>, <xxx>) is tedious, dat/clua/dungeon.lua
-wraps the Lua chunk for the map into an environment that defines
-wrappers for all the functions in 'dgn' as:
-
- function <xyz>(...)
- dgn.<xyz>(<map>, ...)
- end
-
-i.e. for every function <xyz> in the 'dgn' table, we define a new
-function <xyz> that just calls dgn.<xyz>() with the current map as the
-first parameter, and the other parameters as passed in. Thus Lua code
-that you write as:
-
-tags("no_rotate")
-
-is translated to the correct dgn.tags(<map>, "no_rotate").
-
-While this is done automatically for map code, if you need to call Lua
-code that was not defined in the scope of the map, as in the example
-statue_pool_map() function, you need to pass in the map environment to
-that function if you want it to modify the map. Thus the call to
-statue_pool_map looks like:
-
-: statue_pool_map(_G)
-
-Steps involved in processing .des files
----------------------------------------
-
-* Level files are compiled into a series of Lua chunks. Each map can
- have one or more Lua chunks associated with it: the prelude, the
- body, and a validation chunk. The body is mandatory, but validation
- and prelude chunks are necessary only if your map needs validation
- or fancy selection criteria.
-
-* When first compiling a .des file, Crawl compiles each map's Lua
- chunks, then compiles and runs the prelude, body and validation
- immediately to verify that the Lua code is not broken. Lua errors at
- this stage will cause Crawl to exit with an error message (hopefully
- relevant). Note that the validation Lua chunk's return code is
- completely ignored at this stage - it is only run to check for
- syntax errors in the code.
-
-* When a new game is started, Crawl will run the Lua preludes for all
- maps (most maps should have no prelude - map preludes slow the game
- down). At this point, preludes can change the map's placement or
- availability.
-
-* When the dungeon builder selects a map (based on TAGS, DEPTH,
- PLACE), it re-runs the map prelude and the map body, applies
- transforms (SUBST, SHUFFLE) if any, then calls the map's validation
- Lua. If the map passes validation, the dungeon builder continues
- with level-generation; otherwise, it restarts from the map prelude.
-
-The global prelude
-------------------
-
-Every .des file can have (at the start of the file) Lua code that is
-not associated with any specific map, but with all maps in the file.
-This is called the global prelude. The global prelude is run before
-running any other Lua code in the file, once during compilation, and
-once at start of game.
-
-You can use the global prelude to define functions and set up globals
-that the rest of the maps in the .des file use. If you have a lot of
-common code, you should probably add it to dungeon.lua instead.
-
-Syntax for using Lua in .des files
-----------------------------------
-
-* Colon-prefixed lines are individual Lua lines, extending to the end
- of the line. E.g.
-
- : crawl.mpr("Hello")
-
- Colon-prefixed lines are always in the main Lua chunk, unless they occur
- before any map definitions, in which case they go to the global prelude.
-
-* Lua blocks for the main (body) Lua
- lua {{ <code> }}
- or
- lua {{
- <code>
- }}
- The "lua" word is optional; you can also use:
- {{ <code> }}
- and
- {{
- <code>
- }}
-
-NOTE: Colon-prefixed lines, or lua {{ }} blocks defined before any
-map's NAME: directive will add the Lua code to the global prelude.
-
-* Lua blocks for the prelude:
- prelude {{ <code> }}
- or
- prelude {{
- <code>
- }}
-
-* Lua blocks for the validate chunk:
- validate {{ <code> }}
- or
- validate {{
- <code>
- }}
-
-Debugging Lua
--------------
-
-Since Lua action happens in the guts of Crawl, it can be hard to tell
-what's going on. Lua debugging involves the time-honoured method of
-peppering your code with print statements:
-
-* Use error() or print() for compile-time work (i.e. when Crawl reads
- the .des file). Note that print() just writes to the terminal and
- keeps going, while error() forces Crawl to exit immediately (at
- compile time; errors during level-generation are handled
- differently).
-
-* Use crawl.mpr() for output when the game has started (at
- level-generation time).
-
-It's very important that your finished level never croaks during
-level-generation. A Lua error at this stage is considered a validation
-failure.
-
-Special dungeon-related Lua marker properties
----------------------------------------------
-
-There are several properties a Lua marker can have which will affect the
-dungeon cell which they are on:
-
-* connected_exclude: Consider the cell to be separate from neighboring
- cells with identical or similar features. Currently only useful
- for preventing adjacent doors from grouping together into a gate,
- forcing them to open and close as separate doors. See the Evil
- Zoo (minivault_9) in dat/mini.des for an example.
-
-* door_description_suffix: A string to append to the description of
- any door the marker is on. This should be used for doors
- rather than the feature_description property since it elemintates
- the need to track if the door is opened or closed, plus it will
- have no effect on secret doors which have yet to be detected.
-
-* feature_description: What to use as the short description of the
- cell's feature.
-
-* feature_description_long: What to use as the long description of the
- cell's feature.
-
-* stop_explore: If set to anything, and placed on a cell with a statue
- or orcish idol, will cause auto-explore to stop with the message
- "Found <whatever>."
-
-* stop_explore_msg: Like stop_explore, but when auto-explore is stopped
- the content of the property will be printed out as a message.
-
-* veto_disintegrate: If this property is set to "veto" then the cell
- will be immune to disintegration.
-
-* veto_fragmentation: If this proprety is set to "veto" then the cell
- will be unaffected by fragmentation (Lee's Rapid Deconstruction
- spell).
-
-* veto_shatter: If this property is set to "veto" then the cell will
- be unaffected by the Shatter spell.
-
-Lua API reference
------------------
-a. The Map.
-b. Global game state.
-c. Character information.
-
-
-Lua API - the Map
------------------
-
-Lua functions dealing with the map are mostly grouped under the "dgn"
-module. For convenience, .des file Lua chunks are run in an environment
-such that function calls written as:
-
- fn(x, y, ...)
-
-are translated to
-
- dgn.fn(map, x, y, ...)
-
-where "map" is the reference to the map that the currently executing
-Lua chunk belongs to. This is only for Lua chunks that belong to a
-map, Lua code in the global prelude does not get this treatment
-(because the global prelude is not associated with any map).
-
-Functions in the dgn module:
-
-default_depth, name, depth, place, tags, tags_remove, chance, weight,
-orient, shuffle, shuffle_remove, subst, subst_remove, map, mons, item,
-kfeat, kitem, kmons, grid, points_connected, gly_point, gly_points,
-original_map, glyphs_connected, orig_glyphs_connected, orig_gly_point,
-orig_gly_points, load_des_file, feature_number, feature_name,
-dgn_event_type, register_listener, remove_listener, remove_marker,
-num_matching_markers, feature_desc, feature_desc_at, item_from_index,
-mons_from_index, change_level_flags, change_branch_flags,
-set_random_mon_list
-
-
-Additionally, the dgn module provides a global "mapgrd" variable that
-can access the current map glyphs. The top left symbol in the map
-can be assigned like this:
-
- mapgrd[1][1] = 'x'
-
-The bottom right symbol can be assigned like this:
-
- mapgrd[width()][height()] = "."
-
-
-Lua API - global game state
----------------------------
-
-The "crawl" module provides functions that describe the game state or
-provide utility methods.
-
-mpr, mesclr, random2, coinflip, one_chance_in, redraw_screen,
-input_line, c_input_line, getch, kbhit, flush_input, sendkeys,
-playsound, runmacro, bindkey, setopt, msgch_num, msgch_name, regex,
-message_filter, trim, split, game_started, err_trace, args,
-mark_milestone
-
-
-Lua API - character information
--------------------------------
-
-The "you" module provides functions that describe the player character.
-
-turn_is_over, spells, abilities, name, race, class, god, hp, mp,
-hunger, strength, intelligence, dexterity, xl, exp, res_poison,
-res_fire, res_cold, res_draining, res_shock, res_statdrain,
-res_mutation, res_slowing, gourmand, levitating, flying, transform,
-stop_activity, floor_items, where, branch, subdepth, absdepth
-
-
-J. Feature Names
-==================
-
-These are the feature names usable in MARKER declarations:
-
-unseen, rock_wall, stone_wall, closed_door, metal_wall, secret_door,
-green_crystal_wall, orcish_idol, wax_wall, permarock_wall,
-silver_statue, granite_statue, orange_crystal_statue,
-statue_reserved_1, statue_reserved_2, lava, deep_water, shallow_water,
-water_stuck, floor, exit_hell, enter_hell, open_door, trap_mechanical,
-trap_magical, trap_iii, undiscovered_trap, enter_shop,
-enter_labyrinth, stone_stairs_down_i, stone_stairs_down_ii,
-stone_stairs_down_iii, escape_hatch_down, stone_stairs_up_i,
-stone_stairs_up_ii, stone_stairs_up_iii, escape_hatch_up, enter_dis,
-enter_gehenna, enter_cocytus, enter_tartarus, enter_abyss, exit_abyss,
-stone_arch, enter_pandemonium, exit_pandemonium, transit_pandemonium,
-builder_special_wall, builder_special_floor, enter_orcish_mines,
-enter_hive, enter_lair, enter_slime_pits, enter_vaults, enter_crypt,
-enter_hall_of_blades, enter_zot, enter_temple, enter_snake_pit,
-enter_elven_halls, enter_tomb, enter_swamp, enter_shoals,
-enter_reserved_2, enter_reserved_3, enter_reserved_4,
-return_from_orcish_mines, return_from_hive, return_from_lair,
-return_from_slime_pits, return_from_vaults, return_from_crypt,
-return_from_hall_of_blades, return_from_zot, return_from_temple,
-return_from_snake_pit, return_from_elven_halls, return_from_tomb,
-return_from_swamp, return_from_shoals, return_reserved_2,
-return_reserved_3, return_reserved_4, enter_portal_vault,
-exit_portal_vault, altar_zin, altar_shining_one, altar_kikubaaqudgha,
-altar_yredelemnul, altar_xom, altar_vehumet, altar_okawaru,
-altar_makhleb, altar_sif_muna, altar_trog, altar_nemelex_xobeh,
-altar_elyvilon, altar_lugonu, altar_beogh, fountain_blue,
-fountain_sparkling, fountain_blood, dry_fountain_blue,
-dry_fountain_sparkling, dry_fountain_blood, permadry_fountain
-
-
-K. Map Statistics
-===================
-
-Full-debug Crawl builds (this does not include normal Crawl builds
-that have wizard-mode - you must build Crawl with "make debug", not
-"make wizard") can produce map generation statistics. To generate
-statistics, run crawl from the command-line as:
-
-crawl -mapstat
-
-This will generate 100 Crawl dungeons and report on the maps used in a
-file named "mapgen.log" in the working directory.
-
-You can change the number of dungeons to generate:
-
-crawl -mapstat 10
-
-Will generate 10 dungeons. If you merely want statistics on the
-probabilities of the random map on each level, use:
-
-crawl -mapstat 1
-
-L. Map Generation
-===================
-
-Full-debug Crawl builds (see above for more information) include a test for
-generating specific vaults and outputting a copy of the map to a text file for
-inspection. This is most useful for portal and other encompass vaults which
-use randomisation heavily.
-
-To use the test, you must edit source/test/vault_generation.lua. Fill in the
-following variables:
-
- * map_to_test: The exact name of the vault you want to generate.
- * checks: How many times to generate the vault. Default value is 10.
- * output_to: The basic filename to output the results of generation to. This
- will have ".<iteration>" appended. For example, "volcano.map" will
- result in files named "volcano.map.1", "volcano.map.2", etc.
- * need_to_load_des: If the file is not included in one of the .des files that
- are listed in source/dat/clua/loadmaps.lua, this should be set to true,
- and the following variable should be set.
- * des_file: The name of the file to load. The file should be located in the
- source/dat folder.
-
-Once you have saved your changes, run crawl:
-
- crawl -test vault_generation
-
-Once all of the tests have been finished successfully you should find the
-relevant files in your working directory.