diff options
author | David Ploog <dploog@users.sourceforge.net> | 2009-10-06 06:45:22 +0200 |
---|---|---|
committer | David Ploog <dploog@users.sourceforge.net> | 2009-10-06 06:45:22 +0200 |
commit | fcb71db1fcffedef3196865d271fc897e562448d (patch) | |
tree | 4cf1e5457397d9f3b6dadcfbce840e4c2dca14e0 /crawl-ref/docs/develop | |
parent | 5eb01527a7ee911e6a7f2022e9bdfdc236095b99 (diff) | |
download | crawl-ref-fcb71db1fcffedef3196865d271fc897e562448d.tar.gz crawl-ref-fcb71db1fcffedef3196865d271fc897e562448d.zip |
Add develop/ and license/ folders in docs/.
Diffstat (limited to 'crawl-ref/docs/develop')
-rw-r--r-- | crawl-ref/docs/develop/coding_conventions.txt | 368 | ||||
-rw-r--r-- | crawl-ref/docs/develop/level_design.txt | 1916 | ||||
-rw-r--r-- | crawl-ref/docs/develop/monster_speech.txt | 900 | ||||
-rw-r--r-- | crawl-ref/docs/develop/patch_guide.txt | 134 | ||||
-rw-r--r-- | crawl-ref/docs/develop/release.txt | 122 | ||||
-rw-r--r-- | crawl-ref/docs/develop/tiles_creation.txt | 106 |
6 files changed, 3546 insertions, 0 deletions
diff --git a/crawl-ref/docs/develop/coding_conventions.txt b/crawl-ref/docs/develop/coding_conventions.txt new file mode 100644 index 0000000000..132ab62320 --- /dev/null +++ b/crawl-ref/docs/develop/coding_conventions.txt @@ -0,0 +1,368 @@ +Crawl coding conventions +======================== + +Introduction +============ + +This file documents the style conventions currently in use in the Crawl +codebase, as well as the conventions that new and/or modified code should +conform to. It is explicitly not meant to be a didactic "how to program +effectively" treatise. That is something best left to books and websites, +as well as experience. + +Conventions +=========== + +A) Indenting +------------ + +Generally, use 4 spaces to indent, and indent with spaces only (no tabs). +Empty lines don't need any spacing at all. + +Methods +------- + +If the parameter list of a method runs longer than a line length (80 columns), +the remaining parameters are usually indented in the lines below. + + static void replace_area(int sx, int sy, int ex, int ey, + dungeon_feature_type replace, + dungeon_feature_type feature, unsigned mapmask) + { + [...] + } + +The same is true when a method is called: + + // place guardian {dlb}: + mons_place( MONS_GUARDIAN_NAGA, BEH_SLEEP, MHITNOT, true, + sr.x1 + random2( sr.x2 - sr.x1 ), + sr.y1 + random2( sr.y2 - sr.y1 ) ); + + +There are cases where this is not possible because the parameters themselves +are too long for that, or because the method is already heavily indented, +but otherwise, this convention should be followed. + + +B) Logical operators +-------------------- + +Conditionals longer than a line should be indented under the starting bracket. +This probably seems obvious for simple ifs ... + + if (!player_in_branch(BRANCH_COCYTUS) + && !player_in_branch(BRANCH_SWAMP) + && !player_in_branch(BRANCH_SHOALS)) + { + _prepare_water( level_number ); + } + +... but it should also be followed for else if conditionals. + + else if (keyin == ESCAPE || keyin == ' ' + || keyin == '\r' || keyin == '\n') + { + canned_msg( MSG_OK ); + return (false); + } + +Space allowing, logical connectives of different precedence should use nested +indenting. + + case ABIL_MAPPING: // Sense surroundings mutation + if (abil.ability == ABIL_MAPPING + && player_mutation_level(MUT_MAPPING) < 3 + && (you.level_type == LEVEL_PANDEMONIUM + || !player_in_mappable_area())) + { + mpr("You feel momentarily disoriented."); + return (false); + } + +If a logical connective needs to be distributed over several lines, +the conjunction/disjunction operators (&&, ||) should be placed at the +beginning of the new line rather than at the end of the old one. + + else if (you.mutation[mutat] >= 3 + && mutat != MUT_STRONG && mutat != MUT_CLEVER + && mutat != MUT_AGILE && mutat != MUT_WEAK + && mutat != MUT_DOPEY && mutat != MUT_CLUMSY) + { + return (false); + } + +Since conjunctions (&&) take precedence over disjunctions (||), pure +conjunctive logical connectives don't need to be bracketed, unless this is +absolutely vital for understanding. (Nested indenting helps here.) + + if (you.skills[SK_ICE_MAGIC] > you.skills[SK_FIRE_MAGIC] + || you.skills[SK_FIRE_MAGIC] == you.skills[SK_ICE_MAGIC] + && you.skills[SK_AIR_MAGIC] > you.skills[SK_EARTH_MAGIC]) + { + book = BOOK_CONJURATIONS_II; + } + +In a switch conditional, the case listings don't have to be indented, though +the conditional statements should be. + + switch (mons_intel(monster_index(mon))) + { + case I_HIGH: + memory = 100 + random2(200); + break; + case I_NORMAL: + memory = 50 + random2(100); + break; + case I_ANIMAL: + case I_INSECT: + memory = 25 + random2(75); + break; + case I_PLANT: + memory = 10 + random2(50); + break; + } + +Comparisons using the ? shortcut may be indented ... + + mpr( forget_spell() ? "You have forgotten a spell!" + : "You get a splitting headache." ); + +... but really short ones don't have to be. + + beam.thrower = (cause) ? KILL_MISC : KILL_YOU; + + +C) Variable naming +------------------ + +When naming variables, use underscores_as_spaces instead of mixedCase. Other +conventions are pointed out by example, below. + +Global variables are capitalized and underscored. +Warning: there are currently many globals which don't do this. + + int Some_Global_Variable; + +Internal functions are prefixed with underscores. +Warning: This is a new convention, so much code doesn't follow it. + + static void _remove_from_inventory(item_def* item); + +Functions use underscores_as_spaces, but there are currently a lot of +mixedCase functions. + + void destroy_item(item_def* item) + { + // - Variables use underscores too. + int item_weight = /* ... */; + } + +There's no convention for class/struct names (yet?) + + class TextDB + { + public: + // - No rules for static member functions; they're not used often anyway. + static void whatever(); + + // - Public member functions: named like functions. + void* get_value(); + + private: + // - Internal member functions: also named like functions. + void _parse_text_file(const char*); + + // - Member variables get a prefix. + DB* m_db; + + // - Static member variables get a prefix, too. + std::vector<DB*> sm_all_dbs; + }; + + +But structures tend to use underscores + + struct coord_def + { + // - Simple structures don't need the "m_" prefixes + int x, y; + }; + + +D) Braces +--------- + +Braces are always put on their own lines. + + do + { + curse_an_item(false); + } + while ( !one_chance_in(3) ); + + +If many comparisons are necessary, this can result in a number of nested +braces. These can sometimes be omitted, as long as the underlying logic isn't +changed, of course. The following assumes that the conditions are followed by +single statements. + +If both the condition itself and the conditional code are single line +statements, the braces may be omitted. + + if (item != NULL) + _remove_from_inventory(item); + else + _something_else(); + + +Otherwise, place braces. + + if (tran == TRAN_STATUE || tran == TRAN_ICE_BEAST + || tran == TRAN_AIR || tran == TRAN_LICH + || tran == TRAN_SPIDER) // monster spiders don't bleed either + { + return (false); + } + + + for ( int i = 0; i < power_level * 5 + 2; ++i ) + { + create_monster(result, std::min(power/50, 6), + friendly ? BEH_FRIENDLY : BEH_HOSTILE, + you.x_pos, you.y_pos, MHITYOU, MONS_PROGRAM_BUG); + } + +Also place braces if this is only the case because of one or more comment +lines. + + for (j = 0; j < num_to_make; j++) + { + // places items (eg darts), which will automatically stack + itrap(beam, i); + } + + +In the case of nested if-conditionals, try to combine the conditions, e.g. +instead of + + if (A) + { + if (B) + do_something(); + } + +use + + if (A && B) + do_something(); + +Place braces as per the conventions above. + +Else, whenever if-conditional nesting can't be avoided, always use braces. I +could't find an example where that isn't already necessary for logical reasons, +so these should be really rare. + +In a row of if-else if-statements or in a switch-case loop, the optional braces +should be used if the bigger part of statements needs braces for logical +reasons or because of one of the conventions above. Otherwise, they may be +omitted. + + if (mons_neutral(monster)) + { + if (coinflip()) // neutrals speak half as often + return false; + + prefixes.push_back("neutral"); + } + else if (mons_friendly(monster)) + prefixes.push_back("friendly"); + else + prefixes.push_back("hostile"); + +When for-loops are nested and the outer loop has no further statements, the +braces may be omitted. + + for (int x = 0; x < GXM; x++) + for (int y = 0; y < GYM; y++) + { + if (grd[x][y] == DNGN_LAVA) + lava_spaces++; + if (grd[x][y] == DNGN_DEEP_WATER || grd[x][y] == NGN_SHALLOW_WATER) + water_spaces++; + } + +The same is true for combined for- and if-conditionals as long as all +statements fill less than four lines. + + for (i = 0; i < MAX_SHOPS; i++) + if (env.shop[i].type == SHOP_UNASSIGNED) + break; + +If the order of if- and for-conditionals is reversed, however, place braces. + + [...] + else if (enhanced < 0) + { + for (ndx = enhanced; ndx < 0; ndx++) + power /= 2; + } + + +If there are more than three nested statements with optional bracing, use braces +to roughly divide them into halves. (See example below.) + +Should such nested code be followed by code other than a closing brace, +leave a free line between them. + + for (int y = 1; y < GYM; ++y) + for (int x = 1; x < GXM; ++x) + { + if (grd[x][y] == feat) + return coord_def(x, y); + } + + return coord_def(0, 0); + + +E) Commenting +------------- + +If you feel that a method is complicated enough to be difficult to understand, +or has restrictions or effects that might not be obvious, add explanatory +comments before it. You may sign your comments if you wish to. + + // Note that this function *completely* blocks messaging for monsters + // distant or invisible to the player ... look elsewhere for a function + // permitting output of "It" messages for the invisible {dlb} + // Intentionally avoids info and str_pass now. -- bwr + bool simple_monster_message(const monsters *monster, const char *event, + msg_channel_type channel, int param, + description_level_type descrip) + [...] + +Adding explanatory comments to somewhat complicated, already existing methods +is very much welcome, as long as said comments are correct. :) + +Suboptimal code should be marked for later revision using the keywords "XXX", +"FIXME" or "TODO", so that other coders can easily find it by searching the +source. Also, some editors will highlight such keywords. Don't forget to add +a comment about what should be changed, or to remove said comment if you fix +the issue. + + // XXX Unbelievably hacky. And to think that my goal was to clean up the code. + // Identical to find_square, except that input (tx, ty) and output + // (mfp) are in grid coordinates rather than view coordinates. + static char find_square_wrapper( int tx, int ty, + [...] + + + if (death_type == KILLED_BY_LEAVING + || death_type == KILLED_BY_WINNING) + { + // TODO: strcat "after reaching level %d"; for LEAVING + [...] + + + // FIXME: implement this for tiles + void update_monster_pane() {} diff --git a/crawl-ref/docs/develop/level_design.txt b/crawl-ref/docs/develop/level_design.txt new file mode 100644 index 0000000000..6c973d6220 --- /dev/null +++ b/crawl-ref/docs/develop/level_design.txt @@ -0,0 +1,1916 @@ +----------------------------------------------- +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 + + +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 + 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 + temple.des - Ecumenical Temples, and Temple entrances + vaults.des - entrances for the Vaults, Vaults-specific vaults, the + Hall of Blades, and Vaults:8 + 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 +================ + +Terrain +------- + x - rock wall (DNGN_ROCK_WALL) + X - permanent rock wall - always undiggable (DNGN_PERMAROCK_WALL) + c - stone wall - only affected by Shatter (DNGN_STONE_WALL) + m - clear rock wall (DNGN_CLEAR_ROCK_WALL) + n - clear stone wall - only affected by Shatter (DNGN_CLEAR_STONE_WALL) + o - clear permanent rock wall - always undiggable (DNGN_CLEAR_PERMAROCK_WALL) + v - metal wall - grounds electricity (DNGN_METAL_WALL) + b - crystal wall - reflects cold and fire (DNGN_GREEN_CRYSTAL_WALL) + a - wax wall - can melt (DNGN_WAX_WALL) + + . - floor (DNGN_FLOOR) + + - closed door (DNGN_CLOSED_DOOR) + = - secret door (DNGN_SECRET_DOOR) + + W - shallow water + w - deep water - can be randomly turned into shallow water by the + level-builder; you can prevent this conversion with the no_pool_fixup TAG. + Also, water may automatically receive water creatures! For entry + vaults, avoid this with the no_monster_gen TAG or KMASK. + l - lava - again, use the no_monster_gen TAG or KMASK for entry vaults! + +Features +-------- + @ - entry point - must be on outside edge. If you use ORIENT: float, and do + not use any @, the dungeon builder will connect at least one floorspace on + the edge of your map to the rest of the level; if there is no floorspace + on the edge of your map, it will be isolated. + }{ - Stone stairs - You must be able to reach these from each other. The + { upstair is also the stair on which the player will enter the + dungeon for entry vaults. + )( - Stone stairs, set 2. + ][ - Stone stairs, set 3. + + >< - escape hatches - you can leave the level by these but will usually + not land on stairs/hatches + + I - orcish idol (does nothing) + + ^ - random trap. + ~ - random trap suitable for the branch and depth the map is being used. + + A - Vestibule gateway (opened by Horn). + B - Altar. These are assigned specific types (e.g. of Zin etc) in dungeon.cc, + in order. + C - Random Altar. + + G - Granite statue (does nothing) - you can see through but not walk through. + Also, sight-based effects like smiting work past granite statues, as does + apportation. + + NOTE: F used to put down a granite Statue most of the time but occasionally + Orange or Silver or Ice statues (1 in 100). You can reproduce this by using + F on the map together with + SUBST: F = G:100 F:1 + KMONS: F = orange crystal statue / silver statue / ice statue + + T - Water fountain + U - Magic fountain + V - Permanently dry fountain + Y - Blood fountain (use sparingly!) + +Items +----- + $ - gold + % - normal item + * - higher level item (good) + | - acquirement-level item (almost guaranteed excellent) + O - place an appropriate rune here. For portal vaults, place the portal here. + Z - the Orb of Zot + d-k - item array item. See section below on ITEM: arrays for more info. + +NOTE: The P (place a rune here with 1/3 chance) and R (place a honeycomb with + 2/3 chance or else a royal jelly) symbols have been deprecated. You can + (and should) produce the desired behaviour using + SUBST: P = O.. + KITEM: R = w:2 honeycomb / w:1 royal jelly + +Monsters +-------- + 0 - normal monster + 9 - +5 depth monster + 8 - (+2) * 2 depth monster (aargh!). Can get golden dragons and titans + this way. + 1-7 - monster array monster. See section below on MONS: arrays for more + information + + +D. Header information +======================= + +(All declarations apart from NAME: are translated to Lua function calls +behind the scenes. See the Lua reference for more information.) + +Try to respect line lengths of 80 characters. Should some line exceed that +(which is quite possible, especially for ITEM and MONS lines), you can use +the \ symbol to break a line. You can break a line anywhere, with the +exception of comma-separated lists, where you cannot start a new line with +a comma. See the end of this section for examples. + +NAME: a_string + Each map must have a unique name. Underscores and digits are ok. + +ORIENT: (float |encompass | north | northwest | ... | southeast) + + Some kind of ORIENT: line is mandatory for vaults; skipping + ORIENT: makes your map a minivault. As a rule of thumb, if + you're writing a small random map, skip the ORIENT: line and + make it a minivault. + + * "float": The dungeon builder puts your vault wherever it wants to. + * "some_direction": The vault lies along that side of the map: + xxxxxxxxxx xxxxxxxxxxxxx + xORIENT:Nx xORIENT:NW|.. + x.VAULT..x x.VAULT...|.. + x--------x x---------|.. + xrest....x xrest........ + x...of...x x.....of..... + x...levelx x.......level + + ORIENT: float vaults give a lot of flexibility to the dungeon + generator; float should generally be preferred to other ORIENT: + settings for new vaults. + +DEPTH: For random vaults, branch entry vaults, and minivaults, this + specifies the range of levels where the vault may be placed + in the dungeon. E.g. + + DEPTH: 7-20 + + DEPTH: does not force a map to be placed in a particular place; it + applies only when the dungeon builder is looking for a random vault + or minivault, so you can control at what depths your vault gets + placed. + + A simple DEPTH: declaration that does not specify a branch + applies to all branches. A map declared with depth 7-20 could + be used in the Lair, for instance. (Lair:1 will be treated as + a depth of 12 if the Lair entrance is on D:11.) + + You can constrain a map by branch: + + DEPTH: Lair:3-6 + + (Anywhere between levels 3-6 of the Lair, inclusive.) + + You can apply multiple constraints in one DEPTH line, + comma-separated: + + DEPTH: 7-20, !12-14 + + (Anywhere in the dungeon between depths 7-20, but not on levels + 12-14.) + + DEPTH: 7-20, !Orc + + (Anywhere in the dungeon between depths 7-20, but never in the Orcish + Mines.) + + DEPTH: Lair:* + + (Anywhere in the Lair. Can also be expressed as "DEPTH: Lair".) + + Maps that do not specify a DEPTH: attribute will inherit their depth + constraints from the closest preceding default-depth: line. If there + is no preceding default-depth directive in the .des file, the map will + have no DEPTH: constraint. Note that maps without a DEPTH: constraint + cannot be selected as random vaults or minivaults. + +CHANCE: <priority>:<roll> or <roll> + + CHANCE allows you to control the probability that your map + is used on any given level with an absolute roll. + + There are two ways to specify the CHANCE roll: + + CHANCE: 500 + or + CHANCE: 5% + + If specified as a raw number, the chance of selecting the + vault is <number> in 10000. If specified as a percentage, + the chance of selecting the vault is <perc> * 100 in 10000. + Note that CHANCE accepts only integers, no fractions or + decimals. + + For any map with alternatives, a CHANCE influences how + likely the map is to be picked instead of the alternatives. + If a map has a CHANCE, Crawl will roll a random number in + the range 1-10000, and select the map if the CHANCE is >= + the rolled random number. + + If there are multiple alternative maps with CHANCE, they + will be tested in an unspecified order; the first map that + makes the CHANCE roll will be used. If you'd like to specify + an order of testing CHANCEs, specify a CHANCE with a + priority: + + CHANCE: 10 : 20% + + This specifies a CHANCE of 20%, with a priority of 10, which + means this vault will be checked before any other vault with + a lower priority (the default priority is 0). + + If no map with a CHANCE is picked, Crawl will select a map + based on WEIGHT, ignoring vaults with a CHANCE set. + + Note that the Lua equivalent for CHANCE is a two-argument + function: + + : chance(<priority>, <number>) + + These lines are all equivalent: + CHANCE: 5% + CHANCE: 500 + CHANCE: 0 : 5% + CHANCE: 0 : 500 + : chance(0, 500) + + A common case when using CHANCE is to assign a CHANCE to a + set of maps. For instance, if you have a set of portal vault + entries, and you want one of the set to be used on 5% of all + levels, you can do this: + + NAME: portal_a + CHANCE: 50 : 5% + TAGS: chance_portal_group + ... + + NAME: portal_b + CHANCE: 50 : 5% + TAGS: chance_portal_group + ... + + That is, if you have a set of maps that use CHANCE and are + tagged chance_xxx, then one map of that set will be used + when the chance is met. + +WEIGHT: (number with 10 being default) + For entry vaults and any other vaults randomly picked from among + a set, this type of line affects the likelihood of the given vault + being picked in a given game. The default WEIGHT: is 10. The + likelihood of a vault getting picked is: + [vault's WEIGHT: / sum of all WEIGHT:s of vaults of that type] + +PLACE: Used to specify certain special levels. Existing special levels + include most branch ends. + The branches need to use the official abbreviations also used e.g. in + the overmap (Ctrl-O): D, Temple, Orc, Elf, Lair, Swamp, Shoal, Slime, + Snake, Hive, Vault, Blade, Crypt, Tomb, Hell, Dis, Geh, Coc, Tar, Zot. + + PLACE can also be used to specify arbitrary places, like D:3, which + will force the map (or one of the maps with PLACE: D:3) to be picked + when D:3 is generated. + + PLACE cannot be used to specify places in the Abyss, Pandemonium, + or Labyrinths. + + PLACE can be used with random vaults and minivaults for testing them. + +TAGS: Tags go an a TAGS: line and are space-separated. You can have several + TAGS: lines, or use \ for very long ones. Valid tags are: + * "allow_dup": Vaults are normally used only once per game. If you + have a vault that can be used more than once, use allow_dup to tell + the dungeon builder that the vault can be reused. + * "chance_FOO": Maps can be tagged chance_ with any unique suffix + to indicate that if the map's CHANCE roll is made, one of the maps + tagged chance_FOO should be picked. + * "dummy": this tag indicates that the vault is a stub; if the dungeon + builder picks a dummy vault, it pretends no vault was selected. + Dummies are used to reduce the probability of other vaults at the + same depth / place. + * "entry": this tag MUST be there for a vault to be pickable as an + entry vault. + * "extra": requests that the dungeon builder treat this as + an extra vault and try to immediately place another vault of the + same type it was trying to place when it placed this vault. + "extra" is good to use for things like labyrinth entries + that should not affect the chance of other minivaults on the level. + If you use "extra", you probably want to use one of the + "luniq" tags as well if your map is tagged "allow_dup". + * "generate_awake": Monsters placed (using MONS, KMONS) in this vault + will be generated awake. + * "patrolling": Monsters placed (using MONS, KMONS) in this vault + will be generated with their starting position as patrol point. + If not otherwise occupied (fighting, seeking) they will patrol + the area. + * "no_item_gen": Prevents random item generation in the vault. + Items explicitly placed by the vault are not affected. + * "mini_float": applicable only to minivaults, requests that + the dungeon builder pick random exits from the minivault and + connect it to the rest of the level, similar to the exit behaviour + for floating vaults. + * "no_monster_gen": Prevents random monster generation at the time of + the vault's creation. Highly advised for entry vaults with a + player-hostile geography, MUST-HAVE for those with water/lava. + Can be applied only to particular symbols with KMASK. + * "no_pool_fixup": prevents water squares next to land from being + randomly converted from deep water (the default) to shallow. + * "no_wall_fixup": In Dis, the Vaults and the Crypt a vault's + rock walls will be changed to be the same as the wall type of + the rest of the level. If you don't want that to happen then + use this tag. + * "uniq_BAR": (uniq_ with any suffix) specifies that only one of + the vaults with this tag can be used in a game. + * "luniq": specifies that this vault can be used only once on a + given level. "luniq" is only relevant when used with "allow_dup". + * "luniq_BAR": (luniq_ with any suffix) specifies that only one + of the vaults with this tag can be used on any given level. + "luniq_BAR" is only relevant when used with "allow_dup". + * "branch_entry" eg. "orc_entry", "lair_entry" etc. + If chosen, these maps will contain the stairs for that branch. + Use "O" to place the stairs. If a branch has very few entries, + a dummy entry is advisable to make sure the player doesn't get + bored of the same few entries recycled ad nauseam. + Note: if any TAG argument contains an "entry", the vault will + be no longer eligible for random placement. (Currently, + this just affects your choice of BAR when using uniq_BAR.) + * "mnoleg" or the name of some other pandemonium lord. This makes + the map eligible for said pan lord's lair. See pan.des. + * "minotaur" turns this into a labyrinth exit vault. + "lab" turns this into an additional labyrinth flavour vault. + See lab.des for examples and details. + * "no_rotate": Normally, the dungeon builder can, at its whim, + rotate your vault. This flag tells it, "hey, don't do that to my + vault!" + * "no_hmirror": Like no_rotate, but for horizontal mirroring. + * "no_vmirror": Like no_rotate, but for vertical mirroring. + * "layout": Lua code that dungeon.cc uses for generating level + layouts. Do *NOT* use or mess with unless you know what + you're doing. + * "layout_foo": Indicates what sort of level layouts this vault is + compatible with, for vaults that don't fit in with all layouts; + the absence of this type of tags means it can go with any layout. + Multiple layout_foo tags can be used if it can be used with + multiple layouts. Current values for "foo" are: rooms, city, + open, caves, cross, shoals, swamp, labyrinth (though currently + random vaults aren't placed in the last three). + * "trowel_portal": This vault can be created by the Trowel card. + This tag should be used exclusively for the generic (one tile) + entries to portal vaults, like bazaars and labyrinths. Other + portal vaults may be eligible for Trowel, too. + +LFLAGS: Persistent, changeable per-level flags which affect game behaviour + (FLAGS just controls how the vault is placed); should only be used + 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 + later be altered using Lua markers; for examples, look at the slime + pit vault in lair.des, and the vaults in hell.des and elf.des. + + Valid flags are: + * no_tele_control - prevents the player from using teleport control + * not_mappable - prevents the player from remembering where + they've been (like in the Abyss) + * no_magic_map - which prevents magic mapping from working. + +BFLAGS: Persistent, changeable per-*branch* flags which affect game + behaviour; should only be used for vaults which go on the first + level of a particular branch. These flags can later be altered + using Lua markers; see the Tomb vaults in vaults.lua for an + example. + + Valid flags are: + * no_tele_control - prevents the player from using teleport control + * not_mappable - prevents the player from remembering where + they've been (like in the Abyss) + * no_magic_map - which prevents magic mapping from working. + +LFLOORCOL: blue + LFLOORCOL: allows you to set the floor colour for the level the + vault appears in. Should only be used for bazaars and other + portal vaults. + +LROCKCOL: yellow + LROCKCOL: allows you to set the colour of rock walls for the level + the vault appears in. Should only be used for bazaars and other + portal vaults. + +LFLOORTILE: (tile name string, e.g. "floor_tomb") + Like LFLOORCOL, this overrides the default floor tiles used for + this level. If the tile specified has variations, those will be + used automatically. + +LROCKTILE: (tile name string, e.g. "wall_hive") + Same as LFLOORTILE, but for rock walls. + +ITEM: (list of items, separated by comma) + These are used to help place specified items at specific places + within a vault. They create an array with up to 8 positions. What's + in the first position in the array will be used when the dungeon + builder sees a "d" in the vault definition, the second will be used + for "e"s, etc. Positions are comma-separated; several ITEM: lines + are possible as well. The following defines letters 'd' - 'g': + ITEM: stone, ring mail, meat ration, ring of hunger + + Positions can contain multiple possibilities, one of which the + builder will choose randomly. Separate such multiple possibilities + using a slash. Note that "nothing" (without the quotes) is a valid + possibility. The random choice is done for each individual occurence + of the letter. You can also give possibilities a "weight," which + affects their chance of being picked. The default weight is 10. You + can abbreviate "weight:30" by "w:30". The chance to pick a + possibility is + [possibility's weight: / sum of all weight:s in that array position] + + For example, the following line makes letter 'd' into a bread ration + with 50% chance, or apple or orange with 25% chance each: + + ITEM: bread ration / w:5 apple / w:5 orange + + Modifiers: + * "q:N" sets the item quantity to N (if N > 0). Does nothing + if the item is not stackable. + * "no_uniq" prevents the item from being turned into an artefact. + * "good_item" makes the builder try to make the item a good one + (acquirement quality). + * "acquire" requests the use of the acquirement code itself, + ensuring that the player gets wearable armour, etc. You can + also use acquire:<god> to request that the acquired item be + treated as a god gift. Examples: "acquire any", "acquire armour", + "acquire:sif_muna book", "acquire:trog weapon". + * "level:N" sets the object's item level (can't be used with + "good_item"). If set to -2 then the object's item level will + be the same as a "*" symbol item (five plus twice the + vault's level number). + * "damaged" sets the item plusses to -1..-4. + * "cursed" gets a curse plus plusses as in "damaged". + * "any" by itself gives a random choice; you can combine "any" with + "good_item." + * "any book", "any misc" etc. gives a random item of that class. + Valid item class names are: gold, weapon, missile, armour, wand, + food, scroll, jewelry, potion, book, staff, orb, misc, carrion. + All of these are usable in map definitions, apart from "orb" and + "carrion". + * "race:race_name", where "race_name" is "elven", "dwarven" or + "orcish"; it can also be "none" or "no_race" to prevent the item + from being randomly being made racial. Has no effect if the item + can't take that kind of racial setting. + NOTE: Can result in a non-racial item if used with "any" and the + chosen item isn't compatible with the desired race. + * "ego:ego_name", where "ego_name" is something like "running" or + "fire_resistance", and so on; "none" can be used to prevent the + item from getting an ego. The item must be fully specified, so + trying "any weapon ego:vorpal" or "any armour ego:positive_energy" + will result in an error. Trying to give an ego to something which + can't accept an ego will also result in an error. + * "unrand:item_name" will make a given unrandom by a given name, + like "long sword unrand:singing_sword". Omit any apostrophes + in the name (e.g, unrand:vampires_tooth). If the name has + something between double quotes then use that string (e.g, + unrand:bloodbane). If the unique already exists, the base item + will be given instead. + + WARNING: While checks are done to make sure that an armour ego + isn't given to a weapon, a weapon ego to a missile, and so on, and + also to make sure that egos are only given to amrours, weapons and + missiles, no other checking is done. Thus it is possible to create + a demonic weapon of holy wrath or a helmet of running. + + Limitations: You can't specify specific pluses or number of charges + or force a randart. You also can't lay down corpses, skeletons, or + chunks. + +MONS: (list of monsters) + These are used to help place specific monsters at specific places + in a vault. They create an array with up to 7 positions. What's in + the first position in the array will be used when the dungeon + builder sees a "1" in the vault definition, the second for "2," + etc. Note that if, for example, you place a 3 on the map, but your + MONS: line has no third position, the 3 will be filled with + RANDOM_MONSTER. Also note that for entry vaults (D:1), all monsters + in sight of the hero are removed. This does not hold for plants. + You can use weights as for ITEM: lines. + + Individual monsters may be prefixed with the "generate_awake" + (without the quotes). Use this sparingly: + MONS: generate_awake giant beetle + + Individual monsters may be prefixed with the "patrolling" + (without the quotes). Use this sparingly: + MONS: patrolling naga guardian + + Monsters can also be given colours that override their default + colour. Use this *very* sparingly: + MONS: col:darkgrey fungus + + Note that 8, 9, 0 also place monsters (see the table). + + If you want to place a random monster suitable for the level + the map is generated on, you can use + MONS: random + + If you want to place a random monster suitable for some other + place, you can use a place: tag in the monster spec: + MONS: place:Abyss + or + MONS: place:Slime:6 + + Using place: with MONS implies that you want a random monster. + You can also request zombies from random monsters suitable + for some other depth as: + MONS: place:Elf:7 zombie + or + MONS: place:Zot:5 simulacrum + or + MONS: place:Vault:8 spectre + + The available modifiers are "zombie", "skeleton", + "simulacrum" and "spectre". + + If a monster is a member of a band, you can request that it + be eligible for band members by adding the keyword "band" to + the name. For instance: + MONS: orc warlord band + + Specifying "band" doesn't force bands to be placed - it + requests that the game use the normal chances of creating a + band. If you use "band", leave some empty space around the + monster for its band members to be placed. + + A monster can be given specific items by following the monster + name with a semi-colon and then with an item list as described + in ITEM:, but with slashes replaced with pipes and commas replaced + with periods. For example: + MONS: orc ; katana | quick blade . chain mail | scale mail + + will generate an orc wielding either a katana or a quick blade + and wearing either a chain mail or a scale mail. Randarts are + never generated, and ego items are only generated if the ego + is explicitly stated. Note that any items that the monster was + originally generated with will be removed and destroyed. This + can be used to force a monster to have no items whatsoever: + MONS: orc; nothing + + Items given to an orc or an elf will be made orcish or elven + unless the item's race type is explicitly set otherwise. + + Limitations: If an item in the item list has alternatives, + there's no way to force all monsters dervied from that monster + spec to choose the same alternative. If a monster is given + a random launcher, there is no way to force the ammo type to + match the launcher type. + +COLOUR: . = green / blue:5 / red / none + COLOUR: allows you to attach explicit colours to any feature. + Explicit colours will override the default colour for that + feature. The example shown above colours all . (floor) in the + map green, blue, red, or unchanged (use the default colour). + + You can use : to specify that all glyphs get the same colour: + COLOUR: x : red / blue + will colour all rock walls in the map red, or all rock + walls blue. + + COLOUR: should be used very sparingly, and only for features + where it won't cause confusion (i.e.: never re-colour features + like lava or traps unless you really know what you do!) + + If you apply COLOUR to a glyph and then apply a SUBST, + the COLOUR will transfer to the resulting transformed glyph. + +FTILE: . = floor_grass:20 / floor_dirt / none + Similar to COLOUR, FTILE allows you to attach explicit floor + tiles to any glyph. In non-tiles builds, this does nothing. + If the tile specified has variations, those will be used + automatically. Only tiles from the dungeon image can be used. + + This will not (necessarily) replace the feature tile itself, + only the floor. If you set the FTILE on a fountain glyph, + then the fountain will still appear normally, but the floor + underneath it will be the tile that was specified. + + If a feature that normally covers the floor (e.g. rock walls) is + destroyed, this floor tile will be used in place of the normal floor. + Thus, it can be useful even for non-floor features. + + Like COLOUR, this should be used sparingly. + +RTILE: x = wall_hive:15 / wall_lair / none + Identical to FTILE, but for rock walls. Not useful for anything + but the rock wall feature. + +SHUFFLE: def, 12/3? + This allows you to randomly permute glyphs on the map. There are + two ways: + + SHUFFLE: 123w (i.e. list of glyphs, NOT slash-separated) + could, for example, swap all occurences of "1" with "2", as well as + swapping all "3" with "w" (or any other of the 24 possibilities). + + SHUFFLE: 12/3w (i.e. list of slash-separated blocks of same size) + will either do nothing or swap all "1" with "3" and then also swap + "2" with "w" everywhere. + + Several SHUFFLE: lines can be used, and mixed with SUBST:, and the + shuffles and substitutions will be applied in order. You can also + put multiple SHUFFLEs on one line, comma-separated. Shuffles cannot + use , or /. All spaces are stripped before shuffling. + +SUBST: ?=xc, !:bv, 1=2 1:100 + The SUBST: directive allows you to specify a placeholder symbol + that is replaced with a random glyph from a set. For instance: + + SUBST: ? = TUV + replaces occurrences of ? with one of TUV. Since whitespaces are + irrelevant, this is the same as + SUBST: ? = T U V + + SUBST: ? = T:20 U V + makes T twice as likely to be used as U or V (the default weight + is 10). Note that there has to be at least one space before and + after T:20 and that whitespace in T:20 is not permitted. + + SUBST: ? : TUV + replaces occurrences of ? with one of TUV, and guarantees that all + occurrences of ? will get the same replacement symbol. + + The placeholder and replacement symbols can be any non-space, + printable character, including : and =, apart from commas. For + example, the following is valid: + SUBST: = = +=:123def" + + SUBST: lines can safely replace symbols with themselves, as in: + SUBST: w = wW + + Multiple SUBST: lines can be used, and mixed with SHUFFLE:, and + will be applied in order. Multiple substitutions can be performed + on one line, using commas. + +NSUBST: ? = 3:w / *:l + + NSUBST is similar to SUBST, replacing placeholders with + replacement values. Unlike SUBST, however, it allows you to + replace different instances of the same placeholder with + completely different substitutions. For instance: + + ? = 3:w / *:l + + replaces three occurrences (randomly selected) of ? with w + and all others with l. + + You can use complex SUBST specifications: + + ? = 3= w .:15 A / *: =+CF + + This is equivalent to SUBST: ? = w .:15 A for three ? and + SUBST: ? : =+CF for all the others. + + You can use any number of NSUBST specifiers: + + ? = wW / l / A / 1234 + + Each specifier is preceded by the number of symbols to apply + it to, followed by : or = (: to use one substitution for all + occurrences, = to randomly pick for each occurrence). If you + omit the initial N: or N=, then 1= is assumed, except for the + last spec where *= is assumed. + +KFEAT: Z = C / needle trap / antique armour shop / altar_zin + The KFEAT: directive allows you to specify a placeholder symbol + that is replaced with another symbol, named feature, trap, or + shop. For example, the line above will replace occurrences of Z + with C (random altar), a needle trap, an antique armour shop, or + an altar of Zin. Different instances of Z may receive different + replacements. To force a single replacement for all Z, use: + + KFEAT: Z : C / needle trap / antique armour shop + + You'll notice that 'Z' is the symbol of the Orb of Zot. Kxxx + directives allow you to assign arbitrary definitions to any symbol. + + KFEAT features are specified as a feature name (see section I + for a full list of feature names). As another example, you can + place a portal to the Abyss as: + + KFEAT: A = enter_abyss + + If you want no feature as an option in a KFEAT line, use '.' or + 'floor'. If you do not want to specify the type of shop, use + 'any shop' or 'random shop'. + + If you want a trap to start out known to the player, add "known" + to the trap name: + + KFEAT: A = known needle trap + + The placeholder used by KFEAT can be shared by KITEM and KMONS; + see below. If the placeholder is shared, all defined Kxxxx + operations for the placeholder are performed. Also, all Kxxx + lines accept weights as for MONS or ITEM. + +KMONS: ? = orc priest / w:3 deep elf priest + + KMONS: allows you to specify a placeholder symbol that indicates + the position of a monster (or monsters). + Using KMONS: allows you to exceed the 7 slot limit for monsters. + It is also useful if you want to place a monster on a non-floor + square (used in association with a KFEAT:). For example, + KFEAT: Z = W + KMONS: Z = rat + places a rat on a shallow water square for all occurrences of Z. + + KMONS: also allows you to specify alternative monsters if the + primary monster you want to place is unavailable (because it + is a unique that was already generated). For instance, if you want + to generate one of Terence, Michael or Erica or a generic human + (whoever is available, in that order, you can use): + KMONS: n = Terence, Michael, Erica, human + Or if you want to pick randomly: + KMONS: n = Terence / Michael / Erica, human + +KMASK: Z = no_monster_gen + + KMASK allows you set or unset various masks for particular + symbols, rather than for the entire vault like if you did it + with TAGS. Valid masks are + + * "no_item_gen": Prevents random item on that symbol. Items + explicitly placed on that symbol aren't affected. + * "no_monster_gen": Prevents random monster generation on that + symbol. MUST-HAVE for those with water/lava symbols in + entry vaults. + * "no_pool_fixup": prevents a water square next to land from being + randomly converted from deep water (the default) to shallow. + * "no_secret_doors": prevents a door from randomly being turned + into a secret door. + + For example + + KMASK: W = no_monster_gen + + will prevent monsters from randomly being generated on shallow water + squares. Note that if shuffling and substitutions cause W to end up + as water 10% of the time and floor 90% of the time, then those floor + squares will still have no_monster_gen set, but that's still a higher + degree of control than you get with TAGS. + + If TAGS has been used to set a mask for the entire vault, you can use + KMASK to remove that mask from particular symbols. For instance: + + TAGS: no_monster_gen + KMASK: W = !no_monster_gen + + would make it so that monsters are only randomly generated on shallow + water squares. + +KITEM: ? = potion of healing / potion of restore abilities + KITEM: places the specified item at all occurrences of the + placeholder. It can be combined with KFEAT: and KMONS: lines for + the same placeholder. + + You can use "gold" or "$" to place gold: + KITEM: ? = nothing / gold + KITEM: ? = nothing / $ + + You can use q: to specify quantities: + KITEM: ? = q:100 gold + + KITEM: allows you to place multiple items on the same square: + KITEM: ? = bread ration, potion of water, potion of porridge + +MARKER: A = feat:<feature_name> or lua:<marker_expr> + + A marker ties a square on the map to a game-trigger of some sort + (which depends on the marker and what feature it is on). + + The portals to the Hells in the Vestibule of Hell are each annotated + with feature markers like this: + + MARKER: D=feat:enter_dis, G=feat:enter_gehenna + + When the horn is sounded, the stone arch at D becomes the portal to + Dis, the arch at G becomes the portal to Gehenna. This behaviour + applies only to the Vestibule of Hell. + + Lua markers are used for more complex triggers, such as for bazaar + and labyrinth gates, rune pickup triggers for the branches of Hell, + fog generators, etc. + + Here's a Lua marker that creates a cloud generator (for a + full explanation of the various parameters, read the header + of dat/clua/lm_fog.lua): + + MARKER: A = lua:fog_machine { \ + pow_max = 15, delay_min = 100, delay_max = 150, \ + size = 1, size_buildup_amnt = 29, \ + size_buildup_time = 1000 } + + Feature names used in markers must be names matching the names in + the source code. There's a full list of feature names in section I + (Feature names) at the end of this document. + + An important note about markers is that they are also considered map + transforms along with SUBST, NSUBST and SHUFFLE. You usually want + to place a MARKER line after all SUBST, NSUBST and SHUFFLE lines so + that the final position of the marker key is used. For instance, if + you want to attach a marker to the rune in a map which randomises + the position of the rune, this is a mistake: + + MARKER: O = lua:<expr> + SHUFFLE: Oa/|c + + because the marker will be placed at O (the rune), then O may be + shuffled to a different position. The correct order in this case is: + + SHUFFLE: Oa/|c + MARKER: O = lua:<expr> + +For most map headers, you can split long lines by ending the line that will be +continued on the next line with \ as: + +KMONS: * = orc ; katana | quick blade . chain mail | scale mail / \ + goblin ; dagger + +If you're using continuation lines for comma-separated lists of monsters or +items, split your line after the comma, not before. For example: + +Wrong: + ITEM: potion of healing \ + , potion of speed +Right: + ITEM: potion of healing, \ + potion of speed + +But in general, it is preferable to use multiple ITEM or MONS lines if you're +splitting comma-separated values: + +Preferred: + ITEM: potion of healing + ITEM: potion of speed + +Spaces before the \ of the continued line are significant, leading spaces of +the next (continuing) line are not. In other words, given: + +ITEM: potion of\ + healing + +Crawl will see "potion ofhealing", not "potion of healing". + + +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. + + +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 + + +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 diff --git a/crawl-ref/docs/develop/monster_speech.txt b/crawl-ref/docs/develop/monster_speech.txt new file mode 100644 index 0000000000..d497fd2bbf --- /dev/null +++ b/crawl-ref/docs/develop/monster_speech.txt @@ -0,0 +1,900 @@ +Overview +======== + +As of Dungeon Crawl Stone Soup 0.3 the previously hard-coded monster +speech has been outsourced. This makes changing existing messages, +or adding new ones really easy. This file will hopefully help you in +this endeavour. + +The outsourced messages are used to create two databases out of which +Crawl randomly draws the necessary speech text. + +* shout.db (handling speech when monsters first notice you), and +* speak.db (for all other cases). + +Because of the amount of definitions necessary, they have been divided +over a number of different files. + +The shout database is constructed from the following two files: + +* shout.txt handles message output for monsters noticing you +* insult.txt handles insults thrown at you by imps and demons + +The speak database contains messages defined in these files: + +* monspeak.txt handles messages for monsters communicating with you +* wpnnoise.txt handles randart weapons with the noises property +* godspeak.txt handles randomized speech by the gods, as well as + speech used for some divine abilities +* insult.txt Same file as above. + +The messages defined in insult.txt form a part of both databases. +Apart from that, keywords and statements defined for one database +cannot be automatically accessed from the other. Rather, they have to +be defined a second time. + +Whenever Dungeon Crawl is started, the game checks whether any of the +databases needs to be updated. If one of the underlying files has been +changed since the last check, the database is automatically rerolled. +That means that if you'd like to change one of the descriptions or add +some new monster speech all you have to do is modify the file, save, +and restart the game to test your changes. + + +Contents: + A. Monster speech probabilities + B. A simple example + C. Key lookup in detail + D. Values in detail + E. Testing your changes + F. Publishing your changes + APPENDIX: List of monster glyphs + + +A. Monster speech probabilities +================================ + +Not all monsters are equally likely to speak. Rather there are +different chances involved, depending on several attributes, and most +of the time the database lookup stage isn't even reached. + +First, the player will only ever hear monsters speak if they are in +line of sight, and monsters will only ever speak if they are not +asleep, not submerged in water, air or lava, and not wandering around +aimlessly (unless neutral). + +Berserk monsters are too busy killing and maiming to speak. Also, +invisible monsters the player can't see (for lack of see invisible) +will always stay silent, unless confused. + +Monsters capable of speech (i.e. all intelligent humanoid monsters, as +well as all uniques and some non-unique demons) have a base chance of +1/21 of speaking, while humanoid monsters incapable of speech will +never communicate with the player in any form. + +Non-humanoid monsters get a 1/84 probability of "speaking" per turn +(non-verbal actions, more like). This chance is divided by another 10, +if the monster in question was generated as a member of a group. +Chances are again doubled if this non-humanoid monster is fleeing, and +doubled again if confused. + +Neutral monsters only speak half as often, and for charmed monsters +the probability is divided by 3. The same applies to silenced +monsters, i.e. monsters that are not naturally silent will only get to +even attempt to speak in one out of three tries where the above +chances hold. + +Note that the definition of which monsters are capable of speech is +entirely hardcoded. We think we made this apply to all sensible +monsters, i.e. all intelligent humanoid monsters, but of course it is +possible we've overlooked something, so if you find that your +carefully constructed monster speech never gets printed, and this +documentation also doesn't help you solve the problem, you might want +to post a bug report on Dungeon Crawl's SourceForge site [1]. + +The exception to the above is when the monster goes away due to dying, being +banished, or a summoned monster being abjured or having it's time run out. In +that case the monster always speaks if the player can see the grid the monster +is on (assuming that there's a speech entry defined for that occasion, of +course). + +B. A simple example +==================== + +If you have a look at some of the speech files, you'll see that all +entries have basically the same structure: a key, followed by one or +more values. +Here is an example. + + %%%% + # Friendly imps are very common so they speak very rarely + friendly '5' + + w:1 + @The_monster@ laughs. + + w:1 + @_friendly_imp_@ + + __NONE + %%%% + +Let's look at this entry's components in more detail. + + %%%% + +Four percentage signs mark beginning and end of a database entry. If +you forget to place these, you will get buggy monster speech. + + # Friendly imps are very common so they speak very rarely + +A '#' sign at the beginning of a line causes it to be ignored; these +are comment lines. + + friendly '5' + +The first non-comment, non-blank line is interpreted as the key of an +entry. Many keys are hardcoded, but there's place for user defined +ones as well. More on that later, though. +In this case, the key is "friendly '5'". + +'5' refers to the monster glyph, so the speech will not be entirely +restricted to imps, though they are by far the most common type of +minor demons. + + w:1 + @The_monster@ laughs. + +The rest of the entry consists of messages, separated by blank +lines. This is one of them. Each may be prefixed with an optional +weight ("w:1"). A message will be chosen with a probability of its +weight out of the sum of weights for its entry. Weight defaults to 10 +if not specified. In this example, this particular message will be +selected 1 time out of 12. + + @The_monster@ laughs. + +This is the message that will be printed. The '@' markers indicate +variables that will be substituted before printing. This particular +variable "@The_monster@" is treated specially by the game; the +substitution will change based on the monster giving the speech. See +below for more details. + + w:1 + @_friendly_imp_@ + +This is another case of a substitution. Here, "_friendly_imp_" is an +entry in speak.txt. A random line from that entry will be substituted. + + __NONE + +This is a special command; it prints no message. See below for more +details on special commands. + + +C. Key lookup in detail +======================== + +Key lookup is always case-insensitive. The game will make many +different attempts when trying to find monster speech, all of which +are explained in detail below. You'll find some examples at the end of +this section. + +First, a monster may have one or more of a list of attributes that +might influence its speech. We will henceforth refer to these +attributes as prefixes. + +"friendly" is one of a couple of allowed prefixes, distinguishing the +speech from "hostile" or "neutral". All prefixes are optional and +tested in the following order: + + default <attitude> fleeing silenced confused [related] <player god> + +where <attitude> can be any of friendly, neutral or hostile. Note that +the game generally treats neutral monsters like hostiles since they +still pose a danger to players. + +The prefix "related" is added if the player and the monster share the +same genus, e.g. if you're playing a Sludge Elf, and the monster in +question is a deep elf blademaster, you both are elves and the monster +speech may reflect that. It's currently only used for friendly +humanoids who will now sometimes mention that you're family, if you +are. Stupid monsters, i.e. animals, will judge your relatedness status +according to your transformed looks while smart monsters will see +through that magic, so that e.g. vampires will recognise a vampire in +bat form as one of their own but a giant bat would think the player +really is a bat. + +The <player god> prefix is constructed according to the religious +belief of the character. If the monster in question is a member of the +orc species and the character worships Beogh, the prefix "beogh" gets +added to the list, though not for charmed orcs who will simply use the +generic friendly statements instead of the orcish followers' cheers. +If you worship one of the good gods instead (Zin, the Shining One, or +Elyvilon), the prefix "good god" is used. Conversely, worshippers of +one of the evil gods (Yredelemnul, Makhleb, Kikubaaqudgha, Lugonu, or +Beogh) will use the prefix "evil god". + +This allows fine-grained handling of monsters depending on your +character's religion status, e.g. orcs will use special speech for +Beogh worshippers, and neutral holy beings (Angel and Daeva) may +shout messages of encouragement to followers of the good gods, while +demons will attempt to slander the good gods. + +Once the entire set of prefixes has been determined, we only need +to add the monster name and start the database search. + +First we search for the complete prefix string in combination with the +monster name. Then we try omitting some very specific prefixes that +might not be so important, first skipping on "hostile", then also +ignoring religion status, then hostile and "related", then all three +of them, and then finally adding "silenced" to the list of ignored +prefixes, where applicable. + +If all of that didn't yield any results, next we'll take the complete +prefix list again, then, reading from left to right, combinations are +tested, beginning at three prefixes and ending at none. At this stage +the list of prefixes is always prepended with "default". This ensures +that, for example, fleeing uniques won't output their normal menacing +speech but rather the default speech defined for fleeing humanoids in +general. + +In practice this means that database keys starting with "default" are +the fallback solution if the exact look-up has failed. As such, the +messages should be generic enough to allow for all the possibly +skipped prefixes, or else those cases should be caught earlier, e.g. +if you have "default friendly humanoid", you should also define +"default friendly fleeing humanoid" and "default friendly confused +humanoid" (and possibly both combined) even if only with "__NONE" +(stay silent), as the general friendly messages may look odd for a +monster such afflicted. + +Only keys that match a search string perfectly (ignoring case) will +be used. Once all prefixes have been exhausted and still no match has +been found, the database lookup will try for a more general monster +description. There are several possible ways this is attempted, in the +following order: + +1. The actual monster name. + Examples: "crystal golem", "default confused moth of wrath" +2. The monster genus. + Examples: If "friendly ogre-mage" wasn't found, try "friendly ogre" + instead. Same for "dragon" if "swamp drake" was + unsuccessful. +3. Then the monster glyph, with prefix "cap-" for capital letters. + Examples: "default 'cap-J'", "default confused 'k'" +4. A group description (such as 'insect' or 'humanoid') defined by the + monster's body shape (winged, tailed etc). The definition of the + latter is entirely hardcoded, though. + Examples: "default winged insect", "default confused humanoid" + +If you are playing with tiles, you may not know the monster glyphs, +but internally the monsters are still treated the same, and even under +tiles, the glyph keys used for step 3 are entirely valid. In case you +need to know the monster glyphs for your speech definitions you'll +find a list of monster glyphs at the end of this file. Also, for many +monsters you can find out their glyph in-game by searching the +database ('?/') and entering a vague enough monster name. For example, +entering "drac" will tell you that all draconians use the symbol 'd'. + +Note that changing monster glyphs using the mon_glyph option may also +affect speech of this kind. + +For the last round (shape comparison, e.g. "winged humanoid") +occasionally an additional intelligence estimate ("stupid", "smart") +is prefixed to the search string, depending on the monster type, e.g. +a "stupid humanoid" may still be smarter than a "smart arachnid". + +Here's a list of allowed monster shapes that should hopefully be +self-explanatory: + + humanoid, winged humanoid (angels), tailed humanoid (draconians), + winged tailed humanoid (gargoyles), centaur, naga, quadruped, + tailless quadruped (frogs), winged quadruped (hippogriff), bat, + snake (also eels and worms), fish, insect, winged insect, arachnid, + centipede, snail, plant, fungus, orb (eyes), and blob (jellies). + +If no matching keys are found after all of these rounds, the monster +definitely stays silent. + + +Examples +-------- + +Example 1: + The monster we want to make "speak" is a "confused killer bee". + + However, such an entry cannot be found in the database, so the game + tries first for "default confused killer bee", then "default killer + bee", neither of which yields any results. + The monster genus is also plain "killer bee", so that doesn't help + us any. For the next round we try again with "confused 'k'", which, + by itself, also can't be found in the database, but once the prefix + comparison tries it together with "default", we have a match: + +%%%% +default confused 'k' + +SOUND:@The_monster@ buzzes around in tight circles. +%%%% + +Example 2: + This time, we're interested in "friendly fleeing related beogh orc + wizard". + + This obviously made up example also has no direct equivalent in the + database, so first we try to remove the less important prefixes, in + this case "related" and "beogh". Unfortunately, none of "friendly + fleeing related orc wizard", "friendly fleeing beogh orc wizard", or + "friendly fleeing orc wizard" has any corresponding entry in the + database, so that we now check for "default" in combination with, + one after another, all combinations of three or less prefixes. + + Three prefixes: "default friendly fleeing related orc wizard", + "default friendly fleeing beogh orc wizard", "default friendly + related beogh orc wizard", "default fleeing related beogh orc + wizard". + + Two prefixes: "default friendly fleeing orc wizard", "default + friendly related orc wizard", "default friendly beogh orc wizard", + "default fleeing related orc wizard", "default fleeing beogh orc + wizard", "default related beogh orc wizard". + + One prefix: "default friendly orc wizard", "default fleeing orc + wizard", "default related orc wizard", "default beogh orc wizard". + + No prefix: "default orc wizard". + + Sadly, none of these is successful. The genus of orc wizards is + "orc", so we retry the above using "orc" instead of "orc wizard". + The same is repeated for "friendly fleeing beogh 'o'", and we still + haven't found anything. + + This is starting to get ridiculous, so it's time for desperate + measures: + + With the help of some rather complicated functions the game works + out that orcs can be described as humanoids of average intelligence. + Thus, in a final attempt of making this orc wizard speak, we search + the database for "friendly fleeing related beogh humanoid", + something that, not surprisingly (since Beogh and humanoid don't go + well together), doesn't exist. Annoyingly enough, neither do the + variants "friendly fleeing related humanoid", "friendly fleeing + beogh humanoid" or even "friendly fleeing humanoid". + + Still, we haven't yet tried the prefix combinations: "default + friendly fleeing related humanoid" is still unsuccessful, as + are "default friendly fleeing beogh humanoid", "default friendly + related beogh humanoid", and "default fleeing related beogh + humanoid", but with "default friendly fleeing humanoid" we finally + strike gold: + +%%%% +default friendly fleeing humanoid + +w:30 +VISUAL:@The_monster@ tries to hide somewhere. + +@The_monster@ @shouts@, "WAIT FOR ME, @player_name@! Could you help me?" + +... + + We'll leave it at that, even though the database code still has work + to do, namely add up the weights of all the entries (and there are + several more), and randomly choose one of them. + + +Weapon speech +------------- +For obvious reasons, weapon noises get by without any such prefixes, +and the only hardcoded keywords are "noisy weapon" for weapons with +the noises property, and "singing sword" for (who'd have guessed?) the +Singing Sword. + +Death speech +------------ +You can define messages for the monster to give for when it goes away in three +different manners: + +* If it really died, then the game will look up a speech entry with the + same keys as usual, but with " killed" appended to all the keys. + +* If it was banished, then the game will append " banished" to all the + lookup keys. + +* If the monster was summoned and rather than being killed was abjured or + ran out of time, then the game will append " unsummoned" to all of the + lookup keys. + +The game will always do a lookup in these circumstances if the player can see +the square the monster is on, so if you only want a death message to be given +occasionally then make one of the messages "__NONE" and give it a high weight. + +Of course, if no keys with the given suffix are in the database then the +monster will say nothing in that circumstance. + +Special monster speech +---------------------- +Rarely, monster speech will also rely on hard-coded keys. If such a hard-coded +key is changed or removed, the speech in question will simply not be printed. +This may look odd in the game, but will have no other effect. Sometimes, +default messages will be output instead. + +God speech +---------- +The keys used to search for god speech are entirely hard-coded, though +some local synonyms have been defined as well. Hopefully, the comments +will explain what the different speech messages are used for. + + +D. Values in detail +==================== + +Spacing +------- + +There have to be blank lines between the different messages. If +messages are placed directly one after another they will be printed as +a block. This can be useful, e.g. for outputting first a "spell" and +then its (fake) result. + +Note that this does not work for weapon noises. Here only the first +part of a paragraph before a carriage return is parsed. + +The message entries themselves can be longer than a line, though Crawl +will simply truncate it should it exceed the screen width (assuming 80 +columns or less). The actual message length will usually differ from +the one defining an entry as parameters can be stripped from the entry +or replaced by other values, as explained in the following section. + +Variables +--------- + +Values can contain variable references, which look like text +surrounded by @@. These variables may be defined by entries in +shout.txt for the shouting database, or monspeak.txt or one of the +other files for the speech database, in which case they are replaced +with a random value from the entry in question; or they may have +hardcoded expansions defined by the game. + +Note that variable replacement is recursive, so be careful to avoid +infinite loops. Though the game will abort after a number of +replacement attempts, it will still look ugly in the monster speech. + +The following variables are hardcoded: + +@monster@ : Plain monster name, e.g. "rat" or "Sigmund" +@a_monster@ : Indefinite article plus monster name, + or only the name if it is unique ("Sigmund"). +@the_monster@ : Definite article plus monster name ("the rat"), + or a possessive if it is friendly ("your rat"), + or only the name if it is unique ("Sigmund"). +@something@ : Like @monster@, with monster name replaced by + "something" if the monster is invisible and the + player cannot see invisible. +@a_something@ : similar +@the_something@ : similar +@player_name@ : Player name. +@player_species@: Player base species, with Draconian rather than the + actual subtype. +@player_genus@ : Player genus, i.e. "Elf" rather than the exact type, + or "Ogre" instead of "Ogre-Mage". +@player_genus_plural@ : pluralised genus form. +@player_god@ : Player's god name, or "you" if non-religious. +@Player_god@ : Player's god name, or "You" if non-religious. +@god_is@ : replaced with "<god name> is" or "you are", if + non-religious. +@God_is@ : replaced with "<god name> is" or "You are", if + non-religious. +@surface@ : Whatever the monster is standing on. +@feature@ : The monster's square's feature description. +@pronoun@ : it, she, he, as appropriate +@possessive@ : its, her, his, as appropriate +@imp_taunt@ : imp type insults (see insult.txt) +@demon_taunt@ : demon type insults (see insult.txt) +@says@ : synonym of "say" that fits monsters' (hardcoded) + speech pattern and noise level. + +Capitalized forms (@Monster@, @A_something@, @Possessive@, @Pronoun@) +are expanded with capitalized text. + +Also, in insult.txt you'll find the hardcoded variables +@species_insult_adj1@, @species_insult_adj2@, and +@species_insult_noun@. These are sometimes used in the construction of +imps' or demons' generic insults, and allow for species-dependent +handling. If the parser encounters such a variable, it will search the +database for the corresponding specific entry, i.e. "insult <genus> +adj1/adj2/noun" where <genus> is replaced with the actual genus name, +that is "draconian", "elf" or "dwarf" instead of the more specific +species name, "ogre" for "ogre-mage", or the actual species name in all +other cases. + +If this specific search doesn't yield any results, i.e., such an +entry hasn't been defined for the species in question, the general +replacements are used instead. + +Weapon noises are handled differently in that most of the above +replacements don't hold. Instead you can use @The_weapon@, +@the_weapon@, @Your_weapon@, @your_weapon@ and @weapon@ which will get +replaced by "The (weapon name)", "the (weapon name)", "Your (weapon +name)", "your (weapon name)", and the plain weapon name, +respectively. +@player_name@ is expanded as above, as is @player_god@, though for +atheists it returns "atheism". + +Note that the Singing Sword, being unique, cannot be referred to by +the possessive variants, so they will be replaced with the appropriate +definite article ones. + +Examples of pre-defined variables in the database include +_high_priest_, _mercenary_guard_, _wizard_, _hostile_adventurer_, +_friendly_imp_, _hostile_imp_, and _tormentor_, but more get added all +the time. There are also a few synonyms defined at the beginning of +monspeak.txt such as for @ATTACK@, @pointless@, @shouts@, @wails@, and +others. + +Weapon noises also use a number of synonyms which are defined at the +end of wpnnoise.txt. + +The best way to learn about how variables and other concepts can be +used is probably to see how it has been done for existing messages. + +Speaking to the player vs to another monster +-------------------------------------------- + +If a message contains a variable starting with "@player", then that message +will only be used by friendly/good-neutral monsters, or by hostile/neutral +monsters which have the player as their foe (and it will never be used in the +arena). Additionally, any message containing a line starting with "You" or +ending in a bare "you." (but not a quoted "you.") will also be considered +player centric. You can also make a message player-centric by appending +"@player_only@" to the end of any of the lines in the message, which will be +removed before displaying it to the player. + +If you want a message to be able to apply to a foe which is another monster in +*addition* to the player, you can replace "@player" with "@foe" to have the +variable filled out with a string appropriate for the foe being either the +player or another monster. If "@foe_name@" is present then the message will +only be used on monster foes which are named, and if "@foe_god@" or "@god_is@" +is present then it will only be used on priestly monsters or god gift monsters. +"@Foe@" or "@foe@" will be replaced with "You" or "you" if directed at the +player, or if directed at another monster be the same as "@The_monster@" or +"@the_monster@", but with the foe monster's name rather than the speaking +monster's name. "@foe_possessive@" will be replaced with "your" if directed +at the player, or expanded like "@foe@'s" if directed at a monster. + +If you want to indicate which monster a message is directed at, you can put in +"@to_foe@" or "@at_foe@" to fill in the foe name when the message is directed +at a monster; when directed at the player the variable will be removed. For +example, with the message: + + @The_monster@ says @to_foe@, "Defend yourself!" + +If directed at an orc will expand to: + + @The_monster@ says to the orc, "Defend yourself!" + +but if directed at the player will expand to: + + @The_monster@ says, "Defend yourself!" + +You can do something similar with "@foe,@" for asking questions and the like. +For example: + + @The_monster@ asks @foe,@ "Who are you?" + +when directed at an orc will expand to: + + @The_monster@ asks the orc, "Who are you?" + +and when directed at the player will expand to: + + @The_monster@ asks, "Who are you?" + +Channels +-------- + +An optional channel name at the beginning of a string causes messages +to be sent to that channel. For example: + + SPELL:@The_monster@ casts a spell. + WARN:Your equipment suddenly seems to weigh more. + +Spacing after the channel parameter won't get stripped, so it's a good +idea to double check that the speech message directly follows the +colon. + +Here are the defined channels: + + TALK : MSGCH_TALK (Default value.) + DANGER : MSGCH_DANGER + ENCHANT : MSGCH_MONSTER_ENCHANT + PLAIN : MSGCH_PLAIN + SOUND : MSGCH_SOUND + SPELL : MSGCH_MONSTER_SPELL + VISUAL : MSGCH_TALK_VISUAL + WARN : MSGCH_WARN + +The channels have been assigned different colours and are sometimes +treated differently, e.g. any of MSGCH_TALK, MSGCH_SOUND and +MSGCH_TALK_VISUAL will never interrupt resting or travel unless +specifically added in the options file. + +Note that MSGCH_SOUND and MSGCH_TALK get filtered out when you are +silenced. For similar reasons monster speech of channel SPELL is muted +under silence, along with ENCHANT and WARN, both of which currently +only occur in combination with SPELL. To allow for silent spells along +with fake warnings and enchantments, you can combine these with VISUAL +and enforce output even when silenced. + + VISUAL ENCHANT : MSGCH_MONSTER_ENCHANT + VISUAL SPELL : MSGCH_MONSTER_SPELL + VISUAL WARN : MSGCH_WARN + +Note, though, that these only will take effect if a VISUAL message +just happens to be chosen. As stated above, the database search +doesn't really care whether a monster is supposed to be silent, so it +may pick any noisy monster speech, but the message output will care +and refuse to print such nonsense, so that in this case the monster +will actually stay silent after all. + +To summarize, chances of silent "speech" are overall lower (as is +intended) but only VISUAL messages even have a chance to be printed +under these circumstances. + +As explained earlier, "silenced" is one of the prefixes that are +regarded as "less important" and can be ignored in the exact string +search. So that both specially defined silenced messages for a +particular monster and its normal VISUAL messages can sometimes take +effect, chances for actually skipping on silenced in the direct string +matching are 50:50. + +Example 3: + The player has just cast Silence when a Killer Klown wanders into + view. (Uh oh!) This "silenced Killer Klown" is now attempting to say + something. The exact look-up is unsuccessful, but now there's a 50% + chance of skipping on the "silenced" prefix. If this route is chosen + we may get results such as + +%%%% +Killer Klown + +@The_monster@ giggles crazily. + +@The_monster@ laughs merrily. + +... + + none of which, if chosen, would actually be printed, but luckily the + "Killer Klown" entry also contains VISUAL statements like the + following: + +... + +VISUAL:@The_monster@ beckons to you. + +VISUAL:@The_monster@ does a flip. + +... + + If one of these is chosen, we get a non-verbal "speech" statement of + this silenced monster. + + However, what happens if the other 50% take effect and we will *not* + ignore the "silenced" prefix? In this case, we'll simply continue + like in the earlier examples above, get no results for either of + "default silenced Killer Klown" or "default Killer Klown", and try + the genus next: human, which cannot be found in the database, + silenced or no. Neither will we find anything for the monster glyph + '@'. Now all that remains is to check the monster shape, which is + "humanoid" again. "silenced humanoid" won't get us any results, nor + will simply "humanoid", but "default silenced humanoid" has some + statements defined. + +%%%% +default silenced humanoid + +w:30 +VISUAL:@The_monster@ says something but you don't hear anything. + +w:30 +VISUAL:@The_monster@ gestures. + +... + + All of the statements in these predefined "silenced" entries have to + be of the type VISUAL; otherwise they'll never get printed. + + +For shouts the default channel is also MSGCH_TALK, which is +automatically changed to MSGCH_TALK_VISUAL for monsters that can't +speak (animals, usually), and manually set to MSGCH_SOUND for all +those variants of "You hear a shout!" + +Monster spells and enchantments will only interrupt resting/running +if done by a non-friendly creature, and, as stated above, messages +passed through the TALK or SOUND channels never will. + +For weapon noises only a subset of the above is relevant, as anything +including VISUAL and the channel keys SPELL and ENCHANT is considered +invalid and will trigger a default message instead. Again, the default +channel is MSGCH_TALK. + +Special commands and variables +------------------------------ + +Message entries may also be one of several special commands. These +aren't variables, so they aren't surrounded by @@. Accordingly, they +are not expanded, but rather they produce special game behaviour. + + __NONE : no message + __NEXT : try a more general monster description + __MORE : enforce a "--more--" prompt + __YOU_RESIST : print "You resist." + __NOTHING_HAPPENS : print "Nothing appears to happen." + +Some special keys are defined in monspeak.txt and shout.txt, such as +__RESIST_OR_NOTHING and __SHOUT. These are normal variable expansions, +and may be used as such. They are given special-looking names because +Crawl looks up the noises a given monster can produce and looks for +keys that match the string, i.e. __SHOUT, __BARK and so on. + + +E. Testing your changes +======================== + +Get a version of Stone Soup that contains WIZARD mode. You can check +whether this is the case by reading the in-game version information +('?v'). If Wizard mode is not listed among the included features +(likely, since WIZARD builds are generally not distributed), you will +have to compile the game for yourself. + +To build Crawl yourself, download the source code from the Crawl +homepage [1] and read the "INSTALL" file in the main directory for +instructions. Should you still have any questions after reading the +documentation and checking the archives of the Crawl newsgroup [2], +ask away! + +If you have WIZARD mode compiled in, you can access special wizard +commands by pressing '&'. First answer "yes" to the safety question +and then you can test to your heart's content. Pressing '&' followed +by a number of other keys will execute wizard mode commands that are +all listed in the wizard help menu (press '&?'). + +In particular, you can create a monster with '&M', and enforce +behaviour on a monster by examining it (with 'x', as usual). In wizard +mode, examining monsters offers several new sub-commands such as 'F' +(make monster friendly/neutral/hostile) and 's' (make monster shout). +These last two are of particular interest to monster speech designers. + +Also, the Singing Sword and other hardcoded artefacts can be created +with '&o'. To create all artefacts at the same time, use '&|'. The +Elemental Staff and the spear of Voo-Doo are examples of noisy weapons. + +You can also temporarily increase the likelihood of a given message by +adding a high weight value before it, e.g. w:5000, or equally +temporarily push it into another channel (e.g. MSGCH_WARN) to make it +more noticeable. + +If you successfully got Crawl compiled, you can easily enable more +detailed debug information. All you need to do is add + + #define DEBUG_MONSPEAK + +somewhere in AppHdr.h, for example at the beginning of the section +entitled "Debugging Defines", and then compile the game anew, first +using "make clean", then "make wizard". +If you play with DEBUG_MONSPEAK compiled in, whenever the game is +searching the monspeak database you'll get extensive information on +all keys and prefixes tried. Once you're done testing don't forget to +remove (or comment out) the DEBUG_MONSPEAK setting as trying to +actually play that way would sure be annoying. + + +F. Publishing your changes +=========================== + +If you feel that your additions really add something to the game and +would like to make them available to the general public, you can post +them (in the form of a diff file, or in plain text) as a feature +request on sourceforge.net [1] or in the newsgroup [2]. + + +.. [1] http://crawl-ref.sourceforge.net + http://sourceforge.net/projects/crawl-ref + +.. [2] rec.games.roguelike.misc + Since this newsgroup is being shared with a number of other + roguelike games, it is generally considered polite to flag + subjects of posts pertaining only to Crawl with "-crawl-" or + a similar marker. + + +APPENDIX: List of monster glyphs +================================ + +Lowercase letters: +------------------ +a giant cockroach, giant ant, soldier ant +b giant bat, butterfly +c centaur (warrior), yaktaur (captain) +d all draconians +e all elves +f fungus, wandering mushroom +g goblin, hobgoblin, gnoll, boggart +h all hounds (jackal, hound, wolf, warg, war dog, hell hound) + and hogs (hog, hell-hog) +j snails: elephant slug, giant slug, giant snail +k winged insects: killer bee, bumblebee +l lizards (giant newt/gecko/iguana/lizard, gila monster, komodo + dragon), + and drakes (swamp drake, firedrake, death drake, lindwurm) +n ghouls: necrophage, ghoul, rotting hulk +o all orcs +p all ghosts, phantom, and insubstantial wisp +r all rodents (rats of all colours, and quokka) +s arachnides (giant mite, giant centipede, scorpion, wolf spider, + redback) +t minotaur +u (very) ugly thing +v all vortices, and air elemental +w all worms and larvae +x unseen horror, small abomination +y wasps, giant mosquito, giant blowfly, moth of wrath +z small zombie/skeleton/simulacrum, skeletal warrior, flying skull, + curse skull, and curse toe + +Uppercase letters: +------------------ +A Angel, Daeva +B all beetles +C all giants, ettin, cyclops, and titan +D all dragons, wyvern, and hydra +E efreet +F all frogs +G all eyes, giant spore, and giant orange brain +H hippogriff, manticore, griffon, and sphinx +I ice beast +J all jellies, oozes, and slime creature, pulsating lump, + giant amoeba, jellyfish, and acid blob +K all kobolds +L all liches +M all mummies +N all naga +O all ogres +P all plants +Q queen bee, and queen ant +R (fake) rakshasa +S all snakes, and salamander +T all trolls +U all bears +V all vampires +W all wraiths, wight, and spectral warrior/thing +X abomination, tentacled monstrosity, and Orb Guardian +Y all yaks, and sheep +Z large zombie/skeleton/simulacrum + +Numbers: +-------- +1 all Fiends, Executioner, Blue/Green Death, Balrug, Cacodemon +2 sun demon, reaper, soul eater, ice devil, and Lorocyproca +3 hellion, tormentor, blue/iron devil, neqoxec, orange/shadow demon, + hellwing, ynoxinul, and demonic crawler +4 red/rotting/hairy devil, beast, and smoke demon +5 all imps, and other minor demons (quasit, lemure, ufetubus, manes, + midge) +8 all golems, and living statues +9 all gargoyles + +Other symbols: +-------------- +# earth elemental, fire elemental, and vapour +{ water elemental +; all fish, and electric eel +@ all humans, (glowing) shapeshifter, and Killer Klown +& all pandemonium lords +* ball lightning, and orb of fire +% death cob + shadow + +For uniques, see their monster description for their subtype. diff --git a/crawl-ref/docs/develop/patch_guide.txt b/crawl-ref/docs/develop/patch_guide.txt new file mode 100644 index 0000000000..2ecc4fab2e --- /dev/null +++ b/crawl-ref/docs/develop/patch_guide.txt @@ -0,0 +1,134 @@ +How to Write a Patch +==================== + +The following is a rough guide on how to write patches to the code. It is +meant to make writing patches easier to inexperienced programmers, but +following these steps does not guarantee that any patch will actually be +accepted - that depends entirely on content. + +Also, we're talking about Stonesoup here, but the principle could be applied to +any other program as well. :) + +Required tools +-------------- +At the very least you'll need the source code, and some helpful programs like +diff and grep. The latter come preinstalled with Unix, whereas Windows users +will have to download and install them. This may be a bit of work, but is +totally worth it if you're programming a lot. You can get them at e.g. + http://gnuwin32.sourceforge.net/packages/diffutils.htm + http://gnuwin32.sourceforge.net/packages/grep.htm + +Main steps +---------- +0.) If you're not interested in coding or have no intention of compiling the + game, you can still submit patches for the documentation, descriptions + or vaults. In that case, simply ignore the steps relating to compilation + and coding. You don't even need the source code for that, instead you can + simply use the binary distributation that also contains the relevant + text files. + +1.) Get the source code, either from the most recent release + (http://sourceforge.net/projects/crawl-ref/files/) or use SVN to get the + current trunk: + svn co https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk/crawl-ref trunk + +2.) Compile the code. To do this you might have to install a compiler and/or + additional libraries. See INSTALL.txt for details. If you have any + questions, you can ask for help on crawl-ref-discuss@lists.sourceforge.net. + +3.) Once you've got everything set up, you should copy the folder containing + the files you're going to change, i.e. docs/, dat/descript/ or the entire + source/ folder, as appropriate. This is done so you can later easily create + the patch against the original version. + +4.) Now you can start tweaking the code. Depending on your coding background + you may want to experiment with smaller changes first. If you intend to + submit the patch to the dev team, please skim coding_conventions.txt. + Following these guidelines will save us some time later when integrating + the patch. + +5.) Compile again to test your changes. + +6.) Once everything works as it should, you can use diff or git to create a + patch out of the differences between the two folders, e.g. + diff -ur trunk-orig trunk-new > mypatch.diff + + You may want to apply the patch against the unmodified folder to test + whether everything worked correctly. + +7.) Upload the patch on the patch tracker of Sourceforge at + http://sourceforge.net/tracker/?group_id=143991&atid=757515. + + Here it is immensely helpful if you give a summary of what the patch is + about. If you created it in response to a bug report or feature request, + mention the id, and you might also want to post a reply in said item + pointing out your new patch. Please also mention the revision/version you + used for patching, e.g. 0.5 or r10078, and anything you think may still + need to be improved or modified. + + Thank you! :D + + +Tips +---- +Tip 1: The code is rougly divided into files according to functionality that + is reflected in the file names, so monstuff.cc, mon-util.cc and + mstuff2.cc all handle code relating to monsters, while spl-data.h, + spl-cast.cc and spells2.cc deal with spells. Note that related code can + still be found in other files, too, but these are a good starting point. + +Tip 2: Start out with the simple, generic stuff where you only have to copy + and minimally tweak existing code, and only once that works move on to + the more complicated implementations. + +Tip 3: Use grep to find all occurences of a similar instance of the same type + as the one you're implementing, e.g. + grep GOD_ELYVILON *.cc when adding a new god, or + grep SCR_FOG *.cc when adding a new scroll. + + That way you'll be able to repurpose a lot of code for your new + implementation, and it also helps cut down on coding errors. + + +Example +------- +I want to add a spell that creates some kind of clouds. The first similar spell +I can think of is Mephitic Cloud. I know that this spell is defined as +SPELL_MEPHITIC_CLOUD (and if I didn't know I could find out by grepping for +"Mephitic Cloud"), so I type (within the source folder) + grep SPELL_MEPHITIC_CLOUD *.h and + grep SPELL_MEPHITIC_CLOUD *.cc + +... which tells me that code regarding this spell turns up in enum.h (its +declaration), mon-spll.h (monsters "casting" the spell), spl-data.h (the +definition), and ghost.cc (monster spell), it_use3.cc (some kind of misc. +item, maybe), mstuff2.cc (helpfully with a comment mentioning swamp +drakes), spl-book.cc (spellbooks that contain this spell), and spl-cast.cc +(actually casting the spell). + +That gives me some ideas on where to start looking at code to duplicate +for my new spell. I'd start out with the basics, the declaration and +definition, copying all values from Mephitic Cloud and only changing the +SPELL_xxxx tag. Then I add the new spell to the list in spl-cast.cc, at which +point I'll also notice that Mephitic Cloud uses a function called +stinking_cloud() and that various other cloud spells (helpfully sorted by +effect type) use functions like cast_big_c() and others. Using grep I can +quickly find out that both functions are declared in spells1.h, meaning their +definitions can be found in spells1.cc. + +stinking_cloud() contains a definition of a beam that defines damage and +such. The various properties are not self-explanatory but they're also not +totally obscure, so you should be able to find out a lot about them by just +fiddling with the values. In particular, beam.flavour is set to something +called BEAM_POTION_STINKING_CLOUD which looks interesting enough to check +out, so I grep the source (*.h and *.cc) for all occurences and have a look +at all files this turns up. And so on. + +Evaluating and prioritising the findings takes some experience with the source +code but even if you have no idea what the files are likely to contain, using +grep still greatly reduces the number of files you have to look at. To find +the code you're interested in, search the files for the same keyword you used +for grepping. + +Good luck with your patch! If you have any questions, don't hesitate to ask. +Thank you for your support! diff --git a/crawl-ref/docs/develop/release.txt b/crawl-ref/docs/develop/release.txt new file mode 100644 index 0000000000..e370a797af --- /dev/null +++ b/crawl-ref/docs/develop/release.txt @@ -0,0 +1,122 @@ +Steps to a Successful Release +----------------------------- +The following is a step-by-step guide for how to preceed through the releasing +process. For minor releases (0.X.y), steps 0 and 1 have already been done, so +for those the list begins with step 2. + +0. Delay branching as long as possible + Try to delay additions that forseeably won't make it into the next version + until the release is not too far off. Once trunk has been branched, you + have to commit all important changes twice, and it's easy to forget one + of them. + + At the same time if something big does get introduced it's better to branch + before doing so seeing how otherwise you'll have to later turn it off in + branch before the release. + Thus, you'll need to branch as early as necessary and as late as possible. + +1. Branch trunk into the new version + svn cp https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk + https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/branches/stone_soup-0.X + +2. Update version information + version.h: Set VER_NUM = 0.X, VER_QUAL = empty. + Undefine DISPLAY_BUILD_REVISION (not needed for official distributions). + +3. Modify branch as needed + Turn off all features that are not balanced or finished enough to make it + into the release. This can be a bit of a hassle, so do try to avoid this in + the first place. (See 0.) + +4. Wait and fix some bugs + Wait at least a week for some last minute bug reports. When fixing bugs + concentrate on the important ones (crashes and gamebreakers), but you + might also want to handle the trivial ones for extra polishing. + If you add any last minute feature or bug fixes doublecheck everything, + so you don't accidentally introduce new bugs, and wait at least another + day for potential bug reports to roll in. + + Reread the entire documentation to make sure it's up to date. + Also update the change log! + +5. Sanity testing + Build the binaries (preferably on all platforms) to see if the code + compiles correctly, then run some basic sanity tests including at least + the following: + * start a new game (both prechosen and random) + * saving/loading + * being killed + * level creation for all branches/portal vaults (using &~, &L) + * accessing all help screens (including the ? submenus) + + If you want to be thorough, play a tutorial for each of the three character + builds. This way, you get to test melee, spellcasting, and ranged combat, + and at the same time you can check that the information handed out during + the tutorial is up to date. + +6. Package the source tarball and zip + On Linux, run "make package-source" in the source tree to create the source + tarball and zip. Extract the resulting packages into different folders, + compile their source and run the basic sanity tests. Also check whether + the doc, settings and dat subfolders contain all relevant files. + +7. Tag the release + svn cp https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/branches/stone_soup-0.X + https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/tags/stone_soup-0.X + + The tags are some sort of frozen state of the source for all releases, so + this is the last step you take before the actual release. All further + changes either make it into the next (minor) version, or, if they are + important bug fixes and happen before the release took place, have to be + merged into trunk AND branch AND the corresponding tag. + +8. Checkout the release tag + svn co https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/tags/stone_soup-0.X + + Do a clean checkout into a new folder, so you don't get any compilation + stuff into the distribution. Package the source (as described in 6.) and + build the binaries. If you want you can do some more sanity testing but it + shouldn't be necessary anymore. + +9. Upload the files to Sourceforge + Probably requires SF permissions for file releases. + + You could use for example rsync (on Linux) or FTP. + See https://sourceforge.net/apps/trac/sourceforge/wiki/File%20management%20service + for reference. Compare the file sizes to make sure the upload went + correctly. + + If using rsync, the correct command is: + rsync -avP -e ssh FILENAME USERNAME,crawl-ref@frs.sourceforge.net:/home/frs/project/c/cr/crawl-ref/ + +10. Create a new release in Sourceforge's file release system + Requires SF permissions for file releases. + + * Click on Project Admin -> File Manager + * Create a new folder under "Stone Soup" named by version number -> 0.X.y + * Right-click on the uploaded files to Cut and later Paste them into the + newly created folder. + * Click on a file to change its settings. + * Mark a file as default for a given platform, so the "Download Now" link + can refer the user to the most fitting file for their system. + + You can use an older release as a guideline (but don't actually modify it!) + See https://sourceforge.net/apps/trac/sourceforge/wiki/Release%20files%20for%20download + for some more detailed explanations. + +11. Update the homepage + ssh -t username,crawl-ref@shell.sourceforge.net create + + Go to /home/groups/c/cr/crawl-ref/htdocs. + Paste the changelog into a newly created 0.X.txt + Modify index.html to update the version number (the link will always point + to the most recent release) and add a new news item. For the latter, use + <a href="0.X.txt">text</a> as a link to the change log. + +12. Announce the release + Post a release announcement to rec.games.roguelike.misc and + rec.games.roguelike.announce. Also send an email over crawl-ref-discuss. + If you want you can also write a news item on Sourceforge. + +13. Lean back and enjoy the excitement + -- until the first bug reports roll in. ;) diff --git a/crawl-ref/docs/develop/tiles_creation.txt b/crawl-ref/docs/develop/tiles_creation.txt new file mode 100644 index 0000000000..1c86d59ca4 --- /dev/null +++ b/crawl-ref/docs/develop/tiles_creation.txt @@ -0,0 +1,106 @@ +A short guide to Tiles creation +=============================== + +This is a short guide to creating tiles. We assume that you've got some kind of +image editing software (such as Photoshop or the GIMP) as well as some basic +knowledge of how to use it. While the fancier functions can occasionally come +in useful, the simple tools for drawing, selection and erasing of pixels will +usually suffice. + +You may want to download the latest source or even the trunk version. Either +way, the source/rltiles/ directory contains all tiles used in the game, and +some more that are currently unused. While you can create new tiles without +having access to existing ones, it can be useful to have them around for +comparison. Also, you'll probably want to compile them into the game to test +your creations. (You don't have to, though.) For compiling the game, see +INSTALL.txt. +If you're going to change anything in the main code you'll probably want to +create a patch. See docs/patch_guide.txt for help with that. + +Crawl's tiles are png files in a format of 32x32 pixels. Larger tiles such +as for the pandemonium demons are possible, but require additional coding and +should be used sparingly. The tile background should be transparent, but if +your image software doesn't support png files with transparent background you +can also submit a png or bmp with the background coloured differently from the +rest of the tile, so we can easily remove it later. + + +1.) Before setting out to draw a new tile, you may want to check out the + UNUSED/ directory in case there's already a tile that could be used for + this purpose. + +2.) When creating new tiles you often don't need to start from zero. A lot of + the time existing tiles can be repurposed to other uses. If you tweak them + enough no-one will even notice you cheated. + For example, the spell tile deaths_door.png is basically a modified version + of the reaper, with the hour glass copied over from the slowing potion's + i-slowing.png that was then changed significantly. + +3.) For items and monsters don't forget to add a shadow. You can use standard + black for this. + +4.) New randart weapons and armour items need not only a tile for the item + itself but also a smaller variant for the player doll to wield or wear. + To define the new artefact tiles, you'll need to modify two files: + source/art-data.txt, and rltiles/dc-player.txt, with the details nicely + described in art-data.txt. + If you have perl installed, run "perl art-data.pl" from source/util. If you + made any mistakes in modifying the two files the script will complain. + + If an artefact doesn't get a special equipment tile the base type's tile + will be used instead. + +5.) Otherwise, add the tile to the relevant dc-xxxx.txt file and use the same + (uppercase) definition in the matching tileidx_xxxx() function in + tilepick.cc. Note that the latter is not necessary if you are adding tiles + for the player doll. + + Tiles are assumed to have been placed within the rltiles/ directory + structure. You can use a relative path for the tile definition, or you can + use the %sdir command to change the current default relative path. + + Use the %rim property to control whether Crawl needs to draw a black + outline around your tile or whether it already has one. To change the rim + property for your tile, use e.g.: + %rim 1 + new_tile NEW_TILE + %rim 0 + +6.) For humanoid monsters' tiles you may want to add a line in + get_weapon_offset() in tilemcache.cc to allow it to be drawn with a wielded + weapon. + +7.) New monsters may also need a unique corpse tile. In that case you'll also + need to modify dc-corpse.txt and add a tile in _tileidx_corpse() of + tilepick.cc. + + +When compiling the game you may need to delete or rename the relevant tile +section's png file, i.e. main.png (for items), player.png (for player, doll +equipment, and monsters), dngn.png (for dungeon features) or gui.png (for +spells), to make the changes take effect. The first step during the compilation +will be to rebuild this file, so you can check right away whether your new tile +was added correctly. Changes in dc-xxxx.txt automatically cause the relevant +image files to be recreated. + + +In case you'd like to draw some tiles but have run out of ideas, here's an +incomplete (and possibly outdated) list of suggestions: + +* tiles of the various runes +* zombie/skeleton tiles taking into account the monster type +* tiles for abilities +* alternative tiles for dungeon features to allow for "animations" +* equipment tiles for the player doll +* improvement of existing tiles +etc. + +You can also check the SourceForge Feature Requests for tracker items +containing the "\o/" emblem in the title. At least one of them will be a call +for more tiles. + +Please submit your new tiles as a new item on our feature request or patches +tracker, or send one of the tiles developers (ennewalker, evktalo, j-p-e-g) an +email containing your tiles. + +Thanks a lot for your support! |