Fixed some inconsistencies in the handling of Lua errors. Tweaked the lexer to allow spaces before Lua chunk prefixes.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1676 c06c8d41-db1a-0410-9941-cceddc491573
WE3JT43OR4L6675GINGU4B3YDBMURJZHDDYY3VLHUJEBAKH2HYEAC N52GRYCIYJDHUEVAZ7V3DA76FN4YQYDOO47E5MRQ3OM54DFSP3YQC MAZKIKR4PWBJQAYC37MGS5ZDSXTF2KIX62TMURVHPENBU72WCVUAC UU5EKED2RA2U3CFZ3UEJQEWSWHQPEU7ZD4KH3I22IIVZFHD4Y67QC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC ZJLJGSB2XSBQU42OFQMXL3EG4CXAQGOYAU6YTV2SAWZEJIPFH2CAC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC WKTZHLOJ65WSK6FR5MF7RWGSMZ22T2D6LHB66FV3IPGXIBLYHHNAC NCDWWDJQLAU5ORSAQGZKJJ5E22VTDGGPJMVVBWQFHQ2B3U3UFHDQC B3SRWSFITQMJRVEBHGQQJARETYPSSDV6XKMQSSUTXEHTXRZKIQJQC SCWXQW5H65OXUP2MEJ2MEEAVPSRJDT3RQGKYCMKVTORS2334PQSQC RGHXFBNIULRVRYLBGG5JZDMYVM2E2JJ2Y5KQPMU6PUS3V26G6ZXQC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC R6XS2HO5QX2FJUGL5UQQRNETKCMYWTUFPHPPS5SYWK3OQA4UDUQQC ILN2K6ASDZSMEHOPJ22IZLZJUO6DDGZTKAKXM3YXG6JZZHJNLX4AC const char *channel = lua_tostring(ls, 2);if (channel)ch = str_to_channel(channel);if (ch == -1)
if (lua_isnumber(ls, 2))ch = luaL_checkint(ls, 2);else{const char *channel = lua_tostring(ls, 2);if (channel)ch = str_to_channel(channel);}if (ch < 0 || ch >= NUM_MESSAGE_CHANNELS)
Before explaining the many technical details of the level syntax, we givea fictional temple entry so that you can the general map structure by wayof example. This is a _bad_ entry - do not recycle it!
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.
First of all, each and every map consists of a name, a header and the actualmap itself (order is often not important but try to stick to this one).Note that lines starting with # are comments. The keywords are explainedvery briefly after the map and in detail in the following sections.
E. Conditionalising levels-----------------------------Crawl translated level (.des) files into Lua code chunks and runsthese chunks to produce the final level that is generated. While youdon't need to use Lua for most levels, using Lua allows you toconditionalise 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:1ORIENT: floatMAPxxxxxxxxxxxxxxxxxxxx........{........xxxxAxxxxxBxxxxxCxxxxxx.xxxxx.xxxxx.xxxxxx@xxxxx@xxxxx@xxxENDMAPNow let's say you want A, B, and C to be randomly rock or floor, but Bshould be floor if both A and C are rock. Here's one way to do it (addthese 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"): endThis 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 beingrock, and the same for C. If we made both A and C rock, we force B tobe floor, otherwise we use a subst that gives B the same 50% chance ofbeing rock.You can conditionalise on various factors, such as player experiencelevel:NAME: condition_002DEPTH: 1-27ORIENT: float: if you.xl() > 18 thenMONS: greater mummy: elseMONS: deep elf priest / deep elf sorcerer / deep elf demonologist: endMAPxxxxxxx1...xx1...+x1...xxxxxxxENDMAPOr based on where the map is being generated:NAME: condition_003DEPTH: Elf:*, Orc:*ORIENT: float: if you.branch() == "Orc" thenMONS: orc priest, orc high priest: elseMONS: deep elf priest, deep elf high priest: endMAPxxxxxxx1...xx2...+x1...xxxxxxxENDMAPWhen conditionalising maps, remember that your Lua code executes intwo 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 APIin general, because the game hasn't started. This is generallyignorable (as in the case above) because the compilation phase justchecks 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_004DEPTH: Elf:*, Orc:*ORIENT: float: if you.branch() == "Orc" thenMONS: orc priest, orc high priestMAPxxxxxxx1...xx2.I.+x1...xxxxxxxENDMAP: elseif you.branch() == "Elf" thenMONS: deep elf priest, deep elf high priestMAPxxxxxxx1...xx2.U.+x1...xxxxxxxENDMAP: endThis map will break the compile with the cryptic message "Must definemap." (to compound the confusion, the line number for this error willbe the first line number of the map following the buggy map).
E. Hints for level makers
This error is because although the map is Elf or Orc only, at compiletime, the branch is *neither* Elf nor Orc.Lua code can detect the compile phase using crawl.game_started() whichreturns true only when the player has started a game (and will returnfalse when the map is being initially compiled).For more details on the available Lua API and syntax, see the Luareference section.F. Validating levels-----------------------If you have a map with lots of transforms (SUBST and SHUFFLE), andwant to guarantee that the map is sane after the transforms, you canuse a validation hook.To take a very contrived example:NAME: contrived_001PLACE: D:2ORIENT: floatTAGS: no_pool_fixupSUBST: .=.wSUBST: c=x.MAPxxxxxxx{.+.cx..+>xxxxxxxENDMAPThis map has a chance of leaving the player stuck on the upstairwithout access to the rest of the level if the two floor squares nearthe doors are substituted with deep water (from the SUBST line), orthe 'c' glyph is substituted with rock. Since a cut-off vault isuncool, 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 toleave the vault (without digging, etc.) from the position of the {glyph. (This takes things like the merfolk ability to swim intoaccount, so a merfolk character may see deep water between the stairand door.)The validate Lua returns false (or nil) to indicate that the map isinvalid, which will force the dungeon builder to reapply transforms(SUBST and SHUFFLE) and validate the map again. If the map failsvalidation enough times, the dungeon builder will discard the entirelevel 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 playercan reach the > downstair, you can use:validate {{ return glyphs_connected('{', '>') }}NOTE: You cannot use the colon-prefixed syntax for validation Lua. Ifyou 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
H. Lua reference-------------------How maps are processed----------------------Crawl uses Lua heavily when dealing with .des files:* Level files are compiled into a series of Lua chunks. Each map canhave one or more Lua chunks associated with it: the prelude, thebody, and a validation chunk. The body is mandatory, but prelude andvalidation chunks are necessary only if your map needs validation orfancy selection criteria.* When first compiling a .des file, Crawl compiles each map's Luachunks, then compiles and runs the prelude, body and validationimmediately to verify that the Lua code is not broken. Lua errors atthis stage will cause Crawl to exit with an error message (hopefullyrelevant). Note that the validation Lua chunk's return code iscompletely ignored at this stage - it is only run to check forsyntax errors in the code.* When a new game is started, Crawl will run the Lua preludes for allmaps (most maps should have no prelude - map preludes slow the gamedown). At this point, preludes can change the map's placement oravailability.* When the dungeon builder selects a map (based on TAGS, DEPTH,PLACE), it re-runs the map prelude and the map body, appliestransforms (SUBST, SHUFFLE) if any, then calls the map's validationLua. If the map passes validation, the dungeon builder continueswith 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 isnot associated with any specific map, but with all maps in the file.This is called the global prelude. The global prelude is run beforerunning any other Lua code in the file, once during compilation, andonce at start of game.You can use the global prelude to define functions and set up globalsthat the rest of the maps in the .des file use. If you have a lot ofcommon 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 endof the line. E.g.: crawl.mpr("Hello")Colon-prefixed lines are always in the main Lua chunk, unless they occurbefore any map definitions, in which case they go to the global prelude.* Lua blocks for the main (body) Lualua {{ <code> }}orlua {{<code>}}NOTE: Colon-prefixed lines, or lua {{ }} blocks defined before anymap's NAME: directive will add the Lua code to the global prelude.* Lua blocks for the prelude:prelude {{ <code> }}orprelude {{<code>}}* Lua blocks for the validate chunk:validate {{ <code> }}orvalidate {{<code>}}Debugging Lua-------------Since Lua action happens in the guts of Crawl, it can be hard to tellwhat's going on. Lua debugging involves the time-honoured method ofpeppering your code with print statements:* Use error() or print() for compile-time work (i.e. when Crawl readsthe .des file). Note that print() just writes to the terminal andkeeps going, while error() forces Crawl to exit immediately (atcompile time; errors during level-generation are handleddifferently).* Use crawl.mpr() for output when the game has started (atlevel-generation time).It's very important that your finished level never croaks duringlevel-generation. A Lua error at this stage is considered a validationfailure.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 environmentsuch that function calls written as:fn(x, y, ...)are translated todgn.fn(map, x, y, ...)where "map" is the reference to the map that the currently executingLua chunk belongs to. This is only for Lua chunks that belong to amap, 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.Lua API - global game state---------------------------The "crawl" module provides functions that describe the game state orprovide utility methods.mpr, mesclr, random2, 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, argsLua 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