From a90da56f3e29ff3399a01c28f9e7a66a05b74990 Mon Sep 17 00:00:00 2001 From: dshaligram Date: Sat, 5 May 2007 07:45:25 +0000 Subject: Merge trunk (1403-1409] back to 0.2: - Level-design howto. - Horn of Geryon now jelly-safe. - Mac users can double-click to run Crawl. - Prices of rings of slaying calculated correctly. - Fixed travel cache losing stair distances. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/branches/stone_soup-0.2@1410 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/docs/level-design.txt | 564 ++++++++++++++++++++++++++++++++++++++++ crawl-ref/source/AppHdr.h | 1 + crawl-ref/source/chardump.cc | 2 +- crawl-ref/source/externs.h | 12 +- crawl-ref/source/files.cc | 25 +- crawl-ref/source/files.h | 5 +- crawl-ref/source/food.cc | 4 +- crawl-ref/source/initfile.cc | 56 ++-- crawl-ref/source/macro.cc | 2 +- crawl-ref/source/makefile.unix | 6 +- crawl-ref/source/monstuff.cc | 49 ++-- crawl-ref/source/newgame.cc | 4 +- crawl-ref/source/shopping.cc | 2 +- crawl-ref/source/spl-book.cc | 12 + crawl-ref/source/travel.cc | 25 +- 15 files changed, 697 insertions(+), 72 deletions(-) create mode 100644 crawl-ref/docs/level-design.txt diff --git a/crawl-ref/docs/level-design.txt b/crawl-ref/docs/level-design.txt new file mode 100644 index 0000000000..923f7d313c --- /dev/null +++ b/crawl-ref/docs/level-design.txt @@ -0,0 +1,564 @@ +How to make levels for Dungeon Crawl Stone Soup +=============================================== + +Contents: A. Introduction + B. Sample map + C. Map symbols + D. Header information + E. Hints for level makers + + +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: + + splev.des: * branch endings (like Elf:7, Vaults:8) + * premade level maps (like the Ecumenical Temples and Geryon) + * Pan lairs of named demons + * branch entries (these can ornament the stairs for a branch) + + vaults.des: * random vaults (Crawl sometimes chooses a special level + when making up a new level; these are often challenging + and sometimes contain loot) + * entry vaults (each game - except turorials - uses one of + these premade maps for the vicinity of the entrance) + * minivaults + * Pan minivaults + + tricky.des: * a few entry vaults which are harder in some regard + (use of special items, or being puzzles) + + asciiart.des: * a few entry vaults which contain proper ASCII art. + These are a matter of taste - some find this annoying. + + +Kinds of Vaults +--------------- +The different kinds of vaults used by Crawl are described briefly below. In +most cases, when the dungeon builder places a vault on a level, the rest of the +level (assuming the vault is not a full-map vault) is generated as +rooms+corridors. The only exceptions to this are branch entry vaults and +minivaults, both of which are placed after the rest of the level is generated, +and therefore do not influence level generation. + +* 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 "_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). + + +B. Sample Map +--------------- + +Before explaining the many technical details of the level syntax, we give +a fictional temple entry so that you can the general map structure by way +of example. This is a _bad_ entry - do not recycle it! + +First of all, each and every map consists of a name, a header and the actual +map itself (order is often not important but try to stick to this one). + +Note that lines starting with # are comments. The keywords are explained +very briefly after the map and in detail in the following sections. + +# name below: +NAME: a_useless_temple_entry_02 +# header section below: +ORIENT: float +CHANCE: 5 +TAGS: temple_entry +FLAGS: no_rotate +SHUFFLE: de +SUBST: 1=12. +MONS: butterfly, plant +ITEM: stone, w:10 any_good book / w:90 nothing +# actual map below: +MAP +xx1@2xx +x1wWw2x +ewwOwwd +x2www1x +xx1.1xx +ENDMAP + +"ORIENT: float" tells the level builder that this entry can be anywhere on the + level; other ORIENT: values can force a map to one edge of the + level. +"CHANCE: 5" makes it appear less often (default is 10). +"TAGS: temple_entry" turns the 'O' on the map into stairs to the Temple. +"FLAGS: no_rotate" forbids rotation (but mirroring is still allowed). +"SHUFFLE: dw" may replace all 'd' with 'w' in the map. +"SUBST: 1=12." may replace each '1' with either '1' or '2' or '.'. +"MONS: butterfly, plant" turns all '1' into butterflies, and '2' into plants. +"ITEM: stone" turns all 'd' into stones +"ITEM: w:10 any book / w:100 nothing" turns all 'e' into a book + (with 10% chance) or creates no object (with 90% chance). + +The symbols on the map: + x - rock wall + w - water (could be deep or shallow) + W - shallow water + . - plain floor + @ - entry point (this square will be connected to the rest of the map) + O - stairs to the Temple + 1 - first monster from the list (here butterfly) - note the SUBST: 1=12. + 2 - second monster from the list (plant) + d - first item from the list (here stones) + e - second item from the list (here occassionally a book) + + +D. 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) + 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 turned into shallow water; prevent this with the + no_pool_fixup TAG. Also, this may receive water creatures! For entry + vaults, avoid this with the no_monster_gen TAG. + l - lava - again, use the no_monster_gen TAG for entry vaults! + +Features +-------- + @ - entry point - must be on outside and, except in ORIENT:float layouts, + must always be on a particular side or sides - see templates. 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. + }{ - stairs 82/86 - 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. + )( - stairs 83/87 + ][ - stairs 84/88 + + >< - extra rock stairs - you can leave level by these but will rarely be + placed on them from another level + + 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 (eg of Zin etc) in dungeon.cc, + in order. + C - Random Altar. + + F - Usually a Granite Statue, but may be Orange or Silver or Ice (1 in 100) + G - Granite statue (does nothing) - you can see through but not walk through + H - orange crystal statue (attacks mind) + S - Silver statue (summons demons). Avoid using (rare). + + T - Water fountain + U - Magic fountain + V - Permanently dry fountain + +# Note: Due to the level maker having seen incremental improvements over the +# years, there are some inconsistencies. For examples, dangerous statues +# (orange, silver ice) are now genuine monsters. In particular, the 'G' and 'S' +# glyphs could be dispensed with (but many older vaults use them, of course) - +# especially as there's no glyph for ice statues. +# +# Also, the most of the other feature glyphs can be replaced with KFEAT: +# lines. The same goes for some item glyphs ('O', 'P', 'R', 'Z') which could +# be replaced by KITEM: lines. + +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. + P - maybe place a rune here (50%) + R - honeycomb (2/3) or royal jelly (1/3) + Z - the Orb of Zot + d-k - item array item. See section below on ITEM: arrays for more info. + +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 +----------------------- + +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, unless you want the vault to + be a minivault, which is usually not what you want. Valid values are: + * "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 + ...which brings us to padding. With any some_direction orientation, + you need 6 layers of x-padding along any level-edge that the vault + borders. For instance, if your map is ORIENT: north, you must have a + 6 deep border of rock wall (or any other kind of wall) along the + northern, eastern, and western edges of the map. + * "encompass": the vault completely occupies the entire level. + Padding is needed on all 4 sides. + +DEPTH: For random vaults and minivaults, this gives the range where the vault + may be placed in the main dungeon. E.g. + DEPTH: 7-20 + +CHANCE: (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 CHANCE: is 10. The + likelihood of a vault getting picked is: + [vault's CHANCE: / sum of all CHANCE:s of vaults of that type] + +PLACE: Used to specify certain special levels. Existing special levels are: + Temple, Hell, Dis:7, Geh:7, Coc:7, Tar:7, Hive:4, Vault:8, Snake:5, + Elf:7, Slime:6, Blade, Zot:5, Tomb:1, Tomb:2, Tomb:3, Swamp:5. + + 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: generate_awake, no_item_gen, no_monster_gen, no_pool_fixup, orc_entry + Tags go an a TAGS: line and are space-separated. Valid tags are: + * "entry": this tag MUST be there for a vault to be pickable as + an entry vault. + * "generate_awake": Monsters placed (using MONS, KMONS) in this + vault will be generated awake. + * "no_item_gen": Prevents random item generation in the vault. + Items explicitly placed by the vault are not affected. + * "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. + * "no_pool_fixup": prevents water squares next to land from being + randomly converted from deep water (the default) to shallow. + * "branch_entry" eg. "orc_entry", "lair_entry" etc. Currently used: + temple_entry, orc_entry, vault_entry, lair_entry, hive_entry. + If chosen, these maps will contain the stairs for that branch. + Use "O" to place the stairs. Branch entries should go to splev.des. + As long as a branch has very few entries, a dummy one is a must. + * "mnoleg" or the name of some other pandemonium lord. This makes + the map eligible for said pan lord's lair (and it can't appear + anywhere else). + +FLAGS: no_rotate, no_hmirror, no_vmirror + Flags go on a FLAGS: line and are space-separated. Valid flags are: + * "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. + +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: + * "good_item" makes the builder try to make the item a good one. + * "any" by itself gives random choice; you can combine "any" with + "good_item." + * "any book", "any misc" etc. gives a random item of that class. + + Limitations: You can't affect stack quantity for stackable items, + nor can you affect curse status nor item race, nor can you give + specific egos, nor can give fixedarts. 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. + You can use weights as for ITEM: lines. + + Individual monsters may be prefixed with the "generate_awake" + (without the quotes). Use this sparingly. + Note that 8, 9, 0 also place monsters (see the table). + + +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. + +KFEAT: Z = C / needle trap / antique armour shop / altar of 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. + + If you want no feature as an option in a KFEAT line, use 'floor'. + If you do not want to specify the type of shop, use 'any shop' or + 'random shop'. + + 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:). Fr example, + KFEAT: Z = W + KMONS: Z = rat + places a rat on a shallow water square for all occurrences of Z. + +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. + + For items like gold or fountains, you have to use the description + of items instead of their shortcuts. For example, + KITEM: none / gold + works but the following does not: + KITEM: none / $ + +E. Hints for level makers +---------------------------- + +* Technical stuff: + If your map is not a minivault or a floating vault, make sure the + side(s) forming the border have a rock wall padding at least 6 deep. For + instance, if your map is ORIENT: north, you must have a 6 deep border of + rock wall (or any other kind of wall) along the northern, eastern, and + western edges of the map. If you're doing a fullscreen map (encompass), + you must pad all around the map with 6 layers of wall. + + 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). + + The entry point '@' must be present (except full-screen vaults where it + must not). For ORIENT: float maps, all @ will be connected to floors in + the rest of the map. Make sure that no part of your entry level can be + cut off! If no @ is present, the level builder will put one on a random + floor space '.' at the circumference. (Sometimes this may be used for + good effect. When you give no @'s with this feature in mind, please make + a comment stating this - else somebody may just add @'s later on :) + + Note that non-rectangular maps will be padded with rock walls for the + smallest rectangle containing them. Unfortunately. + + 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 now very supportive for making a single map + appear in many versions. Use the SHUFFLE: and SUBST: directives and look + at the existing entry vaults. Besides reducing tedium, this avoids giving + veterans a spoiled edge. For 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 CHANCE 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 (to me at least) to have a theme in mind before + making the actual level. For entry vaults, something simple like + 'fortress' or 'forest' 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 + necromancy or Traps & Doors) is even better. + +* Testing your maps. + This is easy for entry vaults. Temporarily introducing a CHANCE: 5000 + will make your entry appear almost always. For other vaults, you can + for the moment declare them as entry vaults with a huge CHANCE: 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 CHANCE: again, if the map has alternatives (like Snake:5, or + Coc:7). + + 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 degraded to tricky.des (or just removed). 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. + +* levdes.vim. + Your choice of editor can greatly affect your level building. While vi's + reputation may be close to Crawl's when it comes to fiendishness, there + is a style file (levdes.vim in the dat directory) which helps tremendously + (it colours the various glyphs on the maps, and it highlights the syntax). + Even Windows users can profit from this by using gvim, a freeware vim + clone with a graphical user interface which knows shortcuts like Ctrl-X + etc. diff --git a/crawl-ref/source/AppHdr.h b/crawl-ref/source/AppHdr.h index 8bad5c10e4..29215ba520 100644 --- a/crawl-ref/source/AppHdr.h +++ b/crawl-ref/source/AppHdr.h @@ -174,6 +174,7 @@ // NT and better are happy with /; I'm not sure how 9x reacts. #define FILE_SEPARATOR '/' + #define ALT_FILE_SEPARATOR '\\' // Uncomment to play sounds. winmm must be linked in if this is uncommented. // #define WINMM_PLAY_SOUNDS diff --git a/crawl-ref/source/chardump.cc b/crawl-ref/source/chardump.cc index 3319e307ad..1ed9daeeaa 100644 --- a/crawl-ref/source/chardump.cc +++ b/crawl-ref/source/chardump.cc @@ -962,7 +962,7 @@ static std::string morgue_directory() { std::string dir = !Options.morgue_dir.empty()? Options.morgue_dir : - SysEnv.crawl_dir ? SysEnv.crawl_dir : ""; + !SysEnv.crawl_dir.empty() ? SysEnv.crawl_dir : ""; if (!dir.empty() && dir[dir.length() - 1] != FILE_SEPARATOR) dir += FILE_SEPARATOR; diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 6bdf8f1562..004cc278c4 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -1132,11 +1132,13 @@ extern std::vector ghosts; struct system_environment { - char *crawl_name; - char *crawl_pizza; - char *crawl_rc; - char *crawl_dir; - char *home; // only used by MULTIUSER systems + std::string crawl_name; + std::string crawl_pizza; + std::string crawl_rc; + std::string crawl_dir; + std::string crawl_base; // Directory from argv[0], may be used to + // locate datafiles. + std::string home; // only used by MULTIUSER systems bool board_with_nail; // Easter Egg silliness #ifdef DGL_SIMPLE_MESSAGING diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index 07b4676347..0f1b12150c 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -218,6 +218,19 @@ std::vector get_dir_files(const std::string &dirname) return (files); } +std::string get_parent_directory(const std::string &filename) +{ + std::string::size_type pos = filename.rfind(FILE_SEPARATOR); + if (pos != std::string::npos) + return filename.substr(0, pos + 1); +#ifdef ALT_FILE_SEPARATOR + pos = filename.rfind(ALT_FILE_SEPARATOR); + if (pos != std::string::npos) + return filename.substr(0, pos + 1); +#endif + return (""); +} + static bool file_exists(const std::string &name) { FILE *f = fopen(name.c_str(), "r"); @@ -277,9 +290,14 @@ static bool create_dirs(const std::string &dir) return (true); } -std::string datafile_path(const std::string &basename, bool croak_on_fail) +std::string datafile_path(const std::string &basename, + bool croak_on_fail, + bool test_base_path) { - std::string cdir = SysEnv.crawl_dir? SysEnv.crawl_dir : ""; + if (test_base_path && file_exists(basename)) + return (basename); + + std::string cdir = !SysEnv.crawl_dir.empty()? SysEnv.crawl_dir : ""; const std::string rawbases[] = { #ifdef DATA_DIR_PATH @@ -291,7 +309,6 @@ std::string datafile_path(const std::string &basename, bool croak_on_fail) const std::string prefixes[] = { std::string("dat") + FILE_SEPARATOR, - std::string("data") + FILE_SEPARATOR, std::string("docs") + FILE_SEPARATOR, std::string("..")+FILE_SEPARATOR+std::string("docs")+FILE_SEPARATOR, std::string("..") + FILE_SEPARATOR, @@ -312,6 +329,8 @@ std::string datafile_path(const std::string &basename, bool croak_on_fail) } #ifndef DATA_DIR_PATH + if (!SysEnv.crawl_base.empty()) + bases.push_back(SysEnv.crawl_base); bases.push_back(""); #endif diff --git a/crawl-ref/source/files.h b/crawl-ref/source/files.h index ff0af7c108..59ea5a9bde 100644 --- a/crawl-ref/source/files.h +++ b/crawl-ref/source/files.h @@ -27,8 +27,9 @@ extern FixedArray tmp_file_pairs; std::string datafile_path(const std::string &basename, - bool croak_on_fail = true); - + bool croak_on_fail = true, + bool test_base_path = false); +std::string get_parent_directory(const std::string &filename); bool check_dir(const std::string &what, std::string &dir); bool travel_load_map( char branch, int absdepth ); diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index 185b83185e..81da3e2164 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -1033,8 +1033,8 @@ static void eating(unsigned char item_class, int item_type) restore_stat(STAT_ALL, false); break; case FOOD_PIZZA: - if (SysEnv.crawl_pizza && !one_chance_in(3)) - snprintf(info, INFO_SIZE, "Mmm... %s", SysEnv.crawl_pizza); + if (!SysEnv.crawl_pizza.empty() && !one_chance_in(3)) + mprf("Mmm... %s.", SysEnv.crawl_pizza.c_str()); else { temp_rand = random2(9); diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc index b948c557e8..4bd3855747 100644 --- a/crawl-ref/source/initfile.cc +++ b/crawl-ref/source/initfile.cc @@ -882,11 +882,11 @@ void game_options::add_cset_override(char_set_type set, dungeon_char_type dc, std::string read_init_file(bool runscript) { const char* locations_data[][2] = { - { SysEnv.crawl_rc, "" }, - { SysEnv.crawl_dir, "init.txt" }, + { SysEnv.crawl_rc.c_str(), "" }, + { SysEnv.crawl_dir.c_str(), "init.txt" }, #ifdef MULTIUSER - { SysEnv.home, "/.crawlrc" }, - { SysEnv.home, "init.txt" }, + { SysEnv.home.c_str(), "/.crawlrc" }, + { SysEnv.home.c_str(), "init.txt" }, #endif { "", "init.txt" }, #ifdef WIN32CONSOLE @@ -1527,10 +1527,17 @@ void game_options::read_option_line(const std::string &str, bool runscript) else if (key == "lua_file" && runscript) { #ifdef CLUA_BINDINGS - clua.execfile(field.c_str()); - if (clua.error.length()) - fprintf(stderr, "Lua error: %s\n", - clua.error.c_str()); + const std::string lua_file = datafile_path(field, false, true); + if (lua_file.empty()) + { + fprintf(stderr, "Unable to find lua file: %s\n", field.c_str()); + } + else + { + clua.execfile(lua_file.c_str()); + if (clua.error.length()) + fprintf(stderr, "Lua error: %s\n", clua.error.c_str()); + } #endif } else if (key == "colour" || key == "color") @@ -1786,14 +1793,7 @@ void game_options::read_option_line(const std::string &str, bool runscript) { // We shouldn't bother to allocate this a second time // if the user puts two crawl_dir lines in the init file. - if (!SysEnv.crawl_dir) - SysEnv.crawl_dir = (char *) calloc(kPathLen, sizeof(char)); - - if (SysEnv.crawl_dir) - { - strncpy(SysEnv.crawl_dir, field.c_str(), kPathLen - 1); - SysEnv.crawl_dir[ kPathLen - 1 ] = 0; - } + SysEnv.crawl_dir = field; } else if (key == "race") { @@ -2360,17 +2360,22 @@ void game_options::read_option_line(const std::string &str, bool runscript) } } +static std::string check_string(const char *s) +{ + return (s? s : ""); +} + void get_system_environment(void) { // The player's name - SysEnv.crawl_name = getenv("CRAWL_NAME"); + SysEnv.crawl_name = check_string( getenv("CRAWL_NAME") ); // The player's pizza - SysEnv.crawl_pizza = getenv("CRAWL_PIZZA"); + SysEnv.crawl_pizza = check_string( getenv("CRAWL_PIZZA") ); // The directory which contians init.txt, macro.txt, morgue.txt // This should end with the appropriate path delimiter. - SysEnv.crawl_dir = getenv("CRAWL_DIR"); + SysEnv.crawl_dir = check_string( getenv("CRAWL_DIR") ); #ifdef DGL_SIMPLE_MESSAGING // Enable DGL_SIMPLE_MESSAGING only if SIMPLEMAIL and MAIL are set. @@ -2383,17 +2388,24 @@ void get_system_environment(void) #endif // The full path to the init file -- this over-rides CRAWL_DIR - SysEnv.crawl_rc = getenv("CRAWL_RC"); + SysEnv.crawl_rc = check_string( getenv("CRAWL_RC") ); // rename giant and giant spiked clubs SysEnv.board_with_nail = (getenv("BOARD_WITH_NAIL") != NULL); #ifdef MULTIUSER // The user's home directory (used to look for ~/.crawlrc file) - SysEnv.home = getenv("HOME"); + SysEnv.home = check_string( getenv("HOME") ); #endif } // end get_system_environment() +static void set_crawl_base_dir(const char *arg) +{ + if (!arg) + return; + + SysEnv.crawl_base = get_parent_directory(arg); +} // parse args, filling in Options and game environment as we go. // returns true if no unknown or malformed arguments were found. @@ -2427,6 +2439,8 @@ bool arg_seen[num_cmd_ops]; bool parse_args( int argc, char **argv, bool rc_only ) { + set_crawl_base_dir(argv[0]); + if (argc < 2) // no args! return (true); diff --git a/crawl-ref/source/macro.cc b/crawl-ref/source/macro.cc index 2420971a40..ee444f24f7 100644 --- a/crawl-ref/source/macro.cc +++ b/crawl-ref/source/macro.cc @@ -171,7 +171,7 @@ static std::string get_macro_file() { std::string dir = !Options.macro_dir.empty()? Options.macro_dir : - SysEnv.crawl_dir? SysEnv.crawl_dir : ""; + !SysEnv.crawl_dir.empty()? SysEnv.crawl_dir : ""; if (!dir.empty()) { diff --git a/crawl-ref/source/makefile.unix b/crawl-ref/source/makefile.unix index e1516fb05e..6ab25c5445 100644 --- a/crawl-ref/source/makefile.unix +++ b/crawl-ref/source/makefile.unix @@ -155,8 +155,10 @@ install: $(GAME) ifeq ($(DATADIR),) $(error DATADIR not set! Set DATADIR and run make clean install again) endif - mkdir -p $(DATADIR)/data - cp dat/*.des $(DATADIR)/data + mkdir -p $(DATADIR)/dat + cp dat/*.des $(DATADIR)/dat + cp dat/*.txt $(DATADIR)/dat + cp -r lua $(DATADIR)/dat mkdir -p $(DATADIR)/docs cp ../docs/*.txt $(DATADIR)/docs chown -R $(INSTALL_UGRP) $(DATADIR) diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index fd0cf3f637..a14d593a2e 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -3784,6 +3784,32 @@ static bool monster_wants_weapon(const monsters *monster, const item_def &weap) return (true); } +static bool is_item_jelly_edible(const item_def &item) +{ + // don't eat artefacts (note that unrandarts are randarts) + if (is_fixed_artefact(item) || is_random_artefact(item)) + return (false); + + // shouldn't eat stone things + // - but what about wands and rings? + if (item.base_type == OBJ_MISSILES + && (item.sub_type == MI_STONE || item.sub_type == MI_LARGE_ROCK)) + { + return (false); + } + + // don't eat special game items + if (item.base_type == OBJ_ORBS + || (item.base_type == OBJ_MISCELLANY + && (item.sub_type == MISC_RUNE_OF_ZOT + || item.sub_type == MISC_HORN_OF_GERYON))) + { + return (false); + } + + return (true); +} + //--------------------------------------------------------------- // // handle_pickup @@ -3816,28 +3842,9 @@ static bool handle_pickup(struct monsters *monster) { int quant = mitm[item].quantity; - // don't eat artefacts (note that unrandarts are randarts) - if (is_fixed_artefact(mitm[item]) || - is_random_artefact(mitm[item])) - continue; - - // shouldn't eat stone things - // - but what about wands and rings? - if (mitm[item].base_type == OBJ_MISSILES - && (mitm[item].sub_type == MI_STONE - || mitm[item].sub_type == MI_LARGE_ROCK)) - { - continue; - } - - // don't eat special game items - if (mitm[item].base_type == OBJ_ORBS - || (mitm[item].base_type == OBJ_MISCELLANY - && mitm[item].sub_type == MISC_RUNE_OF_ZOT)) - { + if (!is_item_jelly_edible(mitm[item])) continue; - } - + if (mitm[igrd[monster->x][monster->y]].base_type != OBJ_GOLD) { if (quant > max_eat - eaten) diff --git a/crawl-ref/source/newgame.cc b/crawl-ref/source/newgame.cc index 196bf9e142..363c9f5c19 100644 --- a/crawl-ref/source/newgame.cc +++ b/crawl-ref/source/newgame.cc @@ -384,9 +384,9 @@ bool new_game(void) // copy name into you.your_name if set from environment -- // note that you.your_name could already be set from init.txt // this, clearly, will overwrite such information {dlb} - if (SysEnv.crawl_name) + if (!SysEnv.crawl_name.empty()) { - strncpy( you.your_name, SysEnv.crawl_name, kNameLen ); + strncpy( you.your_name, SysEnv.crawl_name.c_str(), kNameLen ); you.your_name[ kNameLen - 1 ] = 0; } diff --git a/crawl-ref/source/shopping.cc b/crawl-ref/source/shopping.cc index 31c52d943d..d988e1e6d0 100644 --- a/crawl-ref/source/shopping.cc +++ b/crawl-ref/source/shopping.cc @@ -1407,7 +1407,7 @@ unsigned int item_value( item_def item, id_arr id, bool ident ) valued += 10 * item.plus; if (item.sub_type == RING_SLAYING && item.plus2 > 0) - valued += 10 * item.plus; + valued += 10 * item.plus2; } switch (item.sub_type) diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index 2f850333c0..c5fb33aebd 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -1231,6 +1231,18 @@ bool learn_spell(void) return (false); } + if (you.conf) + { + mpr("You are too confused!"); + return (false); + } + + if (you.berserker) + { + canned_msg(MSG_TOO_BERSERK); + return (false); + } + if (!which_spellbook( book, spell )) return (false); diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index 19299a1506..dfdb0ab4fd 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -2237,7 +2237,6 @@ static int find_transtravel_stair( const level_id &cur, for (int i = 0, count = stairs.size(); i < count; ++i) { stair_info &si = stairs[i]; - int deltadist = li.distance_between(this_stair, &si); if (!this_stair) { @@ -2250,7 +2249,8 @@ static int find_transtravel_stair( const level_id &cur, // deltadist == 0 is legal (if this_stair is NULL), since the player // may be standing on the stairs. If two stairs are disconnected, // deltadist has to be negative. - if (deltadist < 0) continue; + if (deltadist < 0) + continue; int dist2stair = distance + deltadist; if (si.distance == -1 || si.distance > dist2stair) @@ -2299,7 +2299,8 @@ static int find_transtravel_stair( const level_id &cur, } // If we don't know where these stairs go, we can't take them. - if (!dest.is_valid()) continue; + if (!dest.is_valid()) + continue; // We need to get the stairs at the new location and set the // distance on them as well. @@ -2768,13 +2769,12 @@ void LevelInfo::correct_stair_list(const std::vector &s) stair_distances.clear(); // First we kill any stairs in 'stairs' that aren't there in 's'. - for (std::vector::iterator i = stairs.begin(); - i != stairs.end(); ++i) + for (int i = ((int) stairs.size()) - 1; i >= 0; --i) { bool found = false; for (int j = s.size() - 1; j >= 0; --j) { - if (s[j] == i->position) + if (s[j] == stairs[i].position) { found = true; break; @@ -2782,7 +2782,7 @@ void LevelInfo::correct_stair_list(const std::vector &s) } if (!found) - stairs.erase(i--); + stairs.erase(stairs.begin() + i); } // For each stair in 's', make sure we have a corresponding stair @@ -2817,7 +2817,9 @@ void LevelInfo::correct_stair_list(const std::vector &s) } } - stair_distances.reserve( stairs.size() * stairs.size() ); + const int nstairs = stairs.size(); + stair_distances.reserve( nstairs * nstairs ); + stair_distances.resize( nstairs * nstairs, 0 ); } int LevelInfo::distance_between(const stair_info *s1, const stair_info *s2) @@ -2829,7 +2831,7 @@ int LevelInfo::distance_between(const stair_info *s1, const stair_info *s2) int i1 = get_stair_index(s1->position), i2 = get_stair_index(s2->position); if (i1 == -1 || i2 == -1) return 0; - + return stair_distances[ i1 * stairs.size() + i2 ]; } @@ -2887,9 +2889,10 @@ void LevelInfo::save(FILE *file) const if (stair_count) { // Save stair distances as short ints. - for (int i = stair_count * stair_count - 1; i >= 0; --i) + const int sz = stair_distances.size(); + for (int i = 0, n = stair_count * stair_count; i < n; ++i) { - if (i >= (int) stair_distances.size()) + if (i >= sz) writeShort(file, -1); else writeShort(file, stair_distances[i]); -- cgit v1.2.3-54-g00ecf