git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7496 c06c8d41-db1a-0410-9941-cceddc491573
BPPMLLPJLP6W2LZSPAMOMYA7YWCIFJTNNL3XBWU2MRHAQBZ5M4XAC
A4WHP5XZMXDCFMGRPS43OVHXHMU5KJBZKF4IRIO6F3KTXNHKAERQC
6I5MNJV4MZTMTCUO5ON5XB7J6WPFKP7LNHUENGCHCUM2XJT66KMAC
WE3JT43OR4L6675GINGU4B3YDBMURJZHDDYY3VLHUJEBAKH2HYEAC
R6XS2HO5QX2FJUGL5UQQRNETKCMYWTUFPHPPS5SYWK3OQA4UDUQQC
AFE345BJ7IX2YYYX3I5I6RYLXNWJCFE4WMH6F5JMIR6X7WUP75CAC
BTZ3QR6G7FV4DJCEG2CTDFH6YSUM4Y7NXM7NTAKBRLBOUIPKZM4QC
RLARLENIGHKWCS2X7NOUYUWYYZ5H2TJLTIH42YQURZLQFQTPBBVAC
ZTDYCQQQQSLWGFJOUB3HKGHMJLNY6UHF4OOC2XVVHXSZTTZK5A5AC
MXOCLQAUGWLOS7AOTYZ46JZDMRL4EVRK5YN4JJUQ76GLKBOBHEVAC
K6ELQ4HEZYDROC7CJFLPJS64AAJQ4G6RVLL4GBRUG6FJMKSBDDIQC
ILN2K6ASDZSMEHOPJ22IZLZJUO6DDGZTKAKXM3YXG6JZZHJNLX4AC
S74D5KQSRSDBHFN6OTSQQTLUEGNA3FZQMG7IPR5JXDUXJ3AOL2UAC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
TLA5UN6LZPXGKERI27EFY4HKIIU3VU5Y7ZU54WXL6ANBUV2VOTMQC
EH4VJW3I5Y4V6DT3YMLNDA3NW2DEAV4LRE4T5IEXAVB4WB3JJMGAC
TPZWAV3USKO7RX4IGHLZKVPRN36K33PJPSZYL6FZMX4XBHTYOQYAC
ANVCMXEDN6Y62OGEKKQQYNHJKJVI3TMGQM6KD7SJJIQC46G2SSIQC
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
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
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
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
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).
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).
Random minivaults are small maps that are placed onto a level that the
dungeon builder has already constructed fully otherwise (the level may
Random minivaults are small maps that are placed onto a level that the
dungeon builder has already constructed fully otherwise (the level may
Similarly, 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
Similarly, 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
PLACE: Used to specify certain special levels. Existing special levels are:
Temple, Hell, Dis:7, Geh:7, Coc:7, Tar:7, Hive:4, Vault:8, Snake:5,
Elf:7, Slime:6, Blade, Zot:5, Tomb:1, Tomb:2, Tomb:3, Swamp:5, Crypt:4
PLACE: Used to specify certain special levels. Existing special levels
include most branch ends.
for vaults with ORIENT encompass or with PLACE. Causes a level's
flags to be set when the level is first created. These flags can
for vaults with ORIENT encompass or with PLACE. Causes a level's
flags to be set when the level is first created. These flags can
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.
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.
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
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
H. Lua reference
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. Furthermore, there is only one go at a portal vault - if
you leave, it's gone for good. Finally, you can apply special rules to a
portal vault, like disabling mapping. Bazaars and labyrinths are typical
examples.
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 _uniq (otherwise the level builder assumes that the
vault is a branch entry).
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. The dst string is also used to link the destination maps
to the entry maps.
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.
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!
I. Lua reference
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 uses Lua heavily when dealing with .des files:
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
---------------------------------------
body, and a validation chunk. The body is mandatory, but prelude and
validation chunks are necessary only if your map needs validation or
fancy selection criteria.
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.
that have wizard-mode) can produce map generation statistics. To
generate statistics, run crawl from the command-line as:
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:
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. Furthermore, there is only one go at a portal vault - if
you leave, it's gone for good. Finally, you can apply special rules to a
portal vault, like disabling mapping. Bazaars and labyrinths are typical
examples.
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 _uniq (otherwise the level builder assumes that the
vault is a branch entry).
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. The dst string is also used to link the destination maps
to the entry maps.
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.
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!