diff options
Diffstat (limited to 'crawl-ref/docs')
-rw-r--r-- | crawl-ref/docs/develop/levels/advanced.txt | 894 | ||||
-rw-r--r-- | crawl-ref/docs/develop/levels/introduction.txt | 349 | ||||
-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. |