packages feed

Allure 0.10.2.0 → 0.10.3.0

raw patch · 33 files changed

+2025/−1512 lines, 33 filesdep −containersdep ~LambdaHackPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: containers

Dependency ranges changed: LambdaHack

API changes (from Hackage documentation)

- Content.ItemKindEmbed: pattern S_SANDSTONE_ROCK :: GroupName ItemKind
+ Content.ItemKindActor: pattern S_SANDSTONE_ROCK :: GroupName ItemKind
+ Content.TileKind: pattern FLOOR_ACTOR_ITEM_DARK :: GroupName TileKind
+ Content.TileKind: pattern ICE_BUILDUP :: GroupName TileKind
+ Content.TileKind: pattern S_CLOSED_DOOR :: GroupName TileKind
+ Content.TileKind: pattern S_OPEN_DOOR :: GroupName TileKind
+ Content.TileKind: pattern S_PILLAR :: GroupName TileKind
+ Content.TileKind: pattern S_RUBBLE_PILE :: GroupName TileKind
+ Content.TileKind: pattern S_SHALLOW_WATER_DARK :: GroupName TileKind
+ Content.TileKind: pattern S_SHALLOW_WATER_LIT :: GroupName TileKind
+ Content.TileKind: pattern TRANSPARENT_WALL :: GroupName TileKind
+ Content.TileKind: pattern WORKSHOP :: GroupName TileKind

Files

Allure.cabal view
@@ -1,4 +1,4 @@-cabal-version: 2.2+cabal-version: 2.4 name:          Allure -- The package version. See the Haskell package versioning policy (PVP) -- for standards guiding when and how versions should be incremented.@@ -6,7 +6,7 @@ -- PVP summary:+-+------- breaking API changes --             | |  +----- minor or non-breaking API additions --             | |  | +--- code changes with no API change-version:       0.10.2.0+version:       0.10.3.0 synopsis:      Near-future Sci-Fi roguelike and tactical squad combat game description: Allure of the Stars is a near-future Sci-Fi roguelike              and tactical squad combat game. Binaries and the game manual@@ -33,11 +33,11 @@ bug-reports:   http://github.com/AllureOfTheStars/Allure/issues license:       AGPL-3.0-or-later license-file:  COPYLEFT-tested-with:   GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.4,+tested-with:   GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.7,                GHC==9.0.1--- Enough files is included to build and run CI and manual tests--- and be presentable on Hackage, but not enough to create--- a user-friendly binary package, e.g., no InGameHelp.txt.+-- Enough files is included in the Hackage package to run CI and manual+-- tests off it, to be presentable on Hackage and to create user-friendly+-- binary packages. extra-source-files: GameDefinition/config.ui.default,                     GameDefinition/PLAYING.md,                     GameDefinition/fonts/16x16xw.woff,@@ -50,13 +50,16 @@                     GameDefinition/fonts/BinaryCodeProLH-Bold.ttf.woff,                     GameDefinition/fonts/DejaVuLGCSans.ttf.woff,                     GameDefinition/fonts/DejaVuLGCSans-Bold.ttf.woff,-                    GameDefinition/fonts/Hack-Bold.ttf.woff,-                    GameDefinition/fonts/ubuntu-v14-latin-ext_latin-regular.ttf.woff,-                    GameDefinition/fonts/ubuntu-v14-latin-ext_latin-500.ttf.woff,-                    GameDefinition/fonts/ubuntu-mono-v9-latin-ext_latin-700.ttf.woff,-                    Makefile,+                    GameDefinition/fonts/Hack-Bold.ttf.woff+extra-doc-files:    GameDefinition/InGameHelp.txt,+                    README.md,                     CHANGELOG.md,-                    README.md+                    LICENSE,+                    COPYLEFT,+                    CREDITS,+                    cabal.project,+                    cabal.project.LH.dir,+                    Makefile author:        Andres Loeh, Mikolaj Konarski and others maintainer:    Mikolaj Konarski <mikolaj.konarski@funktory.com> category:      Game@@ -83,10 +86,10 @@  common options   default-language:   Haskell2010-  default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings+  default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings,                       BangPatterns, RecordWildCards, NamedFieldPuns, MultiWayIf,                       LambdaCase, DefaultSignatures, InstanceSigs,-                      PatternSynonyms, StrictData, CPP+                      PatternSynonyms, StrictData, CPP, TypeApplications   other-extensions:   TemplateHaskell, MultiParamTypeClasses, RankNTypes,                       TypeFamilies, FlexibleContexts, FlexibleInstances,                       DeriveFunctor, FunctionalDependencies,@@ -94,7 +97,7 @@                       DeriveFoldable, DeriveTraversable,                       ExistentialQuantification, GADTs, StandaloneDeriving,                       DataKinds, KindSignatures, DeriveGeneric, DeriveLift-  ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists -Wpartial-fields+  ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists -Wpartial-fields -Wunused-packages -Winvalid-haddock   ghc-options:        -Wall-missed-specialisations   ghc-options:        -fno-ignore-asserts -fexpose-all-unfoldings -fspecialise-aggressively -fsimpl-tick-factor=200 @@ -110,7 +113,7 @@     ghcjs-options:    -DREMOVE_TELETYPE  common exe-options-  ghc-options:        -threaded -rtsopts+  ghc-options:        -rtsopts -- (Ignored by GHCJS) Minimize median lag at the cost of occasional bigger -- GC lag, which fortunately sometimes fits into idle time between turns): -- (Ignored by GHCJS) Avoid frequent GCs. Only idle-GC during a break in@@ -149,7 +152,7 @@                       Implementation.MonadServerImplementation   other-modules:      Paths_Allure   autogen-modules:    Paths_Allure-  build-depends:      ,LambdaHack >= 0.10.2.0 && < 0.10.3.0+  build-depends:      ,LambdaHack >= 0.10.3.0 && < 0.10.4.0                       ,async                       ,base >= 4.10 && < 99                       ,enummapset@@ -180,15 +183,10 @@   type:               exitcode-stdio-1.0   hs-source-dirs:     test   main-is:            Spec.hs-  other-modules:      SessionUIMock   build-depends:      ,LambdaHack                       ,Allure-                      ,async                       ,base-                      ,containers-                      ,filepath                       ,optparse-applicative                       ,tasty >= 1.0                       ,tasty-hunit                       ,text-                      ,transformers
CHANGELOG.md view
@@ -1,3 +1,30 @@+## [v0.10.3.0](https://github.com/AllureOfTheStars/Allure/compare/v0.10.2.0...v0.10.3.0)++- Work around regression https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076 by making the scalable square font the default as the map font; the tiny map fonts, for which there is no such workaround, wont work for anybody with freetype 2.11.0+- Enable display of details in right pane in many menus+- Switch mouse wheel to move selection now that it changes right pane display+- Make the line where messages wrap configurable in config file+- Remove rarely used options from config file and code+- Prepare client-server for delays when operating over the network+- Reveal all map at game over and make it explorable+- Don't run with -threaded, increasing speed by a couple dozen percent+- Don't show messages during enemy turn (except when under AI control)+- Spawn many actors at once with probability correlated to spawning at all+- Gut out Ubuntu Font Family fonts+- Add a few tutorial messages, reword some in-game texts+- Move with CI from Travis to GitHub Actions (but GHA can't keep up as well)+- Make place content directly define legends instead of overriding them+- Add a warning that crosshair is out of flinging range+- Add ANSI display to SDL2 frontend; remove vty, hcurses and GTK frontends+- Make Teletype frontend more usable (input, speed, overlays)+- Remove sight malus from light sources to avoid micromanagement+- Keep convenience settings between new games+- Add an option to mark FOV area with grey background+- Refactor and clean up a lot of the code+- Unhardwire various constants (hat tip to Jamie)+- Fix a couple of bugs and improve documentation+- Add whole new kinds of tests (even greater hat tip to Jamie)+ ## [v0.10.2.0](https://github.com/AllureOfTheStars/Allure/compare/v0.9.5.0...v0.10.2.0)  - Make GLASS_HAIL less deadly and less noisy visually@@ -730,7 +757,7 @@ - Start using cabal-plan - Fix and improve Makefile, cabal file and CI scripts - Improve and update game manual and help texts wrt game changes-- Countless changes in the LambdaHack engine and the induced changed in Allure+- Countless changes in the LambdaHack engine and the induced changes in Allure  ## [v0.9.5.0](https://github.com/AllureOfTheStars/Allure/compare/v0.9.4.0...v0.9.5.0) 
+ CREDITS view
@@ -0,0 +1,98 @@+All kinds of contributions to Allure of the Stars are gratefully welcome!+Some of the contributors are listed below, in chronological order.++Andres Loeh+Mikolaj Konarski+Krzysztof Pławski+Daniel Keefe+Pablo Reszczynski+Simon Michael+bulbousBullfrog++++Fonts 16x16xw.woff, 16x16xw.bdf, 16x16x.fnt, 8x8x.fnt and 8x8xb.fnt+are are derived from fonts taken from+https://github.com/angband/angband, copyrighted by Leon Marrick,+Sheldon Simms III and Nick McConnell and released by them under+GNU GPL, version 2 or any later version (confirmed at+http://www.thangorodrim.net/development/opensource.html).+Any further modifications by authors of LambdaHack+are also released under GNU GPL version 2.++Font 16x16xw.bdf is derived from 16x16x.fon by changing all but+a few glyphs, converting to BDF format, extending character set+and hacking the font file with bdftopcf and pcf2bdf to include+full bitmaps, not only non-zero portions, for otherwise SDL2-ttf+was not able to display the glyphs.+Font 16x16xw.woff was derived from 16x16xw.bdf by changing format+to TTF with bitsnpicas, faking descendent offsets to be 1 point lower+to prevent freetype from adding an extra pixel to the descendent,+tweaking with fontforge glyphs 3 5 6 8 A a S s b d h to prevent antialiasing+of their vital parts when zoomed out, auto-hinting, manually simplifying+hints in some glyphs and converting to WOFF format.++++Fonts BinarySansProLH-Regular.ttf.woff, BinarySansProLH-Semibold.ttf.woff+and BinaryCodeProLH-Bold.ttf.woff are compiled from sources+at https://github.com/adobe-fonts/source-sans-pro+and https://github.com/adobe-fonts/source-code-pro+published with the following copyright notice:++Copyright 2010-2019 Adobe (http://www.adobe.com/), with Reserved Font+Name 'Source'. All Rights Reserved. Source is a trademark of Adobe+in the United States and/or other countries.+This Font Software is licensed under the SIL Open Font License, Version 1.1.++The sources are modified and processed as follows (see+https://github.com/adobe-fonts/source-sans-pro/issues/193 for background):++in Roman/Instances/Semibold/font.ufo/glyphs/s.glif+put advance width="452"+rm Roman/Instances/Semibold/font.ufo/data/com.adobe.type.processedHashMap+bash build.sh+pyftfeatfreeze -f 'cv03' -S -U LH -R 'Source/Binary' target/TTF/SourceSansPro-Semibold.ttf BinarySansProLH-Semibold.ttf+pyftsubset BinarySansProLH-Semibold.ttf --unicodes="*" --flavor=woff --with-zopfli --output-file=BinarySansProLH-Semibold.ttf.woff++and similarly for BinarySansProLH-Regular.ttf.woff, while+for BinaryCodeProLH-Bold.ttf.woff the last steps are:++pyftfeatfreeze -f 'cv02' -S -U LH -R 'Source/Binary' target/TTF/SourceCodePro-Bold.ttf BinaryCodeProLH-Bold.ttf+pyftsubset BinaryCodeProLH-Bold.ttf --unicodes="*" --flavor=woff --with-zopfli --output-file=BinaryCodeProLH-Bold.ttf.woff++++Fonts DejaVuLGCSans.ttf.woff and DejaVuLGCSans-Bold.ttf.woff+are downloaded from https://github.com/dejavu-fonts/dejavu-fonts+and compresses with pyftsubset. Their copyright notice is+DejaVu fonts 2.37 (c)2004-2016 DejaVu fonts team.+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.++Font Hack-Bold.ttf.woff is downloaded from+https://github.com/source-foundry/Hack+and compresses with pyftsubset. Its copyright notice is+Hack work is (c) 2018 Source Foundry Authors. MIT License.+Bitstream Vera Sans Mono (c) 2003 Bitstream, Inc. (with Reserved Font+Names _Bitstream_ and _Vera_). Bitstream Vera License.++++The Ubuntu Font Family fonts are currently removed, because they are+a little too tall (some glyphs on consecutive lines are touching)+and also considered non-free by Debian and so we can't include them+in Debian and some other GNU/Linux distributions anyway.+If there is sufficient interest, they may be brought back.++They were fonts ubuntu-v14-latin-ext_latin-regular.ttf.woff,+ubuntu-v14-latin-ext_latin-500.ttf.woff+and ubuntu-mono-v9-latin-ext_latin-700.ttf.woff+that were generated via google-webfonts-helper.herokuapp.com+from 'Ubuntu Font Family' fonts with the following copyright notice:+Copyright 2011 Canonical Ltd. Licensed under the Ubuntu Font Licence 1.0++The files were compressed with pyftsubset to save some space and make+sure they are created from TTF, not coverted OTF fonts. Unfortunately,+being TTF fonts and having broken hinting in the mono variant, they+require forcing TTF.Light hinting in the SDL frontend. If ever OTF fonts+emerge, they should be used instead (similarly compressed).
GameDefinition/Content/CaveKind.hs view
@@ -34,6 +34,7 @@ import           Game.LambdaHack.Content.TileKind import           Game.LambdaHack.Core.Dice import           Game.LambdaHack.Definition.Defs+import           Game.LambdaHack.Definition.DefsInternal  -- * Group name patterns @@ -106,14 +107,14 @@   , chidden       = 7   , cactorCoeff   = 50  -- the maze requires time to explore   , cactorFreq    = [(MONSTER, 50), (ANIMAL, 20), (ROBOT, 30)]-  , citemNum      = 17 `d` 2 + 25 - 25 `dL` 1  -- at depth quality over quantity+  , citemNum      = 17 `d` 2 + 25 - 25 `dL` 1+      -- deep down quality over quantity; generally not too random,+      -- sacrificing replayability for consistent balance   , citemFreq     = [ (IK.COMMON_ITEM, 40), (IK.CRAWL_ITEM, 20)     -- CRAWL_ITEM items are used only in long scenarios, such as multi-level     -- dungeon crawl; these may be powerful or a mundate item,     -- unlike @TREASURE@ items                     , (IK.TREASURE, 40) ]-    -- note that the groups are flattened; e.g., if an item is moved to another-    -- group included here with the same weight, the outcome wouldn't change   , cplaceFreq    = [(ROGUE, 1)]   , cpassable     = False   , labyrinth     = False@@ -127,8 +128,6 @@   , cfenceTileS   = S_BASIC_OUTER_FENCE   , cfenceTileW   = HABITAT_CONTAINMENT_WALL   , cfenceApart   = False-  , clegendDarkTile = LEGEND_DARK-  , clegendLitTile  = LEGEND_LIT   , cminStairDist = 30   , cmaxStairsNum = 2 + 1 `d` 2   , cescapeFreq   = []@@ -139,7 +138,7 @@   , cskip         = []   , cdesc         = "Winding tunnels stretch into the dark. A few areas are passable but the remainder is packed with tanks and cells of raw materials and machinery."   }-residential = rogue+residential = rogue  -- an alternative with lit corridors but dark rooms   { cfreq         = [(DEFAULT_RANDOM, 70), (CAVE_RESIDENTIAL, 1)]   , cname         = "Residential area"   , cminPlaceSize = DiceXY (2 `d` 2 + 4) (1 `d` 2 + 9)  -- merge vert.@@ -344,7 +343,7 @@ empty = rogue   { csymbol       = 'E'   , cname         = "Construction site"-  , cfreq         = []+  , cfreq         = []  -- just a template for some others   , ccellSize     = DiceXY (2 `d` 8 + 14) 16   , cminPlaceSize = DiceXY 9 9  -- normally don't merge   , cmaxPlaceSize = DiceXY 50 20  -- often maximize horizontally@@ -495,7 +494,6 @@   , cescapeFreq   = [(INDOOR_ESCAPE_UP, 1)]   , cstairFreq    = []   , cstairAllowed = []-  , cskip         = [0]   , cdesc         = "Mold spreads across the walls and scuttling sounds can be heard in the distance."   } brawl = rogue  -- many random solid tiles, to break LOS, since it's a day@@ -519,7 +517,7 @@   , citemFreq     = [ (IK.COMMON_ITEM, 50)                     , (STARTING_WEAPON, 200), (STARTING_ARMOR, 400)                     , (IK.ANY_SCROLL, 100), (IK.ANY_POTION, 600) ]-                    -- introducing vials in this scenario+                      -- introducing vials in this scenario   , cplaceFreq    = [(BRAWL, 1)]   , cpassable     = True   , cdefTile      = BRAWL_SET_LIT@@ -636,7 +634,7 @@   , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []-  , cskip         = [0]  -- don't start heroes on exit+  , cskip         = [0]  -- don't start heroes nor opponents on escape   , cdesc         = "Graffiti scrawls across the walls and the heavy scents of stimulants hang in the air."   } zoo = rogue  -- few lights and many solids, to help the less numerous heroes
GameDefinition/Content/ItemKind.hs view
@@ -30,6 +30,7 @@ import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal import Game.LambdaHack.Definition.Flavour  -- * Group name patterns@@ -78,11 +79,9 @@  -- ** Allure-specific --- Below Allure-specific definitions are interspersed among generic--- definitions to make managing related definitions easier.+-- In the Content section, Allure-specific definitions are interspersed+-- among generic definitions to make managing related definitions easier. --- The @UNKNOWN@ patterns don't need to be exported. Used internally.--- They also represent singleton groups. pattern COOKED_PLANT_UNKNOWN = GroupName "cooked plant unknown"  pattern COOKED_PLANT = GroupName "cooked plant"@@ -106,9 +105,9 @@  items :: [ItemKind] items =-  [sandstoneRock, steelScrap, needle, dart, spike, spike2, slingStone, slingBullet, needleSleep, paralizingProj, harpoon, harpoon2, harpoon3, net, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, flaskEmpty, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, potion13, potion14, potion15, potion16, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, scroll14, scroll15, scroll16, scrollAd1, rawMeatChunk, roastedMeatChunk, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, ediblePlant8, cookedPlantTemplate, cookedPlant1, cookedPlant2, cookedPlant3, cookedPlant4, cookedPlant5, cookedPlant6, cookedPlant7, cookedPlant8, light1, lightDoused1, light2, lightDoused2, light3, blanket, chisel, hacksaw, adjustableSpanner, steelFile, honingSteel, whetstone, diagonalPliers, snips, loppers, boltCutter, solderingIron, duckTape, thickCord, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, motionScanner, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, ring9, ring10, armorLeather, armorLeather2, armorMail, meleeEnhancement, spacesuit, spacesuitTorn, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, heavyBoot, ragTangle, buckler, shield, shield2, shield3, blowtorch, laserSharpener, crowbar, catsPaw, shortClub, longClub, hammerTemplate, hammer1, hammer2, hammer3, hammer4, hammer5, hammerParalyze, hammerSpark, knife, daggerDropBestWeapon, dagger, sword, swordImpress, swordNullify, swordNullifySharp, halberd, oxTongue, halberdPushActor, halberdPushActorSharp, fireAxe, pollaxe, militaryKnife, militaryBaton, cattleProd, grassStitcher, ladiesFork, hoe, spade, treePruner, cleaningPole, staff, pipe, longPole, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency, jumpingPole, constructionHooter, wasteContainer, spotlight, seeingItem]+  [sandstoneRock, steelScrap, needle, dart, spike, spike2, slingStone, slingBullet, needleSleep, paralizingProj, harpoon, harpoon2, harpoon3, net, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, flaskEmpty, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, potion13, potion14, potion15, potion16, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, scroll14, scroll15, scroll16, scrollAd1, rawMeatChunk, roastedMeatChunk, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, ediblePlant8, cookedPlantTemplate, cookedPlant1, cookedPlant2, cookedPlant3, cookedPlant4, cookedPlant5, cookedPlant6, cookedPlant7, cookedPlant8, light1, lightDoused1, light2, lightDoused2, light3, blanket, chisel, hacksaw, adjustableSpanner, steelFile, honingSteel, whetstone, diagonalPliers, snips, loppers, boltCutter, solderingIron, duckTape, thickCord, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, motionScanner, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, ring9, ring10, armorLeather, armorLeather2, armorMail, meleeEnhancement, spacesuit, spacesuitTorn, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, heavyBoot, ragTangle, buckler, shield, shield2, shield3, blowtorch, laserSharpener, crowbar, catsPaw, shortClub, longClub, hammerTemplate, hammer1, hammer2, hammer3, hammer4, hammer5, hammerParalyze, hammerSpark, knife, daggerDischarge, dagger, sword, swordImpress, swordNullify, swordNullifySharp, halberd, oxTongue, halberdPushActor, halberdPushActorSharp, fireAxe, pollaxe, militaryKnife, militaryBaton, cattleProd, grassStitcher, ladiesFork, hoe, spade, treePruner, cleaningPole, staff, pipe, longPole, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency, jumpingPole, constructionHooter, wasteContainer, spotlight, seeingItem] -sandstoneRock,    steelScrap, needle, dart, spike, spike2, slingStone, slingBullet, needleSleep, paralizingProj, harpoon, harpoon2, harpoon3, net, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, flaskEmpty, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, potion13, potion14, potion15, potion16, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, scroll14, scroll15, scroll16, scrollAd1, rawMeatChunk, roastedMeatChunk, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, ediblePlant8, cookedPlantTemplate, cookedPlant1, cookedPlant2, cookedPlant3, cookedPlant4, cookedPlant5, cookedPlant6, cookedPlant7, cookedPlant8, light1, lightDoused1, light2, lightDoused2, light3, blanket, chisel, hacksaw, adjustableSpanner, steelFile, honingSteel, whetstone, diagonalPliers, snips, loppers, boltCutter, solderingIron, duckTape, thickCord, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, motionScanner, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, ring9, ring10, armorLeather, armorLeather2, armorMail, meleeEnhancement, spacesuit, spacesuitTorn, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, heavyBoot, ragTangle, buckler, shield, shield2, shield3, blowtorch, laserSharpener, crowbar, catsPaw, shortClub, longClub, hammerTemplate, hammer1, hammer2, hammer3, hammer4, hammer5, hammerParalyze, hammerSpark, knife, daggerDropBestWeapon, dagger, sword, swordImpress, swordNullify, swordNullifySharp, halberd, oxTongue, halberdPushActor, halberdPushActorSharp, fireAxe, pollaxe, militaryKnife, militaryBaton, cattleProd, grassStitcher, ladiesFork, hoe, spade, treePruner, cleaningPole, staff, pipe, longPole, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency, jumpingPole, constructionHooter, wasteContainer, spotlight, seeingItem :: ItemKind+sandstoneRock,    steelScrap, needle, dart, spike, spike2, slingStone, slingBullet, needleSleep, paralizingProj, harpoon, harpoon2, harpoon3, net, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, flaskEmpty, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, potion13, potion14, potion15, potion16, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, scroll14, scroll15, scroll16, scrollAd1, rawMeatChunk, roastedMeatChunk, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, ediblePlant8, cookedPlantTemplate, cookedPlant1, cookedPlant2, cookedPlant3, cookedPlant4, cookedPlant5, cookedPlant6, cookedPlant7, cookedPlant8, light1, lightDoused1, light2, lightDoused2, light3, blanket, chisel, hacksaw, adjustableSpanner, steelFile, honingSteel, whetstone, diagonalPliers, snips, loppers, boltCutter, solderingIron, duckTape, thickCord, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, motionScanner, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, ring9, ring10, armorLeather, armorLeather2, armorMail, meleeEnhancement, spacesuit, spacesuitTorn, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, heavyBoot, ragTangle, buckler, shield, shield2, shield3, blowtorch, laserSharpener, crowbar, catsPaw, shortClub, longClub, hammerTemplate, hammer1, hammer2, hammer3, hammer4, hammer5, hammerParalyze, hammerSpark, knife, daggerDischarge, dagger, sword, swordImpress, swordNullify, swordNullifySharp, halberd, oxTongue, halberdPushActor, halberdPushActorSharp, fireAxe, pollaxe, militaryKnife, militaryBaton, cattleProd, grassStitcher, ladiesFork, hoe, spade, treePruner, cleaningPole, staff, pipe, longPole, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency, jumpingPole, constructionHooter, wasteContainer, spotlight, seeingItem :: ItemKind  -- Keep the dice rolls and sides in aspects small so that not too many -- distinct items are generated (for display in item lore and for narrative@@ -124,29 +123,29 @@  -- * Item group symbols, from Angband and variants -symbolProjectile, _symbolLauncher, symbolLight, symbolTool, symbolSpecial, symbolGold, symbolNecklace, symbolRing, symbolPotion, symbolFlask, symbolScroll, symbolTorsoArmor, symbolMiscArmor, symbolClothes, symbolShield, symbolPolearm, symbolEdged, symbolHafted, symbolWand, _symbolStaff, symbolFood :: Char+symbolProjectile, _symbolLauncher, symbolLight, symbolTool, symbolSpecial, symbolGold, symbolNecklace, symbolRing, symbolPotion, symbolFlask, symbolScroll, symbolTorsoArmor, symbolMiscArmor, symbolClothes, symbolShield, symbolPolearm, symbolEdged, symbolHafted, symbolWand, _symbolStaff, symbolFood :: ContentSymbol ItemKind -symbolProjectile = rsymbolProjectile standardRules  -- '{'-_symbolLauncher  = '}'-symbolLight      = '('-symbolTool       = ')'-symbolSpecial    = '*'  -- don't overuse, because it clashes with projectiles-symbolGold       = '$'  -- also gems-symbolNecklace   = '"'-symbolRing       = '='-symbolPotion     = '!'  -- concoction, bottle, jar, vial, canister-symbolFlask      = '!'-symbolScroll     = '?'  -- book, note, tablet, remote, chip, card-symbolTorsoArmor = '['-symbolMiscArmor  = '['-symbolClothes    = '['-symbolShield     = ']'-symbolPolearm    = '/'-symbolEdged      = '|'-symbolHafted     = '\\'-symbolWand       = '-'  -- magical rod, transmitter, pistol, rifle, instrument-_symbolStaff     = '_'  -- scanner-symbolFood       = ','  -- also body part; distinct from floor: not middle dot+symbolProjectile = rsymbolProjectile $ ritemSymbols standardRules+_symbolLauncher  = toContentSymbol '}'+symbolLight      = rsymbolLight $ ritemSymbols standardRules+symbolTool       = rsymbolTool $ ritemSymbols standardRules+symbolSpecial    = rsymbolSpecial $ ritemSymbols standardRules+symbolGold       = rsymbolGold $ ritemSymbols standardRules+symbolNecklace   = rsymbolNecklace $ ritemSymbols standardRules+symbolRing       = rsymbolRing $ ritemSymbols standardRules+symbolPotion     = rsymbolPotion $ ritemSymbols standardRules+symbolFlask      = rsymbolFlask $ ritemSymbols standardRules+symbolScroll     = rsymbolScroll $ ritemSymbols standardRules+symbolTorsoArmor = rsymbolTorsoArmor $ ritemSymbols standardRules+symbolMiscArmor  = rsymbolMiscArmor $ ritemSymbols standardRules+symbolClothes    = rsymbolClothes $ ritemSymbols standardRules+symbolShield     = rsymbolShield $ ritemSymbols standardRules+symbolPolearm    = rsymbolPolearm $ ritemSymbols standardRules+symbolEdged      = rsymbolEdged $ ritemSymbols standardRules+symbolHafted     = rsymbolHafted $ ritemSymbols standardRules+symbolWand       = rsymbolWand $ ritemSymbols standardRules+_symbolStaff     = toContentSymbol '_'+symbolFood       = rsymbolFood $ ritemSymbols standardRules  -- ** Thrown weapons @@ -241,7 +240,7 @@   , icount   = 1 + 5 `dL` 5   , iverbHit = "penetrate"   , iweight  = 150-  , idamage = 4 `d` 1  -- not useful for melee, because hurt skill too low+  , idamage  = 4 `d` 1  -- not useful for melee, because hurt skill too low   , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5                , SetFlag MinorEffects                , Odds (10 * 1 `dL` 10) [] [toVelocity 70] ]@@ -547,8 +546,8 @@   , irarity  = [(1, 1)]  -- not every playthrough needs one   , iaspects = ELabel "of resolution spirit"                : iaspects flaskTemplate-  , ieffects = [ toOrganGood S_RESOLUTE (500 + 1 `d` 200)  -- long, for scouting-               , RefillCalm 60  -- not to make it a drawback, via @calmEnough@+  , ieffects = [ toOrganGood S_RESOLUTE (100 + 1 `d` 20)  -- long, for scouting+               , RefillCalm 100  -- not to make it a drawback, via @calmEnough@                , OnSmash (Explode S_RESOLUTION_DUST) ]   } flask7 = flaskTemplate@@ -861,7 +860,7 @@                , OnSmash (Explode S_YOUTH_SPRINKLE) ]   } potion16 = potionTemplate-  { ifreq    = [(CRAWL_ITEM, 100), (ANY_POTION, 50)]+  { ifreq    = [(CRAWL_ITEM, 100), (ANY_POTION, 50), (ANY_GLASS, 100)]   , icount   = 1 `dL` 2   , irarity  = [(1, 1), (10, 6)]   , iaspects = ELabel "of melee deflection"@@ -1008,7 +1007,7 @@   , iaspects = ELabel "of tourist guide"                : iaspects scrollTemplate   , ieffects = [ Impress  -- mostly flavour, but this is useful-               , toOrganGood S_RESOLUTE (500 + 1 `d` 200)+               , toOrganGood S_RESOLUTE (100 + 1 `d` 20)                    -- a drawback (at least initially) due to @calmEnough@                , Explode S_RHINO_HOLOGRAM                , Detect DetectLoot 5 ]  -- short so useless most of the time@@ -1208,11 +1207,9 @@   , iverbHit = "scorch"   , iweight  = 1000   , idamage  = 1 `d` 1  -- strong missile, but betrays the flinger-  , iaspects = [ AddSkill SkShine 3, AddSkill SkSight (-2)-                   -- not only flashes, but also sparks,-                   -- so unused by AI due to the mixed blessing-               , SetFlag Durable, SetFlag Lobable-               , SetFlag Meleeable, EqpSlot EqpSlotShine ]+  , iaspects = [ AddSkill SkShine 3  -- no malus, to lessen micromanagement+               , SetFlag Durable, SetFlag Lobable, SetFlag Meleeable+               , EqpSlot EqpSlotShine ]                    -- partially durable; reusable flare;                    -- the staff culled when crafting, so no velocity malus   , ieffects = [Burn 2, torchDestruct]  -- no timeout, but destructs@@ -1238,12 +1235,12 @@   , iverbHit = "burn"   , iweight  = 1600   , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkShine 3, AddSkill SkSight (-1)+  , iaspects = [ AddSkill SkShine 3                , SetFlag Lobable, SetFlag Fragile, SetFlag Equipable                , EqpSlot EqpSlotShine ]   , ieffects = [ Explode S_FOCUSED_BURNING_OIL_2                , OnSmash (Explode S_VIOLENT_BURNING_OIL_2) ]-  , idesc    = "A restaurant table glass lamp filled with plant oil feeding a slender wick. Or a makeshift caricature thereof."+  , idesc    = "A tiny restaurant table glass lamp filled with plant oil feeding a slender wick. Or a functional equivalent thereof."   , ikit     = []   } lightDoused2 = light2@@ -1264,7 +1261,7 @@   , iverbHit = "burn"   , iweight  = 3000   , idamage  = 2 `d` 1-  , iaspects = [ AddSkill SkShine 4, AddSkill SkSight (-1)+  , iaspects = [ AddSkill SkShine 4                , SetFlag Lobable, SetFlag Fragile, SetFlag Equipable                , EqpSlot EqpSlotShine ]   , ieffects = [ Explode S_FOCUSED_BURNING_OIL_4@@ -1526,13 +1523,19 @@                , (S_REFRIGERATION_COIL, 1), (ANY_JEWELRY, 100)                , (COLD_SOURCE, 1) ]   , iaspects = ELabel "of superconducting refrigeration"-               : Timeout ((1 + 1 `d` 3) * 5)+               : Timeout ((3 + 1 `d` 3) * 10)+                   -- high timeout to prevent spam obscuring messages+                   -- when other actors act and annoying bumping into+                   -- projectiles caused by own necklace when walking                : delete (SetFlag Precious) iaspects_necklaceTemplate   , ieffects = [Explode S_CURRENT_DISCHARGE]   } necklace10 = necklaceTemplate   { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100), (PERFUME, 1)]-  , iaspects = Timeout ((1 + 1 `d` 3) * 5)+  , iaspects = Timeout ((3 + 1 `d` 3) * 10)+                   -- high timeout to prevent spam obscuring messages+                   -- when other actors act and annoying bumping into+                   -- projectiles caused by own necklace when walking                : iaspects_necklaceTemplate   , ieffects = [Explode S_FRAGRANCE]   }@@ -1564,7 +1567,7 @@   , iverbHit = "rattle"   , iweight  = 700   , idamage  = 0-  , iaspects = [ AddSkill SkNocto 1, AddSkill SkSight (-1)+  , iaspects = [ AddSkill SkNocto 1                , AddSkill SkArmorMelee $ (-6 + 1 `dL` 3) * 5                , SetFlag Precious, SetFlag Equipable                , EqpSlot EqpSlotMiscBonus ]@@ -1776,7 +1779,7 @@   , iweight  = 250000  -- including the fake gravity mass from two boots   , idamage  = 0   , iaspects = [ AddSkill SkHurtMelee (-30)  -- restricted arm movement-               , AddSkill SkSight (-1)+               , AddSkill SkSight (-2)                , AddSkill SkHearing (-3), AddSkill SkSmell (-99)                , AddSkill SkArmorMelee $ (8 + 1 `dL` 2) * 10  -- ~ 95                , AddSkill SkArmorRanged $ (1 `dL` 3) * 12  -- ~ 25.5@@ -1796,7 +1799,7 @@   , iverbHit = "entangle"   , iweight  = 10000   , iaspects = [ AddSkill SkHurtMelee (-30)-               , AddSkill SkSight (-1)  -- obstructed despite the tears+               , AddSkill SkSight (-2)  -- obstructed despite the tears                , AddSkill SkArmorMelee $ (1 + 1 `d` 3) * 10  -- shallow, no `dL`                , AddSkill SkArmorRanged $ (1 `d` 2) * 6                , SetFlag Durable, SetFlag Equipable@@ -1914,7 +1917,7 @@   , iaspects = [ Timeout 4                , AddSkill SkArmorMelee $ (1 `dL` 3) * 5                , AddSkill SkArmorRanged $ (2 + 1 `dL` 2) * 3  -- headshot-               , AddSkill SkSight (-1)+               , AddSkill SkSight (-2)                , AddSkill SkHearing (-3), AddSkill SkSmell (-5)                , SetFlag Durable, SetFlag Meleeable                , EqpSlot EqpSlotArmorRanged@@ -2295,7 +2298,7 @@   , ifreq    = [(COMMON_ITEM, 100), (STARTING_WEAPON, 100), (S_CLEAVER, 1)]   , iflavour = zipPlain [BrCyan]   , icount   = 1-  , irarity  = [(1, 3), (3, 3), (5, 40), (9, 1)]+  , irarity  = [(1, 5), (3, 5), (5, 35), (9, 1)]                  -- useful initially and for crafting mid-game   , iverbHit = "cut"   , iweight  = 1000@@ -2310,7 +2313,7 @@   , idesc    = "A heavy professional kitchen blade. Will do fine cutting any kind of meat, bone and an occasional metal can. Does not penetrate deeply, but is quick to move and hard to block. Especially useful in conjunction with a larger weapon."   , ikit     = []   }-daggerDropBestWeapon = knife+daggerDischarge = knife   { iname    = "The Double Dagger"   , ifreq    = [(TREASURE, 40), (MUSEAL, 100)]   , irarity  = [(1, 3), (10, 2)]
GameDefinition/Content/ItemKindActor.hs view
@@ -7,7 +7,7 @@ -- | Actor (or rather actor body trunk) definitions. module Content.ItemKindActor   ( -- * Group name patterns-    pattern S_WOODEN_TORCH+    pattern S_WOODEN_TORCH, pattern S_SANDSTONE_ROCK   , pattern HERO, pattern SCOUT_HERO, pattern RANGER_HERO, pattern ESCAPIST_HERO, pattern AMBUSHER_HERO, pattern BRAWLER_HERO, pattern SOLDIER_HERO, pattern CIVILIAN, pattern MONSTER, pattern MOBILE_MONSTER, pattern SCOUT_MONSTER, pattern ANIMAL, pattern MOBILE_ANIMAL, pattern IMMOBILE_ANIMAL   , pattern ADD_SIGHT, pattern ARMOR_RANGED, pattern ADD_NOCTO_1, pattern WEAK_ARROW, pattern LIGHT_ATTENUATOR, pattern FIREPROOF_CLOTH, pattern RING_OF_OPPORTUNITY_SNIPER, pattern ANY_ARROW, pattern STARTING_ARMOR, pattern STARTING_WEAPON, pattern GEM   , pattern CRAWL_HERO, pattern MERCENARY_HERO, pattern AQUATIC_ANIMAL, pattern AQUATIC_MONSTER, pattern EXPLOSIVE_MONSTER, pattern ROBOT, pattern MOBILE_ROBOT, pattern IMMOBILE_ROBOT, pattern CONSTRUCTION_ROBOT@@ -27,15 +27,16 @@ import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal import Game.LambdaHack.Definition.Flavour  -- * Group name patterns  actorsGNSingleton :: [GroupName ItemKind] actorsGNSingleton =-       [S_WOODEN_TORCH, S_BULLTEPROOF_VEST, S_PERFUME_POTION, S_EMPTY_FLASK]+       [S_WOODEN_TORCH, S_SANDSTONE_ROCK, S_BULLTEPROOF_VEST, S_PERFUME_POTION, S_EMPTY_FLASK] -pattern S_WOODEN_TORCH, S_BULLTEPROOF_VEST, S_PERFUME_POTION, S_EMPTY_FLASK :: GroupName ItemKind+pattern S_WOODEN_TORCH, S_SANDSTONE_ROCK, S_BULLTEPROOF_VEST, S_PERFUME_POTION, S_EMPTY_FLASK :: GroupName ItemKind  actorsGN :: [GroupName ItemKind] actorsGN =@@ -81,6 +82,7 @@  -- ** Common pattern S_WOODEN_TORCH = GroupName "wooden torch"+pattern S_SANDSTONE_ROCK = GroupName "sandstone rock"  pattern ADD_SIGHT = GroupName "sight improvement" pattern ARMOR_RANGED = GroupName "ranged armor"@@ -148,7 +150,7 @@                   -- and slighty different backstory emphasis between games                   -- matches human plasticity. warrior = ItemKind-  { isymbol  = '@'+  { isymbol  = toContentSymbol '@'   , iname    = "adventurer"  -- modified if initial actors in hero faction   , ifreq    = [(HERO, 100), (CRAWL_HERO, 100), (MOBILE, 1)]   , iflavour = zipPlain [BrWhite]@@ -175,8 +177,8 @@ warrior2 = warrior   { ifreq    = [(CRAWL_HERO, 100), (MOBILE, 1)]   , ikit     = humanOrgans-               ++ [ (GENETIC_FLAW_10, COrgan), (S_CURRENCY, CStash)-                  , (COOKED_FOOD, CStash), (COMMON_ITEM, CStash) ]+               ++ [ (GENETIC_FLAW_10, COrgan)+                  , (S_EMPTY_FLASK, CStash), (COOKED_FOOD, CStash) ]   } scout = warrior   { iname    = "scout"@@ -272,7 +274,7 @@ -- They have bright colours, because they are not natural.  eye = ItemKind  -- depends on items it finds rather than special organs-  { isymbol  = 'w'+  { isymbol  = toContentSymbol 'w'   , iname    = "beckoning walker"   , ifreq    = [ (MONSTER, 100), (MOBILE, 1)                , (MOBILE_MONSTER, 100), (SCOUT_MONSTER, 10) ]@@ -295,7 +297,7 @@                , (S_SAPIENT_BRAIN, COrgan) ]  -- no voice, no hearing   } fastEye = ItemKind  -- glass cannon; depends mostly on items it finds-  { isymbol  = 'b'+  { isymbol  = toContentSymbol 'b'   , iname    = "rolling biter"   , ifreq    = [ (MONSTER, 100), (MOBILE, 1)                , (MOBILE_MONSTER, 100), (SCOUT_MONSTER, 60) ]@@ -321,7 +323,7 @@ nose = ItemKind  -- sniffs only; a tank requiring multiple weapon hits to beat;                  -- no armor, so special kinds of damage don't help;                  -- slow, but despite that, danger when strong weapons wielded!-  { isymbol  = 'h'+  { isymbol  = toContentSymbol 'h'   , iname    = "crawling horror"   , ifreq    = [ (MONSTER, 100), (MOBILE, 1), (MOBILE_MONSTER, 100)                , (AQUATIC, 30), (AQUATIC_MONSTER, 30) ]  -- likes liquids@@ -339,12 +341,13 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "A blind, slimy mass of fluid tissue. You'd think it's powerless, but as soon as it touches your trembling body, slapping, stinging and burning, it won't let go."-  , ikit     = [ (S_STING, COrgan), (S_TIP, COrgan), (S_LIP, COrgan)+  , ikit     = [ (S_TIP, COrgan), (S_LIP, COrgan)  -- at least one non-timed+               , (S_STING, COrgan)                , (S_NOSTRIL, COrgan), (S_HUNGRY, COrgan)                , (S_SAPIENT_BRAIN, COrgan) ]  -- no sight nor hearing   } elbow = ItemKind-  { isymbol  = 's'+  { isymbol  = toContentSymbol 's'   , iname    = "creepy shooter"   , ifreq    = [ (MONSTER, 100), (MOBILE, 1)                , (MOBILE_MONSTER, 100), (SCOUT_MONSTER, 30) ]@@ -369,22 +372,22 @@                , (ANY_ARROW, CEqp), (ANY_ARROW, CStash)                , (WEAK_ARROW, CEqp), (WEAK_ARROW, CStash) ]   }++-- * Allure-specific aliens+ elbowTank = elbow   { iname    = "armored shooter"   , iflavour = zipFancy [Magenta]   , irarity  = [(10, 0), (40, 30)]  -- only appears among late spawns   , ikit     = ikit elbow ++ [(S_ARMORED_SKIN, COrgan), (S_JET_BOOSTER, COrgan)]   }---- * Allure-specific aliens- -- TODO: the main fun in this actor will be chain explosions and for this, -- it needs to spawn in groups and move in groups. Neither is implemented yet. -- For now, we try to amass the actors on arena levels over water and in rooms. -- Low HP is needed to ensure the chain reaction. Lack of ranged combat -- makes the rule to attack it from a distance straightforward. intruder = ItemKind-  { isymbol  = 'i'+  { isymbol  = toContentSymbol 'i'   , iname    = "bobbing intruder"   , ifreq    = [ (MONSTER, 100), (MOBILE, 1), (MOBILE_MONSTER, 100)                , (EXPLOSIVE_MONSTER, 100)@@ -404,7 +407,7 @@                , AddSkill SkFlying 10  -- flies slowly, but far                , SetFlag Durable ]   , ieffects = []-  , idesc    = "It starts, bobs and halts, scanning for movement. Effortlessly, it resumes covering the ground, gliding two meters above the floor. The pumped organic body is large, but looks fragile. However, it doesn't skirt the walls, but instead seems to take ownership of any space it boldly parks in the middle of."+  , idesc    = "It starts, bobs and halts, scanning for movement. Effortlessly it resumes covering the ground, gliding two meters above the floor. Its pumped organic body, though large, pulses with fragility. It doesn't skirt the walls and instead takes ownership of any space it boldly parks in the middle of."   , ikit     = [ (S_FLOTATION_BAG, COrgan)                , (S_TENTACLE, COrgan), (S_TENTACLE, COrgan)                , (S_TIP, COrgan)  -- at least one non-timed@@ -416,7 +419,7 @@ -- * Alien uniques  torsor = ItemKind-  { isymbol  = 'M'+  { isymbol  = toContentSymbol 'M'   , iname    = "The Maker"   , ifreq    = [(MONSTER, 100), (MOBILE, 1)]   , iflavour = zipFancy [BrCyan]@@ -454,7 +457,7 @@ -- They have dull colors, except for yellow, because there is no dull variant.  goldenJackal = ItemKind  -- basically a much smaller, slower and nosy hyena-  { isymbol  = 'j'+  { isymbol  = toContentSymbol 'j'   , iname    = "golden jackal"   , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)                , (SCAVENGER, 50) ]@@ -478,7 +481,7 @@                , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   } griffonVulture = ItemKind  -- keep it boring and weak, because it summons-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "griffon vulture"   , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)                , (SCAVENGER, 30) ]@@ -508,7 +511,7 @@                , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   } skunk = ItemKind-  { isymbol  = 's'+  { isymbol  = toContentSymbol 's'   , iname    = "hog-nosed skunk"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [White]@@ -531,7 +534,7 @@                , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   } armadillo = ItemKind  -- a tank with armor, so special damage defeats it-  { isymbol  = 'a'+  { isymbol  = toContentSymbol 'a'   , iname    = "giant armadillo"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Brown]@@ -555,7 +558,7 @@                , (RAW_MEAT_CHUNK, CEqp) ]   } gilaMonster = ItemKind-  { isymbol  = 'g'+  { isymbol  = toContentSymbol 'g'   , iname    = "Gila monster"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Magenta]@@ -576,7 +579,7 @@                , (GENETIC_FLAW_3, COrgan) ]   } rattlesnake = ItemKind-  { isymbol  = 's'+  { isymbol  = toContentSymbol 's'   , iname    = "rattlesnake"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Brown]@@ -601,7 +604,7 @@                , (RAW_MEAT_CHUNK, CEqp) ]   } hyena = ItemKind-  { isymbol  = 'h'+  { isymbol  = toContentSymbol 'h'   , iname    = "spotted hyena"   , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)                , (SCAVENGER, 20) ]@@ -623,7 +626,7 @@                , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   } komodoDragon = ItemKind-  { isymbol  = 'k'+  { isymbol  = toContentSymbol 'k'   , iname    = "Komodo dragon"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [BrRed]  -- speedy, so bright red@@ -650,7 +653,7 @@   } alligator = ItemKind  -- late, slow, deadly semi-tank with some armor;                       -- too deadly to get more HP; bombs the only recourse-  { isymbol  = 'a'+  { isymbol  = toContentSymbol 'a'   , iname    = "alligator"   , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)                , (AQUATIC, 70), (AQUATIC_ANIMAL, 70) ]  -- amphibious@@ -682,7 +685,7 @@ -- * Allure-specific animals  giantOctopus = ItemKind-  { isymbol  = 'o'+  { isymbol  = toContentSymbol 'o'   , iname    = "giant octopus"   , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)                , (AQUATIC, 90), (AQUATIC_ANIMAL, 90) ]  -- weak on land@@ -710,7 +713,7 @@                , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp) ]  } lion = ItemKind  -- emphatically not a tank-  { isymbol  = 'l'+  { isymbol  = toContentSymbol 'l'   , iname    = "Lion"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Red]@@ -738,7 +741,7 @@ -- * Animal uniques  rhinoceros = ItemKind  -- impressive tank boss with some armor-  { isymbol  = 'R'+  { isymbol  = toContentSymbol 'R'   , iname    = "Billy"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1)]   , iflavour = zipPlain [Brown]@@ -748,7 +751,7 @@   , iweight  = 80000   , idamage  = 0   , iaspects = [ SetFlag Unique, ELabel "the Maddened Rhinoceros"-               , AddSkill SkMaxHP 200, AddSkill SkMaxCalm 60+               , AddSkill SkMaxHP 200, AddSkill SkMaxCalm 60  -- quite late                , AddSkill SkSpeed 27, AddSkill SkNocto 2                , AddSkill SkHurtMelee 50  -- mass gives extra damage                , AddSkill SkAggression 2@@ -769,7 +772,7 @@ -- * Non-animal animals  beeSwarm = ItemKind-  { isymbol  = 'b'+  { isymbol  = toContentSymbol 'b'   , iname    = "bee swarm"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1)]   , iflavour = zipPlain [Brown]@@ -791,7 +794,7 @@                , (S_INSECT_MORTALITY, COrgan), (S_ANIMAL_BRAIN, COrgan) ]   } hornetSwarm = ItemKind  -- kind of tank with armor, but short-lived-  { isymbol  = 'h'+  { isymbol  = toContentSymbol 'h'   , iname    = "hornet swarm"   , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Magenta]@@ -816,7 +819,7 @@                , (S_INSECT_MORTALITY, COrgan), (S_ANIMAL_BRAIN, COrgan) ]   } thornbush = ItemKind  -- the wimpiest kind of early tank-  { isymbol  = 't'+  { isymbol  = toContentSymbol 't'   , iname    = "thornbush"   , ifreq    = [(ANIMAL, 25), (IMMOBILE_ANIMAL, 40)]   , iflavour = zipPlain [Brown]@@ -841,7 +844,7 @@ -- for other factions.  razorwireFence = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "razorwire fence"   , ifreq    = [(ROBOT, 15), (IMMOBILE_ROBOT, 10)]   , iflavour = zipPlain [Cyan]@@ -860,7 +863,7 @@   , ikit     = [(S_RAZOR, COrgan), (S_THORN, COrgan)]   } electricFence = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "electric fence"   , ifreq    = [(ROBOT, 40), (IMMOBILE_ROBOT, 10)]   , iflavour = zipPlain [Blue]@@ -880,7 +883,7 @@                , (POLE_OR_HANDLE, CEqp) ]   } activeFence = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "active fence"   , ifreq    = [(ROBOT, 30), (IMMOBILE_ROBOT, 20)]   , iflavour = zipPlain [BrMagenta]@@ -904,7 +907,7 @@                    -- a long period   } steamFaucet = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "steam faucet"   , ifreq    = [(ROBOT, 8), (IMMOBILE_ROBOT, 15)]   , iflavour = zipPlain [BrGreen]@@ -922,7 +925,7 @@   , ikit     = [(S_BOILING_VENT, COrgan), (S_BOILING_FISSURE, COrgan)]   } coolingFaucet = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "cooling faucet"   , ifreq    = [(ROBOT, 8), (IMMOBILE_ROBOT, 15)]   , iflavour = zipPlain [BrBlue]@@ -940,7 +943,7 @@   , ikit     = [(S_COOLING_VENT, COrgan), (S_COOLING_FISSURE, COrgan)]   } medbotFaucet = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "nano medbot faucet"   , ifreq    = [(ROBOT, 10), (IMMOBILE_ROBOT, 100)]   , iflavour = zipPlain [BrYellow]@@ -958,7 +961,7 @@   , ikit     = [(S_MEDBOT_VENT, COrgan), (S_MEDBOT_FISSURE, COrgan)]   } dustFaucet = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "dust faucet"   , ifreq    = [ (ROBOT, 4)  -- usually nothing to ignite                , (IMMOBILE_ROBOT, 30) ]  -- except when other faucets around@@ -977,7 +980,7 @@   , ikit     = [(S_DUST_VENT, COrgan), (S_DUST_FISSURE, COrgan)]   } fuelFaucet = ItemKind-  { isymbol  = 'f'+  { isymbol  = toContentSymbol 'f'   , iname    = "burning fuel faucet"   , ifreq    = [(ROBOT, 30)]       -- common not in outermost level, but in the dungeon, because it's@@ -997,7 +1000,7 @@   , ikit     = [(S_FUEL_VENT, COrgan), (S_FUEL_FISSURE, COrgan)]   } surveillanceDrone = ItemKind-  { isymbol  = 'd'+  { isymbol  = toContentSymbol 'd'   , iname    = "surveillance drone"   , ifreq    = [(ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)]   , iflavour = zipPlain [Blue]@@ -1019,7 +1022,7 @@                , (S_VISION_16, COrgan), (S_ROBOT_BRAIN, COrgan) ]   } shepherdDrone = ItemKind-  { isymbol  = 'd'+  { isymbol  = toContentSymbol 'd'   , iname    = "oversight drone"   , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)                , (CONSTRUCTION_ROBOT, 100) ]@@ -1043,7 +1046,7 @@                , (S_ROBOT_BRAIN, COrgan) ]   } huntingDrone = ItemKind-  { isymbol  = 'd'+  { isymbol  = toContentSymbol 'd'   , iname    = "hunting drone"   , ifreq    = [(ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)]   , iflavour = zipPlain [Green]@@ -1067,7 +1070,7 @@                , (NEEDLE, CStash), (TRANQUILIZER_DART, CStash) ]   } homeRobot = ItemKind-  { isymbol  = 'r'+  { isymbol  = toContentSymbol 'r'   , iname    = "feral home robot"                -- TODO: name another 'deranged', tertiary imperative: survival   , ifreq    = [(ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)]@@ -1088,7 +1091,7 @@                , (S_ROBOT_BRAIN, COrgan) ]   } wasteRobot = ItemKind  -- not a tank, because smell-only alien is already a tank-  { isymbol  = 'r'+  { isymbol  = toContentSymbol 'r'   , iname    = "waste disposal robot"   , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)                , (CONSTRUCTION_ROBOT, 100) ]@@ -1109,7 +1112,7 @@                , (WASTE_CONTAINER, CEqp) ]   } lightRobot = ItemKind-  { isymbol  = 'r'+  { isymbol  = toContentSymbol 'r'   , iname    = "decoration robot"   , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)                , (CONSTRUCTION_ROBOT, 100) ]@@ -1138,7 +1141,7 @@   } heavyRobot = ItemKind  -- summoning tank with armor, but fortunately weak                        -- weapons; danger when strong weapons wielded!-  { isymbol  = 'r'+  { isymbol  = toContentSymbol 'r'   , iname    = "demolition robot"   , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)                , (CONSTRUCTION_ROBOT, 70) ]@@ -1168,7 +1171,7 @@ -- * Robot uniques, Allure-specific  weldedRobot = ItemKind-  { isymbol  = 'L'+  { isymbol  = toContentSymbol 'L'   , iname    = "Bob"   , ifreq    = [(ROBOT, 100), (IMMOBILE_ROBOT, 100)]   , iflavour = zipPlain [BrCyan]@@ -1194,7 +1197,7 @@                    -- establish stash to ensure heroes pick up blowtorch ASAP   } cleanerRobot = ItemKind-  { isymbol  = 'C'+  { isymbol  = toContentSymbol 'C'   , iname    = "The Void Cleaner Robot"   , ifreq    = [(ROBOT, 100), (MOBILE, 1)]   , iflavour = zipPlain [BrGreen]
GameDefinition/Content/ItemKindBlast.hs view
@@ -7,10 +7,10 @@ -- | Blast definitions. module Content.ItemKindBlast   ( -- * Group name patterns-    pattern S_FIRECRACKER, pattern S_VIOLENT_FRAGMENTATION, pattern S_FRAGMENTATION, pattern S_FOCUSED_FRAGMENTATION, pattern S_VIOLENT_CONCUSSION, pattern S_CONCUSSION, pattern S_FOCUSED_CONCUSSION, pattern S_VIOLENT_FLASH, pattern S_FOCUSED_FLASH, pattern S_GLASS_HAIL, pattern S_FOCUSED_GLASS_HAIL, pattern S_PHEROMONE, pattern S_CALMING_MIST, pattern S_DISTRESSING_ODOR, pattern S_HEALING_MIST, pattern S_HEALING_MIST_2, pattern S_WOUNDING_MIST, pattern S_DISTORTION, pattern S_SMOKE, pattern S_BOILING_WATER, pattern S_GLUE, pattern S_WASTE, pattern S_ANTI_SLOW_MIST, pattern S_ANTIDOTE_MIST, pattern S_SLEEP_MIST, pattern S_DENSE_SHOWER, pattern S_SPARSE_SHOWER, pattern S_MELEE_PROTECTIVE_BALM, pattern S_RANGE_PROTECTIVE_BALM, pattern S_DEFENSELESSNESS_RUNOUT, pattern S_RESOLUTION_DUST, pattern S_HASTE_SPRAY, pattern S_EYE_DROP, pattern S_IRON_FILING, pattern S_SMELLY_DROPLET, pattern S_EYE_SHINE, pattern S_WHISKEY_SPRAY, pattern S_YOUTH_SPRINKLE, pattern S_POISON_CLOUD, pattern S_PING_PLASH, pattern S_VIOLENT_BURNING_OIL_2, pattern S_VIOLENT_BURNING_OIL_3, pattern S_VIOLENT_BURNING_OIL_4, pattern S_BURNING_OIL_2, pattern S_BURNING_OIL_3, pattern S_BURNING_OIL_4, pattern S_FOCUSED_BURNING_OIL_2, pattern S_FOCUSED_BURNING_OIL_3, pattern S_FOCUSED_BURNING_OIL_4+    pattern S_FIRECRACKER, pattern S_VIOLENT_FRAGMENTATION, pattern S_FRAGMENTATION, pattern S_FOCUSED_FRAGMENTATION, pattern S_VIOLENT_CONCUSSION, pattern S_CONCUSSION, pattern S_FOCUSED_CONCUSSION, pattern S_VIOLENT_FLASH, pattern S_FOCUSED_FLASH, pattern S_GLASS_HAIL, pattern S_FOCUSED_GLASS_HAIL, pattern S_PHEROMONE, pattern S_CALMING_MIST, pattern S_DISTRESSING_ODOR, pattern S_HEALING_MIST, pattern S_HEALING_MIST_2, pattern S_WOUNDING_MIST, pattern S_DISTORTION, pattern S_SMOKE, pattern S_BOILING_WATER, pattern S_GLUE, pattern S_WASTE, pattern S_ANTI_SLOW_MIST, pattern S_ANTIDOTE_MIST, pattern S_SLEEP_MIST, pattern S_DENSE_SHOWER, pattern S_SPARSE_SHOWER, pattern S_MELEE_PROTECTIVE_BALM, pattern S_RANGE_PROTECTIVE_BALM, pattern S_DEFENSELESSNESS_RUNOUT, pattern S_RESOLUTION_DUST, pattern S_HASTE_SPRAY, pattern S_VIOLENT_SLOWNESS_MIST, pattern S_SLOWNESS_MIST, pattern S_FOCUSED_SLOWNESS_MIST, pattern S_EYE_DROP, pattern S_IRON_FILING, pattern S_SMELLY_DROPLET, pattern S_EYE_SHINE, pattern S_WHISKEY_SPRAY, pattern S_YOUTH_SPRINKLE, pattern S_POISON_CLOUD, pattern S_PING_PLASH, pattern S_VIOLENT_BURNING_OIL_2, pattern S_VIOLENT_BURNING_OIL_3, pattern S_VIOLENT_BURNING_OIL_4, pattern S_BURNING_OIL_2, pattern S_BURNING_OIL_3, pattern S_BURNING_OIL_4, pattern S_FOCUSED_BURNING_OIL_2, pattern S_FOCUSED_BURNING_OIL_3, pattern S_FOCUSED_BURNING_OIL_4   , blastNoStatOf, blastBonusStatOf   , pattern ARMOR_MISC-  , pattern S_VIOLENT_SLOWNESS_MIST, pattern S_SLOWNESS_MIST, pattern S_FOCUSED_SLOWNESS_MIST, pattern S_PAINT_DROPLET, pattern S_RHINO_HOLOGRAM, pattern S_CURRENT_DISCHARGE, pattern S_CURRENT_RECHARGE+  , pattern S_PAINT_DROPLET, pattern S_RHINO_HOLOGRAM, pattern S_CURRENT_DISCHARGE, pattern S_CURRENT_RECHARGE   , pattern ADVERTISEMENT, pattern STORY_TELLING   , pattern FIRE_SOURCE, pattern OIL_SOURCE, pattern WATER_SOURCE, pattern COLD_SOURCE, pattern BLAST_SOURCE   , blastsGNSingleton, blastsGN@@ -28,21 +28,22 @@ import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal import Game.LambdaHack.Definition.Flavour  -- * Group name patterns  blastsGNSingleton :: [GroupName ItemKind] blastsGNSingleton =-       [S_FIRECRACKER, S_VIOLENT_FRAGMENTATION, S_FRAGMENTATION, S_FOCUSED_FRAGMENTATION, S_VIOLENT_CONCUSSION, S_CONCUSSION, S_FOCUSED_CONCUSSION, S_VIOLENT_FLASH, S_FOCUSED_FLASH, S_GLASS_HAIL, S_FOCUSED_GLASS_HAIL, S_PHEROMONE, S_CALMING_MIST, S_DISTRESSING_ODOR, S_HEALING_MIST, S_HEALING_MIST_2, S_WOUNDING_MIST, S_DISTORTION, S_SMOKE, S_BOILING_WATER, S_GLUE, S_WASTE, S_ANTI_SLOW_MIST, S_ANTIDOTE_MIST, S_SLEEP_MIST, S_DENSE_SHOWER, S_SPARSE_SHOWER, S_MELEE_PROTECTIVE_BALM, S_RANGE_PROTECTIVE_BALM, S_DEFENSELESSNESS_RUNOUT, S_RESOLUTION_DUST, S_HASTE_SPRAY, S_EYE_DROP, S_IRON_FILING, S_SMELLY_DROPLET, S_EYE_SHINE, S_WHISKEY_SPRAY, S_YOUTH_SPRINKLE, S_POISON_CLOUD, S_PING_PLASH, S_VIOLENT_BURNING_OIL_2, S_VIOLENT_BURNING_OIL_3, S_VIOLENT_BURNING_OIL_4, S_BURNING_OIL_2, S_BURNING_OIL_3, S_BURNING_OIL_4, S_FOCUSED_BURNING_OIL_2, S_FOCUSED_BURNING_OIL_3, S_FOCUSED_BURNING_OIL_4]+       [S_FIRECRACKER, S_VIOLENT_FRAGMENTATION, S_FRAGMENTATION, S_FOCUSED_FRAGMENTATION, S_VIOLENT_CONCUSSION, S_CONCUSSION, S_FOCUSED_CONCUSSION, S_VIOLENT_FLASH, S_FOCUSED_FLASH, S_GLASS_HAIL, S_FOCUSED_GLASS_HAIL, S_PHEROMONE, S_CALMING_MIST, S_DISTRESSING_ODOR, S_HEALING_MIST, S_HEALING_MIST_2, S_WOUNDING_MIST, S_DISTORTION, S_SMOKE, S_BOILING_WATER, S_GLUE, S_WASTE, S_ANTI_SLOW_MIST, S_ANTIDOTE_MIST, S_SLEEP_MIST, S_DENSE_SHOWER, S_SPARSE_SHOWER, S_MELEE_PROTECTIVE_BALM, S_RANGE_PROTECTIVE_BALM, S_DEFENSELESSNESS_RUNOUT, S_RESOLUTION_DUST, S_HASTE_SPRAY, S_VIOLENT_SLOWNESS_MIST, S_SLOWNESS_MIST, S_FOCUSED_SLOWNESS_MIST, S_EYE_DROP, S_IRON_FILING, S_SMELLY_DROPLET, S_EYE_SHINE, S_WHISKEY_SPRAY, S_YOUTH_SPRINKLE, S_POISON_CLOUD, S_PING_PLASH, S_VIOLENT_BURNING_OIL_2, S_VIOLENT_BURNING_OIL_3, S_VIOLENT_BURNING_OIL_4, S_BURNING_OIL_2, S_BURNING_OIL_3, S_BURNING_OIL_4, S_FOCUSED_BURNING_OIL_2, S_FOCUSED_BURNING_OIL_3, S_FOCUSED_BURNING_OIL_4]   ++ map firecrackerAt [1..4]   ++ map blastNoStatOf noStatGN   ++ map blastBonusStatOf bonusStatGN-  ++ [S_VIOLENT_SLOWNESS_MIST, S_SLOWNESS_MIST, S_FOCUSED_SLOWNESS_MIST, S_PAINT_DROPLET, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE, S_CURRENT_RECHARGE]+  ++ [S_PAINT_DROPLET, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE, S_CURRENT_RECHARGE] -pattern S_FIRECRACKER, S_VIOLENT_FRAGMENTATION, S_FRAGMENTATION, S_FOCUSED_FRAGMENTATION, S_VIOLENT_CONCUSSION, S_CONCUSSION, S_FOCUSED_CONCUSSION, S_VIOLENT_FLASH, S_FOCUSED_FLASH, S_GLASS_HAIL, S_FOCUSED_GLASS_HAIL, S_PHEROMONE, S_CALMING_MIST, S_DISTRESSING_ODOR, S_HEALING_MIST, S_HEALING_MIST_2, S_WOUNDING_MIST, S_DISTORTION, S_SMOKE, S_BOILING_WATER, S_GLUE, S_WASTE, S_ANTI_SLOW_MIST, S_ANTIDOTE_MIST, S_SLEEP_MIST, S_DENSE_SHOWER, S_SPARSE_SHOWER, S_MELEE_PROTECTIVE_BALM, S_RANGE_PROTECTIVE_BALM, S_DEFENSELESSNESS_RUNOUT, S_RESOLUTION_DUST, S_HASTE_SPRAY, S_EYE_DROP, S_IRON_FILING, S_SMELLY_DROPLET, S_EYE_SHINE, S_WHISKEY_SPRAY, S_YOUTH_SPRINKLE, S_POISON_CLOUD, S_PING_PLASH, S_VIOLENT_BURNING_OIL_2, S_VIOLENT_BURNING_OIL_3, S_VIOLENT_BURNING_OIL_4, S_BURNING_OIL_2, S_BURNING_OIL_3, S_BURNING_OIL_4, S_FOCUSED_BURNING_OIL_2, S_FOCUSED_BURNING_OIL_3, S_FOCUSED_BURNING_OIL_4 :: GroupName ItemKind+pattern S_FIRECRACKER, S_VIOLENT_FRAGMENTATION, S_FRAGMENTATION, S_FOCUSED_FRAGMENTATION, S_VIOLENT_CONCUSSION, S_CONCUSSION, S_FOCUSED_CONCUSSION, S_VIOLENT_FLASH, S_FOCUSED_FLASH, S_GLASS_HAIL, S_FOCUSED_GLASS_HAIL, S_PHEROMONE, S_CALMING_MIST, S_DISTRESSING_ODOR, S_HEALING_MIST, S_HEALING_MIST_2, S_WOUNDING_MIST, S_DISTORTION, S_SMOKE, S_BOILING_WATER, S_GLUE, S_WASTE, S_ANTI_SLOW_MIST, S_ANTIDOTE_MIST, S_SLEEP_MIST, S_DENSE_SHOWER, S_SPARSE_SHOWER, S_MELEE_PROTECTIVE_BALM, S_RANGE_PROTECTIVE_BALM, S_DEFENSELESSNESS_RUNOUT, S_RESOLUTION_DUST, S_HASTE_SPRAY, S_VIOLENT_SLOWNESS_MIST, S_SLOWNESS_MIST, S_FOCUSED_SLOWNESS_MIST, S_EYE_DROP, S_IRON_FILING, S_SMELLY_DROPLET, S_EYE_SHINE, S_WHISKEY_SPRAY, S_YOUTH_SPRINKLE, S_POISON_CLOUD, S_PING_PLASH, S_VIOLENT_BURNING_OIL_2, S_VIOLENT_BURNING_OIL_3, S_VIOLENT_BURNING_OIL_4, S_BURNING_OIL_2, S_BURNING_OIL_3, S_BURNING_OIL_4, S_FOCUSED_BURNING_OIL_2, S_FOCUSED_BURNING_OIL_3, S_FOCUSED_BURNING_OIL_4 :: GroupName ItemKind -pattern S_VIOLENT_SLOWNESS_MIST, S_SLOWNESS_MIST, S_FOCUSED_SLOWNESS_MIST, S_PAINT_DROPLET, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE, S_CURRENT_RECHARGE :: GroupName ItemKind+pattern S_PAINT_DROPLET, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE, S_CURRENT_RECHARGE :: GroupName ItemKind  blastsGN :: [GroupName ItemKind] blastsGN =@@ -88,6 +89,9 @@ pattern S_DEFENSELESSNESS_RUNOUT = GroupName "acid spray" pattern S_RESOLUTION_DUST = GroupName "resolution dust" pattern S_HASTE_SPRAY = GroupName "haste spray"+pattern S_VIOLENT_SLOWNESS_MIST = GroupName "violent nitrogen mist"+pattern S_SLOWNESS_MIST = GroupName "nitrogen mist"+pattern S_FOCUSED_SLOWNESS_MIST = GroupName "focused nitrogen mist" pattern S_EYE_DROP = GroupName "eye drop" pattern S_IRON_FILING = GroupName "iron filing" pattern S_SMELLY_DROPLET = GroupName "smelly droplet"@@ -118,9 +122,6 @@ pattern ARMOR_MISC = GroupName "miscellaneous armor"  -- ** Allure-specific-pattern S_VIOLENT_SLOWNESS_MIST = GroupName "violent nitrogen mist"-pattern S_SLOWNESS_MIST = GroupName "nitrogen mist"-pattern S_FOCUSED_SLOWNESS_MIST = GroupName "focused nitrogen mist" pattern S_PAINT_DROPLET = GroupName "paint droplet" pattern S_RHINO_HOLOGRAM = GroupName "rhino hologram" pattern S_CURRENT_DISCHARGE = GroupName "discharge current"@@ -155,7 +156,7 @@  spreadBurningOil :: Int -> GroupName ItemKind -> ItemKind spreadBurningOil n grp = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "burning oil"   , ifreq    = [(grp, 1), (FIRE_SOURCE, 1), (OIL_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]@@ -188,7 +189,7 @@ spreadBurningOil84 = spreadBurningOil8 4 S_BURNING_OIL_4 focusedBurningOil :: Int -> GroupName ItemKind -> GroupName ItemKind -> ItemKind focusedBurningOil n grp grpExplode = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "igniting oil"   , ifreq    = [(grp, 1), (FIRE_SOURCE, 1), (OIL_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]  -- all ignitions yellow to avoid appearing@@ -211,7 +212,7 @@ focusedBurningOil4 = focusedBurningOil 4 S_FOCUSED_BURNING_OIL_4 S_BURNING_OIL_4 firecracker :: Int -> ItemKind firecracker n = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "firecracker"   , ifreq    = [(if n == 5                  then S_FIRECRACKER@@ -240,7 +241,7 @@ -- * Focused blasts  spreadFragmentation = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "fragmentation burst"   , ifreq    = [(S_VIOLENT_FRAGMENTATION, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [Red]@@ -266,7 +267,7 @@       -- smaller radius, so worse for area effect, but twice the direct damage   } focusedFragmentation = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "deflagration ignition"  -- improvised fertilizer, etc.   , ifreq    = [(S_FOCUSED_FRAGMENTATION, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]@@ -284,7 +285,7 @@   , ikit     = []   } spreadConcussion = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "concussion blast"   , ifreq    = [(S_VIOLENT_CONCUSSION, 1), (BLAST_SOURCE, 1)]                  -- only the strongest explosion breaches@@ -305,7 +306,7 @@   , ieffects = [ DropItem maxBound 1 CEqp ARMOR_MISC                , PushActor (ThrowMod 400 25 1)  -- 1 step, fast; after DropItem                    -- this produces spam for braced actors; too bad-               , toOrganBad S_IMMOBILE 3  -- no balance+               , toOrganBad S_IMMOBILE 1  -- no balance                , toOrganBad S_DEAFENED 23 ]   , idesc    = "Shock wave, hot gases and smoke. Able to demolish small obstacles."   , ikit     = []@@ -319,7 +320,7 @@                , AddSkill SkShine 3, AddSkill SkHurtMelee $ -8 * 5 ]   } focusedConcussion = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "detonation ignition"  -- stabilized high explosive liquid   , ifreq    = [(S_FOCUSED_CONCUSSION, 1), (BLAST_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]@@ -335,7 +336,7 @@   , ikit     = []   } spreadFlash = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "magnesium flash"  -- or aluminum, but let's stick to one   , ifreq    = [(S_VIOLENT_FLASH, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrWhite]@@ -347,8 +348,8 @@   , iaspects = [ ToThrow $ ThrowMod 100 20 4  -- 4 steps, 1 turn                , SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 5 ]-  , ieffects = [ toOrganBad S_WEAKENED 20-               , Discharge 1 $ 40 - 1 `d` 20 ]+  , ieffects = [ Discharge 1 $ 40 - 1 `d` 20+               , toOrganBad S_WEAKENED 20 ]                  -- Wikipedia says: blind for five seconds and afterimage                  -- for much longer, harming aim. But blind is boring                  -- and prevents the player from seeing fun explosions@@ -366,7 +367,7 @@                , AddSkill SkShine 5 ]   } focusedFlash = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "magnesium ignition"   , ifreq    = [(S_FOCUSED_FLASH, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]@@ -394,7 +395,7 @@   , ikit     = []   } glassPiece = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "glass piece"   , ifreq    = [(S_GLASS_HAIL, 1)]   , iflavour = zipPlain [Blue]@@ -423,11 +424,11 @@ -- * Assorted blasts that don't induce conditions or not used mainly for them  fragrance = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "fragrance"  -- instant, fast fragrance   , ifreq    = [(S_FRAGRANCE, 1)]   , iflavour = zipPlain [Magenta]-  , icount   = 12+  , icount   = 7   , irarity  = [(1, 1)]   , iverbHit = "engulf"   , iweight  = 1@@ -441,7 +442,7 @@   , ikit     = []   } pheromone = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "musky whiff"  -- a kind of mist rather than fragrance   , ifreq    = [(S_PHEROMONE, 1)]   , iflavour = zipPlain [BrMagenta]@@ -457,7 +458,7 @@   , ikit     = []   } mistCalming = ItemKind  -- unused-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(S_CALMING_MIST, 1)]   , iflavour = zipPlain [BrGreen]@@ -473,7 +474,7 @@   , ikit     = []   } odorDistressing = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "distressing whiff"   , ifreq    = [(S_DISTRESSING_ODOR, 1)]   , iflavour = zipFancy [BrRed]  -- salmon@@ -491,7 +492,7 @@   , ikit     = []   } mistHealing = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"  -- powerful, so slow and narrow   , ifreq    = [(S_HEALING_MIST, 1)]   , iflavour = zipFancy [BrGreen]@@ -508,7 +509,7 @@   , ikit     = []   } mistHealing2 = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(S_HEALING_MIST_2, 1)]   , iflavour = zipPlain [Green]@@ -525,7 +526,7 @@   , ikit     = []   } mistWounding = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(S_WOUNDING_MIST, 1)]   , iflavour = zipPlain [BrRed]@@ -541,7 +542,7 @@   , ikit     = []   } distortion = ItemKind  -- currently unused-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "vortex"   , ifreq    = [(S_DISTORTION, 1)]   , iflavour = zipPlain [White]@@ -557,7 +558,7 @@   , ikit     = []   } smoke = ItemKind  -- slow, long, weakly blinding blast; smoke, octopus's ink-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "dark fume"  -- pluralizes better than 'smokes'   , ifreq    = [(S_SMOKE, 1)]   , iflavour = zipPlain [BrBlack]@@ -568,14 +569,14 @@   , idamage  = 0   , iaspects = [ toVelocity 20  -- 4 steps, 2 turns                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [ toOrganBad S_BLIND (1 + 1 `d` 2)-               , toOrganBad S_WITHHOLDING (5 + 1 `d` 3) ]+  , ieffects = [ toOrganBad S_WITHHOLDING (5 + 1 `d` 3)+               , toOrganBad S_BLIND (1 + 1 `d` 2) ]                   -- choking and tears or just obscured view, can't aim   , idesc    = "Twirling clouds of black, sticky vapour."   , ikit     = []   } boilingWater = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "boiling water"   , ifreq    = [(S_BOILING_WATER, 1)]  -- not enough water to create puddles   , iflavour = zipPlain [White]@@ -591,7 +592,7 @@   , ikit     = []   } glue = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "glue droplet"   , ifreq    = [(S_GLUE, 1)]   , iflavour = zipPlain [Cyan]@@ -607,7 +608,7 @@   , ikit     = []   } waste = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "waste piece"   , ifreq    = [(S_WASTE, 1)]   , iflavour = zipPlain [Brown]@@ -623,7 +624,7 @@   , ikit     = []   } mistAntiSlow = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(S_ANTI_SLOW_MIST, 1)]   , iflavour = zipFancy [BrYellow]@@ -639,7 +640,7 @@   , ikit     = []   } mistAntidote = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(S_ANTIDOTE_MIST, 1)]   , iflavour = zipFancy [BrBlue]@@ -655,7 +656,7 @@   , ikit     = []   } mistSleep = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(S_SLEEP_MIST, 1)]   , iflavour = zipFancy [BrMagenta]@@ -679,7 +680,7 @@ -- A few are slower 'mists'.  denseShower = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "dense shower"   , ifreq    = [(S_DENSE_SHOWER, 1)]   , iflavour = zipFancy [Green]@@ -694,7 +695,7 @@   , ikit     = []   } sparseShower = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "sparse shower"   , ifreq    = [(S_SPARSE_SHOWER, 1)]   , iflavour = zipFancy [Red]@@ -709,13 +710,13 @@   , ikit     = []   } protectingBalmMelee = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "oil spray"   , ifreq    = [(S_MELEE_PROTECTIVE_BALM, 1), (OIL_SOURCE, 1)]   , iflavour = zipFancy [Brown]   , icount   = 6   , irarity  = [(1, 1)]-  , iverbHit = "old"+  , iverbHit = "oil"   , iweight  = 1   , idamage  = 0   , iaspects = [ toLinger 0  -- 0 steps, 1 turn@@ -727,7 +728,7 @@   , ikit     = []   } protectingBalmRanged = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "balm droplet"   , ifreq    = [(S_RANGE_PROTECTIVE_BALM, 1)]   , iflavour = zipPlain [BrYellow]@@ -742,7 +743,7 @@   , ikit     = []   } defenselessnessRunout = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "acid spray"   , ifreq    = [(S_DEFENSELESSNESS_RUNOUT, 1)]   , iflavour = zipFancy [BrRed]@@ -757,7 +758,7 @@   , ikit     = []   } resolutionDust = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "resolution dust"   , ifreq    = [(S_RESOLUTION_DUST, 1)]   , iflavour = zipPlain [Brown]@@ -773,7 +774,7 @@   , ikit     = []   } hasteSpray = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "haste spray"   , ifreq    = [(S_HASTE_SPRAY, 1)]   , iflavour = zipFancy [BrYellow]@@ -787,8 +788,47 @@   , idesc    = "A quick spurt."   , ikit     = []   }+spreadNitrogen = ItemKind+  { isymbol  = toContentSymbol '`'+  , iname    = "nitrogen mist"+  , ifreq    = [(S_VIOLENT_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]+  , iflavour = zipPlain [BrBlack]+  , icount   = 15+  , irarity  = [(1, 1)]+  , iverbHit = "freeze"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ toVelocity 10  -- 2 steps, 2 turns, mist, slow+               , SetFlag Fragile, SetFlag Blast ]+  , ieffects = [toOrganBad S_SLOWED (2 + 1 `d` 3)]+  , idesc    = "Colourless cold clammy fog, making each movement an effort."+  , ikit     = []+  }+spreadNitrogen8 = spreadNitrogen+  { ifreq    = [(S_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]+  , icount   = 7+  , iaspects = [ toVelocity 5  -- 1 step, 1 turn, mist, slow+               , SetFlag Fragile, SetFlag Blast ]+  }+focusedNitrogen = ItemKind+  { isymbol  = toContentSymbol '`'+  , iname    = "liquid nitrogen droplet"+  , ifreq    = [(S_FOCUSED_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]+  , iflavour = zipFancy [White]+  , icount   = 4 -- 28 in total vs 15, higher spread+  , irarity  = [(1, 1)]+  , iverbHit = "freeze"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ toLinger 0  -- 0 steps, 1 turn+               , SetFlag Fragile, SetFlag Blast ]+  , ieffects = [ OnSmash $ Explode S_SLOWNESS_MIST+               , toOrganBad S_SLOWED (2 + 1 `d` 3) ]+  , idesc    = "Colourless and colder than ice."+  , ikit     = []+  } eyeDrop = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "eye drop"   , ifreq    = [(S_EYE_DROP, 1)]   , iflavour = zipFancy [BrCyan]@@ -803,7 +843,7 @@   , ikit     = []   } ironFiling = ItemKind  -- fast, short, strongly blinding blast-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "iron filing"   , ifreq    = [(S_IRON_FILING, 1)]   , iflavour = zipPlain [Red]@@ -818,7 +858,7 @@   , ikit     = []   } smellyDroplet = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "smelly droplet"   , ifreq    = [(S_SMELLY_DROPLET, 1)]   , iflavour = zipFancy [Blue]@@ -833,7 +873,7 @@   , ikit     = []   } eyeShine = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "eye shine"   , ifreq    = [(S_EYE_SHINE, 1)]   , iflavour = zipFancy [Cyan]@@ -848,7 +888,7 @@   , ikit     = []   } whiskeySpray = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "whiskey spray"   , ifreq    = [(S_WHISKEY_SPRAY, 1)]   , iflavour = zipFancy [Brown]@@ -863,7 +903,7 @@   , ikit     = []   } youthSprinkle = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "youth sprinkle"   , ifreq    = [(S_YOUTH_SPRINKLE, 1)]   , iflavour = zipFancy [BrGreen]@@ -879,7 +919,7 @@   , ikit     = []   } poisonCloud = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "poison cloud"   , ifreq    = [(S_POISON_CLOUD, 1)]   , iflavour = zipFancy [BrMagenta]@@ -895,7 +935,7 @@   , ikit     = []   } pingFlash = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "flash"   , ifreq    = [(S_PING_PLASH, 1)]   , iflavour = zipFancy [Green]@@ -913,7 +953,7 @@   } blastNoStat :: GroupName ItemKind -> ItemKind blastNoStat grp = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "mist"   , ifreq    = [(blastNoStatOf grp, 1)]   , iflavour = zipFancy [White]@@ -938,7 +978,7 @@ blastNoSkApply = blastNoStat S_PARSIMONIOUS blastBonusStat :: GroupName ItemKind -> ItemKind blastBonusStat grp = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "dew"   , ifreq    = [(blastBonusStatOf grp, 1)]   , iflavour = zipFancy [White]@@ -976,7 +1016,7 @@ -- no effects.  cruiseAdHologram = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "cruise ad hologram"   , ifreq    = [(S_RHINO_HOLOGRAM, 1), (ADVERTISEMENT, 10)]   , iflavour = zipFancy [BrMagenta]@@ -1014,47 +1054,8 @@  -- ** Misc -spreadNitrogen = ItemKind-  { isymbol  = '`'-  , iname    = "nitrogen mist"-  , ifreq    = [(S_VIOLENT_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]-  , iflavour = zipPlain [BrBlack]-  , icount   = 15-  , irarity  = [(1, 1)]-  , iverbHit = "freeze"-  , iweight  = 1-  , idamage  = 0-  , iaspects = [ toVelocity 10  -- 2 steps, 2 turns, mist, slow-               , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [toOrganBad S_SLOWED (2 + 1 `d` 3)]-  , idesc    = "Colourless cold clammy fog, making each movement an effort."-  , ikit     = []-  }-spreadNitrogen8 = spreadNitrogen-  { ifreq    = [(S_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]-  , icount   = 7-  , iaspects = [ toVelocity 5  -- 1 step, 1 turn, mist, slow-               , SetFlag Fragile, SetFlag Blast ]-  }-focusedNitrogen = ItemKind-  { isymbol  = '`'-  , iname    = "liquid nitrogen droplet"-  , ifreq    = [(S_FOCUSED_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]-  , iflavour = zipFancy [White]-  , icount   = 4 -- 28 in total vs 15, higher spread-  , irarity  = [(1, 1)]-  , iverbHit = "freeze"-  , iweight  = 1-  , idamage  = 0-  , iaspects = [ toLinger 0  -- 0 steps, 1 turn-               , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [ OnSmash $ Explode S_SLOWNESS_MIST-               , toOrganBad S_SLOWED (2 + 1 `d` 3) ]-  , idesc    = "Colourless and colder than ice."-  , ikit     = []-  } paintSpray = ItemKind-  { isymbol  = '`'+  { isymbol  = toContentSymbol '`'   , iname    = "fluorescent paint"   , ifreq    = [(S_PAINT_DROPLET, 1)]   , iflavour = zipPlain [BrRed]@@ -1069,11 +1070,11 @@   , ikit     = []   } currentDischarge = ItemKind-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "static current"   , ifreq    = [(S_CURRENT_DISCHARGE, 1)]   , iflavour = zipFancy [BrBlue]-  , icount   = 12+  , icount   = 9   , irarity  = [(1, 1)]   , iverbHit = "reset"   , iweight  = 1
GameDefinition/Content/ItemKindEmbed.hs view
@@ -8,7 +8,6 @@ module Content.ItemKindEmbed   ( -- * Group name patterns     pattern SCRATCH_ON_WALL, pattern OBSCENE_PICTOGRAM, pattern SUBTLE_FRESCO, pattern SIGNAGE, pattern SMALL_FIRE, pattern SMALL_FIRE_5, pattern BIG_FIRE, pattern FROST, pattern RUBBLE, pattern DOORWAY_TRAP_UNKNOWN, pattern DOORWAY_TRAP, pattern STAIRS_UP, pattern STAIRS_DOWN, pattern ESCAPE, pattern STAIRS_TRAP_UP, pattern STAIRS_TRAP_DOWN, pattern LECTERN, pattern SHALLOW_WATER, pattern STRAIGHT_PATH, pattern FROZEN_GROUND-  , pattern S_SANDSTONE_ROCK   , pattern STAIRS_UP_OUTDOOR, pattern STAIRS_DOWN_OUTDOOR, pattern ABANDONED_CACHE, pattern JEWELRY_DISPLAY_TRAP, pattern BLACK_STARRY_SKY, pattern DISENGAGED_DOCKING_GEAR, pattern RUINED_FIRST_AID_KIT, pattern FIRE_FIGHTING_GEAR, pattern DISPLAY_3D, pattern CRACKED_FLUE, pattern BLOOD_ON_WALL, pattern DEPOSIT_BOX, pattern JEWELRY_CASE, pattern EDIBLE_PLANT_RIPE, pattern STAIRS_TRAP_DOWN_OIL, pattern DOOR_TRAP_PUSH, pattern LIFT_UP, pattern LIFT_DOWN, pattern LIFT_TRAP, pattern SHUTTLE_HARDWARE, pattern OIL_PUDDLE, pattern DECONTAMINATION_CHAMBER, pattern BARREL_CONTENTS, pattern WORKSHOP_BENCH   , pattern MUSEAL, pattern EDIBLE_PLANT, pattern FIRE_FIGHTING_ITEM, pattern STEEL_SCRAP, pattern HANDLE, pattern HANDLE_AND_STEEL, pattern POLE_AND_STEEL, pattern SPACESUIT_PART, pattern THICK_CLOTH, pattern PERFUME, pattern STARTING_HAMMER, pattern CLOTH_RAG   , pattern S_ENCHANCED_BERRY, pattern S_COOKED_BERRY, pattern S_FRAYED_FUNGUS, pattern S_COOKED_FUNGUS, pattern S_THIC_LEAF, pattern S_COOKED_LEAF, pattern S_RECONFIGURED_FRUIT, pattern S_COOKED_FRUIT, pattern S_FRAGRANT_HERB, pattern S_COOKED_HERB, pattern S_DULL_FLOWER, pattern S_COOKED_FLOWER, pattern S_SPICY_BARK, pattern S_COOKED_BARK, pattern S_PUMPKIN, pattern S_COOKED_PUMPKIN, pattern S_REFRIGERATION_COIL, pattern S_DOUSED_WOODEN_TORCH, pattern S_DOUSED_OIL_LAMP, pattern S_OIL_LAMP, pattern S_ROSE_WATER_FLASK, pattern S_WATER_FLASK, pattern S_SPACESUIT_JACKET, pattern S_SPACESUIT_TROUSERS, pattern S_SPACESUIT_GLOVE, pattern S_SPACESUIT_HELMET, pattern S_SPACESUIT_BOOT, pattern S_SPACESUIT, pattern S_SPACESUIT_TORN, pattern S_HARPOON_CARGO, pattern S_HARPOON_SHARP, pattern S_SHIELD_BLUNT, pattern S_SHIELD_SHARP, pattern S_SHORT_BLUNT_HAMMER, pattern S_LONG_BLUNT_HAMMER, pattern S_SHORT_SHARP_HAMMER, pattern S_LONG_SHARP_HAMMER, pattern S_CLEAVER, pattern S_DAGGER, pattern S_RAPIER_BLUNT, pattern S_RAPIER_SHARP, pattern S_POLE_CLEAVER, pattern S_LONG_SPEAR, pattern S_SHORT_CLUB, pattern S_LONG_CLUB, pattern S_CROWBAR, pattern S_FIRE_AXE, pattern S_POLL_AXE, pattern S_HALBERD_BLUNT, pattern S_HALBERD_SHARP, pattern S_STAFF, pattern S_PIPE, pattern S_SHARPENED_PIPE@@ -26,21 +25,18 @@ import Content.ItemKindOrgan import Content.ItemKindTemporary import Game.LambdaHack.Content.ItemKind-import Game.LambdaHack.Content.TileKind (floorSymbol) import Game.LambdaHack.Core.Dice import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal import Game.LambdaHack.Definition.Flavour  -- * Group name patterns  embedsGNSingleton :: [GroupName ItemKind] embedsGNSingleton =-     S_SANDSTONE_ROCK-     : [S_ENCHANCED_BERRY, S_COOKED_BERRY, S_FRAYED_FUNGUS, S_COOKED_FUNGUS, S_THIC_LEAF, S_COOKED_LEAF, S_RECONFIGURED_FRUIT, S_COOKED_FRUIT, S_FRAGRANT_HERB, S_COOKED_HERB, S_DULL_FLOWER, S_COOKED_FLOWER, S_SPICY_BARK, S_COOKED_BARK, S_PUMPKIN, S_COOKED_PUMPKIN, S_REFRIGERATION_COIL, S_DOUSED_WOODEN_TORCH, S_DOUSED_OIL_LAMP, S_OIL_LAMP, S_ROSE_WATER_FLASK, S_WATER_FLASK, S_SPACESUIT_JACKET, S_SPACESUIT_TROUSERS, S_SPACESUIT_GLOVE, S_SPACESUIT_HELMET, S_SPACESUIT_BOOT, S_SPACESUIT, S_SPACESUIT_TORN, S_HARPOON_CARGO, S_HARPOON_SHARP, S_SHIELD_BLUNT, S_SHIELD_SHARP, S_SHORT_BLUNT_HAMMER, S_LONG_BLUNT_HAMMER, S_SHORT_SHARP_HAMMER, S_LONG_SHARP_HAMMER, S_CLEAVER, S_DAGGER, S_RAPIER_BLUNT, S_RAPIER_SHARP, S_POLE_CLEAVER, S_LONG_SPEAR, S_SHORT_CLUB, S_LONG_CLUB, S_CROWBAR, S_FIRE_AXE, S_POLL_AXE, S_HALBERD_BLUNT, S_HALBERD_SHARP, S_STAFF, S_PIPE, S_SHARPENED_PIPE]--pattern S_SANDSTONE_ROCK :: GroupName ItemKind+       [S_ENCHANCED_BERRY, S_COOKED_BERRY, S_FRAYED_FUNGUS, S_COOKED_FUNGUS, S_THIC_LEAF, S_COOKED_LEAF, S_RECONFIGURED_FRUIT, S_COOKED_FRUIT, S_FRAGRANT_HERB, S_COOKED_HERB, S_DULL_FLOWER, S_COOKED_FLOWER, S_SPICY_BARK, S_COOKED_BARK, S_PUMPKIN, S_COOKED_PUMPKIN, S_REFRIGERATION_COIL, S_DOUSED_WOODEN_TORCH, S_DOUSED_OIL_LAMP, S_OIL_LAMP, S_ROSE_WATER_FLASK, S_WATER_FLASK, S_SPACESUIT_JACKET, S_SPACESUIT_TROUSERS, S_SPACESUIT_GLOVE, S_SPACESUIT_HELMET, S_SPACESUIT_BOOT, S_SPACESUIT, S_SPACESUIT_TORN, S_HARPOON_CARGO, S_HARPOON_SHARP, S_SHIELD_BLUNT, S_SHIELD_SHARP, S_SHORT_BLUNT_HAMMER, S_LONG_BLUNT_HAMMER, S_SHORT_SHARP_HAMMER, S_LONG_SHARP_HAMMER, S_CLEAVER, S_DAGGER, S_RAPIER_BLUNT, S_RAPIER_SHARP, S_POLE_CLEAVER, S_LONG_SPEAR, S_SHORT_CLUB, S_LONG_CLUB, S_CROWBAR, S_FIRE_AXE, S_POLL_AXE, S_HALBERD_BLUNT, S_HALBERD_SHARP, S_STAFF, S_PIPE, S_SHARPENED_PIPE]  pattern S_ENCHANCED_BERRY, S_COOKED_BERRY, S_FRAYED_FUNGUS, S_COOKED_FUNGUS, S_THIC_LEAF, S_COOKED_LEAF, S_RECONFIGURED_FRUIT, S_COOKED_FRUIT, S_FRAGRANT_HERB, S_COOKED_HERB, S_DULL_FLOWER, S_COOKED_FLOWER, S_SPICY_BARK, S_COOKED_BARK, S_PUMPKIN, S_COOKED_PUMPKIN, S_REFRIGERATION_COIL, S_DOUSED_WOODEN_TORCH, S_DOUSED_OIL_LAMP, S_OIL_LAMP, S_ROSE_WATER_FLASK, S_WATER_FLASK, S_SPACESUIT_JACKET, S_SPACESUIT_TROUSERS, S_SPACESUIT_GLOVE, S_SPACESUIT_HELMET, S_SPACESUIT_BOOT, S_SPACESUIT, S_SPACESUIT_TORN, S_HARPOON_CARGO, S_HARPOON_SHARP, S_SHIELD_BLUNT, S_SHIELD_SHARP, S_SHORT_BLUNT_HAMMER, S_LONG_BLUNT_HAMMER, S_SHORT_SHARP_HAMMER, S_LONG_SHARP_HAMMER, S_CLEAVER, S_DAGGER, S_RAPIER_BLUNT, S_RAPIER_SHARP, S_POLE_CLEAVER, S_LONG_SPEAR, S_SHORT_CLUB, S_LONG_CLUB, S_CROWBAR, S_FIRE_AXE, S_POLL_AXE, S_HALBERD_BLUNT, S_HALBERD_SHARP, S_STAFF, S_PIPE, S_SHARPENED_PIPE :: GroupName ItemKind @@ -77,8 +73,6 @@ pattern STRAIGHT_PATH = GroupName "straight path" pattern FROZEN_GROUND = GroupName "frozen ground" -pattern S_SANDSTONE_ROCK = GroupName "sandstone rock"- -- ** Allure-specific pattern STAIRS_UP_OUTDOOR = GroupName "stairs outdoor up" pattern STAIRS_DOWN_OUTDOOR = GroupName "stairs outdoor down"@@ -192,7 +186,7 @@ -- and foregoing the fun of guessing how to find entrance to a disjoint part -- of the level by bumping the least number of secret walls. scratchOnWall = ItemKind-  { isymbol  = '?'+  { isymbol  = toContentSymbol '?'   , iname    = "claw mark"   , ifreq    = [(SCRATCH_ON_WALL, 1)]   , iflavour = zipPlain [BrBlack]@@ -203,12 +197,12 @@   , idamage  = 0   , iaspects = [SetFlag Durable]   , ieffects = [ VerbMsg "start making sense of the scratches" "."-               , Detect DetectHidden 3 ]+               , Detect DetectHidden 4 ]   , idesc    = "A seemingly random series of scratches, carved deep into the wall."   , ikit     = []   } obscenePictogram = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "repulsing graffiti"   , ifreq    = [(OBSCENE_PICTOGRAM, 1)]   , iflavour = zipPlain [BrMagenta]@@ -227,7 +221,7 @@   , ikit     = []   } subtleFresco = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "subtle mural"   , ifreq    = [(SUBTLE_FRESCO, 1)]   , iflavour = zipPlain [BrGreen]@@ -243,7 +237,7 @@   , ikit     = []   } treasureCache = ItemKind-  { isymbol  = 'o'+  { isymbol  = toContentSymbol 'o'   , iname    = "set"   , ifreq    = [(ABANDONED_CACHE, 1)]   , iflavour = zipPlain [BrBlue]@@ -260,7 +254,7 @@ reliefMsg :: Effect reliefMsg = VerbMsg "sigh with relief when nothing explodes in your face!" "" treasureCacheTrap = ItemKind-  { isymbol  = '^'+  { isymbol  = toContentSymbol '^'   , iname    = "anti-theft protection"   , ifreq    = [(JEWELRY_DISPLAY_TRAP, 1)]   , iflavour = zipPlain [Red]@@ -278,7 +272,7 @@   , ikit     = []   } signageExit = ItemKind-  { isymbol  = '0'+  { isymbol  = toContentSymbol '0'   , iname    = "sticker"   , ifreq    = [(SIGNAGE, 100)]   , iflavour = zipPlain [BrGreen]@@ -309,7 +303,7 @@   , idesc    = "A list of nearby commercial outlets, constantly updated by tracking merchandise not registered as passenger property. Customers are kindly requeted to refrain from littering in this heavily monitored public area."   } fireSmall = ItemKind-  { isymbol  = 'o'+  { isymbol  = toContentSymbol 'o'   , iname    = "tiny fire"   , ifreq    = [(SMALL_FIRE, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrRed]@@ -332,7 +326,7 @@                , OnCombine roastEffect5 ]   } fireBig = fireSmall-  { isymbol  = '0'+  { isymbol  = toContentSymbol '0'   , iname    = "big fire"   , ifreq    = [(BIG_FIRE, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [Red]@@ -344,7 +338,7 @@   , ikit     = []   } frost = ItemKind-  { isymbol  = '^'+  { isymbol  = toContentSymbol '^'   , iname    = "frozen mass"   , ifreq    = [(FROST, 1), (COLD_SOURCE, 1)]   , iflavour = zipPlain [BrBlue]@@ -361,7 +355,7 @@   , ikit     = []   } rubble = ItemKind-  { isymbol  = '&'+  { isymbol  = toContentSymbol '&'   , iname    = "rubble"   , ifreq    = [(RUBBLE, 1)]   , iflavour = zipPlain [BrYellow]@@ -382,7 +376,7 @@   , ikit     = []   } doorwayTrapTemplate = ItemKind-  { isymbol  = '+'+  { isymbol  = toContentSymbol '+'   , iname    = "doorway trap"   , ifreq    = [(DOORWAY_TRAP_UNKNOWN, 1), (DOORWAY_TRAP, 0)]       -- the void group needed to pick the item for tile triggering@@ -416,7 +410,7 @@   -- , idesc    = ""   } stairsUp = ItemKind-  { isymbol  = '<'+  { isymbol  = toContentSymbol '<'   , iname    = "flight"   , ifreq    = [(STAIRS_UP, 1)]   , iflavour = zipPlain [BrWhite]@@ -432,13 +426,13 @@   , ikit     = []   } stairsDown = stairsUp-  { isymbol  = '>'+  { isymbol  = toContentSymbol '>'   , ifreq    = [(STAIRS_DOWN, 1)]   , ieffects = [Ascend False]   , idesc    = "Stairs that descend towards the outer ring. Narrow enough that only one person can comfortably use them at a time, but short enough that the whole team may climb down in quick succession."   } escape = stairsUp-  { isymbol  = '>'+  { isymbol  = toContentSymbol '>'   , iname    = "way"   , ifreq    = [(ESCAPE, 1)]   , iflavour = zipPlain [BrGreen]@@ -449,7 +443,7 @@                  -- for moon outdoors, spaceship, everywhere   } stairsTrapUp = ItemKind-  { isymbol  = '^'+  { isymbol  = toContentSymbol '^'   , iname    = "staircase trap"   , ifreq    = [(STAIRS_TRAP_UP, 1)]   , iflavour = zipPlain [BrRed]@@ -476,7 +470,7 @@   , idesc    = "A treacherous slab, to teach those who are too proud."   } lectern = ItemKind-  { isymbol  = '?'+  { isymbol  = toContentSymbol '?'   , iname    = "VR harness"   , ifreq    = [(LECTERN, 1)]   , iflavour = zipFancy [BrYellow]@@ -495,7 +489,7 @@   , ikit     = []   } shallowWater = ItemKind-  { isymbol  = '~'+  { isymbol  = toContentSymbol '~'   , iname    = "shallow water"   , ifreq    = [(SHALLOW_WATER, 1)]  -- may be too shallow to be source   , iflavour = zipFancy [BrCyan]@@ -510,7 +504,7 @@   , ikit     = []   } straightPath = ItemKind-  { isymbol  = floorSymbol+  { isymbol  = toContentSymbol '.'   , iname    = "straight path"   , ifreq    = [(STRAIGHT_PATH, 1)]   , iflavour = zipFancy [BrRed]@@ -525,7 +519,7 @@   , ikit     = []   } frozenGround = ItemKind-  { isymbol  = floorSymbol+  { isymbol  = toContentSymbol '.'   , iname    = "shade"   , ifreq    = [(FROZEN_GROUND, 1)]   , iflavour = zipFancy [BrBlue]@@ -552,7 +546,7 @@   , idesc    = "Stairs that descend towards the underground. Narrow enough that only one person can comfortably use them at a time, but short enough that the whole team may climb down in quick succession."   } blackStarrySky = ItemKind-  { isymbol  = ' '+  { isymbol  = toContentSymbol ' '   , iname    = "black starry sky"   , ifreq    = [(BLACK_STARRY_SKY, 1)]   , iflavour = zipPlain [Black]@@ -568,7 +562,7 @@   , ikit     = []   } disengagedDocking = ItemKind-  { isymbol  = '>'+  { isymbol  = toContentSymbol '>'   , iname    = "disengaged docking gear"   , ifreq    = [(DISENGAGED_DOCKING_GEAR, 1)]   , iflavour = zipPlain [BrBlack]@@ -583,7 +577,7 @@   , ikit     = []   } desertedAirlock = ItemKind-  { isymbol  = '>'+  { isymbol  = toContentSymbol '>'   , iname    = "the Initial Entrance"   , ifreq    = [(DISENGAGED_DOCKING_GEAR, 10000)]   , iflavour = zipPlain [Green]@@ -601,7 +595,7 @@                , (S_CROWBAR, CGround) ]   } ruinedFirstAidKit = ItemKind-  { isymbol  = '?'+  { isymbol  = toContentSymbol '?'   , iname    = "ruined first aid kit"   , ifreq    = [(RUINED_FIRST_AID_KIT, 1)]   , iflavour = zipPlain [BrGreen]@@ -620,7 +614,7 @@   , ikit     = []   } fireFightingGear = ItemKind-  { isymbol  = '?'+  { isymbol  = toContentSymbol '?'   , iname    = "fire fighting gear"   , ifreq    = [(FIRE_FIGHTING_GEAR, 1), (WATER_SOURCE, 1)]   , iflavour = zipPlain [BrRed]@@ -636,7 +630,7 @@   , ikit     = []   } fireFightingGearIntact = ItemKind-  { isymbol  = '?'+  { isymbol  = toContentSymbol '?'   , iname    = "the Fire Fighting Set"   , ifreq    = [(FIRE_FIGHTING_GEAR, 1), (WATER_SOURCE, 1)]   , iflavour = zipPlain [Red]@@ -653,7 +647,7 @@   , ikit     = []   } wall3dBillboard = ItemKind-  { isymbol  = '*'+  { isymbol  = toContentSymbol '*'   , iname    = "3D display"   , ifreq    = [(DISPLAY_3D, 1)]   , iflavour = zipPlain [BrBlue]@@ -670,7 +664,7 @@   , ikit     = []   } crackedFlue = ItemKind-  { isymbol  = '|'+  { isymbol  = toContentSymbol '|'   , iname    = "cracked flue"   , ifreq    = [(CRACKED_FLUE, 1)]  -- TODO: ("methane source", 1)?   , iflavour = zipPlain [BrBlack]@@ -687,7 +681,7 @@   , ikit     = []   } bloodOnWall = ItemKind-  { isymbol  = ','+  { isymbol  = toContentSymbol ','   , iname    = "blotch"   , ifreq    = [(BLOOD_ON_WALL, 60)]   , iflavour = zipPlain [BrRed]@@ -762,7 +756,7 @@   , idesc    = ""   } doorTrapPush = doorwayTrapTemplate-  { isymbol  = '+'+  { isymbol  = toContentSymbol '+'   , iname    = "weak door frame"   , ifreq    = [(DOOR_TRAP_PUSH, 1)]   , iflavour = zipPlain [Blue]@@ -812,7 +806,7 @@   , idesc    = ""   } shuttleHardware = ItemKind-  { isymbol  = '#'+  { isymbol  = toContentSymbol '#'   , iname    = "shuttle hardware"   , ifreq    = [(SHUTTLE_HARDWARE, 1)]   , iflavour = zipPlain [BrWhite]@@ -827,7 +821,7 @@   , ikit     = []   } machineOil = ItemKind-  { isymbol  = '~'+  { isymbol  = toContentSymbol '~'   , iname    = "oil layer"   , ifreq    = [(OIL_PUDDLE, 1), (OIL_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]@@ -845,7 +839,7 @@   , ikit     = []   } crudeWeld = ItemKind  -- this is also an organ-  { isymbol  = '_'+  { isymbol  = toContentSymbol '_'   , iname    = "crude weld"   , ifreq    = [(S_CRUDE_WELD, 1)]   , iflavour = zipPlain [BrMagenta]@@ -860,7 +854,7 @@   , ikit     = []   } decontaminator = ItemKind-  { isymbol  = 'D'+  { isymbol  = toContentSymbol 'D'   , iname    = "decontamination chamber"   , ifreq    = [(DECONTAMINATION_CHAMBER, 1)]   , iflavour = zipPlain [BrBlue]@@ -885,7 +879,7 @@   , ikit     = []   } barrelFuel = ItemKind-  { isymbol  = 'b'+  { isymbol  = toContentSymbol 'b'   , iname    = "fuel"   , ifreq    = [(BARREL_CONTENTS, 20), (OIL_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]@@ -934,7 +928,7 @@   , idesc    = ""   } workshopBench = ItemKind-  { isymbol  = ':'+  { isymbol  = toContentSymbol ':'   , iname    = "bench"   , ifreq    = [(WORKSHOP_BENCH, 1)]   , iflavour = zipPlain [BrBlue]
GameDefinition/Content/ItemKindOrgan.hs view
@@ -22,11 +22,14 @@  import Content.ItemKindBlast import Content.ItemKindTemporary+import Content.RuleKind import Game.LambdaHack.Content.ItemKind+import Game.LambdaHack.Content.RuleKind import Game.LambdaHack.Core.Dice import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal import Game.LambdaHack.Definition.Flavour  -- * Group name patterns@@ -150,10 +153,14 @@ -- Allure-specific animalStomach,       hungry, smallBeak, razor, liveWire, flotationBag, inkSac, powerfulHindLegs, coiledTail, jetBooster, rhinoInertia, electricAmbience, electricAmbienceRecharge, robotBrain, hullPlating, mouthVent, dustVent, dustFissure, fuelVent, fuelFissure, geneticFlaw3BadArmorMelee, geneticFlaw3BadArmorRanged, geneticFlaw10BadArmorMelee, geneticFlaw10BadArmorRanged, backstoryFluffTemplate, backstoryFluff1, backstoryGoodTemplate, backstoryGood1, backstoryGood2, backstoryGood3, backstoryBadTemplate, backstoryBad1, backstoryBad2, backstoryBad3, backstoryBad4, backstoryMixedTemplate, backstoryMixed1, backstoryMixed2, backstoryNeutralTemplate, backstoryNeutral1, backstoryNeutral2, backstoryNeutral3 :: ItemKind +symbolNecklace, symbolWand :: ContentSymbol ItemKind+symbolNecklace = rsymbolNecklace $ ritemSymbols standardRules+symbolWand = rsymbolWand $ ritemSymbols standardRules+ -- * No-cooldown melee damage organs without effects  thorn = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "thorn"   , ifreq    = [(S_THORN, 1)]   , icount   = 2 + 1 `d` 2  -- unrealistic, but not boring@@ -172,7 +179,7 @@   , idesc    = ""   } fist = ItemKind-  { isymbol  = ','+  { isymbol  = toContentSymbol ','   , iname    = "fist"   , ifreq    = [(S_FIST, 1)]   , iflavour = zipPlain [Red]@@ -274,7 +281,7 @@ -- * Direct damage organs with effects  beeSting = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "bee sting"   , ifreq    = [(S_BEE_STING, 1)]   , icount   = 1@@ -287,7 +294,7 @@   , idesc    = "Painful, but beneficial."   } sting = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "sting"   , ifreq    = [(S_STING, 1)]   , icount   = 1@@ -310,7 +317,7 @@   , idesc    = ""   } venomTooth = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "venom tooth"   , ifreq    = [(S_VENOM_TOOTH, 1)]   , iverbHit = "bite"@@ -321,7 +328,7 @@   , idesc    = "A chilling numbness spreads from its bite."   } hookedClaw = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "hooked claw"   , ifreq    = [(S_HOOKED_CLAW, 1)]   , icount   = 2  -- even if more, only the fore claws used for fighting@@ -333,7 +340,7 @@   , idesc    = "A curved talon."   } screechingBeak = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "screeching beak"   , ifreq    = [(S_SCREECHING_BEAK, 1)]   , icount   = 1@@ -345,7 +352,7 @@   , idesc    = "Both a weapon and a beacon, calling more scavengers to the meal."   } antler = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "antler"   , ifreq    = [(S_ANTLER, 1)]   , iverbHit = "ram"@@ -357,7 +364,7 @@   , idesc    = ""   } rhinoHorn = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "ugly horn"  -- made of keratin, unlike real horns   , ifreq    = [(S_RHINO_HORN, 1)]   , icount   = 1  -- single, unlike real horns@@ -369,7 +376,7 @@   , idesc    = "Very solid, considering it has the same composition as fingernails."   } hugeTail = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "huge tail"   , ifreq    = [(S_HUGE_TAIL, 1)]   , icount   = 1@@ -386,7 +393,7 @@ -- * Melee weapons without direct damage  venomFang = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "venom fang"   , ifreq    = [(S_VENOM_FANG, 1)]   , iverbHit = "bite"@@ -411,7 +418,7 @@   , idesc    = ""   } boilingFissure = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "fissure"   , ifreq    = [(S_BOILING_FISSURE, 1)]   , icount   = 2 + 1 `d` 2@@ -437,7 +444,7 @@ -- * Armor organs  armoredSkin = ItemKind-  { isymbol  = ','+  { isymbol  = toContentSymbol ','   , iname    = "armored skin"   , ifreq    = [(S_ARMORED_SKIN, 1)]   , iflavour = zipPlain [Red]@@ -560,7 +567,7 @@   } speedGland :: Int -> GroupName ItemKind -> ItemKind speedGland n grp = armoredSkin-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "speed gland"   , ifreq    = [(grp, 1)]   , iverbHit = "spit at"@@ -574,7 +581,7 @@ speedGland5 = speedGland 5 S_SPEED_GLAND_5 speedGland10 = speedGland 10 S_SPEED_GLAND_10 scentGland = armoredSkin-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "scent gland"   , ifreq    = [(S_SCENT_GLAND, 1)]   , icount   = 10 + 1 `d` 3  -- runs out@@ -589,7 +596,7 @@   , idesc    = ""   } sulfurVent = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "vent"   , ifreq    = [(S_MEDBOT_VENT, 1)]   , iflavour = zipPlain [BrYellow]@@ -600,7 +607,7 @@   , idesc    = ""   } boilingVent = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "vent"   , ifreq    = [(S_BOILING_VENT, 1)]   , iflavour = zipPlain [BrGreen]@@ -611,7 +618,7 @@   , idesc    = ""   } arsenicVent = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "vent"   , ifreq    = [(S_COOLING_VENT, 1)]   , iflavour = zipPlain [White]@@ -625,7 +632,7 @@ -- * Special  bonusHP = armoredSkin-  { isymbol  = 'H'  -- '+' reserved for conditions+  { isymbol  = toContentSymbol 'H'  -- '+' reserved for conditions   , iname    = "bonus HP"   , ifreq    = [(S_BONUS_HP, 1)]   , iflavour = zipPlain [BrBlue]@@ -635,7 +642,7 @@   , idesc    = "Special training and connections in the right places give this adventurer reinforced musculature and augmented internal organs, much more resilient to damage."   } braced = armoredSkin-  { isymbol  = 'B'+  { isymbol  = toContentSymbol 'B'   , iname    = "braced"   , ifreq    = [(S_BRACED, 1)]   , iflavour = zipPlain [BrGreen]@@ -647,7 +654,7 @@   , idesc    = "Apart of increased resilience to attacks, being braced protects from displacement by foes and other forms of forced translocation, e.g., pushing or pulling."   } asleep = armoredSkin-  { isymbol  = 'S'+  { isymbol  = toContentSymbol 'S'   , iname    = "asleep"   , ifreq    = [(S_ASLEEP, 1)]   , iflavour = zipPlain [BrGreen]  -- regenerates HP (very slowly)@@ -661,7 +668,7 @@   , idesc    = "Sleep helps to regain health, albeit extremely slowly. Being asleep makes you vulnerable, with gradually diminishing effects as the slumber wears off over several turns. Any non-idle action, not only combat but even yawning or stretching removes a sizable portion of the sleepiness."   } impressed = armoredSkin-  { isymbol  = 'I'+  { isymbol  = toContentSymbol 'I'   , iname    = "impressed"  -- keep the same as in @ifreq@, to simplify code   , ifreq    = [(S_IMPRESSED, 1), (CONDITION, 1)]   , iflavour = zipPlain [BrRed]@@ -691,7 +698,7 @@   , idesc    = "Cute, but painful."   } liveWire = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "live wire"   , ifreq    = [(S_LIVE_WIRE, 1)]   , icount   = 1@@ -704,7 +711,7 @@   , idesc    = ""   } razor = fist-  { isymbol  = '-'+  { isymbol  = symbolWand   , iname    = "razor edge"   , ifreq    = [(S_RAZOR, 1)]   , icount   = 1 + 1 `d` 2@@ -740,7 +747,7 @@ -- * Allure-specific other  animalStomach = armoredSkin-  { isymbol  = 'u'+  { isymbol  = toContentSymbol 'u'   , iname    = "animal stomach"   , ifreq    = [(S_ANIMAL_STOMACH, 1)]   , iverbHit = "burp"@@ -750,7 +757,7 @@   , idesc    = ""   } hungry = armoredSkin-  { isymbol  = 'U'+  { isymbol  = toContentSymbol 'U'   , iname    = "hungry"  -- keep the same as in @ifreq@, to simplify code   , ifreq    = [(S_HUNGRY, 1), (CONDITION, 1)]   , iflavour = zipPlain [BrRed]@@ -768,7 +775,7 @@   , idesc    = "Hunger limits physical fitness. In extreme cases, when compounded, it causes such fragility that the slightest stress becomes lethal."   } flotationBag = armoredSkin-  { isymbol  = 'O'+  { isymbol  = toContentSymbol 'O'   , iname    = "flotation bag"   , ifreq    = [(S_FLOTATION_BAG, 1)]   , iverbHit = "uplift"@@ -780,7 +787,7 @@   , idesc    = "A large organ that enables effortless flight. It is essentially a hydrogen container with easily regulated internal pressure. It evolved a protection against blunt trauma, but not against puncture."   } inkSac = armoredSkin  -- neither melee nor aspects nor periodic, so to be-  { isymbol  = '"'    -- triggered, needs a special symbol+  { isymbol  = symbolNecklace    -- triggered, needs a special symbol   , iname    = "ink sac"   , ifreq    = [(S_INK_SAC, 1)]   , iverbHit = "squirt"@@ -791,7 +798,7 @@   , idesc    = ""  -- TODO: https://en.wikipedia.org/wiki/Octopus#Ink_sac   } powerfulHindLegs = armoredSkin  -- neither melee nor periodic so to trigger-  { isymbol  = '"'              -- needs a special symbol+  { isymbol  = symbolNecklace              -- needs a special symbol   , iname    = "pair"   , ifreq    = [(S_POWERFUL_HIND_LEGS, 1)]   , iverbHit = "jump"@@ -808,7 +815,7 @@   , idesc    = "When the coiled tail springs, expect a lurch that leaves you no time to react."   } jetBooster = armoredSkin  -- neither melee nor periodic so to be triggered,-  { isymbol  = '"'        -- needs a special symbol+  { isymbol  = symbolNecklace        -- needs a special symbol   , iname    = "jet booster"   , ifreq    = [(S_JET_BOOSTER, 1)]   , iverbHit = "dart"@@ -824,7 +831,7 @@   , idesc    = "It's a struggle to move the mass and it's a reinforced concrete wall that stops it."   } electricAmbience = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "static current ambience"   , ifreq    = [(ELECTRIC_AMBIENCE, 1)]   , iverbHit = "shortcut"@@ -856,7 +863,7 @@   , idesc    = ""   } mouthVent = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "mouth vent"   , ifreq    = [(S_MOUTH_VENT, 1)]   , iflavour = zipPlain [BrMagenta]@@ -864,7 +871,7 @@   , iaspects = [ Timeout 7                , SetFlag Periodic, SetFlag Durable ]   , ieffects = [OneOf $-      map (\msg -> AndEffect (Explode S_SMOKE) (uncurry VerbMsg msg))+      map (AndEffect (Explode S_SMOKE) . uncurry VerbMsg)           [ ("say: Sir, your luggage has already been collected", ".")           , ("ask: Would you kindly help me?", "")           , ("complain: I can't reach you with this tool", "." ) ]@@ -874,7 +881,7 @@   , idesc    = ""   } dustVent = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "vent"   , ifreq    = [(S_DUST_VENT, 1)]   , iflavour = zipPlain [BrCyan]@@ -885,7 +892,7 @@   , idesc    = ""   } fuelVent = armoredSkin-  { isymbol  = 'v'+  { isymbol  = toContentSymbol 'v'   , iname    = "vent"   , ifreq    = [(S_FUEL_VENT, 1)]   , iflavour = zipPlain [BrRed]@@ -902,7 +909,7 @@ -- at actor death, avoiding a lot of spam and bringing him back to life. geneticFlaw :: Int -> Bool -> Int -> GroupName ItemKind -> ItemKind geneticFlaw fr badArmorMelee n grp = armoredSkin-  { isymbol  = 'F'+  { isymbol  = toContentSymbol 'F'   , iname    = "genetic flaw"  -- keep the same as in @ifreq@, to simplify code   , ifreq    = [(GENETIC_FLAW, fr), (grp, 1)]   , iflavour = zipPlain [BrRed]@@ -947,7 +954,7 @@ -- and so reveal the backstory item too early and without the fun. -- The exception is fluff items, where the effects are marginal by definition. backstoryFluffTemplate = ItemKind-  { isymbol  = '?'+  { isymbol  = toContentSymbol '?'   , iname    = "unrevealed rumination"   , ifreq    = [(BACKSTORY_FLUFF_UNKNOWN, 1)]   , iflavour = zipStory [BrBlue, Blue]
GameDefinition/Content/ItemKindTemporary.hs view
@@ -25,6 +25,7 @@ import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal import Game.LambdaHack.Definition.Flavour  -- * Group name patterns@@ -114,7 +115,7 @@ tmpAspects grp aspects =   let name = fromGroupName grp  -- @iname@ must match @ifreq@, see @myBadGrps@   in ItemKind-    { isymbol  = '+'+    { isymbol  = toContentSymbol '+'     , iname    = name     , ifreq    = [(grp, 1), (CONDITION, 1)]     , iflavour = zipPlain [BrWhite]
GameDefinition/Content/ModeKind.hs view
@@ -29,6 +29,7 @@ import Game.LambdaHack.Content.ModeKind import Game.LambdaHack.Core.Dice import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal  -- * Group name patterns @@ -117,7 +118,7 @@   , mtutorial = True   , mroster = rosterBrawl   , mcaves  = cavesBrawl-  , mendMsg = [ (Killed, "That treacherous villain didn't honour his word and brought his friends to the fight. It would still not turn so bad if we remembered to use terrain to protect us from missiles or even completely hide our presence and if we honourably kept together to the end, at the same time preventing the overwhelming enemy forces from brutishly ganging up on our modest-sized, though valiant, squad. Having to repurchase the genetic therapy is the most painful result. If repeated, that's going to send you broke and in shame to Earth, to start collecting your Basic Income.")+  , mendMsg = [ (Killed, "That treacherous villain didn't honour his word and brought his friends to the fight. It would still not turn so bad if we remembered to use terrain to protect us from missiles or even completely hide our presence and if we honourably kept together to the end, at the same time preventing the overwhelming enemy forces from brutishly ganging up on our modest-sized, though valiant, squad.\nHaving to repurchase the genetic therapy is the most painful result. If repeated, that's going to send you broke and in shame to Earth, to start collecting your Basic Income.")               , (Conquer, "Bringing help was a sober and prudent move that resulted in well-earned victory and a splendid trophy of a title to a real inter-planetary space vessel. Unfortunately, the treacherous foe called reinforcements at the last moment, a new wave arriving even now. It may be wise to move the celebration of the victory to a more fitting area, assuming that the dignified translocation can be accomplished timely and inconspicuously.") ]   , mrules  = T.intercalate "\n"       [ "* Two levels"@@ -125,8 +126,8 @@       , "* Minimize losses"       , "* Incapacitate all enemies ASAP"       ]-  , mdesc   = "\"You scoundrel! You cheated in the sewers, fighting two against one. Come alone to the woody biosphere behind the saloon at noon, if you dare. Given that I win, I take back all your gold. Otherwise, you get the scrapping rights for the giant spaceliner's hull in orbit.\nYes, it's mine, you tramp; here's the docking transmitter and the paperwork. The fight is to the last man standing, no evasion, no breaks for nano-healing in town.\""-  , mreason = "In addition to advancing the game plot, this encounter trains melee, squad formation, stealth and stairs use. On each level separately, the battle is symmetric, both in numbers, goals (incapacitate all enemies) and squad capabilities (only the pointman moves, while all others either melee or wait). Observe and mimic the enemies. If you can't see an enemy that apparently can see you, in reversed circumstances you would have the same advantage. Savour the relative fairness --- you won't find any in the main crawl adventure that follows."+  , mdesc   = "Last evening: \"You scoundrel! You cheated in the sewers, fighting two against one. Come alone to the woody biosphere behind the saloon at noon, if you dare. Given that I win, I take back all your gold. Otherwise, you get the scrapping rights for the giant spaceliner's hull in orbit.\nYes, it's mine, you tramp; here's the docking transmitter and the paperwork. The fight is to the last man standing, no evasion, no breaks for nano-healing in town.\"\nIt's noon now."+  , mreason = "In addition to advancing the game plot, this encounter trains melee, squad formation, stealth and stairs use. On each level separately, the battle is symmetric: three vs three. Similar are also goals (incapacitate all enemies) and squad capabilities (only the pointman moves, while all others either melee or wait). Observe and mimic the enemies. If you can't see an enemy that apparently can see you, in reversed circumstances you would have the same advantage. Savour the relative fairness --- you won't find any in the main crawl adventure that follows."   , mhint   = "Run a short distance with Shift or LMB, switch the pointman with Tab, repeat. In open terrain, if you keep distance between teammates, this resembles the leap frog infantry tactics. For best effects, end each sprint behind a cover or concealment.\nOnce you clear a level, descend by bumping into stairs. Use Tab to switch to remaining heroes until all gather on the new level.\nIf you get beaten repeatedly, try using all consumables you find, particularly the vials that collect healing extracts abounding in this rich biosphere. Ponder the hints from the defeat message, in particular the one about keeping your party together once the opponents are spotted. However, if you want to discover a winning tactics on your own, make sure to ignore any such tips until you succeed."   } @@ -166,7 +167,7 @@   , mtutorial = False   , mroster = rosterShootout   , mcaves  = cavesShootout-  , mendMsg = [ (Killed, "This is a disgrace. How is a thuggish robbery in broad daylight even possible in a moon city that styles itself as the capital of Outer System technological innovation and commercial opportunity? Where are the municipal surveillance drones, normally so eager to eavesdrop and needlessly complicate an honest tax-free business, when one's health and wealth for once depend on their nosy presence? Speaking of drones, we could use one in this skirmish, or even just a human lookout placed in a covered but unobstructed spot. Then the rest of the squad could snipe from concealment or from a safe distance.\nBarring that, we would end up in a better shape even if we all hid and fired blindly. We'd listen to impact sounds and wait vigilantly for incoming enemy missiles in order to register their trajectories and derive hints of enemy location. Apparently, ranged combat requires a change of pace and better planning than our previous illustrious successes accustomed us to.")+  , mendMsg = [ (Killed, "This is a disgrace. How is a thuggish robbery in broad daylight even possible in a moon city that styles itself as the capital of Outer System technological innovation and commercial opportunity? Where are the municipal surveillance drones, normally so eager to eavesdrop and needlessly complicate an honest tax-free business, when one's health and wealth for once depend on their nosy presence?\nSpeaking of drones, we could use one in this skirmish, or even just a human lookout placed in a covered but unobstructed spot. Then the rest of the squad could snipe from concealment or from a safe distance.\nBarring that, we would end up in a better shape even if we all hid and fired blindly. We'd listen to impact sounds and wait vigilantly for incoming enemy missiles in order to register their trajectories and derive hints of enemy location. Apparently, ranged combat requires a change of pace and better planning than our previous illustrious successes accustomed us to.")               , (Conquer, "That was a good fight, with skillful application of missiles, cover and concealment. The outcome is especially commendable given the high bar of tactical proficiency. Not even professional enforcement units can routinely deduce enemy position from the trajectory of their projectiles nor by firing without line of sight and interpreting auditory cues. However, while this steep hurdle is overcome, the chase is not over yet.") ]   , mrules  = T.intercalate "\n"       [ "* One level only"@@ -186,7 +187,7 @@   , mtutorial = False   , mroster = rosterHunt   , mcaves  = cavesHunt-  , mendMsg = [ (Killed, "Next time let's try to remember we are not on a sightseeing expedition. Also, leaving concealment is risky, leaving cover is foolhardy and wandering off is deadly. Also, what was that taking pictures by the mangrove tree all about? Were you trying to immortalize your handsome faces in case our shared pool of money was not enough to revive your sorry carcasses after the defeat? Good call, because after paying the techno-medical bills we are broke, while the gang foot soldiers that chase us seem to have military grade communication and reaction fire implants. And we were so close to complete victory and unfathomable wealth, if only we strove to lower the difficulty of this mission instead of raising it.")+  , mendMsg = [ (Killed, "Next time let's try to remember we are not on a sightseeing expedition. Also, leaving concealment is risky, leaving cover is foolhardy and wandering off is deadly. Also, what was that taking pictures by the mangrove tree all about? Were you trying to immortalize your handsome faces in case our shared pool of money was not enough to revive your sorry carcasses after the defeat?\nGood call, because after paying the techno-medical bills we are broke, while the gang foot soldiers that chase us seem to have military grade communication and reaction fire implants. And we were so close to complete victory and unfathomable wealth, if only we strove to lower the difficulty of this mission instead of raising it.")       -- the guy is wrong about implants (though the items are genetically attuned), but being wrong is plausible when the team is killed off/chased off and can't scour the battleground       -- this is in the middle of the scenario list and the mission is not tricky, so a subtle reminder about lowering difficulty, in case the player struggles               , (Conquer, "We chased them off, like we knew that we would. It feels nice to stick together and prevail. Now we can do no wrong just minding our business and going our way to the spaceport. We taught them a lesson, despite their superior equipment, and nobody else needs to be harmed while we take possession of our rightful property, the glorious spaceship in Triton's orbit.") ]@@ -208,8 +209,8 @@   , mtutorial = False   , mroster = rosterEscape   , mcaves  = cavesEscape-  , mendMsg = [ (Killed, "Somebody must have tipped the gang guards off. However, us walking along a lit trail, yelling, could have been a contributing factor. Also, it's worth noting that the torches prepared for this assault are best used as thrown makeshift flares. On the other hand, equipping a lit torch makes one visible in the dark, regrettably but not quite unexpectedly. Lastly, the goal of this foray was to find the exit back to the city, marked by a yellow '>' sign, and to gather some treasure along the way. Not to harass every local evildoer, as much as they do deserve it.")-              , (Conquer, "It was enough to reach the escape area, namely the exit tunnel from the park marked by yellow '>' symbol. Spilling that much blood was risky and unnecessary. Having said that --- impressive indeed.")+  , mendMsg = [ (Killed, "Somebody must have tipped the gang guards off. However, us walking along a lit trail, yelling, could have been a contributing factor. Also, it's worth noting that the torches prepared for this assault are best used as thrown makeshift flares.\nOn the other hand, equipping a lit torch makes one visible in the dark, regrettably but not quite unexpectedly. Lastly, the goal of this foray was to find the exit back to the city, marked by a yellow '>' sign, and to gather some treasure along the way. Not to harass every local evildoer, as much as they do deserve it.")+              , (Conquer, "It was enough to reach the escape area, namely the exit tunnel from the park marked by yellow '>' symbol. Spilling that much blood was risky. unnecessary and alerted the authorities. Having said that --- impressive indeed.")               , (Escape, "Congratulations, you took your revenge and it's heavy in your pockets.") ]   , mrules  = T.intercalate "\n"       [ "* One level only"@@ -258,7 +259,7 @@   , mtutorial = False   , mroster = rosterAmbush   , mcaves  = cavesAmbush-  , mendMsg = [ (Killed, "You turned out to be the prey, this time, not the hunter. In fact, you are not even in the hunters' league. When fighting against such odds, passively waiting for enemy to spring a trap is to no avail, because a professional team can sneak in darkness and ambush the ambushers. Granted, good positioning is crucial, so that each squad member can overwatch the battlefield and fire opportunistically, using the recently recovered mil-grade communication equipment. However, there is no hope without active scouting, throwing lit objects and probing suspect areas with missiles while paying attention to sounds. And that may still not be enough.")+  , mendMsg = [ (Killed, "You turned out to be the prey, this time, not the hunter. In fact, you are not even in the hunters' league. When fighting against such odds, passively waiting for enemy to spring a trap is to no avail, because a professional team can sneak in darkness and ambush the ambushers.\nGranted, good positioning is crucial, so that each squad member can overwatch the battlefield and fire opportunistically, using the recently recovered mil-grade communication equipment. However, there is no hope without active scouting, throwing lit objects and probing suspect areas with missiles while paying attention to sounds. And that may still not be enough.")               , (Conquer, "The new communication equipment enabling simultaneous ranged attacks with indirect aiming proved effective beyond expectation. With the mercenaries gone and nobody else having the slightest wish to interfere, the shuttle to the space cruiser at orbit is easy to launch at last. You let yourself bask in the battle's afterglow of bliss and relief. Now your turbulent adventure ends and the boring life of space cruiser scrap parts supplier or, as it may be, of a refurbished giant space liner operator, commences.\nA pity that the last round of shoddy genetic enhancements, bought at the grey market, scandalously auto-reverts at this very moment, leaving your personalized equipment that attuned to the previous genetic configuration inoperable. Fortunately, danger, debt and the gangster debt collectors are now behind you and the grey market won't see you ever again.") ]   , mrules  = T.intercalate "\n"       [ "* One level only"@@ -502,16 +503,16 @@ rosterRaid, rosterBrawl, rosterCrawl, rosterShootout, rosterHunt, rosterEscape, rosterZoo, rosterAmbush, rosterSafari, rosterCrawlEmpty, rosterCrawlSurvival, rosterSafariSurvival, rosterBattle, rosterBattleDefense, rosterBattleSurvival, rosterDefense, rosterDefenseEmpty :: Roster  rosterRaid = Roster-  { rosterList = [ ( playerHero {fhiCondPoly = hiHeroShort}+  { rosterList = [ ( playerAnimal  -- starting over escape+                   , Nothing+                   , [(2, 2, ANIMAL)] )+                 , ( playerHero {fhiCondPoly = hiHeroShort}                    , Just teamExplorer                    , [(2, 2, HERO)] )                  , ( playerAntiHero { fname = "Red Collar Bro"                                     , fhiCondPoly = hiHeroShort }                    , Just teamCompetitor                    , [(2, 1, RANGER_HERO)] )-                 , ( playerAnimal  -- starting over escape-                   , Nothing-                   , [(2, 2, ANIMAL)] )                  , ( playerRobot                    , Nothing                    , [(2, 1, ROBOT)] )@@ -544,7 +545,7 @@   , rosterAlly = [] }  rosterCrawl = Roster-  { rosterList = [ ( playerHero+  { rosterList = [ ( playerHero  -- start on stairs so that stash is handy                    , Just teamExplorer                    , [(3, 3, CRAWL_HERO)] )                  , ( playerMonster@@ -605,7 +606,6 @@ rosterEscape = Roster   { rosterList = [ ( playerAntiHero { fname = "Red Collar Bro"                                     , fcanEscape = False  -- start on escape-                                    , fneverEmpty = False  -- loot after killing                                     , fhiCondPoly = hiHeroMedium }                    , Just teamCompetitor                    , [(7, 6, AMBUSHER_HERO), (7, 1, SCOUT_HERO)] )@@ -701,16 +701,18 @@  rosterSafariSurvival = rosterSafari   { rosterList = [ ( playerMonsterTourist-                       { fleaderMode = LeaderAI $ AutoLeader True True-                       , fhasUI = False }+                       { fleaderMode = Just $ AutoLeader True True+                       , fhasUI = False+                       , funderAI = True }                    , Nothing                    , [(5, 15, MONSTER)] )                  , ( playerHunamConvict                    , Just teamCivilian                    , [(5, 3, CIVILIAN)] )                  , ( playerAnimalMagnificent-                       { fleaderMode = LeaderUI $ AutoLeader True False-                       , fhasUI = True }+                       { fleaderMode = Just $ AutoLeader True False+                       , fhasUI = True+                       , funderAI = False }                    , Nothing                    , [(10, 20, MOBILE_ANIMAL)] )                  , ( playerAnimalExquisite@@ -740,11 +742,8 @@                  , ("Robot Anarchy", "Animal Kingdom") ] }  rosterBattleDefense = rosterBattle-  { rosterList = [ ( playerHero { fcanEscape = False-                                , fhiCondPoly = hiHeroLong-                                , fleaderMode =-                                    LeaderAI $ AutoLeader False False-                                , fhasUI = False }+  { rosterList = [ ( playerAntiHero { fcanEscape = False+                                    , fhiCondPoly = hiHeroLong }                    , Just teamExplorer                    , [(10, 5, SOLDIER_HERO)] )                  , ( playerMonster { fneverEmpty = True@@ -759,11 +758,8 @@                    , [(10, 15, MOBILE_ROBOT)] ) ] }  rosterBattleSurvival = rosterBattle-  { rosterList = [ ( playerHero { fcanEscape = False-                                , fhiCondPoly = hiHeroLong-                                , fleaderMode =-                                    LeaderAI $ AutoLeader False False-                                , fhasUI = False }+  { rosterList = [ ( playerAntiHero { fcanEscape = False+                                    , fhiCondPoly = hiHeroLong }                    , Just teamExplorer                    , [(10, 5, SOLDIER_HERO)] )                  , ( playerMonster {fneverEmpty = True}
GameDefinition/Content/ModeKindPlayer.hs view
@@ -36,13 +36,15 @@   , fhiCondPoly = hiHeroLong   , fhasGender = True   , fdoctrine = TExplore-  , fleaderMode = LeaderUI $ AutoLeader False False+  , fleaderMode = Just $ AutoLeader False False   , fhasUI = True+  , funderAI = False   }  playerAntiHero = playerHero-  { fleaderMode = LeaderAI $ AutoLeader True False+  { fleaderMode = Just $ AutoLeader True False   , fhasUI = False+  , funderAI = True   }  playerCivilian = Player@@ -54,8 +56,9 @@   , fhiCondPoly = hiHeroMedium   , fhasGender = True   , fdoctrine = TPatrol-  , fleaderMode = LeaderNull  -- unorganized+  , fleaderMode = Nothing  -- unorganized   , fhasUI = False+  , funderAI = True   }  playerMonster = Player@@ -70,13 +73,15 @@   , fleaderMode =       -- No point changing leader on level, since all move and they       -- don't follow the leader.-      LeaderAI $ AutoLeader True True+      Just $ AutoLeader True True   , fhasUI = False+  , funderAI = True   }  playerAntiMonster = playerMonster-  { fleaderMode = LeaderUI $ AutoLeader True True+  { fleaderMode = Just $ AutoLeader True True   , fhasUI = True+  , funderAI = False   }  playerAnimal = Player@@ -89,8 +94,9 @@   , fhiCondPoly = hiDweller   , fhasGender = False   , fdoctrine = TRoam  -- can't pick up, so no point exploring-  , fleaderMode = LeaderNull+  , fleaderMode = Nothing   , fhasUI = False+  , funderAI = True   }  -- | A special player, for summoned actors that don't belong to any@@ -107,8 +113,9 @@   , fhiCondPoly = []   , fhasGender = False   , fdoctrine = TPatrol  -- disoriented-  , fleaderMode = LeaderNull+  , fleaderMode = Nothing   , fhasUI = False+  , funderAI = True   }  playerMonsterTourist =@@ -117,11 +124,13 @@                     , fneverEmpty = True  -- no spawning                     , fhiCondPoly = hiHeroMedium                     , fdoctrine = TFollow  -- follow-the-guide, as tourists do-                    , fleaderMode = LeaderUI $ AutoLeader False False }+                    , fleaderMode = Just $ AutoLeader False False+                    , funderAI = False }  playerHunamConvict =   playerCivilian { fname = "Hunam Convict"-                 , fleaderMode = LeaderAI $ AutoLeader True False }+                 , fleaderMode = Just $ AutoLeader True False+                 , funderAI = True }  playerAnimalMagnificent =   playerAnimal { fname = "Animal Magnificent Specimen Variety"@@ -196,6 +205,7 @@   , fhasGender = False   , fdoctrine = TRoam       -- TODO:TFollow -- coordinated via net, follow alien leader-  , fleaderMode = LeaderNull+  , fleaderMode = Nothing   , fhasUI = False+  , funderAI = True   }
GameDefinition/Content/PlaceKind.hs view
@@ -21,12 +21,14 @@  import Game.LambdaHack.Core.Prelude +import qualified Data.EnumMap.Strict as EM import qualified Data.Text as T  import Content.TileKind hiding (content, groupNames, groupNamesSingleton) import Game.LambdaHack.Content.PlaceKind import Game.LambdaHack.Content.TileKind (TileKind) import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal  -- * Group name patterns @@ -170,7 +172,39 @@       spaceshipEscapes = map switchEscapeToSpaceshipDown escapeDownBasic   in upEscapes ++ outdoorEscapes ++ spaceshipEscapes --- The dots below are @'\x00B7'@, as defined in @TileKind.floorSymbol@.+-- The dots below are @'\x00B7'@, as defined in 'TileKind.floorSymbol'.+defaultLegendLit :: EM.EnumMap Char (GroupName TileKind)+defaultLegendLit = EM.fromList+  [ ('#', FILLER_WALL)+  , ('0', S_PILLAR)+  , ('&', S_RUBBLE_PILE)+  , ('+', S_CLOSED_DOOR)+  , ('<', ESCAPE_UP)+  , ('>', ESCAPE_DOWN)+  , ('%', TRANSPARENT_WALL)+  , ('^', ICE_BUILDUP)+  , ('\'', S_OPEN_DOOR)+  , ('·', FLOOR_ACTOR_ITEM_LIT)+  , ('~', S_SHALLOW_WATER_LIT)+  , (':', WORKSHOP)+  , ('I', SIGNBOARD) ]++defaultLegendDark :: EM.EnumMap Char (GroupName TileKind)+defaultLegendDark = EM.fromList+  [ ('#', FILLER_WALL)+  , ('0', S_PILLAR)+  , ('&', S_RUBBLE_PILE)+  , ('+', S_CLOSED_DOOR)+  , ('<', ESCAPE_UP)+  , ('>', ESCAPE_DOWN)+  , ('%', TRANSPARENT_WALL)+  , ('^', ICE_BUILDUP)+  , ('\'', S_OPEN_DOOR)+  , ('·', FLOOR_ACTOR_ITEM_DARK)+  , ('~', S_SHALLOW_WATER_DARK)+  , (':', WORKSHOP)+  , ('I', SIGNBOARD) ]+ deadEnd = PlaceKind  -- needs to have index 0   { psymbol  = 'd'   , pname    = "a dead end"@@ -179,8 +213,8 @@   , pcover   = CStretch   , pfence   = FNone   , ptopLeft = ["·"]-  , poverrideDark = []-  , poverrideLit = []+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } rect = PlaceKind  -- Valid for any nonempty area, hence low frequency.   { psymbol  = 'r'@@ -190,14 +224,14 @@   , pcover   = CStretch   , pfence   = FWall   , ptopLeft = ["·"]-  , poverrideDark = []-  , poverrideLit = []+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } rect2 = rect   { pname    = "a pen"   , pfreq    = [(SHOOTOUT, 1), (ZOO, 10)]   }-rectWindows = PlaceKind+rectWindows = overridePlaceKind [('%', RECT_WINDOWS)] $ PlaceKind   { psymbol  = 'w'   , pname    = "a shed"   , pfreq    = [(ESCAPE, 20)]@@ -207,8 +241,8 @@   , ptopLeft = [ "#%"                , "%·"                ]-  , poverrideDark = [('%', RECT_WINDOWS)]-  , poverrideLit = [('%', RECT_WINDOWS)]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } glasshouse = PlaceKind   { psymbol  = 'g'@@ -220,14 +254,13 @@   , ptopLeft = [ "%%"                , "%·"                ]-  , poverrideDark = []-  , poverrideLit = []+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   }-glasshouse2 = glasshouse+glasshouse2 = override2PlaceKind [('·', DAMP_FLOOR_DARK)]+                                 [('·', DAMP_FLOOR_LIT)] $ glasshouse   { pname    = "a glass cage"   , pfreq    = [(LABORATORY, 2), (ZOO, 30)]-  , poverrideDark = [('·', DAMP_FLOOR_DARK)]-  , poverrideLit = [('·', DAMP_FLOOR_LIT)]   } glasshouse3 = glasshouse   { pname    = "an entertainment center"@@ -237,7 +270,8 @@   { pname    = "an exhibition area"   , pfreq    = [(ARENA, 1), (MUSEUM, 1)]   }-pulpit = PlaceKind+pulpit = overridePlaceKind [('0', S_PULPIT)] $ PlaceKind+           -- except for floor, all will be lit, regardless of night/dark; OK   { psymbol  = 'p'   , pname    = "a stand podium"   , pfreq    = [(ARENA, 15), (MUSEUM, 15), (ZOO, 100)]@@ -248,11 +282,11 @@                , "%··"                , "··0"                ]-  , poverrideDark = [('0', S_PULPIT)]-  , poverrideLit = [('0', S_PULPIT)]-      -- except for floor, this will all be lit, regardless of night/dark; OK+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   }-ruin = PlaceKind+ruin = override2PlaceKind [('·', DAMP_FLOOR_DARK)]+                          [('·', DAMP_FLOOR_LIT)] $ PlaceKind   { psymbol  = 'R'   , pname    = "ruins"   , pfreq    = [(BATTLE, 660), (AMBUSH, 70)]@@ -260,14 +294,14 @@   , pcover   = CStretch   , pfence   = FWall   , ptopLeft = ["X"]-  , poverrideDark = [('·', DAMP_FLOOR_DARK)]-  , poverrideLit = [('·', DAMP_FLOOR_LIT)]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } ruin2 = ruin   { pname    = "a scaffolding"   , pfreq    = [(NOISE, 2000), (EXIT, 5), (MUSEUM, 1)]   }-collapsed = PlaceKind+collapsed = overridePlaceKind [('#', DOORLESS_MACHINERY)] $ PlaceKind   { psymbol  = 'c'   , pname    = "a hardware stack"   , pfreq    = [(NOISE, 1)]@@ -278,8 +312,8 @@   , pfence   = FNone   , ptopLeft = [ "#"                ]-  , poverrideDark = [('#', DOORLESS_MACHINERY)]-  , poverrideLit = [('#', DOORLESS_MACHINERY)]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } collapsed2 = collapsed   { pfreq    = [(NOISE, 1000), (BATTLE, 200)]@@ -322,21 +356,42 @@   } pillar = PlaceKind   { psymbol  = 'p'-  , pname    = "a market"-  , pfreq    = [(ROGUE, 300), (ARENA, 10000), (EMPTY, 400)]+  , pname    = "a court"+  , pfreq    = [ (ROGUE, 250), (ARENA, 15), (MUSEUM, 10)+               , (LABORATORY, 200), (RAID, 50) ]   , prarity  = [(1, 1)]   , pcover   = CStretch   , pfence   = FWall   -- Larger rooms require support pillars.+  , ptopLeft = [ "#··"+               , "···"+               , "···"+               ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit+  }+pillar2 = pillar+  { pname    = "a plaza"+  , pfreq    = [ (ROGUE, 1500), (ARENA, 5000)+               , (MUSEUM, 4000), (LABORATORY, 1500) ]+  , ptopLeft = [ "#·#·"+               , "····"+               , "#···"+               , "····"+               ]+  }+pillar3 = override2PlaceKind [('·', OILY_FLOOR_DARK)]+                             [('·', OILY_FLOOR_LIT)] $ pillar+  { pname    = "a market"+  , pfreq    = [(ROGUE, 300), (ARENA, 10000), (EMPTY, 400)]   , ptopLeft = [ "····"                , "·0··"                , "····"                , "····"                ]-  , poverrideDark = [('·', OILY_FLOOR_DARK)]-  , poverrideLit = [('·', OILY_FLOOR_LIT)]   }-pillar2 = pillar+pillar4 = override2PlaceKind [('~', S_POOL_DARK)]+                             [('~', S_POOL_LIT)] $ pillar   { pname    = "a mall"   , pfreq    = [(ROGUE, 10000), (ARENA, 100000), (EMPTY, 4000)]   , ptopLeft = [ "0····"@@ -345,29 +400,11 @@                , "···0·"                , "····~"                ]-  , poverrideDark = [('~', S_POOL_DARK)]-  , poverrideLit = [('~', S_POOL_LIT)]   }-pillar3 = pillar-  { pname    = "a court"-  , pfreq    = [ (ROGUE, 250), (ARENA, 15), (MUSEUM, 10)-               , (LABORATORY, 200), (RAID, 50) ]-  , ptopLeft = [ "#··"-               , "···"-               , "···"-               ]-  }-pillar4 = pillar-  { pname    = "a plaza"-  , pfreq    = [ (ROGUE, 1500), (ARENA, 5000)-               , (MUSEUM, 4000), (LABORATORY, 1500) ]-  , ptopLeft = [ "#·#·"-               , "····"-               , "#···"-               , "····"-               ]-  }-pillar5 = pillar+pillar5 = overridePlaceKind [ ('&', CACHE_DEPOSIT)+                            , ('i', FLOOR_ACTOR_ITEM)  -- lit or not, randomly+                            , ('p', TRAPPED_DOOR) ] $ pillar+            -- no STUCK_DOOR, because FWall, so would break global pathfinding   { pname    = "a bank outlet"   , pfreq    = [ (ROGUE, 1200), (ARENA, 6000)                , (EMPTY, 600), (EXIT, 600) ]@@ -376,13 +413,12 @@                , "%#p·"                , "····"                ]-  , poverrideDark = [ ('&', CACHE_DEPOSIT), ('p', TRAPPED_DOOR)-                    , ('i', FLOOR_ACTOR_ITEM) ]  -- lit or not, randomly-  , poverrideLit = [ ('&', CACHE_DEPOSIT), ('p', TRAPPED_DOOR)-                   , ('i', FLOOR_ACTOR_ITEM) ]  -- lit or not, randomly-      -- no STUCK_DOOR, because FWall, so would break global pathfinding   }-pillar6 = pillar+pillar6 = override2PlaceKind [('f', BUSH_GROVE_DARK)]+                             [('f', BUSH_GROVE_LIT)] $+          overridePlaceKind [ ('&', CACHE_JEWELRY)+                            , ('0', S_LAMP_POST)+                            , ('a', S_FLOOR_ACTOR_LIT) ] $ pillar   { pname    = "a jewelry store"   , pfreq    = [ (ROGUE, 1200), (ARENA, 6000)                , (MUSEUM, 7000), (EMPTY, 600) ]@@ -391,10 +427,6 @@                , "·f&·"                , "····"                ]-  , poverrideDark = [ ('&', CACHE_JEWELRY), ('0', S_LAMP_POST)-                    , ('f', BUSH_GROVE_DARK) , ('a', S_FLOOR_ACTOR_LIT) ]-  , poverrideLit = [ ('&', CACHE_JEWELRY), ('0', S_LAMP_POST)-                   , ('f', BUSH_GROVE_LIT), ('a', S_FLOOR_ACTOR_LIT) ]   } colonnade = PlaceKind   { psymbol  = 'c'@@ -407,8 +439,8 @@   , ptopLeft = [ "#·"                , "··"                ]-  , poverrideDark = []-  , poverrideLit = []+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } colonnade2 = colonnade   { pfreq    = [(ROGUE, 300)]@@ -474,7 +506,8 @@                , "·0"                ]   }-lampPost = PlaceKind+lampPost = overridePlaceKind [ ('0', S_LAMP_POST)+                             , ('·', S_FLOOR_ACTOR_LIT) ] $ PlaceKind   { psymbol  = 'l'   , pname    = "a lamp-lit area"   , pfreq    = [ (ESCAPE, 200), (ZOO, 100), (AMBUSH, 1000)@@ -486,8 +519,8 @@                , "·0·"                , "X·X"                ]-  , poverrideDark = [('0', S_LAMP_POST), ('·', S_FLOOR_ACTOR_LIT)]-  , poverrideLit = [('0', S_LAMP_POST), ('·', S_FLOOR_ACTOR_LIT)]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } lampPost2 = lampPost   { ptopLeft = [ "···"@@ -513,7 +546,11 @@                , "X···X"                ]   }-treeShade = PlaceKind+treeShade = override2PlaceKind [ ('0', S_TREE_DARK)+                               , ('s', TREE_SHADE_WALKABLE_DARK) ]+                               [ ('0', S_TREE_LIT)+                               , ('s', TREE_SHADE_WALKABLE_LIT) ] $+            overridePlaceKind [('·', S_SHADED_GROUND)] $ PlaceKind   { psymbol  = 't'   , pname    = "a tree shade"   , pfreq    = [(BRAWL, 500)]@@ -524,14 +561,12 @@                , "s0·"                , "Xs·"                ]-  , poverrideDark = [ ('0', S_TREE_DARK)-                    , ('s', TREE_SHADE_WALKABLE_DARK)-                    , ('·', S_SHADED_GROUND) ]-  , poverrideLit = [ ('0', S_TREE_LIT)-                   , ('s', TREE_SHADE_WALKABLE_LIT)-                   , ('·', S_SHADED_GROUND) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   }-fogClump = PlaceKind+fogClump = override2PlaceKind [('f', FOG_CLUMP_DARK)]+                              [('f', FOG_CLUMP_LIT)] $+           overridePlaceKind [(';', S_FOG_LIT)] $ PlaceKind   { psymbol  = 'f'   , pname    = "a foggy patch"   , pfreq    = [(EMPTY, 400), (SHOOTOUT, 70), (ESCAPE, 60), (RAID, 50)]@@ -542,8 +577,8 @@                , ";f"                , ";X"                ]-  , poverrideDark = [('f', FOG_CLUMP_DARK), (';', S_FOG_LIT)]-  , poverrideLit = [('f', FOG_CLUMP_LIT), (';', S_FOG_LIT)]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } fogClump2 = fogClump   { pfreq    = [(EMPTY, 2200), (SHOOTOUT, 400), (ESCAPE, 100), (RAID, 250)]@@ -553,7 +588,11 @@                , "Xff"                ]   }-smokeClump = PlaceKind+smokeClump = override2PlaceKind [ ('f', SMOKE_CLUMP_DARK)+                                , ('·', S_FLOOR_ACTOR_DARK) ]+                                [ ('f', SMOKE_CLUMP_LIT)+                                , ('·', S_FLOOR_ACTOR_LIT) ] $+             overridePlaceKind [(';', S_SMOKE_LIT)] $ PlaceKind   { psymbol  = 's'   , pname    = "a smoky patch"   , pfreq    = [(EXIT, 20), (ZOO, 40), (AMBUSH, 50)]@@ -564,10 +603,8 @@                , ";f"                , ";X"                ]-  , poverrideDark = [ ('f', SMOKE_CLUMP_DARK), (';', S_SMOKE_LIT)-                    , ('·', S_FLOOR_ACTOR_DARK) ]-  , poverrideLit = [ ('f', SMOKE_CLUMP_LIT), (';', S_SMOKE_LIT)-                   , ('·', S_FLOOR_ACTOR_LIT) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } smokeClump2 = smokeClump   { pfreq    = [(EXIT, 100), (ZOO, 200), (AMBUSH, 150)]@@ -591,7 +628,9 @@       -- should not be used in caves with trails, because bushes should       -- not grow over such artificial trails   }-bushClump = PlaceKind+bushClump = override2PlaceKind [('f', BUSH_CLUMP_DARK)]+                               [('f', BUSH_CLUMP_LIT)] $+            overridePlaceKind [(';', S_BUSH_LIT)] $ PlaceKind   { psymbol  = 'b'   , pname    = "a bushy patch"   , pfreq    = [(SHOOTOUT, 120), (EMPTY, 60), (BRAWL, 30)]@@ -602,12 +641,17 @@                , ";Xf"                , ";fX"                ]-  , poverrideDark = [('f', BUSH_CLUMP_DARK), (';', S_BUSH_LIT)]-  , poverrideLit = [('f', BUSH_CLUMP_LIT), (';', S_BUSH_LIT)]+  , plegendDark =  defaultLegendDark+  , plegendLit = defaultLegendLit       -- should not be used in caves with trails, because bushes can't       -- grow over such artificial trails   }-escapeDown = PlaceKind+escapeDown = override2PlaceKind [('r', RUBBLE_OR_WASTE_DARK)]+                                [('r', RUBBLE_OR_WASTE_LIT)] $+             overridePlaceKind [ ('g', S_FROZEN_PATH)+                               , ('0', S_LAMP_POST)+                               , ('b', BARREL)+                               , ('a', S_FLOOR_ACTOR_LIT) ] $ PlaceKind   { psymbol  = '>'   , pname    = "an escape down"   , pfreq    = [(INDOOR_ESCAPE_DOWN, 1)]@@ -616,10 +660,8 @@   , pfence   = FGround   , ptopLeft = [ ">"                ]-  , poverrideDark = [ ('g', S_FROZEN_PATH) , ('0', S_LAMP_POST), ('b', BARREL)-                    , ('a', S_FLOOR_ACTOR_LIT), ('r', RUBBLE_OR_WASTE_DARK) ]-  , poverrideLit = [ ('g', S_FROZEN_PATH), ('0', S_LAMP_POST), ('b', BARREL)-                   , ('a', S_FLOOR_ACTOR_LIT), ('r', RUBBLE_OR_WASTE_LIT) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } escapeDown2 = escapeDown   { pfreq    = [(INDOOR_ESCAPE_DOWN, 200)]@@ -695,7 +737,9 @@                , "aa%%"                ]   }-staircase = PlaceKind+staircase = overridePlaceKind  [ ('<', STAIRCASE_UP)+                               , ('>', STAIRCASE_DOWN)+                               , ('S', FILLER_WALL) ] $ PlaceKind   { psymbol  = '/'   , pname    = "a staircase"   , pfreq    = [(TINY_STAIRCASE, 1)]  -- no cover when arriving; low freq@@ -704,10 +748,8 @@   , pfence   = FGround   , ptopLeft = [ "<S>"                ]-  , poverrideDark = [ ('<', STAIRCASE_UP), ('>', STAIRCASE_DOWN)-                    , ('I', SIGNBOARD), ('S', FILLER_WALL) ]-  , poverrideLit = [ ('<', STAIRCASE_UP), ('>', STAIRCASE_DOWN)-                   , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } staircase1 = staircase   { prarity  = [(1, 1)]  -- no cover when arriving; so low rarity@@ -720,7 +762,11 @@                ]   } -- Allure-specific:-staircaseLift = PlaceKind+overrideLift :: [(Char, GroupName TileKind)]+overrideLift =+  [ ('<', STAIRCASE_LIFT_UP), ('>', STAIRCASE_LIFT_DOWN)+  , ('S', S_LIFT_SHAFT) ]+staircaseLift = overridePlaceKind overrideLift $ PlaceKind   { psymbol  = '|'   , pname    = "a lift"   , pfreq    = [(TINY_LIFT, 1)]@@ -729,8 +775,8 @@   , pfence   = FGround   , ptopLeft = [ "<S>"                ]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } staircase3 = staircaseLift   { prarity  = [(1, 1)]@@ -1070,100 +1116,73 @@  -- * Allure-specific -overrideLift :: [(Char, GroupName TileKind)]-overrideLift = [ ('<', STAIRCASE_LIFT_UP), ('>', STAIRCASE_LIFT_DOWN)-               , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]-staircaseLift11 = staircase11+staircaseLift11 = overridePlaceKind overrideLift $ staircase11   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 2000)]  -- weak cover, low freq-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift12 = staircase12+staircaseLift12 = overridePlaceKind overrideLift $ staircase12   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 4000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift13 = staircase13+staircaseLift13 = overridePlaceKind overrideLift $ staircase13   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 6000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift14 = staircase14+staircaseLift14 = overridePlaceKind overrideLift $ staircase14   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 10000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift15 = staircase15+staircaseLift15 = overridePlaceKind overrideLift $ staircase15   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift16 = staircase16+staircaseLift16 = overridePlaceKind overrideLift $ staircase16   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift17 = staircase17+staircaseLift17 = overridePlaceKind overrideLift $ staircase17   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift18 = staircase18+staircaseLift18 = overridePlaceKind overrideLift $ staircase18   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 80000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift19 = staircase19+staircaseLift19 = overridePlaceKind overrideLift $ staircase19   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift20 = staircase20+staircaseLift20 = overridePlaceKind overrideLift $ staircase20   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 5000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift21 = staircase21+staircaseLift21 = overridePlaceKind overrideLift $ staircase21   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 5000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift22 = staircase22+staircaseLift22 = overridePlaceKind overrideLift $ staircase22   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 2000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift23 = staircase23+staircaseLift23 = overridePlaceKind overrideLift $ staircase23   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 1000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift24 = staircase24+staircaseLift24 = overridePlaceKind overrideLift $ staircase24   { pname     = "a lift"   , pfreq     = [(CLOSED_LIFT, 1000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-staircaseLift25 = staircase25+staircaseLift25 = overridePlaceKind overrideLift $ staircase25   { pname     = "a lift"   , pfreq     = [(WALLED_LIFT, 100)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift   }-pumps = PlaceKind+pumps = override2PlaceKind [ ('·', DAMP_FLOOR_DARK)+                           , ('f', PUMPS_DARK)+                           , (';', UNDERBRUSH_CLUMP_DARK) ]+                           [ ('·', DAMP_FLOOR_LIT)+                           , ('f', PUMPS_LIT)+                           , (';', UNDERBRUSH_CLUMP_LIT) ] $+        overridePlaceKind [('d', DOORLESS_MACHINERY)] $ PlaceKind   { psymbol  = 'w'   , pname    = "water pumps"   , pfreq    = [ (ROGUE, 200), (LABORATORY, 100), (EMPTY, 2000)@@ -1174,16 +1193,22 @@   , ptopLeft = [ "·f"                , "d;"                ]-  , poverrideDark = [ ('·', DAMP_FLOOR_DARK)-                    , ('d', DOORLESS_MACHINERY)-                    , ('f', PUMPS_DARK)-                    , (';', UNDERBRUSH_CLUMP_DARK) ]-  , poverrideLit = [ ('·', DAMP_FLOOR_LIT)-                   , ('d', DOORLESS_MACHINERY)-                   , ('f', PUMPS_LIT)-                   , (';', UNDERBRUSH_CLUMP_LIT) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   }-oval = PlaceKind+oval = override2PlaceKind [ ('1', STUCK_DOOR)+                          , ('2', TRAPPED_DOOR)+                          , ('~', S_POOL_DARK)+                          , (';', S_UNDERBRUSH_DARK) ]+                          [ ('1', TRAPPED_DOOR)  -- reversed vs dark+                          , ('2', STUCK_DOOR)+                          , ('~', S_POOL_LIT)+                          , (';', S_UNDERBRUSH_LIT) ] $+       overridePlaceKind [ ('t', TRAIL_LIT)+                         , ('p', TRAPPED_DOOR)+                         , ('b', BARREL)+                         , ('a', SAFE_TRAIL_LIT)+                         , ('T', S_TREE_LIT) ] $ PlaceKind   { psymbol  = 'o'   , pname    = "a dome"   , pfreq    = [ (ROGUE, 20000), (ARENA, 30000), (MUSEUM, 30000)@@ -1198,24 +1223,8 @@                , "#·t··"                , "··t··"                ]-  , poverrideDark = [ ('t', TRAIL_LIT)-                    , ('p', TRAPPED_DOOR)-                    , ('b', BARREL)-                    , ('a', SAFE_TRAIL_LIT)-                    , ('1', STUCK_DOOR)-                    , ('2', TRAPPED_DOOR)-                    , ('T', S_TREE_LIT)-                    , ('~', S_POOL_DARK)-                    , (';', S_UNDERBRUSH_DARK) ]-  , poverrideLit = [ ('t', TRAIL_LIT)-                   , ('p', TRAPPED_DOOR)-                   , ('b', BARREL)-                   , ('a', SAFE_TRAIL_LIT)-                   , ('2', STUCK_DOOR)  -- reversed vs dark-                   , ('1', TRAPPED_DOOR)-                   , ('T', S_TREE_LIT)-                   , ('~', S_POOL_LIT)-                   , (';', S_UNDERBRUSH_LIT) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } ovalFloor = oval   { pfreq    = [ (ROGUE, 150000), (ARENA, 60000), (MUSEUM, 60000)@@ -1299,10 +1308,10 @@   , pcover   = CStretch   , pfence   = FWall   , ptopLeft = ["~"]-  , poverrideDark = []-  , poverrideLit = []+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   }-floodedRoom2 = PlaceKind+floodedRoom2 = overridePlaceKind [('f', PUMPS_LIT)] $ PlaceKind   { psymbol  = 'p'   , pname    = "a pond"   , pfreq    = [(BRAWL, 100)]@@ -1312,10 +1321,19 @@   , ptopLeft = [ "XXf"                , "f~~"                , "~~X" ]-  , poverrideDark = [('f', PUMPS_LIT)]-  , poverrideLit = [('f', PUMPS_LIT)]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   }-maze = PlaceKind+maze = override2PlaceKind [ ('·', OILY_FLOOR_DARK)+                          , ('f', BUSH_GROVE_DARK)+                          , (';', S_UNDERBRUSH_DARK) ]+                          [ ('·', OILY_FLOOR_LIT)+                          , ('f', BUSH_GROVE_LIT)+                          , (';', S_UNDERBRUSH_LIT) ] $+       overridePlaceKind [ ('&', CACHE_MAZE)+                         , ('p', TRAPPED_DOOR)+                         , ('i', FLOOR_ACTOR_ITEM)  -- lit or not, randomly+                         , ('$', TRAPPABLE_WALL) ] $ PlaceKind   { psymbol  = 'm'   , pname    = "an intricate maze"   , pfreq    = [ (ROGUE, 60), (LABORATORY, 1500), (ARENA, 3)@@ -1327,20 +1345,8 @@                , "#··#"                , "··#·"                ]-  , poverrideDark = [ ('·', OILY_FLOOR_DARK)-                    , ('&', CACHE_MAZE)-                    , ('p', TRAPPED_DOOR)-                    , ('i', FLOOR_ACTOR_ITEM)  -- lit or not, randomly-                    , ('f', BUSH_GROVE_DARK)-                    , (';', S_UNDERBRUSH_DARK)-                    , ('$', TRAPPABLE_WALL) ]-  , poverrideLit = [ ('·', OILY_FLOOR_LIT)-                   , ('&', CACHE_MAZE)-                   , ('p', TRAPPED_DOOR)-                   , ('i', FLOOR_ACTOR_ITEM)  -- lit or not, randomly-                   , ('f', BUSH_GROVE_LIT)-                   , (';', S_UNDERBRUSH_LIT)-                   , ('$', TRAPPABLE_WALL) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } maze2 = maze   { pfreq    = [ (ROGUE, 120), (LABORATORY, 12000), (ARENA, 4)@@ -1385,7 +1391,16 @@                , "$·#iii"                ]   }-cells = PlaceKind+cells = override2PlaceKind [ ('b', RUBBLE_OR_WASTE_DARK)+                           , ('f', BUSH_GROVE_DARK)+                           , ('o', OIL_RESIDUE_DARK)+                           , (';', UNDERBRUSH_CLUMP_DARK) ]+                           [ ('b', RUBBLE_OR_WASTE_LIT)+                           , ('f', BUSH_GROVE_LIT)+                           , ('o', OIL_RESIDUE_LIT)+                           , (';', UNDERBRUSH_CLUMP_LIT) ] $+        overridePlaceKind [ ('d', DOORLESS_MACHINERY)+                          , ('w', S_REINFORCED_WALL) ] $ PlaceKind   { psymbol  = '#'   , pname    = "air filters"   , pfreq    = [ (ROGUE, 40), (LABORATORY, 48), (MUSEUM, 10)@@ -1398,12 +1413,8 @@                , "·d·"                , "··#"                ]-  , poverrideDark = [ ('d', DOORLESS_MACHINERY), ('b', RUBBLE_OR_WASTE_DARK)-                    , ('f', BUSH_GROVE_DARK), ('o', OIL_RESIDUE_DARK)-                    , (';', UNDERBRUSH_CLUMP_DARK), ('w', S_REINFORCED_WALL) ]-  , poverrideLit = [ ('d', DOORLESS_MACHINERY), ('b', RUBBLE_OR_WASTE_LIT)-                   , ('f', BUSH_GROVE_LIT), ('o', OIL_RESIDUE_LIT)-                   , (';', UNDERBRUSH_CLUMP_LIT), ('w', S_REINFORCED_WALL) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } cells2 = cells   { pname    = "humidity equalizers"@@ -1452,7 +1463,9 @@                , "·#o"                ]   }-tank = PlaceKind+tank = overridePlaceKind [ ('#', DOORLESS_WALL)+                         , ('r', S_REINFORCED_WALL)+                         , ('b', BARREL) ] $ PlaceKind   { psymbol  = 'c'   , pname    = "a tank"   , pfreq    = [(EMPTY, 1)]@@ -1463,12 +1476,8 @@   , pfence   = FNone   , ptopLeft = [ "#"                ]-  , poverrideDark = [ ('#', DOORLESS_WALL)-                    , ('r', S_REINFORCED_WALL)-                    , ('b', BARREL) ]-  , poverrideLit = [ ('#', DOORLESS_WALL)-                   , ('r', S_REINFORCED_WALL)-                   , ('b', BARREL) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } tank2 = tank   { pname    = "a barrel stack"@@ -1563,7 +1572,15 @@                , "XXXbb"                ]   }-shuttleHusk = PlaceKind+shuttleHusk = override2PlaceKind [ ('·', OILY_FLOOR_DARK)+                                 , ('r', RUBBLE_OR_WASTE_DARK) ]+                                 [ ('·', OILY_FLOOR_LIT)+                                 , ('r', RUBBLE_OR_WASTE_LIT) ] $+              overridePlaceKind [ ('#', S_SHUTTLE_HULL)+                                , ('c', CACHE_SHUTTLE)+                                , ('u', STUCK_DOOR)+                                , ('h', S_HARDWARE_RACK)+                                , ('w', S_REINFORCED_WALL) ] $ PlaceKind   { psymbol  = 's'   , pname    = "a shuttle husk"   , pfreq    = [(EMPTY, 1000), (EXIT, 15000), (AMBUSH, 15000)]@@ -1580,20 +1597,8 @@                , "XhhchhX"                , "hh#w#hh"                ]-  , poverrideDark = [ ('·', OILY_FLOOR_DARK)-                    , ('r', RUBBLE_OR_WASTE_DARK)-                    , ('#', S_SHUTTLE_HULL)-                    , ('c', CACHE_SHUTTLE)-                    , ('u', STUCK_DOOR)-                    , ('h', S_HARDWARE_RACK)-                    , ('w', S_REINFORCED_WALL) ]-  , poverrideLit = [ ('·', OILY_FLOOR_LIT)-                   , ('r', RUBBLE_OR_WASTE_LIT)-                   , ('#', S_SHUTTLE_HULL)-                   , ('c', CACHE_SHUTTLE)-                   , ('u', STUCK_DOOR)-                   , ('h', S_HARDWARE_RACK)-                   , ('w', S_REINFORCED_WALL) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } shuttleHusk2 = shuttleHusk   { pfreq    = [(EMPTY, 1000), (EXIT, 15000), (AMBUSH, 15000)]@@ -1646,7 +1651,9 @@                , "Xhh#w#hhX"                ]   }-dormitory = PlaceKind+dormitory = overridePlaceKind [ ('d', FLOOR_ACTOR_ITEM_LIT)+                              , ('f', PUMPS_LIT)+                              , ('$', TRAPPABLE_WALL) ] $ PlaceKind   { psymbol  = 'd'   , pname    = "dormitory"   , pfreq    = [(RESIDENTIAL, 10000)]@@ -1658,12 +1665,8 @@                , "+##"                , "ddd"                ]-  , poverrideDark = [ ('d', FLOOR_ACTOR_ITEM_LIT)-                    , ('f', PUMPS_LIT)-                    , ('$', TRAPPABLE_WALL) ]-  , poverrideLit = [ ('d', FLOOR_ACTOR_ITEM_LIT)-                   , ('f', PUMPS_LIT)-                   , ('$', TRAPPABLE_WALL) ]+  , plegendDark = defaultLegendDark+  , plegendLit = defaultLegendLit   } dormitory2 = dormitory   { pfreq    = [(RESIDENTIAL, 10000)]@@ -1722,157 +1725,125 @@ -- * Helper functions  switchExitToUp :: Text -> PlaceKind -> PlaceKind-switchExitToUp terminal s = s- { psymbol   = '<'- , pname     = pname s <+> "up"- , pfreq     = map (\(t, k) -> (GroupName $ fromGroupName t <+> "up", k))-               $ pfreq s- , poverrideDark = ('>', GroupName $ terminal <+> "Dark")-                   : filter ((/= '>') . fst) (poverrideDark s)- , poverrideLit = ('>', GroupName $ terminal <+> "Lit")-                  : filter ((/= '>') . fst) (poverrideLit s)- }+switchExitToUp terminal s = override2PlaceKind+                              [('>', GroupName $ terminal <+> "Dark")]+                              [('>', GroupName $ terminal <+> "Lit")] $ s+  { psymbol   = '<'+  , pname     = pname s <+> "up"+  , pfreq     = renameFreqs (<+> "up") $ pfreq s+  }  switchExitToDown :: Text -> PlaceKind -> PlaceKind-switchExitToDown terminal s = s- { psymbol   = '>'- , pname     = pname s <+> "down"- , pfreq     = map (\(t, k) -> (GroupName $ fromGroupName t <+> "down", k))-               $ pfreq s- , poverrideDark = ('<', GroupName $ terminal <+> "Dark")-                   : filter ((/= '<') . fst) (poverrideDark s)- , poverrideLit = ('<', GroupName $ terminal <+> "Lit")-                  : filter ((/= '<') . fst) (poverrideLit s)- }+switchExitToDown terminal s = override2PlaceKind+                                [('<', GroupName $ terminal <+> "Dark")]+                                [('<', GroupName $ terminal <+> "Lit")] $ s+  { psymbol   = '>'+  , pname     = pname s <+> "down"+  , pfreq     = renameFreqs (<+> "down") $ pfreq s+  }   overrideGatedStaircase :: [(Char, GroupName TileKind)] overrideGatedStaircase =   [ ('<', GATED_STAIRCASE_UP), ('>', GATED_STAIRCASE_DOWN)-  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  , ('S', FILLER_WALL) ]  switchStaircaseToGated :: PlaceKind -> PlaceKind-switchStaircaseToGated s = s- { psymbol   = 'g'- , pname     = T.unwords $ "a gated" : tail (T.words (pname s))- , pfreq     = map (first (\t -> GroupName $ "gated" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideGatedStaircase- , poverrideLit = overrideGatedStaircase- }+switchStaircaseToGated s = overridePlaceKind overrideGatedStaircase $ s+  { psymbol   = 'g'+  , pname     = T.unwords $ "a gated" : tail (T.words (pname s))+  , pfreq     = renameFreqs ("gated" <+>) $ pfreq s+  }  overrideGatedLift :: [(Char, GroupName TileKind)] overrideGatedLift =   [ ('<', GATED_LIFT_UP), ('>', GATED_LIFT_DOWN)-  , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]+  , ('S', S_LIFT_SHAFT) ]  switchLiftToGated :: PlaceKind -> PlaceKind-switchLiftToGated s = s- { psymbol   = 'g'- , pname     = T.unwords $ "a gated" : tail (T.words (pname s))- , pfreq     = map (first (\t -> GroupName $ "gated" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideGatedLift- , poverrideLit = overrideGatedLift- }+switchLiftToGated s = overridePlaceKind overrideGatedLift $ s+  { psymbol   = 'g'+  , pname     = T.unwords $ "a gated" : tail (T.words (pname s))+  , pfreq     = renameFreqs ("gated" <+>) $ pfreq s+  }   overrideDeconStaircase :: [(Char, GroupName TileKind)] overrideDeconStaircase =   [ ('<', DECON_STAIRCASE_UP)   , ('>', S_STAIRCASE_TRAP_DOWN_OIL)  -- talter high enough-  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  , ('S', FILLER_WALL) ]  switchStaircaseToDecon :: PlaceKind -> PlaceKind-switchStaircaseToDecon s = s- { psymbol   = 'd'- , pfreq     = map (first (\t -> GroupName $ "decon" <+> fromGroupName t))-                   (pfreq s)- , poverrideDark = overrideDeconStaircase- , poverrideLit = overrideDeconStaircase- }+switchStaircaseToDecon s = overridePlaceKind overrideDeconStaircase $ s+  { psymbol   = 'd'+  , pfreq     = renameFreqs ("decon" <+>) $ pfreq s+  }  overrideDeconLift :: [(Char, GroupName TileKind)] overrideDeconLift =   [ ('<', DECON_LIFT_UP)   , ('>', STAIRCASE_LIFT_DOWN)-  , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]+  , ('S', S_LIFT_SHAFT) ]  switchLiftToDecon :: PlaceKind -> PlaceKind-switchLiftToDecon s = s- { psymbol   = 'd'- , pfreq     = map (first (\t -> GroupName $ "decon" <+> fromGroupName t))-                   (pfreq s)- , poverrideDark = overrideDeconLift- , poverrideLit = overrideDeconLift- }+switchLiftToDecon s = overridePlaceKind overrideDeconLift $ s+  { psymbol   = 'd'+  , pfreq     = renameFreqs ("decon" <+>) $ pfreq s+  }   overrideWeldedStaircase :: [(Char, GroupName TileKind)] overrideWeldedStaircase =   [ ('<', WELDED_STAIRCASE_UP), ('>', ORDINARY_STAIRCASE_DOWN)-  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  , ('S', FILLER_WALL) ]  switchStaircaseToWelded :: PlaceKind -> PlaceKind-switchStaircaseToWelded s = s- { psymbol   = 'w'- , pfreq     = map (first (\t -> GroupName $ "welded" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideWeldedStaircase- , poverrideLit = overrideWeldedStaircase- }+switchStaircaseToWelded s = overridePlaceKind overrideWeldedStaircase $ s+  { psymbol   = 'w'+  , pfreq     = renameFreqs ("welded" <+>) $ pfreq s+  }  overrideWeldedLift :: [(Char, GroupName TileKind)] overrideWeldedLift =   [ ('<', WELDED_LIFT_UP), ('>', ORDINARY_LIFT_DOWN)-  , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]+  , ('S', S_LIFT_SHAFT) ]  switchLiftToWelded :: PlaceKind -> PlaceKind-switchLiftToWelded s = s- { psymbol   = 'w'- , pfreq     = map (first (\t -> GroupName $ "welded" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideWeldedLift- , poverrideLit = overrideWeldedLift- }+switchLiftToWelded s = overridePlaceKind overrideWeldedLift $ s+  { psymbol   = 'w'+  , pfreq     = renameFreqs ("welded" <+>) $ pfreq s+  }   overrideOutdoor :: [(Char, GroupName TileKind)] overrideOutdoor =   [ ('<', STAIRCASE_OUTDOOR_UP), ('>', STAIRCASE_OUTDOOR_DOWN)-  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  , ('S', FILLER_WALL) ]  switchStaircaseToOutdoor :: PlaceKind -> PlaceKind-switchStaircaseToOutdoor s = s- { psymbol   = 'o'- , pname     = "an outdoor area exit"- , pfreq     = map (first (\t -> GroupName $ "outdoor" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideOutdoor- , poverrideLit = overrideOutdoor- }+switchStaircaseToOutdoor s = overridePlaceKind overrideOutdoor $ s+  { psymbol   = 'o'+  , pname     = "an outdoor area exit"+  , pfreq     = renameFreqs ("outdoor" <+>) $ pfreq s+  }  switchEscapeToUp :: PlaceKind -> PlaceKind-switchEscapeToUp s = s- { psymbol   = '<'- , pname     = "an escape up"- , pfreq     = map (\(_, n) -> (INDOOR_ESCAPE_UP, n)) $ pfreq s- , poverrideDark = ('>', ESCAPE_UP) : poverrideDark s- , poverrideLit = ('>', ESCAPE_UP) : poverrideLit s- }+switchEscapeToUp s = overridePlaceKind [('>', ESCAPE_UP)] $ s+  { psymbol   = '<'+  , pname     = "an escape up"+  , pfreq     = map (\(_, n) -> (INDOOR_ESCAPE_UP, n)) $ pfreq s+  }  switchEscapeToOutdoorDown :: PlaceKind -> PlaceKind-switchEscapeToOutdoorDown s = s- { pname     = "outdoor escape route"- , pfreq     = map (\(_, n) -> (OUTDOOR_ESCAPE_DOWN, n)) $ pfreq s- , poverrideDark = ('>', ESCAPE_OUTDOOR_DOWN) : poverrideDark s- , poverrideLit = ('>', ESCAPE_OUTDOOR_DOWN) : poverrideLit s- }+switchEscapeToOutdoorDown s = overridePlaceKind [('>', ESCAPE_OUTDOOR_DOWN)] $ s+  { pname     = "outdoor escape route"+  , pfreq     = map (\(_, n) -> (OUTDOOR_ESCAPE_DOWN, n)) $ pfreq s+  }  switchEscapeToSpaceshipDown :: PlaceKind -> PlaceKind-switchEscapeToSpaceshipDown s = s- { pname     = "escape from spaceship"- , pfreq     = map (\(_, n) -> (ESCAPE_FROM_SPACESHIP_DOWN, n)) $ pfreq s- , poverrideDark = ('>', ESCAPE_SPACESHIP_DOWN) : poverrideDark s- , poverrideLit = ('>', ESCAPE_SPACESHIP_DOWN) : poverrideLit s- }+switchEscapeToSpaceshipDown s = overridePlaceKind+                                  [('>', ESCAPE_SPACESHIP_DOWN)] $ s+  { pname     = "escape from spaceship"+  , pfreq     = map (\(_, n) -> (ESCAPE_FROM_SPACESHIP_DOWN, n)) $ pfreq s+  }
GameDefinition/Content/RuleKind.hs view
@@ -24,13 +24,15 @@ -- Cabal import qualified Paths_Allure as Self (version) +import Game.LambdaHack.Content.ItemKind (ItemSymbolsUsedInEngine (..)) import Game.LambdaHack.Content.RuleKind+import Game.LambdaHack.Definition.DefsInternal  standardRules :: RuleContent standardRules = RuleContent   { rtitle = "Allure of the Stars"-  , rXmax = 80-  , rYmax = 42+  , rWidthMax = 80+  , rHeightMax = 42   , rexeVersion = Self.version   -- The strings containing the default configuration file   -- included from config.ui.default.@@ -51,5 +53,31 @@   , rscoresFile = "Allure.scores"   , rnearby = 30   , rstairWordCarried = ["staircase", "lift"]-  , rsymbolProjectile = '{'+  , ritemSymbols = ItemSymbolsUsedInEngine+      { rsymbolProjectile = toContentSymbol '{'+      , rsymbolLight      = toContentSymbol '('+      , rsymbolTool       = toContentSymbol ')'+      , rsymbolSpecial    = toContentSymbol '*'+                              -- don't overuse; it clashes with projectiles+      , rsymbolGold       = toContentSymbol '$'+                              -- also gems+      , rsymbolNecklace   = toContentSymbol '"'+      , rsymbolRing       = toContentSymbol '='+      , rsymbolPotion     = toContentSymbol '!'+                              -- also concoction, bottle, jar, vial, canister+      , rsymbolFlask      = toContentSymbol '!'+      , rsymbolScroll     = toContentSymbol '?'+                              -- book, note, tablet, remote, chip, card+      , rsymbolTorsoArmor = toContentSymbol '['+      , rsymbolMiscArmor  = toContentSymbol '['+      , rsymbolClothes    = toContentSymbol '['+      , rsymbolShield     = toContentSymbol ']'+      , rsymbolPolearm    = toContentSymbol '/'+      , rsymbolEdged      = toContentSymbol '|'+      , rsymbolHafted     = toContentSymbol '\\'+      , rsymbolWand       = toContentSymbol '-'+                              -- transmitter, pistol, rifle, instrument+      , rsymbolFood       = toContentSymbol ','+          -- also body part; distinct enough from floor, which is middle dot+      }   }
GameDefinition/Content/TileKind.hs view
@@ -14,10 +14,10 @@   , pattern EMPTY_SET_LIT, pattern EMPTY_SET_DARK, pattern NOISE_SET_LIT, pattern POWER_SET_LIT, pattern POWER_SET_DARK, pattern BATTLE_SET_LIT, pattern BATTLE_SET_DARK, pattern BRAWL_SET_LIT, pattern SHOOTOUT_SET_LIT, pattern ZOO_SET_LIT, pattern ZOO_SET_DARK, pattern ESCAPE_SET_LIT, pattern ESCAPE_SET_DARK, pattern AMBUSH_SET_LIT, pattern AMBUSH_SET_DARK, pattern ARENA_SET_LIT, pattern ARENA_SET_DARK   , pattern ROGUE_SET, pattern MUSEUM_SET_LIT, pattern MUSEUM_SET_DARK, pattern HUNT_SET_LIT, pattern EXIT_SET_LIT     -- ** Used in PlaceKind, but not in CaveKind.-  , pattern TREE_SHADE_WALKABLE_LIT, pattern TREE_SHADE_WALKABLE_DARK, pattern SMOKE_CLUMP_LIT, pattern SMOKE_CLUMP_DARK, pattern BUSH_CLUMP_LIT, pattern BUSH_CLUMP_DARK, pattern FOG_CLUMP_LIT, pattern FOG_CLUMP_DARK, pattern STAIR_TERMINAL_LIT, pattern STAIR_TERMINAL_DARK, pattern SIGNBOARD, pattern STAIRCASE_UP, pattern ORDINARY_STAIRCASE_UP, pattern STAIRCASE_OUTDOOR_UP, pattern GATED_STAIRCASE_UP, pattern STAIRCASE_DOWN, pattern ORDINARY_STAIRCASE_DOWN, pattern STAIRCASE_OUTDOOR_DOWN, pattern GATED_STAIRCASE_DOWN, pattern ESCAPE_UP, pattern ESCAPE_DOWN, pattern ESCAPE_OUTDOOR_DOWN-  , pattern S_LAMP_POST, pattern S_TREE_LIT, pattern S_TREE_DARK, pattern S_PULPIT, pattern S_BUSH_LIT, pattern S_FOG_LIT, pattern S_SMOKE_LIT, pattern S_FLOOR_ACTOR_LIT, pattern S_FLOOR_ACTOR_DARK, pattern S_FLOOR_ASHES_LIT, pattern S_FLOOR_ASHES_DARK, pattern S_SHADED_GROUND-  , pattern BUSH_GROVE_LIT, pattern BUSH_GROVE_DARK, pattern UNDERBRUSH_CLUMP_LIT, pattern UNDERBRUSH_CLUMP_DARK, pattern ASHES_SMOKE_LIT, pattern ASHES_SMOKE_DARK, pattern RECT_WINDOWS, pattern DOORLESS_MACHINERY, pattern PUMPS_LIT, pattern PUMPS_DARK, pattern DOORLESS_WALL, pattern OIL_RESIDUE_LIT, pattern OIL_RESIDUE_DARK, pattern LIFT_TERMINAL_LIT, pattern LIFT_TERMINAL_DARK, pattern STAIRCASE_LIFT_UP, pattern STAIRCASE_LIFT_DOWN, pattern GATED_LIFT_UP, pattern GATED_LIFT_DOWN, pattern DECON_STAIRCASE_UP, pattern DECON_STAIRCASE_DOWN, pattern DECON_LIFT_UP, pattern DECON_LIFT_DOWN, pattern WELDED_STAIRCASE_UP, pattern WELDED_LIFT_UP, pattern ESCAPE_SPACESHIP_DOWN, pattern ORDINARY_LIFT_UP, pattern ORDINARY_LIFT_DOWN, pattern RUBBLE_OR_WASTE_LIT, pattern RUBBLE_OR_WASTE_DARK, pattern CACHE_DEPOSIT, pattern CACHE_JEWELRY, pattern CACHE_MAZE, pattern CACHE_SHUTTLE, pattern TRAPPED_DOOR, pattern FLOOR_ACTOR_ITEM, pattern FLOOR_ACTOR_ITEM_LIT, pattern STUCK_DOOR, pattern BARREL-  , pattern S_POOL_LIT, pattern S_POOL_DARK, pattern S_OIL_SPILL, pattern S_FROZEN_PATH, pattern S_LIFT_SHAFT, pattern S_REINFORCED_WALL, pattern S_SHUTTLE_HULL, pattern S_HARDWARE_RACK, pattern S_STAIRCASE_TRAP_DOWN_OIL, pattern S_UNDERBRUSH_LIT, pattern S_UNDERBRUSH_DARK+  , pattern TREE_SHADE_WALKABLE_LIT, pattern TREE_SHADE_WALKABLE_DARK, pattern SMOKE_CLUMP_LIT, pattern SMOKE_CLUMP_DARK, pattern BUSH_CLUMP_LIT, pattern BUSH_CLUMP_DARK, pattern FOG_CLUMP_LIT, pattern FOG_CLUMP_DARK, pattern STAIR_TERMINAL_LIT, pattern STAIR_TERMINAL_DARK, pattern SIGNBOARD, pattern STAIRCASE_UP, pattern ORDINARY_STAIRCASE_UP, pattern STAIRCASE_OUTDOOR_UP, pattern GATED_STAIRCASE_UP, pattern STAIRCASE_DOWN, pattern ORDINARY_STAIRCASE_DOWN, pattern STAIRCASE_OUTDOOR_DOWN, pattern GATED_STAIRCASE_DOWN, pattern ESCAPE_UP, pattern ESCAPE_DOWN, pattern ESCAPE_OUTDOOR_DOWN, pattern TRANSPARENT_WALL, pattern ICE_BUILDUP, pattern WORKSHOP, pattern FLOOR_ACTOR_ITEM, pattern FLOOR_ACTOR_ITEM_LIT, pattern FLOOR_ACTOR_ITEM_DARK+  , pattern S_PILLAR, pattern S_RUBBLE_PILE, pattern S_LAMP_POST, pattern S_TREE_LIT, pattern S_TREE_DARK, pattern S_PULPIT, pattern S_BUSH_LIT, pattern S_FOG_LIT, pattern S_SMOKE_LIT, pattern S_FLOOR_ACTOR_LIT, pattern S_FLOOR_ACTOR_DARK, pattern S_FLOOR_ASHES_LIT, pattern S_FLOOR_ASHES_DARK, pattern S_SHADED_GROUND, pattern S_SHALLOW_WATER_LIT, pattern S_SHALLOW_WATER_DARK+  , pattern BUSH_GROVE_LIT, pattern BUSH_GROVE_DARK, pattern UNDERBRUSH_CLUMP_LIT, pattern UNDERBRUSH_CLUMP_DARK, pattern ASHES_SMOKE_LIT, pattern ASHES_SMOKE_DARK, pattern RECT_WINDOWS, pattern DOORLESS_MACHINERY, pattern PUMPS_LIT, pattern PUMPS_DARK, pattern DOORLESS_WALL, pattern OIL_RESIDUE_LIT, pattern OIL_RESIDUE_DARK, pattern LIFT_TERMINAL_LIT, pattern LIFT_TERMINAL_DARK, pattern STAIRCASE_LIFT_UP, pattern STAIRCASE_LIFT_DOWN, pattern GATED_LIFT_UP, pattern GATED_LIFT_DOWN, pattern DECON_STAIRCASE_UP, pattern DECON_STAIRCASE_DOWN, pattern DECON_LIFT_UP, pattern DECON_LIFT_DOWN, pattern WELDED_STAIRCASE_UP, pattern WELDED_LIFT_UP, pattern ESCAPE_SPACESHIP_DOWN, pattern ORDINARY_LIFT_UP, pattern ORDINARY_LIFT_DOWN, pattern RUBBLE_OR_WASTE_LIT, pattern RUBBLE_OR_WASTE_DARK, pattern CACHE_DEPOSIT, pattern CACHE_JEWELRY, pattern CACHE_MAZE, pattern CACHE_SHUTTLE, pattern TRAPPED_DOOR, pattern STUCK_DOOR, pattern BARREL+  , pattern S_POOL_LIT, pattern S_POOL_DARK, pattern S_CLOSED_DOOR, pattern S_OPEN_DOOR, pattern S_OIL_SPILL, pattern S_FROZEN_PATH, pattern S_LIFT_SHAFT, pattern S_REINFORCED_WALL, pattern S_SHUTTLE_HULL, pattern S_HARDWARE_RACK, pattern S_STAIRCASE_TRAP_DOWN_OIL, pattern S_UNDERBRUSH_LIT, pattern S_UNDERBRUSH_DARK   , groupNamesSingleton, groupNames     -- * Content   , content@@ -36,6 +36,7 @@ import Game.LambdaHack.Content.TileKind import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.DefsInternal  -- * Group name patterns @@ -43,26 +44,26 @@  groupNamesSingleton :: [GroupName TileKind] groupNamesSingleton =-       [S_LAMP_POST, S_TREE_LIT, S_TREE_DARK, S_PULPIT, S_BUSH_LIT, S_FOG_LIT, S_SMOKE_LIT, S_FLOOR_ACTOR_LIT, S_FLOOR_ACTOR_DARK, S_FLOOR_ASHES_LIT, S_FLOOR_ASHES_DARK, S_SHADED_GROUND]-    ++ [S_POOL_LIT, S_POOL_DARK, S_OIL_SPILL, S_FROZEN_PATH, S_LIFT_SHAFT, S_REINFORCED_WALL, S_SHUTTLE_HULL, S_HARDWARE_RACK]-    ++ [S_RUBBLE_PILE, S_SHALLOW_WATER_LIT, S_SIGNBOARD_UNREAD]-    ++ [S_SUSPECT_WALL, S_CLOSED_DOOR, S_OPEN_DOOR, S_STAIRCASE_TRAP_DOWN_OIL, S_BURNING_INSTALLATION, S_BURNING_TREE, S_BURNING_BUSH, S_BURNING_UNDERBRUSH, S_BURNING_OIL, S_UNDERBRUSH_LIT, S_UNDERBRUSH_DARK]-    ++ [S_BUSH_DARK, S_SHALLOW_WATER_DARK]+       [S_PILLAR, S_RUBBLE_PILE, S_LAMP_POST, S_TREE_LIT, S_TREE_DARK, S_PULPIT, S_BUSH_LIT, S_FOG_LIT, S_SMOKE_LIT, S_FLOOR_ACTOR_LIT, S_FLOOR_ACTOR_DARK, S_FLOOR_ASHES_LIT, S_FLOOR_ASHES_DARK, S_SHADED_GROUND, S_SHALLOW_WATER_LIT, S_SHALLOW_WATER_DARK]+    ++ [S_POOL_LIT, S_POOL_DARK, S_CLOSED_DOOR, S_OPEN_DOOR, S_OIL_SPILL, S_FROZEN_PATH, S_LIFT_SHAFT, S_REINFORCED_WALL, S_SHUTTLE_HULL, S_HARDWARE_RACK]+    ++ [ S_SIGNBOARD_UNREAD]+    ++ [S_SUSPECT_WALL, S_STAIRCASE_TRAP_DOWN_OIL, S_BURNING_INSTALLATION, S_BURNING_TREE, S_BURNING_BUSH, S_BURNING_UNDERBRUSH, S_BURNING_OIL, S_UNDERBRUSH_LIT, S_UNDERBRUSH_DARK]+    ++ [S_BUSH_DARK]  -- ** Used in PlaceKind, but not in CaveKind.-pattern S_LAMP_POST, S_TREE_LIT, S_TREE_DARK, S_PULPIT, S_BUSH_LIT, S_FOG_LIT, S_SMOKE_LIT, S_FLOOR_ACTOR_LIT, S_FLOOR_ACTOR_DARK, S_FLOOR_ASHES_LIT, S_FLOOR_ASHES_DARK, S_SHADED_GROUND :: GroupName TileKind+pattern S_PILLAR, S_RUBBLE_PILE, S_LAMP_POST, S_TREE_LIT, S_TREE_DARK, S_PULPIT, S_BUSH_LIT, S_FOG_LIT, S_SMOKE_LIT, S_FLOOR_ACTOR_LIT, S_FLOOR_ACTOR_DARK, S_FLOOR_ASHES_LIT, S_FLOOR_ASHES_DARK, S_SHADED_GROUND, S_SHALLOW_WATER_LIT, S_SHALLOW_WATER_DARK :: GroupName TileKind  -- ** Allure-specific-pattern S_POOL_LIT, S_POOL_DARK, S_OIL_SPILL, S_FROZEN_PATH, S_LIFT_SHAFT, S_REINFORCED_WALL, S_SHUTTLE_HULL, S_HARDWARE_RACK :: GroupName TileKind+pattern S_POOL_LIT, S_POOL_DARK, S_CLOSED_DOOR, S_OPEN_DOOR, S_OIL_SPILL, S_FROZEN_PATH, S_LIFT_SHAFT, S_REINFORCED_WALL, S_SHUTTLE_HULL, S_HARDWARE_RACK :: GroupName TileKind  -- ** Used only internally in other TileKind definitions or never used.-pattern S_RUBBLE_PILE, S_SHALLOW_WATER_LIT, S_SIGNBOARD_UNREAD :: GroupName TileKind+pattern S_SIGNBOARD_UNREAD :: GroupName TileKind  -- ** Allure-specific-pattern S_SUSPECT_WALL, S_CLOSED_DOOR, S_OPEN_DOOR, S_STAIRCASE_TRAP_DOWN_OIL, S_BURNING_INSTALLATION, S_BURNING_TREE, S_BURNING_BUSH, S_BURNING_UNDERBRUSH, S_BURNING_OIL, S_UNDERBRUSH_LIT, S_UNDERBRUSH_DARK :: GroupName TileKind+pattern S_SUSPECT_WALL, S_STAIRCASE_TRAP_DOWN_OIL, S_BURNING_INSTALLATION, S_BURNING_TREE, S_BURNING_BUSH, S_BURNING_UNDERBRUSH, S_BURNING_OIL, S_UNDERBRUSH_LIT, S_UNDERBRUSH_DARK :: GroupName TileKind  -- * Not used, but needed, because auto-generated. Singletons.-pattern S_BUSH_DARK, S_SHALLOW_WATER_DARK :: GroupName TileKind+pattern S_BUSH_DARK :: GroupName TileKind  -- TODO: if we stick to the current system of generating extra kinds and their -- group names, let's also add the generated group names to @groupNames@.@@ -73,11 +74,11 @@     ++ [HABITAT_CONTAINMENT_WALL, TRANSPORT_ROUTE, ORIELS_FENCE, AIRLOCK_FENCE, EMPTY_AIRLOCK_FENCE, OPENABLE_WALL, TRAPPABLE_WALL, OILY_FLOOR_LIT, OILY_FLOOR_DARK]     ++ [EMPTY_SET_LIT, EMPTY_SET_DARK, NOISE_SET_LIT, POWER_SET_LIT, POWER_SET_DARK, BATTLE_SET_LIT, BATTLE_SET_DARK, BRAWL_SET_LIT, SHOOTOUT_SET_LIT, ZOO_SET_LIT, ZOO_SET_DARK, ESCAPE_SET_LIT, ESCAPE_SET_DARK, AMBUSH_SET_LIT, AMBUSH_SET_DARK, ARENA_SET_LIT, ARENA_SET_DARK]     ++ [ROGUE_SET, MUSEUM_SET_LIT, MUSEUM_SET_DARK, HUNT_SET_LIT, EXIT_SET_LIT]-    ++ [TREE_SHADE_WALKABLE_LIT, TREE_SHADE_WALKABLE_DARK, SMOKE_CLUMP_LIT, SMOKE_CLUMP_DARK, BUSH_CLUMP_LIT, BUSH_CLUMP_DARK, FOG_CLUMP_LIT, FOG_CLUMP_DARK, STAIR_TERMINAL_LIT, STAIR_TERMINAL_DARK, SIGNBOARD, STAIRCASE_UP, ORDINARY_STAIRCASE_UP, STAIRCASE_OUTDOOR_UP, GATED_STAIRCASE_UP, STAIRCASE_DOWN, ORDINARY_STAIRCASE_DOWN, STAIRCASE_OUTDOOR_DOWN, GATED_STAIRCASE_DOWN, ESCAPE_UP, ESCAPE_DOWN, ESCAPE_OUTDOOR_DOWN]-    ++ [BUSH_GROVE_LIT, BUSH_GROVE_DARK, UNDERBRUSH_CLUMP_LIT, UNDERBRUSH_CLUMP_DARK, ASHES_SMOKE_LIT, ASHES_SMOKE_DARK, RECT_WINDOWS, DOORLESS_MACHINERY, PUMPS_LIT, PUMPS_DARK, DOORLESS_WALL, OIL_RESIDUE_LIT, OIL_RESIDUE_DARK, LIFT_TERMINAL_LIT, LIFT_TERMINAL_DARK, STAIRCASE_LIFT_UP, STAIRCASE_LIFT_DOWN, GATED_LIFT_UP, GATED_LIFT_DOWN, DECON_STAIRCASE_UP, DECON_STAIRCASE_DOWN, DECON_LIFT_UP, DECON_LIFT_DOWN, WELDED_STAIRCASE_UP, WELDED_LIFT_UP, ESCAPE_SPACESHIP_DOWN, ORDINARY_LIFT_UP, ORDINARY_LIFT_DOWN, RUBBLE_OR_WASTE_LIT, RUBBLE_OR_WASTE_DARK, CACHE_DEPOSIT, CACHE_JEWELRY, CACHE_MAZE, CACHE_SHUTTLE, TRAPPED_DOOR, FLOOR_ACTOR_ITEM, FLOOR_ACTOR_ITEM_LIT, STUCK_DOOR, BARREL]+    ++ [TREE_SHADE_WALKABLE_LIT, TREE_SHADE_WALKABLE_DARK, SMOKE_CLUMP_LIT, SMOKE_CLUMP_DARK, BUSH_CLUMP_LIT, BUSH_CLUMP_DARK, FOG_CLUMP_LIT, FOG_CLUMP_DARK, STAIR_TERMINAL_LIT, STAIR_TERMINAL_DARK, SIGNBOARD, STAIRCASE_UP, ORDINARY_STAIRCASE_UP, STAIRCASE_OUTDOOR_UP, GATED_STAIRCASE_UP, STAIRCASE_DOWN, ORDINARY_STAIRCASE_DOWN, STAIRCASE_OUTDOOR_DOWN, GATED_STAIRCASE_DOWN, ESCAPE_UP, ESCAPE_DOWN, ESCAPE_OUTDOOR_DOWN, TRANSPARENT_WALL, ICE_BUILDUP, WORKSHOP, FLOOR_ACTOR_ITEM, FLOOR_ACTOR_ITEM_LIT, FLOOR_ACTOR_ITEM_DARK]+    ++ [BUSH_GROVE_LIT, BUSH_GROVE_DARK, UNDERBRUSH_CLUMP_LIT, UNDERBRUSH_CLUMP_DARK, ASHES_SMOKE_LIT, ASHES_SMOKE_DARK, RECT_WINDOWS, DOORLESS_MACHINERY, PUMPS_LIT, PUMPS_DARK, DOORLESS_WALL, OIL_RESIDUE_LIT, OIL_RESIDUE_DARK, LIFT_TERMINAL_LIT, LIFT_TERMINAL_DARK, STAIRCASE_LIFT_UP, STAIRCASE_LIFT_DOWN, GATED_LIFT_UP, GATED_LIFT_DOWN, DECON_STAIRCASE_UP, DECON_STAIRCASE_DOWN, DECON_LIFT_UP, DECON_LIFT_DOWN, WELDED_STAIRCASE_UP, WELDED_LIFT_UP, ESCAPE_SPACESHIP_DOWN, ORDINARY_LIFT_UP, ORDINARY_LIFT_DOWN, RUBBLE_OR_WASTE_LIT, RUBBLE_OR_WASTE_DARK, CACHE_DEPOSIT, CACHE_JEWELRY, CACHE_MAZE, CACHE_SHUTTLE, TRAPPED_DOOR, STUCK_DOOR, BARREL]     ++ [TREE_BURNING_OR_NOT, BUSH_BURNING_OR_NOT]     ++ [OBSCURED_WALL, CACHE_DEPOSIT_OR_NOT, CACHE_DEPOSIT_BREACHED, CACHE_JEWELRY_OR_NOT, CACHE_JEWELRY_TRAPPED_OR_NOT, CACHE_ABANDONED_OR_NOT, RUBBLE_BURNING_OR_NOT]-    ++ [BRAWL_SET_DARK, NOISE_SET_DARK, SHOOTOUT_SET_DARK, EXIT_SET_DARK, HUNT_SET_DARK, FLOOR_ACTOR_ITEM_DARK]+    ++ [BRAWL_SET_DARK, NOISE_SET_DARK, SHOOTOUT_SET_DARK, EXIT_SET_DARK, HUNT_SET_DARK]  pattern FILLER_WALL, FLOOR_CORRIDOR_LIT, FLOOR_CORRIDOR_DARK, TRAIL_LIT, SAFE_TRAIL_LIT, LAB_TRAIL_LIT, DAMP_FLOOR_LIT, DAMP_FLOOR_DARK, DIRT_LIT, DIRT_DARK, FLOOR_ARENA_LIT, FLOOR_ARENA_DARK :: GroupName TileKind @@ -90,10 +91,10 @@ pattern ROGUE_SET, MUSEUM_SET_LIT, MUSEUM_SET_DARK, HUNT_SET_LIT, EXIT_SET_LIT :: GroupName TileKind  -- ** Used in PlaceKind, but not in CaveKind.-pattern TREE_SHADE_WALKABLE_LIT, TREE_SHADE_WALKABLE_DARK, SMOKE_CLUMP_LIT, SMOKE_CLUMP_DARK, BUSH_CLUMP_LIT, BUSH_CLUMP_DARK, FOG_CLUMP_LIT, FOG_CLUMP_DARK, STAIR_TERMINAL_LIT, STAIR_TERMINAL_DARK, SIGNBOARD, STAIRCASE_UP, ORDINARY_STAIRCASE_UP, STAIRCASE_OUTDOOR_UP, GATED_STAIRCASE_UP, STAIRCASE_DOWN, ORDINARY_STAIRCASE_DOWN, STAIRCASE_OUTDOOR_DOWN, GATED_STAIRCASE_DOWN, ESCAPE_UP, ESCAPE_DOWN, ESCAPE_OUTDOOR_DOWN :: GroupName TileKind+pattern TREE_SHADE_WALKABLE_LIT, TREE_SHADE_WALKABLE_DARK, SMOKE_CLUMP_LIT, SMOKE_CLUMP_DARK, BUSH_CLUMP_LIT, BUSH_CLUMP_DARK, FOG_CLUMP_LIT, FOG_CLUMP_DARK, STAIR_TERMINAL_LIT, STAIR_TERMINAL_DARK, SIGNBOARD, STAIRCASE_UP, ORDINARY_STAIRCASE_UP, STAIRCASE_OUTDOOR_UP, GATED_STAIRCASE_UP, STAIRCASE_DOWN, ORDINARY_STAIRCASE_DOWN, STAIRCASE_OUTDOOR_DOWN, GATED_STAIRCASE_DOWN, ESCAPE_UP, ESCAPE_DOWN, ESCAPE_OUTDOOR_DOWN, TRANSPARENT_WALL, ICE_BUILDUP, WORKSHOP, FLOOR_ACTOR_ITEM, FLOOR_ACTOR_ITEM_LIT, FLOOR_ACTOR_ITEM_DARK :: GroupName TileKind  -- ** Allure-specific-pattern BUSH_GROVE_LIT, BUSH_GROVE_DARK, UNDERBRUSH_CLUMP_LIT, UNDERBRUSH_CLUMP_DARK, ASHES_SMOKE_LIT, ASHES_SMOKE_DARK, RECT_WINDOWS, DOORLESS_MACHINERY, PUMPS_LIT, PUMPS_DARK, DOORLESS_WALL, OIL_RESIDUE_LIT, OIL_RESIDUE_DARK, LIFT_TERMINAL_LIT, LIFT_TERMINAL_DARK, STAIRCASE_LIFT_UP, STAIRCASE_LIFT_DOWN, GATED_LIFT_UP, GATED_LIFT_DOWN, DECON_STAIRCASE_UP, DECON_STAIRCASE_DOWN, DECON_LIFT_UP, DECON_LIFT_DOWN, WELDED_STAIRCASE_UP, WELDED_LIFT_UP, ESCAPE_SPACESHIP_DOWN, ORDINARY_LIFT_UP, ORDINARY_LIFT_DOWN, RUBBLE_OR_WASTE_LIT, RUBBLE_OR_WASTE_DARK, CACHE_DEPOSIT, CACHE_JEWELRY, CACHE_MAZE, CACHE_SHUTTLE, TRAPPED_DOOR, FLOOR_ACTOR_ITEM, FLOOR_ACTOR_ITEM_LIT, STUCK_DOOR, BARREL :: GroupName TileKind+pattern BUSH_GROVE_LIT, BUSH_GROVE_DARK, UNDERBRUSH_CLUMP_LIT, UNDERBRUSH_CLUMP_DARK, ASHES_SMOKE_LIT, ASHES_SMOKE_DARK, RECT_WINDOWS, DOORLESS_MACHINERY, PUMPS_LIT, PUMPS_DARK, DOORLESS_WALL, OIL_RESIDUE_LIT, OIL_RESIDUE_DARK, LIFT_TERMINAL_LIT, LIFT_TERMINAL_DARK, STAIRCASE_LIFT_UP, STAIRCASE_LIFT_DOWN, GATED_LIFT_UP, GATED_LIFT_DOWN, DECON_STAIRCASE_UP, DECON_STAIRCASE_DOWN, DECON_LIFT_UP, DECON_LIFT_DOWN, WELDED_STAIRCASE_UP, WELDED_LIFT_UP, ESCAPE_SPACESHIP_DOWN, ORDINARY_LIFT_UP, ORDINARY_LIFT_DOWN, RUBBLE_OR_WASTE_LIT, RUBBLE_OR_WASTE_DARK, CACHE_DEPOSIT, CACHE_JEWELRY, CACHE_MAZE, CACHE_SHUTTLE, TRAPPED_DOOR, STUCK_DOOR, BARREL :: GroupName TileKind  -- ** Used only internally in other TileKind definitions or never used. pattern TREE_BURNING_OR_NOT, BUSH_BURNING_OR_NOT :: GroupName TileKind@@ -102,7 +103,7 @@ pattern OBSCURED_WALL, CACHE_DEPOSIT_OR_NOT, CACHE_DEPOSIT_BREACHED, CACHE_JEWELRY_OR_NOT, CACHE_JEWELRY_TRAPPED_OR_NOT, CACHE_ABANDONED_OR_NOT, RUBBLE_BURNING_OR_NOT :: GroupName TileKind  -- * Not used, but needed, because auto-generated. Not singletons.-pattern BRAWL_SET_DARK, NOISE_SET_DARK, SHOOTOUT_SET_DARK, EXIT_SET_DARK, HUNT_SET_DARK, FLOOR_ACTOR_ITEM_DARK :: GroupName TileKind+pattern BRAWL_SET_DARK, NOISE_SET_DARK, SHOOTOUT_SET_DARK, EXIT_SET_DARK, HUNT_SET_DARK :: GroupName TileKind  -- ** Used in CaveKind and perhaps elsewhere (or a dark/lit version thereof). pattern FILLER_WALL = GroupName "fillerWall"@@ -181,8 +182,16 @@ pattern ESCAPE_UP = GroupName "escape up" pattern ESCAPE_DOWN = GroupName "escape down" pattern ESCAPE_OUTDOOR_DOWN = GroupName "escape outdoor down"+pattern TRANSPARENT_WALL  = GroupName "transparent wall"+pattern ICE_BUILDUP = GroupName "ice buildup"+pattern WORKSHOP = GroupName "workshop"+pattern FLOOR_ACTOR_ITEM = GroupName "floorActorItem"+pattern FLOOR_ACTOR_ITEM_LIT = GroupName "floorActorItemLit"+pattern FLOOR_ACTOR_ITEM_DARK = GroupName "floorActorItemDark"  -- ** Used in PlaceKind, but not in CaveKind. Singletons.+pattern S_PILLAR = GroupName "pillar"+pattern S_RUBBLE_PILE = GroupName "rubble pile" pattern S_LAMP_POST = GroupName "lamp post" pattern S_TREE_LIT = GroupName "tree Lit" pattern S_TREE_DARK = GroupName "tree Dark"@@ -195,6 +204,8 @@ pattern S_FLOOR_ASHES_LIT = GroupName "floor with ashes Lit" pattern S_FLOOR_ASHES_DARK = GroupName "floor with ashes Dark" pattern S_SHADED_GROUND = GroupName "shaded ground"+pattern S_SHALLOW_WATER_LIT = GroupName "shallow water Lit"+pattern S_SHALLOW_WATER_DARK = GroupName "shallow water Dark"  -- ** Allure-specific pattern BUSH_GROVE_LIT = GroupName "bushGroveLit"@@ -232,13 +243,13 @@ pattern CACHE_MAZE = GroupName "cache maze" pattern CACHE_SHUTTLE = GroupName "cache shuttle" pattern TRAPPED_DOOR = GroupName "trapped door"-pattern FLOOR_ACTOR_ITEM = GroupName "floorActorItem"-pattern FLOOR_ACTOR_ITEM_LIT = GroupName "floorActorItemLit" pattern STUCK_DOOR = GroupName "stuck door" pattern BARREL = GroupName "barrel"  pattern S_POOL_LIT = GroupName "poolLit" pattern S_POOL_DARK = GroupName "poolDark"+pattern S_CLOSED_DOOR = GroupName "closed door"+pattern S_OPEN_DOOR = GroupName "open door" pattern S_OIL_SPILL = GroupName "oil spill" pattern S_FROZEN_PATH = GroupName "frozen path" pattern S_LIFT_SHAFT = GroupName "lift shaft"@@ -252,8 +263,6 @@  -- ** Used only internally in other TileKind definitions. Singletons. -pattern S_RUBBLE_PILE = GroupName "rubble pile"-pattern S_SHALLOW_WATER_LIT = GroupName "shallow water Lit" pattern S_SIGNBOARD_UNREAD = GroupName "signboard unread"  -- ** Allure-specific@@ -266,8 +275,6 @@ pattern RUBBLE_BURNING_OR_NOT = GroupName "rubble burning or not"  pattern S_SUSPECT_WALL = GroupName "suspect wall"-pattern S_CLOSED_DOOR = GroupName "closed door"-pattern S_OPEN_DOOR = GroupName "open door" pattern S_STAIRCASE_TRAP_DOWN_OIL = GroupName "slippery staircase down" pattern S_BURNING_INSTALLATION = GroupName "burning installation" pattern S_BURNING_TREE = GroupName "burning tree"@@ -285,11 +292,9 @@ pattern SHOOTOUT_SET_DARK = GroupName "shootoutSetDark" pattern EXIT_SET_DARK = GroupName "exitSetDark" pattern HUNT_SET_DARK = GroupName "huntSetDark"-pattern FLOOR_ACTOR_ITEM_DARK = GroupName "floorActorItemDark"  -- * Not used, but needed, because auto-generated. Singletons. pattern S_BUSH_DARK = GroupName "bush Dark"-pattern S_SHALLOW_WATER_DARK = GroupName "shallow water Dark"  -- * Content @@ -364,7 +369,7 @@ bedrock = TileKind   { tsymbol  = '#'   , tname    = "wall"-  , tfreq    = [ (FILLER_WALL, 1), (LEGEND_LIT, 100), (LEGEND_DARK, 100)+  , tfreq    = [ (FILLER_WALL, 1)                , (ROGUE_SET, 60), (MUSEUM_SET_DARK, 4), (NOISE_SET_LIT, 450)                , (POWER_SET_DARK, 450), (BATTLE_SET_DARK, 250)                , (ESCAPE_SET_DARK, 4)@@ -428,8 +433,7 @@ pillar = TileKind   { tsymbol  = '0'   , tname    = "construction beam"-  , tfreq    = [ (LEGEND_LIT, 100), (LEGEND_DARK, 100)-               , (MUSEUM_SET_DARK, 20), (EMPTY_SET_LIT, 60) ]+  , tfreq    = [(S_PILLAR, 1), (MUSEUM_SET_DARK, 20), (EMPTY_SET_LIT, 60)]   , tcolor   = BrCyan  -- not BrWhite, to tell from heroes   , tcolor2  = Cyan   , talter   = 100@@ -527,7 +531,6 @@   { tsymbol  = '&'   , tname    = "rubble pile"   , tfreq    = [ (S_RUBBLE_PILE, 1), (RUBBLE_BURNING_OR_NOT, 50)-               , (LEGEND_LIT, 1), (LEGEND_DARK, 1)                , (STAIR_TERMINAL_LIT, 6), (STAIR_TERMINAL_DARK, 6)                , (LIFT_TERMINAL_LIT, 6), (LIFT_TERMINAL_DARK, 6)                , (EMPTY_SET_LIT, 12), (EXIT_SET_LIT, 6)@@ -570,7 +573,7 @@ doorClosed = TileKind  -- fireproof   { tsymbol  = '+'   , tname    = "closed door"-  , tfreq    = [(LEGEND_LIT, 100), (LEGEND_DARK, 100), (S_CLOSED_DOOR, 1)]+  , tfreq    = [(S_CLOSED_DOOR, 1)]   , tcolor   = Brown   , tcolor2  = BrBlack   , talter   = 2@@ -644,7 +647,7 @@ escapeUp = TileKind   { tsymbol  = '<'   , tname    = "exit hatch up"-  , tfreq    = [(LEGEND_LIT, 1), (LEGEND_DARK, 1), (ESCAPE_UP, 1)]+  , tfreq    = [(ESCAPE_UP, 1)]   , tcolor   = BrYellow   , tcolor2  = BrYellow   , talter   = 0  -- anybody can escape (or guard escape)@@ -653,7 +656,7 @@ escapeDown = TileKind   { tsymbol  = '>'   , tname    = "exit trapdoor down"-  , tfreq    = [(LEGEND_LIT, 1), (LEGEND_DARK, 1), (ESCAPE_DOWN, 1)]+  , tfreq    = [(ESCAPE_DOWN, 1)]   , tcolor   = BrYellow   , tcolor2  = BrYellow   , talter   = 0  -- anybody can escape (or guard escape)@@ -669,7 +672,7 @@ wallGlass = TileKind   { tsymbol  = '%'   , tname    = "transparent polymer wall"-  , tfreq    = [(LEGEND_LIT, 1), (LEGEND_DARK, 1), (MUSEUM_SET_DARK, 8)]+  , tfreq    = [(TRANSPARENT_WALL, 1), (MUSEUM_SET_DARK, 8)]   , tcolor   = BrCyan   , tcolor2  = Cyan   , talter   = 10@@ -686,7 +689,7 @@ pillarIce = TileKind   { tsymbol  = '^'   , tname    = "ice buildup"-  , tfreq    = [ (LEGEND_LIT, 1), (LEGEND_DARK, 1), (NOISE_SET_LIT, 200)+  , tfreq    = [ (ICE_BUILDUP, 1), (NOISE_SET_LIT, 200)                , (BRAWL_SET_LIT, 15), (LIFT_TERMINAL_DARK, 4) ]                  -- ice only in dark staircases   , tcolor   = BrBlue@@ -804,7 +807,7 @@ doorOpen = TileKind  -- fireproof   { tsymbol  = '\''   , tname    = "open door"-  , tfreq    = [(LEGEND_LIT, 100), (LEGEND_DARK, 100), (S_OPEN_DOOR, 1)]+  , tfreq    = [(S_OPEN_DOOR, 1)]   , tcolor   = Brown   , tcolor2  = BrBlack   , talter   = 4@@ -853,8 +856,7 @@   , tfeature = OftenActor : tfeature floorArena   } floorActorItem = floorActor-  { tfreq    = [ (FLOOR_ACTOR_ITEM, 1), (FLOOR_ACTOR_ITEM_LIT, 1)-               , (LEGEND_LIT, 100) ]+  { tfreq    = [(FLOOR_ACTOR_ITEM, 1), (FLOOR_ACTOR_ITEM_LIT, 1)]   , tfeature = VeryOftenItem : tfeature floorActor   } floorAshes = floorActor  -- always lit@@ -869,7 +871,7 @@ shallowWater = TileKind   { tsymbol  = '~'   , tname    = "water puddle"-  , tfreq    = [ (AQUATIC, 1), (S_SHALLOW_WATER_LIT, 1), (LEGEND_LIT, 100)+  , tfreq    = [ (AQUATIC, 1), (S_SHALLOW_WATER_LIT, 1)                , (EMPTY_SET_LIT, 20), (NOISE_SET_LIT, 30), (SHOOTOUT_SET_LIT, 5)                , (HUNT_SET_LIT, 250), (LIFT_TERMINAL_LIT, 4) ]   , tcolor   = BrCyan@@ -1406,8 +1408,7 @@ workshop = TileKind  -- always lit   { tsymbol  = ':'   , tname    = "workshop"-  , tfreq    = [ (LEGEND_LIT, 100), (LEGEND_DARK, 100)-               , (EMPTY_SET_LIT, 16), (SHOOTOUT_SET_LIT, 2)+  , tfreq    = [ (WORKSHOP, 100), (EMPTY_SET_LIT, 16), (SHOOTOUT_SET_LIT, 2)                , (AMBUSH_SET_DARK, 4), (BATTLE_SET_DARK, 4) ]   , tcolor   = BrBlue   , tcolor2  = Blue
+ GameDefinition/InGameHelp.txt view
@@ -0,0 +1,179 @@+This is a snapshot of in-game help, rendered with the default config file.+For more general gameplay information see+https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/PLAYING.md+++ Minimal cheat sheet for casual play++ Walk throughout a level with mouse or numeric keypad (right diagram below)+ or the Vi editor keys (middle) or the left-hand movement keys (left). Run until+ disturbed with Shift or Control. Go-to a position with LMB (left mouse button).+ In aiming mode, the same keys (and mouse) move the aiming crosshair.++      q w e     y k u     7 8 9+       \|/       \|/       \|/+      a-s-d     h-.-l     4-5-6+       /|\       /|\       /|\+      z x c     b j n     1 2 3++ Press `KP_5` (`5` on keypad) to wait, bracing for impact, which reduces any+ damage taken and prevents displacement by foes. Press `S-KP_5` or `C-KP_5`+ (the same key with Shift or Control) to lurk 0.1 of a turn, without bracing.++ Displace enemies by running into them with Shift/Control or S-LMB. Search,+ open, descend and melee by bumping into walls, doors, stairs and enemies.+ The best, and not on cooldown, melee weapon is automatically chosen+ for attack from your equipment and from among your body parts.++ The following few commands, joined with the movement and running keys,+ let you accomplish almost anything in the game, though not necessarily+ with the fewest keystrokes. You can also play the game exclusively+ with a mouse, or both mouse and keyboard (e.g., mouse for go-to+ and terrain inspection and keyboard for everything else). Lastly,+ you can select a command with arrows or mouse directly from the help+ screen or the dashboard and execute it on the spot.++  keys         command+  I            manage the shared inventory stash+  g or ,       grab item(s)+  ESC          clear messages/open main menu/finish aiming+  RET or INS   open dashboard/accept target+  SPACE        clear messages/show history/cycle detail level+  TAB          cycle among all party members+  *            cycle crosshair among enemies+  /            cycle crosshair among items+  M            modify any admissible terrain+  %            yell or yawn and stop sleeping+++ Optional mouse commands++ Screen area and UI mode (exploration/aiming) determine mouse click+ effects. Here we give an overview of effects of each button over+ the game map area. The list includes not only left and right buttons,+ but also the optional middle mouse button (MMB) and the mouse wheel,+ which is also used over menus to move selection. For mice without RMB,+ one can use Control key with LMB and for mice without MMB, one can use+ C-RMB or C-S-LMB.++  keys         command (exploration/aiming)+  LMB          go to pointer for 25 steps/fling at enemy+  S-LMB        run to pointer collectively for 25 steps/fling at enemy+  RMB or C-LMB start aiming at enemy under pointer/cycle detail level+  S-RMB        modify terrain at pointer+  MMB or C-RMB snap crosshair to floor under pointer/cycle detail level+  WHEEL-UP     swerve the aiming line+  WHEEL-DN     unswerve the aiming line++ Note that mouse is optional. Keyboard suffices, occasionally requiring+ a lookup for an obscure command key in help screens.+++ Mouse button effects per screen area, in exploration and in aiming modes++  Exploration    LMB (left mouse button)         RMB (right mouse button)+  message line   show history                    display help+  pointman tile  grab item(s)                    remove item(s)+  party on map   pick new pointman on screen     select party member on screen+  the map area   go to pointer for 25 steps      set crosshair to enemy+  level number   move aiming one level up        move aiming one level down+  level caption  open dashboard                  clear msgs and open main menu+  percent seen   explore nearest unknown spot    autoexplore 25 times+  crosshair info cycle crosshair among enemies   cycle crosshair among items+  party roster   pick new pointman on screen     select party member on screen+  Calm value     yell or yawn and stop sleeping  yell or yawn and stop sleeping+  HP gauge       rest (wait 25 times)            heed (lurk 0.1 turns 100 times)+  HP value       wait a turn, bracing for impact lurk 0.1 of a turn+  pointman info  auto-fling and keep choice      clear chosen item and crosshair++  Aiming Mode    LMB (left mouse button)         RMB (right mouse button)+  the map area   fling at enemy under pointer    snap crosshair to enemy+  level caption  accept target                   cancel aiming+  percent seen   aim at nearest upstairs         aim at nearest downstairs+++ All item-related commands++  keys         command+  I            manage the shared inventory stash+  O            manage the equipment outfit of the pointman+  g or ,       grab item(s)+  r            remove item(s)+  f            fling in-range projectile+  C-f          auto-fling and keep choice+  t            trigger consumable item+  C-t          trigger item and keep choice+  i            stash item into shared inventory+  o            equip item into outfit of the pointman+  G            manage items on the ground+  T            manage our total team belongings++ Note how lower case item commands (stash item, equip item) place items+ into a particular item store, while upper case item commands (manage Inventory,+ manage Outfit) open management menu for a store. Once a store menu is opened,+ you can switch stores with `<` and `>`, so the multiple commands only determine+ the starting item store. Each store is accessible from the dashboard as well.+++ Terrain exploration and modification commands++  keys         command+  TAB          cycle among all party members+  S-TAB        cycle backwards among all party members+  C-TAB        cycle among party members on the level+  C-S-TAB      cycle backwards among party members on the level+  m            modify door by closing it+  M            modify any admissible terrain+  =            select (or deselect) party member+  _            deselect (or select) all on the level+  ;            go to crosshair for 25 steps+  :            run to crosshair collectively for 25 steps+  [            explore nearest unknown spot+  ]            autoexplore 25 times+  R            rest (wait 25 times)+  C-R          heed (lurk 0.1 turns 100 times)+  0, 1 ... 9   pick a particular actor as the new pointman+++ All aiming commands++  keys         command+  ESC          clear messages/open main menu/finish aiming+  RET or INS   open dashboard/accept target+  SPACE        clear messages/show history/cycle detail level+  *            cycle crosshair among enemies+  /            cycle crosshair among items+  +            swerve the aiming line+  -            unswerve the aiming line+  \            cycle aiming modes+  C-?          set crosshair to nearest unknown spot+  C-/          set crosshair to nearest item+  C-{          aim at nearest upstairs+  C-}          aim at nearest downstairs+  <            move aiming one level up+  >            move aiming one level down+  BACKSPACE    clear chosen item and crosshair+++ Assorted commands++  keys         command+  %            yell or yawn and stop sleeping+  @            describe organs of the pointman+  #            show skill summary of the pointman+  ~            display relevant lore+  C-g          start new game+  C-x          save and exit to desktop+  C-q          quit game and start autoplay+  C-c          exit to desktop without saving+  ?            display help+  F1           display help immediately+  F12          open dashboard+  v            voice last action again+  V            voice recorded macro again+  '            start recording commands+  C-S          save game backup+  C-P          print screen+++ For more playing instructions see file PLAYING.md.
GameDefinition/Main.hs view
@@ -64,13 +64,9 @@   -- because they are not the source of the failure.   !serverOptions <- OA.execParser serverOptionsPI   resOrEx :: Either Ex.SomeException () <- Ex.try $ tieKnot serverOptions-  let unwrapEx e =-#if MIN_VERSION_async(2,2,1)-        case Ex.fromException e of-          Just (ExceptionInLinkedThread _ ex) -> unwrapEx ex-          _ ->-#endif-               e+  let unwrapEx e = case Ex.fromException e of+        Just (ExceptionInLinkedThread _ ex) -> unwrapEx ex+        _ -> e   case resOrEx of     Right () -> return ()     Left e -> case Ex.fromException $ unwrapEx e of
GameDefinition/PLAYING.md view
@@ -31,18 +31,15 @@  unopposed. The feral outer frontier  denizens are not famed for scruples  nor for restraint when using hazardous- nano, cyber and bio technologies,- so never assume you are safe. Be ready- to hide in shadows, create distractions,- set up ambushes, bump into unspeakable- horrors, puzzling machinery and- astounding treasures. Gather clues,- ideas and resources. Creatively use all- you can find, because your survival- depends on grit and ingenuity. Whenever- your turn back in fear, expect to be- chased via sight, sound and smell by- those that don't fear and don't tire.+ nano, cyber and bio technologies. Expect+ to bump into crazed machinery, warped+ nature, deadly residues and astounding+ treasures. Assume you'll be tracked+ and chased via sight, sound and smell.+ Prepare to hide in shadows, distract+ and set up ambushes. Gather clues and+ resources. Use and abuse. Outsmart.+ Outlast. Your survival is at stake.   What to expect@@ -78,7 +75,7 @@   (turn-based just the same)  * time passes and factions pursue their goals on a few levels-  simultaneously, while other levels are frozen (but all are persistent)+  simultaneously, while other floors are frozen (but all are persistent)  * the same laws of simulated world apply to all factions and all actors,   whether player-controlled or AI-controlled; e.g., the same field of view@@ -86,7 +83,7 @@   and terrain use  * combat mechanics is deterministic; randomness comes only from AI-  decisions and procedurally generated terrain+  decisions and procedurally generated world  * there's (almost) no HP regeneration; attrition ensures all past (silly)   decisions matter; HP of every actor starts at around half max@@ -112,30 +109,16 @@  Commands for starting a new game, saving and exiting the current game, configuring convenience settings and toggling AI control of the party-are listed in the main menu, brought up by the Esc key. Of the convenience-settings, the `suspect terrain` choice is of particular interest,-because it determines not only screen display of the level map,+are listed in the main menu, brought up by the Esc key.+Game difficulty level, from the new game setup menu, determines how hard+the survival in the game is. Each of the several named optional challenges+make the game additionally much harder, but usually simpler, as well.+Of the convenience settings, the `suspect terrain` choice is of particular+interest, because it determines not only screen display of the floor map, but also whether suspect tiles are considered for mouse go-to, auto-explore and for the `C-?` command that marks the nearest unexplored position.-Game difficulty, from the game setup menu, determines hitpoints at birth:-difficulty below 5 multiplies hitpoints of player characters, difficulty-over 5 multiplies hitpoints of their enemies. Each of the several named-optional challenges make the game much harder, but usually simpler, as well. -The "cold fish" challenge mode makes it impossible for player-characters to be healed by actors from other factions (this is-a significant restriction in the long crawl adventure).-The "ready goods" challenge mode disables crafting for the player,-making the selection of equipment, especially melee weapons, very limited,-unless the player has the luck to find the rare powerful ready weapons.-The "lone wolf" challenge mode reduces player's starting actors-to exactly one, though later on new heroes may join the party.-The "finder keeper" completely disables flinging projectiles-by the player, which affects not only ranged damage dealing,-but also throwing of consumables that buff teammates engaged in melee combat,-weaken and distract enemies, light dark corners, etc.--The game scenarios, as ordered by their number, lead the player along+Game scenarios, as ordered by their number, lead the player along an optional story arc. The first two adventures double as tutorials that offer rudimentary preparation for the main game, the long crawl. They gradually introduce exploration, stealth and melee combat,@@ -145,21 +128,22 @@ with hints about strategies known to work in the given tactical context. Alternatively, the player may postpone reading these messages and instead try to puzzle out the tactics himself --- this is not so hard, as there are-not so many moving parts to figure, at least in the short adventures.+not yet so many moving parts to figure out in the first two adventures. -As soon as the player learns to navigate initial levels of the crawl game-and starts employing ranged combat, light sources and other means-of gaining or denying battlefield intel, and dies a lot, it makes sense-to return to the remaining short adventures. They bring forth many extra-game features and tactics and prevent the player from missing half the fun-by trying to play the crawl just like a normal roguelike with spare heroes.+In the third scenario, the main 'crawl' game mode, the player starts+employing ranged combat, stealth, light sources, item and terrain alteration.+As soon as the player learns to navigate the initial levels of crawl,+but still dies a lot, it makes sense to return to the remaining+short adventures. They bring forth many extra game features+and tactics and prevent the player from missing half the fun by trying+to play the crawl just like a normal roguelike with spare heroes. The extra scenarios continue the plotline from the initial tutorial adventures in the form of flashbacks and eventually lead up to the events that start the main crawl adventure. The training they provide has narrow focus, drilling a particular skill set, even as exotic as opportunity fire-management, a frantic race to the exit and big asymmetric melee battle.+management, a frantic race to the exit and big asymmetric melee battles. The challenge the scenarios offer may be, accordingly, quite extreme,-particularly at higher difficulty levels and when striving for high scores.+particularly at higher difficulty settings and when striving for high scores.  The main adventure, the long crawl, is the only one that takes place over many floors, some initially blocked, requiring lots of time to beat.@@ -167,16 +151,15 @@ and cooking, crafting, terrain transformation using tools, spotting environment clues and guessing and countering opponents' strategies. The player has a choice of exploring a single level at a time or portions-of many levels along a single staircase. On some levels he may explore+of many floors along a single staircase. On some levels he may explore and loot with a single scout, eluding most opponents. On others he may be-forced to change the pace and perform a complete exterminatory sweep+forced to change pace and perform a complete exterminatory sweep involving his whole party. On yet others, his best course of action may be to defend a key location until the first wave of attackers is broken. The large game arena calls for strategic thinking, including resource-management, frantic races to the exit, big asymmetric melee battles.-Thus, the crawl scenario is the most replayable adventure, but even the small-ones can be refreshed by striving to beat a high score and then-ramping up the difficulty settings.+management and area denial. Thus, the crawl scenario is the most replayable+adventure, but even the small ones can be refreshed by striving to beat+a high score and by ramping up the difficulty settings.   Exploring the world@@ -228,7 +211,7 @@     smoke, fog, open fire         ;     workshop, curtain, foliage    : -The four groups above, in turn, block movement but not view,+The four groups above, from top to bottom, block movement but not view, block both, block neither, block view but not movement. Additionally, each tile, regardless if open and if translucent, may be permanently lit with ambient light or not.@@ -280,7 +263,7 @@ is pressed, though that's usually not a precise enough method of controlling a team. Any sleeping hero is highlighted in blue and can be woken up by yelling with `%` key, which also taunts-or stresses up nearby enemies.+and unnerves nearby enemies.  Next on the bottom-most status line is the pointman's current and maximum Calm (morale, composure, focus, attentiveness), then his current@@ -288,13 +271,13 @@ into a dot signifies that the pointman is in a position without ambient illumination, making stealthy conduct easier. A brace sign instead of a colon after "HP" means the pointman is braced for combat-(see section [Moving and acting](#Moving-and-acting)).+(see chapter [Moving and acting](#Moving-and-acting)).  In the second half of the bottom-most status line, the pointman's name-is shown. Then come damage dice of the pointman's melee weapons and pointman's-appendages, ordered by their power. The dice of the first recharged weapon,-the one that is going to be used now, is adorned with percentage-damage bonus collected from the whole equipment of the pointman.+is shown. Then come damage dice of the pointman's melee weapons and+the pointman's appendages, ordered by their power. The dice of the first+recharged weapon, the one that is going to be used now, is adorned with+percentage damage bonus collected from the whole equipment of the pointman. If the dice are displayed with upper-case `D` instead of lower-case `d`, the weapon has additional effects apart of the usual direct damage. The nature of the effects can be appraised via the equipment outfit menu.@@ -308,11 +291,11 @@ the dice notation `xdy`, which denotes `x` rolls of `y`-sided dice. A variant written `xdLy` is additionally scaled by the level depth in proportion to the maximal level depth (at the first level the result-is always one, then it grows up to the full rolled value at the last level).+is always one; it grows up to the full rolled value at the last level). Section [Battling monsters](#Battling-monsters) below describes combat resolution in detail, including the role of the percentage bonuses. -The upper status line describes the current level in relation+The upper status line describes the currently visited level in relation to the party.      5  Lofty hall    [33% seen] dire basilisk    [__**]@@ -341,16 +324,16 @@ Moving and acting ----------------- -This section is a copy of the few initial bits of in-game help. The in-game-help pages are automatically generated based on a game's keybinding content-definitions and on overrides in the player's config file. The remaining-in-game help screens, not shown here, list all game commands grouped-by categories in detail. A text snapshot of the complete in-game help is in-[InGameHelp.txt](InGameHelp.txt).+This chapter is a copy of the few initial pages of in-game help.+The in-game help is automatically generated based on a game's keybinding+content definitions and on overrides in the player's config file.+The remaining in-game help screens, not shown here, list all game+commands grouped by categories in detail.  Walk throughout a level with mouse or numeric keypad (right diagram below) or the Vi editor keys (middle) or the left-hand movement keys (left). Run until disturbed with Shift or Control. Go-to a position with LMB (left mouse button).+In aiming mode, the same keys (and mouse) move the aiming crosshair.            q w e          y k u          7 8 9            \|/            \|/            \|/@@ -358,15 +341,14 @@            /|\            /|\            /|\           z x c          b j n          1 2 3 -In aiming mode, the same keys (and mouse) move the aiming crosshair. Press `KP_5` (`5` on keypad) to wait, bracing for impact, which reduces any damage taken and prevents displacement by foes. Press `S-KP_5` or `C-KP_5` (the same key with Shift or Control) to lurk 0.1 of a turn, without bracing.  Displace enemies by running into them with Shift/Control or S-LMB. Search,-open, descend and attack by bumping into walls, doors, stairs and enemies.-The best, not on cooldown, melee weapon is automatically chosen from your-equipment and from among your body parts.+open, descend and melee by bumping into walls, doors, stairs and enemies.+The best, and not on cooldown, melee weapon is automatically chosen+for attack from your equipment and from among your body parts.  The following few commands, joined with the movement and running keys, let you accomplish almost anything in the game, though not necessarily@@ -392,7 +374,7 @@ effects. Here we give an overview of effects of each button over the game map area. The list includes not only left and right buttons, but also the optional middle mouse button (MMB) and the mouse wheel,-which is also used over menus, to page-scroll them. For mice without RMB,+which is also used over menus to move selection. For mice without RMB, one can use Control key with LMB and for mice without MMB, one can use C-RMB or C-S-LMB. @@ -405,8 +387,8 @@     WHEEL-UP     swerve the aiming line     WHEEL-DN     unswerve the aiming line -Note that mouse is totally optional. Keyboard suffices, occasionally-requiring a lookup for some obscure command key in the help screens.+Note that mouse is optional. Keyboard suffices, occasionally requiring+a lookup for an obscure command key in help screens.   Battling monsters@@ -477,27 +459,30 @@ Attacking from a distance ------------------------- -For ranged attacks, setting the aiming crosshair before `f` to attack-is pressed is not mandatory. Crosshair is set automatically as soon+Before the player presses `f` to make a ranged attack, he may move+and set the aiming crosshair in aiming mode. However, this is+not often needed, since crosshair is set automatically as soon as a monster comes into view and can still be adjusted for as long-as the missile to fling is not chosen. However, sometimes before-flinging you want to examine the level map tile by tile by moving-the crosshair or assign persistent personal targets to party members.+as the missile to fling is not chosen.++Nevertheless, sometimes before flinging you want to examine+the level map tile by tile by moving the crosshair+or to assign persistent personal targets to party members. The latter is essential in the rare cases when your henchmen (non-pointman characters) can move autonomously or fire opportunistically (via innate skills or rare equipment). Also, if your non-pointman character is adjacent to more than one enemy, setting his target makes him melee a particular foe. -You can enter the aiming mode with the `*` and `KP_*` keys that-select enemies or the `/` and `KP_/` keys that cycle among items-on the floor. You can move crosshair with direction keys and assign-a personal target to the pointman with a `RET` key (Return, Enter).+You can enter the aiming mode with the `*` and `KP_*` keys that select+enemies or the `/` and `KP_/` keys that cycle among items on the level.+You can move crosshair with direction keys and assign a personal+target to the pointman with the `RET` key (Return, Enter). The details about the shared crosshair position are displayed in a status-line close to the bottom of the screen, as explained in section+line close to the bottom of the screen, as explained in chapter [Leading your heroes](#Leading-your-heroes) above. You cycle aiming-mode from foe to spot to vector with the ``\`` key, which is useful-when a monster vanishes but you still want to fling at its last+mode from foe to spot and to vector with the ``\`` key, which is useful,+e.g., when a monster vanishes but you still want to fling at its last known position.  @@ -526,10 +511,10 @@ to bar their way.  The only immutable factor throughout the chain of the heroes' adventures-is their personal life stories, with the implied unique characteristics,-virtues and vices. However, a hero is not determined by the past-nor discouraged by present defeats and the allure of fame, wealth-and glory is always as bright as the stars.+is their personal life stories, with the resulting unique characteristics,+virtues and vices. However, a true hero is not determined by the past+nor deterred by present transient defeats. The allure of fame, wealth+and glory is the beacon that shines as bright as the stars in deep space.   FAQ@@ -540,8 +525,9 @@ A: Role-play a little. Re-read the scenario description, deck descriptions for all three levels and the description of crucial terrain items. Think in-character about visiting the lowest deck.-What would you do? What would you look for? Has anything changed-compared to what the texts describe? Would anything surprise you?+What would you do? What would you look for? Remember you heroes have+carelessly strolled through these decks before. Has anything changed+compared to what the texts recall? Would anything surprise you? When you encounter any special terrain, actors, unexpected groups of actors or items, ask questions, think how that ties to your initial goal from the scenario description, examine terrain, actors and your@@ -555,10 +541,10 @@  - Q: In the crawl scenario, I already investigated and decided I won't use the `[spoiler]` nor `[spoiler]` way of escaping the ship.-`[Spoiler alert: players that didn't, may want to role-play-and try that first, see the previous question.]`+`[Spoiler alert: players that didn't, may want to role-play and try+these ways first, see the previous question. Otherwise, read on.]` Assuming I want to attempt the way through the welded staircase,-how do I open the stairs up welded shut and reach the levels above+how do I open the '"staircase up welded shut' and reach the levels above the first three?  A: Examine the staircase to determine how to open it. Look for clues@@ -592,9 +578,9 @@  - Q: Does power of crafted items depend on the depth of current level? -A: No, unlike items found on the floor or created via other means,-items created by crafting are considered to originate at maximal depth-whether regarding item kind, item stats or lifetime, if applicable.+A: No, unlike items found on the floor, items created by crafting+are considered to originate at maximal depth whether regarding item kind,+item stats or lifetime, if applicable.  - Q: Why do I summon hostile animals all the time, why do I defect to the enemy faction every level, why am I constantly sabotaging@@ -612,14 +598,15 @@ A: Perhaps he's just sleeping (blue box indicates that)? If so, you can wake him up with the `%` command. If he's not asleep, his movement skill may be temporarily drained. Switch to another hero-or perform some other productive action that walking or wait-or rest with `R`.+or perform some other productive action different from walking+or wait with `KP_5` or rest with `R`.  - Q: Is autoexplore safe?  A: Not at all. It doesn't try to guess which hazardous terrain you want to avoid and which to barge through, so be prepared to abort exploration if open fire or slippery ground comes into view. Unless you have HP to spare.+Oh the other hand, running is very safe and go-to is rather safe.  - Q: Why does the percentage of explored tiles turn from 100% to 99%? @@ -639,7 +626,7 @@ and revealing their position or leaving a vantage point from which they can observe and relay enemy movement. For untrained teams, simultaneous synchronized squad movement is not feasible.-It would be practical, if all squaddies had cameras, with a few drones+It would be practical if all squaddies had cameras, with a few drones overhead for best effect, and if a team of off-site coordinators analyzed the situation and micromanaged them all. This is not the case here. @@ -668,27 +655,27 @@ of turns?  A: This is an artifact of time running independently on each level.-Any ideas on how to improve this are welcome. A workaround is to drop and then-pick up the item on the level you want to use it. When picked up,-it gets recharged after, randomly, from one to two times the normal-cooldown period of the item and then recharges normally for as long-as it's used on this level.+Any ideas on how to improve this game mechanics are welcome.+A workaround is to drop and then pick up the item on the level+you want to use it. When picked up, it gets recharged after, randomly,+from one to two times the normal cooldown period of the item+and then recharges normally while it's used on this level.  - Q: Why the bottom line displays a weapon with a timeout to the right of a weapon without timeout? Doesn't it mean the former is never used? -A: Yes, it's never used and quite possibly it's your mistake and if not,-at least a very special situation and the display reminds you about it.-Shuffle the equipment among your team if you want the weapon-to get used.+A: Yes, it's never used and, quite possibly, it's your party inventory+management mistake and if not, at least a very special situation+and the display turns your attention to it. Shuffle the equipment+among your team if you want the weapon to get used.  - Q: When can I learn all characters' backstory items?  A: Each backstory item triggers in specific circumstances, so it may take-some time and even, rarely, never be revealed at all. For vices, that may be-the desired turn of action. Alternatively, you can try to reveal a vice-early and then prevent it from triggering ever again once the activation-condition is known.+some time and even not be revealed during a single playthrough.+For vices, that may be the desired turn of events. Alternatively,+you can try to reveal a vice early and then prevent it from triggering+again, taking advantage of the activation condition becoming known.  - Q: Can I kill off heroes permanently and reset all character backstories? 
GameDefinition/config.ui.default view
@@ -56,22 +56,28 @@ ; and/or to avoid moving due to accidental key presses. movementViKeys_hjklyubn = True movementLeftHandKeys_axwdqezc = True-; recommended: "binary" or "dejavuBold" or, if not a Debian package, "ubuntu"+; recommended: "binary" or "dejavuBold" chosenFontset = "binary" ; 1.0 means don't scale; 1.5, 2.0, 3.0 give good results, <0.7 very bad ; for scales < 1 try fontsets with bolder auxiliary fonts and/or HintingLight allFontsScale = 1.0-; NotFullscreen (default), BigBorderlessWindow (preferred), ModeChange-; for 1920×1080 fullscreen, set allFontsScale = 1.5 above+; NotFullscreen (default), BigBorderlessWindow (preferred), ModeChange.+; For 1920×1080 fullscreen, set allFontsScale = 1.5 above. fullscreenMode = NotFullscreen-screenOneMessagePerLine = True-historyOneMessagePerLine = True+; HP percent at which warning is emitted.+hpWarningPercent = 20+; Wrap messages after this column (measured in small font, if available).+; In Allure there is enough height to fit messages, so this can be low.+; With proportional font this value is theoretically ideal for reading comfort.+msgWrapColumn = 53 ; New historyMax takes effect after removal of savefiles.-; Looks best if is divisble by screenful of message (rheight - 4 = 41).+; Looks best if is divisble by screenful of message (rheight - 4 = 20). historyMax = 5002+; Frames per second throttled at this value. maxFps = 24+; Animations when actors act are not displayed. noAnim = False-hpWarningPercent = 20+; Hardwired commandline arguments to process. overrideCmdline = ""  [fonts]@@ -82,9 +88,6 @@ dejavuRegular = FontProportional "DejaVuLGCSans.ttf.woff" 15 HintingHeavy dejavuBold = FontProportional "DejaVuLGCSans-Bold.ttf.woff" 13 HintingHeavy dejavuMono = FontMonospace "Hack-Bold.ttf.woff" 13 HintingHeavy-ubuntuRegular = FontProportional "ubuntu-v14-latin-ext_latin-regular.ttf.woff" 16 HintingLight-ubuntuBold = FontProportional "ubuntu-v14-latin-ext_latin-500.ttf.woff" 16 HintingLight-ubuntuMono = FontMonospace "ubuntu-mono-v9-latin-ext_latin-700.ttf.woff" 16 HintingLight ; the map fonts (the format is: kind name size hinting cellSizeAdd): ; with allFontsScale < 1, try HintingLight for fuzzy, but less distorted shapes: 16x16xwScalable = FontMapScalable "16x16xw.woff" 16 HintingHeavy 0@@ -94,26 +97,23 @@ 8x8xBitmap = FontMapBitmap "8x8x.fnt" 2  [fontsets]+; This is a temporary hack around bitmap font breakage caused by+; https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076+; In a couple of years this can be removed and the small bitmap fonts will work+; fine again, too.+binary = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwScalable", fontPropRegular = "binaryRegular", fontPropBold = "binaryBold", fontMono = "binaryMono"}+ ; best proportional:-binary = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryRegular", fontPropBold = "binaryBold", fontMono = "binaryMono"}+binary_original = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryRegular", fontPropBold = "binaryBold", fontMono = "binaryMono"} dejavuBold = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "dejavuBold", fontPropBold = "dejavuBold", fontMono = "dejavuMono"} -; Warning: the Ubuntu Font Family fonts are considered non-free by Debian-; and so we don't include them in Debian and some other GNU/Linux packages.-; If you use such a package, feel free to download the fonts from our github-; repository or specify absolute paths to their locations in non-free Debian-; package fonts-ubuntu.-ubuntu = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "ubuntuRegular", fontPropBold = "ubuntuBold", fontMono = "ubuntuMono"}- ; decent proportional: binaryBold = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryBold", fontPropBold = "binaryBold", fontMono = "binaryMono"} dejavu = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "dejavuRegular", fontPropBold = "dejavuBold", fontMono = "dejavuMono"}-ubuntuThin = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "ubuntuRegular", fontPropBold = "ubuntuRegular", fontMono = "ubuntuMono"}  ; monospace, for people that don't like proportional or many fonts: binaryMono = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryMono", fontPropBold = "binaryMono", fontMono = "binaryMono"} dejavuMono = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "dejavuMono", fontPropBold = "dejavuMono", fontMono = "dejavuMono"}-ubuntuMono = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "ubuntuMono", fontPropBold = "ubuntuMono", fontMono = "ubuntuMono"}  ; square: 16x16xw = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "", fontPropBold = "", fontMono = ""}@@ -132,4 +132,4 @@ ; any more, the game crashes. To prevent that, one of the first three components ; of the game version should be bumped whenever fonts are changed. ; Configs from old versions are rejected, preventing the crash.-version = 0.10.2+version = 0.10.3
− GameDefinition/fonts/ubuntu-mono-v9-latin-ext_latin-700.ttf.woff

binary file changed (57416 → absent bytes)

− GameDefinition/fonts/ubuntu-v14-latin-ext_latin-500.ttf.woff

binary file changed (69288 → absent bytes)

− GameDefinition/fonts/ubuntu-v14-latin-ext_latin-regular.ttf.woff

binary file changed (76672 → absent bytes)

GameDefinition/game-src/Client/UI/Content/Input.hs view
@@ -35,19 +35,10 @@   -- Remember to put commands that show information (e.g., enter aiming   -- mode) first. -  -- Main menu-  [ ("s", ([CmdMainMenu], "setup and start new game>", ChallengeMenu))-  , ("x", ([CmdMainMenu], "save and exit to desktop", GameExit))-  , ("v", ([CmdMainMenu], "tweak convenience settings>", SettingsMenu))-  , ("t", ([CmdMainMenu], "toggle autoplay", AutomateToggle))-  , ("?", ([CmdMainMenu], "see command help", Help))-  , ("F12", ([CmdMainMenu], "switch to dashboard", Dashboard))-  , ("Escape", ([CmdMainMenu], "back to playing", AutomateBack))-   -- Minimal command set, in the desired presentation order.   -- A lot of these are not necessary, but may be familiar to new players.   -- Also a few non-minimal item commands to keep proper order.-  , ("I", ( [CmdMinimal, CmdItem, CmdDashboard]+  [ ("I", ( [CmdMinimal, CmdItem, CmdDashboard]           , "manage the shared inventory stash"           , ChooseItemMenu (MStore CStash) ))   , ("O", ( [CmdItem, CmdDashboard]@@ -76,7 +67,6 @@   , ("A-BackTab", memberCycleLevel Backward [])   , ("C-Tab", memberCycleLevel Forward [CmdMove])   , ("C-BackTab", memberCycleLevel Backward [CmdMove])-      -- TODO: the keys are too long to fit in help menu, unless vertically   , ("*", ( [CmdMinimal, CmdAim]           , "cycle crosshair among enemies"           , AimEnemy ))
GameDefinition/game-src/Client/UI/Content/Screen.hs view
@@ -26,7 +26,6 @@ standardLayoutAndFeatures = ScreenContent   { rwidth = 80   , rheight = 45-  , rwrap = 53  -- more with proportional font and then ideal reading comfort   , rwebAddress = "http://allureofthestars.com"   , rintroScreen = $(do       let path = "GameDefinition/PLAYING.md"@@ -55,9 +54,7 @@   , rapplyVerbMap =       EM.fromList [('!', "imbibe"), (',', "eat"), ('?', "activate")]   , rFontFiles =--- Checking USE_SDL would be more accurate, but would require complicating--- .cabal file and slightly larger vty executable is not a problem.-#ifdef USE_JSFILE+#ifdef USE_BROWSER       [] #else       $(embedDir "GameDefinition/fonts")
GameDefinition/game-src/Implementation/MonadServerImplementation.hs view
@@ -143,7 +143,7 @@ -- initial states, in the @IO@ monad. executorSer :: COps -> CCUI -> ServerOptions -> UIOptions -> IO () executorSer cops@COps{corule} ccui soptionsNxtCmdline sUIOptions = do-  soptionsNxtRaw <- case uCmdline sUIOptions of+  soptionsNxtRaw <- case uOverrideCmdline sUIOptions of     []   -> return soptionsNxtCmdline     args -> handleParseResult $ execParserPure defaultPrefs serverOptionsPI args   -- Options for the clients modified with the configuration file.
GameDefinition/game-src/TieKnot.hs view
@@ -63,7 +63,7 @@   -- Set the X size of the dungeon from content ASAP, before it's used.   speedupHackXSizeThawed <- PA.unsafeThawPrimArray speedupHackXSize   PA.writePrimArray speedupHackXSizeThawed 0 $-    RK.rXmax Content.RuleKind.standardRules+    RK.rWidthMax Content.RuleKind.standardRules   void $ PA.unsafeFreezePrimArray speedupHackXSizeThawed   -- This setup ensures the boosting option doesn't affect generating initial   -- RNG for dungeon, etc., and also, that setting dungeon RNG on commandline@@ -75,7 +75,8 @@         if sboostRandomItem         then boostedItems ++ Content.ItemKind.otherItemContent         else Content.ItemKind.content-      coitem = IK.makeData itemContent+      coitem = IK.makeData (RK.ritemSymbols Content.RuleKind.standardRules)+                           itemContent                            Content.ItemKind.groupNamesSingleton                            Content.ItemKind.groupNames       coItemSpeedup = speedupItem coitem@@ -95,7 +96,8 @@         , comode = MK.makeData Content.ModeKind.content                                Content.ModeKind.groupNamesSingleton                                Content.ModeKind.groupNames-        , coplace = PK.makeData Content.PlaceKind.content+        , coplace = PK.makeData cotile+                                Content.PlaceKind.content                                 Content.PlaceKind.groupNamesSingleton                                 Content.PlaceKind.groupNames         , corule = RK.makeData Content.RuleKind.standardRules
+ LICENSE view
@@ -0,0 +1,661 @@+                    GNU AFFERO GENERAL PUBLIC LICENSE+                       Version 3, 19 November 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++                            Preamble++  The GNU Affero General Public License is a free, copyleft license for+software and other kinds of works, specifically designed to ensure+cooperation with the community in the case of network server software.++  The licenses for most software and other practical works are designed+to take away your freedom to share and change the works.  By contrast,+our General Public Licenses are intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users.++  When we speak of free software, we are referring to freedom, not+price.  Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++  Developers that use our General Public Licenses protect your rights+with two steps: (1) assert copyright on the software, and (2) offer+you this License which gives you legal permission to copy, distribute+and/or modify the software.++  A secondary benefit of defending all users' freedom is that+improvements made in alternate versions of the program, if they+receive widespread use, become available for other developers to+incorporate.  Many developers of free software are heartened and+encouraged by the resulting cooperation.  However, in the case of+software used on network servers, this result may fail to come about.+The GNU General Public License permits making a modified version and+letting the public access it on a server without ever releasing its+source code to the public.++  The GNU Affero General Public License is designed specifically to+ensure that, in such cases, the modified source code becomes available+to the community.  It requires the operator of a network server to+provide the source code of the modified version running there to the+users of that server.  Therefore, public use of a modified version, on+a publicly accessible server, gives the public access to the source+code of the modified version.++  An older license, called the Affero General Public License and+published by Affero, was designed to accomplish similar goals.  This is+a different license, not a version of the Affero GPL, but Affero has+released a new version of the Affero GPL which permits relicensing under+this license.++  The precise terms and conditions for copying, distribution and+modification follow.++                       TERMS AND CONDITIONS++  0. Definitions.++  "This License" refers to version 3 of the GNU Affero General Public License.++  "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++  "The Program" refers to any copyrightable work licensed under this+License.  Each licensee is addressed as "you".  "Licensees" and+"recipients" may be individuals or organizations.++  To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy.  The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++  A "covered work" means either the unmodified Program or a work based+on the Program.++  To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy.  Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++  To "convey" a work means any kind of propagation that enables other+parties to make or receive copies.  Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++  An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License.  If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++  1. Source Code.++  The "source code" for a work means the preferred form of the work+for making modifications to it.  "Object code" means any non-source+form of a work.++  A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++  The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form.  A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++  The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities.  However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work.  For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++  The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++  The Corresponding Source for a work in source code form is that+same work.++  2. Basic Permissions.++  All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met.  This License explicitly affirms your unlimited+permission to run the unmodified Program.  The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work.  This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++  You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force.  You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright.  Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++  Conveying under any other circumstances is permitted solely under+the conditions stated below.  Sublicensing is not allowed; section 10+makes it unnecessary.++  3. Protecting Users' Legal Rights From Anti-Circumvention Law.++  No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++  When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++  4. Conveying Verbatim Copies.++  You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++  You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++  5. Conveying Modified Source Versions.++  You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++    a) The work must carry prominent notices stating that you modified+    it, and giving a relevant date.++    b) The work must carry prominent notices stating that it is+    released under this License and any conditions added under section+    7.  This requirement modifies the requirement in section 4 to+    "keep intact all notices".++    c) You must license the entire work, as a whole, under this+    License to anyone who comes into possession of a copy.  This+    License will therefore apply, along with any applicable section 7+    additional terms, to the whole of the work, and all its parts,+    regardless of how they are packaged.  This License gives no+    permission to license the work in any other way, but it does not+    invalidate such permission if you have separately received it.++    d) If the work has interactive user interfaces, each must display+    Appropriate Legal Notices; however, if the Program has interactive+    interfaces that do not display Appropriate Legal Notices, your+    work need not make them do so.++  A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit.  Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++  6. Conveying Non-Source Forms.++  You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++    a) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by the+    Corresponding Source fixed on a durable physical medium+    customarily used for software interchange.++    b) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by a+    written offer, valid for at least three years and valid for as+    long as you offer spare parts or customer support for that product+    model, to give anyone who possesses the object code either (1) a+    copy of the Corresponding Source for all the software in the+    product that is covered by this License, on a durable physical+    medium customarily used for software interchange, for a price no+    more than your reasonable cost of physically performing this+    conveying of source, or (2) access to copy the+    Corresponding Source from a network server at no charge.++    c) Convey individual copies of the object code with a copy of the+    written offer to provide the Corresponding Source.  This+    alternative is allowed only occasionally and noncommercially, and+    only if you received the object code with such an offer, in accord+    with subsection 6b.++    d) Convey the object code by offering access from a designated+    place (gratis or for a charge), and offer equivalent access to the+    Corresponding Source in the same way through the same place at no+    further charge.  You need not require recipients to copy the+    Corresponding Source along with the object code.  If the place to+    copy the object code is a network server, the Corresponding Source+    may be on a different server (operated by you or a third party)+    that supports equivalent copying facilities, provided you maintain+    clear directions next to the object code saying where to find the+    Corresponding Source.  Regardless of what server hosts the+    Corresponding Source, you remain obligated to ensure that it is+    available for as long as needed to satisfy these requirements.++    e) Convey the object code using peer-to-peer transmission, provided+    you inform other peers where the object code and Corresponding+    Source of the work are being offered to the general public at no+    charge under subsection 6d.++  A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++  A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling.  In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage.  For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product.  A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++  "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source.  The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++  If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information.  But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++  The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed.  Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++  Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++  7. Additional Terms.++  "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law.  If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++  When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it.  (Additional permissions may be written to require their own+removal in certain cases when you modify the work.)  You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++  Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++    a) Disclaiming warranty or limiting liability differently from the+    terms of sections 15 and 16 of this License; or++    b) Requiring preservation of specified reasonable legal notices or+    author attributions in that material or in the Appropriate Legal+    Notices displayed by works containing it; or++    c) Prohibiting misrepresentation of the origin of that material, or+    requiring that modified versions of such material be marked in+    reasonable ways as different from the original version; or++    d) Limiting the use for publicity purposes of names of licensors or+    authors of the material; or++    e) Declining to grant rights under trademark law for use of some+    trade names, trademarks, or service marks; or++    f) Requiring indemnification of licensors and authors of that+    material by anyone who conveys the material (or modified versions of+    it) with contractual assumptions of liability to the recipient, for+    any liability that these contractual assumptions directly impose on+    those licensors and authors.++  All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10.  If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term.  If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++  If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++  Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++  8. Termination.++  You may not propagate or modify a covered work except as expressly+provided under this License.  Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++  However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++  Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++  Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License.  If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++  9. Acceptance Not Required for Having Copies.++  You are not required to accept this License in order to receive or+run a copy of the Program.  Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance.  However,+nothing other than this License grants you permission to propagate or+modify any covered work.  These actions infringe copyright if you do+not accept this License.  Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++  10. Automatic Licensing of Downstream Recipients.++  Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License.  You are not responsible+for enforcing compliance by third parties with this License.++  An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations.  If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++  You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License.  For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++  11. Patents.++  A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based.  The+work thus licensed is called the contributor's "contributor version".++  A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version.  For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++  Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++  In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement).  To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++  If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients.  "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++  If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++  A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License.  You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++  Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++  12. No Surrender of Others' Freedom.++  If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License.  If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all.  For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++  13. Remote Network Interaction; Use with the GNU General Public License.++  Notwithstanding any other provision of this License, if you modify the+Program, your modified version must prominently offer all users+interacting with it remotely through a computer network (if your version+supports such interaction) an opportunity to receive the Corresponding+Source of your version by providing access to the Corresponding Source+from a network server at no charge, through some standard or customary+means of facilitating copying of software.  This Corresponding Source+shall include the Corresponding Source for any work covered by version 3+of the GNU General Public License that is incorporated pursuant to the+following paragraph.++  Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU General Public License into a single+combined work, and to convey the resulting work.  The terms of this+License will continue to apply to the part which is the covered work,+but the work with which it is combined will remain governed by version+3 of the GNU General Public License.++  14. Revised Versions of this License.++  The Free Software Foundation may publish revised and/or new versions of+the GNU Affero General Public License from time to time.  Such new versions+will be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++  Each version is given a distinguishing version number.  If the+Program specifies that a certain numbered version of the GNU Affero General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation.  If the Program does not specify a version number of the+GNU Affero General Public License, you may choose any version ever published+by the Free Software Foundation.++  If the Program specifies that a proxy can decide which future+versions of the GNU Affero General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++  Later license versions may give you additional or different+permissions.  However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++  15. Disclaimer of Warranty.++  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++  16. Limitation of Liability.++  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++  17. Interpretation of Sections 15 and 16.++  If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++                     END OF TERMS AND CONDITIONS++            How to Apply These Terms to Your New Programs++  If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++  To do so, attach the following notices to the program.  It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++    <one line to give the program's name and a brief idea of what it does.>+    Copyright (C) <year>  <name of author>++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU Affero General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program is distributed in the hope that it will be useful,+    but WITHOUT ANY WARRANTY; without even the implied warranty of+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+    GNU Affero General Public License for more details.++    You should have received a copy of the GNU Affero General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++  If your software can interact with users remotely through a computer+network, you should also make sure that it provides a way for users to+get its source.  For example, if your program is a web application, its+interface could display a "Source" link that leads users to an archive+of the code.  There are many ways you could offer source, and different+solutions will be better for different programs; see section 13 for the+specific requirements.++  You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU AGPL, see+<http://www.gnu.org/licenses/>.
Makefile view
@@ -1,21 +1,20 @@-# some of this (not the parts run by CI) needs 'cabal install cabal-plan' play:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs  shot:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs --printEachScreen+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs --printEachScreen  expose-lore:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000  dig-lore:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode dig --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode dig --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000  see-caves:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode see --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode see --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000  short-caves:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode short --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode short --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000  configure-debug: 	cabal configure --enable-profiling --profiling-detail=all-functions -fwith_expensive_assertions --disable-optimization@@ -33,10 +32,10 @@ 	google-chrome --no-sandbox --js-flags="--logfile=%t.log --prof" ../allureofthestars.github.io/play/index.html  minific:-	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --externs=/home/mikolaj/r/lambdahack.github.io/lz-string.extern.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js+	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.3.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.3.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --externs=/home/mikolaj/r/lambdahack.github.io/lz-string.extern.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js  minificForNode:-	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --externs=/home/mikolaj/r/lambdahack.github.io/lz-string.extern.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/assert.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/child_process.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/crypto.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dns.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/events.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/globals.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/https.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/os.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/punycode.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/readline.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/stream.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tls.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/url.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/vm.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/buffer.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/cluster.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dgram.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/domain.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/fs.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/http.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/net.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/path.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/querystring.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/repl.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/string_decoder.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tty.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/util.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/zlib.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js+	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.3.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=/home/mikolaj/r/lambdahack.github.io/lz-string.extern.js --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.3.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/assert.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/child_process.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/crypto.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dns.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/events.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/globals.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/https.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/os.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/punycode.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/readline.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/stream.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tls.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/url.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/vm.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/buffer.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/cluster.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dgram.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/domain.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/fs.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/http.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/net.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/path.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/querystring.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/repl.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/string_decoder.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tty.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/util.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/zlib.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js  # Low delay to display animations swiftly and not bore the public too much. # Delay can't be lower than 2, because browsers sometimes treat delay 1@@ -46,61 +45,61 @@ 	../gifsicle/src/gifsicle -O3 --careful -d2 --colors 255 --no-extensions --no-conserve-memory -l ~/.Allure/screenshots/prtscn*.gif -o ~/.Allure/screenshots/screenshot.gif  frontendRaid:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid --benchMessages --exposeActors  frontendBrawl:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode brawl+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode brawl --benchMessages  frontendShootout:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout  frontendHunt:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode hunt+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode hunt  frontendEscape:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape  frontendZoo:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo --exposeActors  frontendAmbush:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush  frontendCrawl:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors  frontendCrawlEmpty:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawlEmpty+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawlEmpty --maxFps 100000 --benchmark  frontendCrawlSurvival:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode crawlSurvival --maxFps 1000+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode crawlSurvival --maxFps 100000 --benchmark  frontendSafari:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode safari --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode safari --exposeActors  frontendSafariSurvival:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode safariSurvival --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode safariSurvival --exposeActors --maxFps 100000 --benchmark  frontendBattle:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 4 --dumpInitRngs --automateAll --gameMode battle --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 4 --dumpInitRngs --automateAll --gameMode battle --exposeActors  frontendBattleDefense:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode battleDefense --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode battleDefense --exposeActors --maxFps 100000 --benchmark  frontendBattleSurvival:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode battleSurvival --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode battleSurvival --exposeActors --maxFps 100000 --benchmark  frontendDefense:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense --exposeItems --exposeActors+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense --exposeItems --exposeActors  frontendDefenseEmpty:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defenseEmpty+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defenseEmpty --maxFps 100000 --benchmark  fastCrawl:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples --noAnim --maxFps 100000 --benchmark+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples --noAnim --maxFps 100000 --benchmark  slowCrawl:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples  # different benchmarks use different arguments RNGOPTS=--setDungeonRng "SMGen 123 123" --setMainRng "SMGen 123 125"@@ -108,164 +107,163 @@ RNGOPTS2=--setDungeonRng "SMGen 129 123" --setMainRng "SMGen 129 125"  benchMemoryAnim:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS2)  --frontendNull --noAnim +RTS -s -A1M -RTS+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --maxFps 100000 --benchmark --benchMessages --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS2) --frontendLazy +RTS -s -A1M -RTS  benchBattle:-	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)  benchBattle-appveyor:-	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)+	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)  benchAnimBattle:-	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 3 --maxFps 100000 --frontendLazy --benchmark --benchMessages --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)  benchFrontendBattle:-	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --benchmark --benchMessages --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)  benchCrawl:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  benchFrontendCrawl:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --benchmark --benchMessages --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  benchDig:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1 --automateAll --keepAutomated --gameMode dig $(RNGOPTS)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 1 --automateAll --keepAutomated --gameMode dig $(RNGOPTS)  benchNull: benchBattle benchAnimBattle benchCrawl  bench: benchBattle benchAnimBattle benchFrontendBattle benchCrawl benchFrontendCrawl  nativeBenchCrawl:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  nativeBenchCrawl-appveyor:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  nativeBenchBattle:-	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)  nativeBench: nativeBenchBattle nativeBenchCrawl  nodeBenchCrawl:-	node $$(cabal-plan list-bin Allure).jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+	node $$(cabal list-bin exe:Allure).jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  nodeBenchBattle:-	node $$(cabal-plan list-bin Allure).jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)+	node $$(cabal list-bin exe:Allure).jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)  nodeBench: nodeBenchBattle nodeBenchCrawl  nodeMinifiedBench:-	node ../allureofthestars.github.io/play/allure.all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)-	node ../allureofthestars.github.io/play/allure.all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+	node ../allureofthestars.github.io/play/allure.all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)+	node ../allureofthestars.github.io/play/allure.all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --benchMessages --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  -test-travis: test-short test-medium--test-travis2: test-sniff benchNull+test: test-short test-medium benchNull -test: test-sniff test-short test-medium benchNull+test-gha: test testCrawl-medium testCrawl-stopAfterGameOver testDefense-medium test-sniff  test-short: test-short-new test-short-load  test-medium: testRaid-medium testBrawl-medium testShootout-medium testHunt-medium testEscape-medium testZoo-medium testAmbush-medium testCrawlEmpty-medium testCrawl-medium-know testSafari-medium testSafariSurvival-medium testBattle-medium testBattleDefense-medium testBattleSurvival-medium testDig-medium testDefenseEmpty-medium testMany-teletype  test-sniff:-	bash -c "./Allure --dbgMsgSer --logPriority 4 --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 1  --dumpInitRngs --automateAll --keepAutomated --gameMode raid --sniff &> /tmp/teletypetest.log"+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 1  --dumpInitRngs --automateAll --keepAutomated --gameMode raid --sniff > /tmp/teletypetest.log 2>&1  testMany-teletype:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 50 --dumpInitRngs --automateAll --keepAutomated 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 50 --dumpInitRngs --automateAll --keepAutomated 2> /tmp/teletypetest.log  testMany-sdlInit:-	./Allure --dbgMsgSer --logPriority 0 --boostRandomItem --newGame 9 --maxFps 100000 --benchmark --stopAfterSeconds 50 --dumpInitRngs --automateAll --keepAutomated 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 0 --boostRandomItem --newGame 9 --maxFps 100000 --benchmark --benchMessages --stopAfterSeconds 50 --dumpInitRngs --automateAll --keepAutomated 2> /tmp/teletypetest.log  testRaid-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log  testBrawl-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log  testShootout-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log  testHunt-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode hunt 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode hunt 2> /tmp/teletypetest.log  testEscape-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log  testZoo-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log  testAmbush-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log  testCrawl-medium:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterGameOver --stopAfterSeconds 350 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --assertExplored 4 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 700 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --assertExplored 4 2> /tmp/teletypetest.log -testCrawl-medium-double:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendLazy --benchmark --stopAfterGameOver --stopAfterSeconds 590 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --assertExplored 4 +testCrawl-stopAfterGameOver:+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --stopAfterGameOver 2> /tmp/teletypetest.log+ testCrawlEmpty-medium:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode crawlEmpty 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode crawlEmpty 2> /tmp/teletypetest.log  testCrawl-medium-know:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix know --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix know --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples 2> /tmp/teletypetest.log  testSafari-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log  testSafariSurvival-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival 2> /tmp/teletypetest.log  testBattle-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log  testBattleDefense-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense 2> /tmp/teletypetest.log  testBattleSurvival-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival 2> /tmp/teletypetest.log  testDefense-medium:-	./Allure --dbgMsgSer --logPriority 4 --newGame 9 --noAnim --maxFps 100000 --frontendLazy --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log--testDig-medium:-	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 100 --dumpInitRngs --automateAll --keepAutomated --gameMode dig 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 9 --noAnim --maxFps 100000 --frontendLazy --benchmark --stopAfterSeconds 500 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log  testDefenseEmpty-medium:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode defenseEmpty 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --benchMessages --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode defenseEmpty 2> /tmp/teletypetest.log +testDig-medium:+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 100 --dumpInitRngs --automateAll --keepAutomated --gameMode dig 2> /tmp/teletypetest.log+ test-short-new:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battle --showItemSamples --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battle --showItemSamples --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log  # $(RNGOPTS) is needed for determinism relative to seed # generated before game save test-short-load:-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log-	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	$$(cabal list-bin exe:Allure) --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log   build-binary-v1:@@ -277,14 +275,11 @@ 	cabal v1-copy --destdir=AllureOfTheStarsInstall  copy-binary:-	cp $$(cabal-plan list-bin Allure) AllureOfTheStars+	cp $$(cabal list-bin --project-file=cabal.project.LH.dir exe:Allure) AllureOfTheStars  configure-binary-v2: 	cabal configure --project-file=cabal.project.LH.dir --disable-tests --disable-library-profiling --disable-profiling --enable-optimization -configure-binary-v2-vty:-	cabal configure -fvty --project-file=cabal.project.LH.dir --disable-tests --disable-library-profiling --disable-profiling --disable-documentation --enable-optimization- build-binary-v2: 	cabal build --project-file=cabal.project.LH.dir --only-dependencies . 	cabal build --project-file=cabal.project.LH.dir exe:Allure@@ -315,10 +310,3 @@ 	LH_VERSION=$$(cat ~/.Allure/stdout.txt); \ 	OS_VERSION=$$(sw_vers -productVersion); \ 	tar -czf Allure_$${LH_VERSION}_macosx-$${OS_VERSION}-amd64.tar.gz AllureOfTheStars--build-directory-vty: configure-binary-v2-vty build-binary-v2 copy-directory copy-binary--build-binary-screen-reader-ubuntu: build-directory-vty-	AllureOfTheStars/Allure --version > /dev/null; \-	LH_VERSION=$$(cat ~/.Allure/stdout.txt); \-	tar -czf Allure_$${LH_VERSION}_screen-reader-ubuntu-16.04-amd64.tar.gz AllureOfTheStars
README.md view
@@ -1,10 +1,9 @@ Allure of the Stars =================== -[![Build Status](https://secure.travis-ci.org/AllureOfTheStars/Allure.svg?master)](https://travis-ci.org/AllureOfTheStars/Allure) [![Hackage](https://img.shields.io/hackage/v/Allure.svg)](https://hackage.haskell.org/package/Allure) [![Join the chat at Discord](https://img.shields.io/discord/688792755564052486.svg?label=chat%20on%20Discord&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/87Ghnws)-[![Join the chat at Matrix](https://img.shields.io/matrix/lambdahack:mozilla.org.svg?label=chat%20on%20Matrix&logo=matrix&server_fqdn=mozilla.modular.im)](https://matrix.to/#/!HnbpAHMjOGHlYtrASl:mozilla.org?via=mozilla.modular.im)+[![Join the chat at Matrix](https://img.shields.io/matrix/lambdahack:mozilla.org.svg?label=chat%20on%20Matrix&logo=matrix&server_fqdn=mozilla.modular.im)](https://matrix.to/#/#lambdahack:mozilla.org)  Allure of the Stars is a near-future Sci-Fi roguelike[2] and tactical squad combat game. Binaries and the game manual@@ -18,8 +17,8 @@ Once you learn to imagine things, though, you can keep exploring and mastering the world and making up stories for a long time. -The game is written in Haskell[1] using the LambdaHack[10]-roguelike game engine.+The game is written in Haskell[1] using the LambdaHack[10] roguelike+game engine. Please see the changelog file for recent improvements and the issue tracker for short-term plans. Long term goals are high replayability and auto-balancing through procedural@@ -40,7 +39,7 @@ you may prefer to use a native binary for your architecture, if it exists.  Pre-compiled game binaries are available through the release page[11]-(and, for Windows, dev versions continuously from AppVeyor[18]).+(and Linux dev versions from GitHub Actions[18] and Windows from AppVeyor[19]). To use a pre-compiled binary archive, unpack it and run the executable in the unpacked directory or use program shortcuts from the installer, if available. On Linux, make sure you have the SDL2 libraries installed@@ -57,9 +56,9 @@ ---------------------------------  The game UI can be configured via a config file.-The default settings, the same that are built into the binary,+The default config settings, the same that are built into the binary, are on github at [GameDefinition/config.ui.default](https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/config.ui.default).-When the game is run for the first time, or whenever the settings file+When the game is run for the first time, or whenever the config file is deleted, the file is written to the default user data location, which is `~/.Allure/` on Linux, `C:\Users\<username>\AppData\Roaming\Allure\`@@ -68,8 +67,8 @@ and `Inspect/Application/Local Storage` under RMB menu when run inside the Chrome browser. If the user config file is outdated or corrupted, it's automatically-moved away together with old savefiles, which guarantees that the new-default config file is ultimately put in its place.+moved away together with old savefiles. At the next game start,+the new default config file appears at its place.  Screen fonts and, consequently, window size can be changed by editing the config file in the user data folder. The default bitmap font@@ -78,8 +77,8 @@ and results in a game window of exactly 720p HD dimensions. The `8x8xb.fnt` bitmap font results in a tiny window and covers latin-1 characters only. The config file parameter `allFontsScale` permits further window size-adjustments, automatically switching to the scalable `16x16xw.woff`-version of the game map font. Config file option `chosenFontset` governs+adjustments, automatically switching to the scalable version of the large+game map font (`16x16xw.woff`). Config file option `chosenFontset` governs not only the main game map font, but also the shape of the rectangular fonts, if any, in which longer texts are overlaid over the map. @@ -89,7 +88,7 @@ too small or, in fullscreen or on retina displays in OS X, the screen may be automatically scaled as a whole, not each letter separately, softening letter edges of the square fonts that should-be pixel-perfect and crisp.+rather be pixel-perfect and crisp.  If you don't have a numeric keypad, you can use the left-hand movement key setup (axwdqezc) or Vi editor keys (aka roguelike keys) or mouse.@@ -100,52 +99,56 @@ may be mouse for menus, go-to, inspecting the map, aiming at distant positions and keyboard for everything else. -If you are using a terminal frontend, e.g. the best supported vty frontend,-then numeric keypad (especially keypad `*` and `/`) may not work correctly,-depending on versions of libraries, terminfo and terminal emulators.-Toggling the Num Lock key may help or make issues worse. As a workaround,-in the vty frontend, numbers are used for movement, which sadly prevents-the number keys from selecting heroes. The commands that require pressing-Control and Shift together won't work either, but fortunately they are-not crucial to gameplay.+If you run the ANSI terminal frontend (`--frontendANSI` on commandline),+then numeric keypad (especially keypad `*`, `/` and `5`) may not work+correctly, depending on the terminal emulator you use. Toggling+the Num Lock key may help or make issues worse. As a work around+these issues, numbers are used for movement in the ANSI frontend,+which sadly prevents the number keys from selecting heroes.+The commands that require pressing Control and Shift together won't work+either, but fortunately they are not crucial to gameplay. -Some effort went into making the vty frontend usable with screen readers,+Some effort went into making the ANSI frontend usable with screen readers, but without feedback it's hard to say how accessible that setup is.+This doesn't work on Windows, due to extra code that would be required. As a side effect of screen reader support, there is no aiming line-nor path in vty frontend and some of map position highlighting+nor path in ANSI frontend and some of map position highlighting is performed using the terminal cursor. Screen readers may also work better with animations turned off, using `--noAnim` or the corresponding-config file option.+config file or main game menu options.   Compiling native binary from source ----------------------------------- -The recommended frontend is based on SDL2, so you need the SDL2 libraries-for your OS. On Linux, remember to install the -dev versions as well,-e.g., libsdl2-dev and libsdl2-ttf-dev on Ubuntu Linux 16.04.-Other frontends are compiled similarly, but compilation to JavaScript-for the browser is more complicated and requires the ghcjs[15] compiler-and optionally the Google Closure Compiler[16].+To compile with the standard frontend based on SDL2, you need the SDL2+libraries for your OS. On Linux, remember to install the -dev versions+as well, e.g., libsdl2-dev and libsdl2-ttf-dev on Ubuntu Linux 16.04.+Compilation to JavaScript for the browser is more complicated+and requires the ghcjs[15] compiler and optionally the Google Closure+Compiler[16].  The latest official version of the game can be downloaded,-compiled for SDL2 and installed automatically using the Cabal tool,-which is already a part of your OS distribution, or available within-The Haskell Platform[7]. Get the game from Hackage[3] as follows+compiled for SDL2 and installed automatically using the 'cabal' tool,+which may already be a part of your OS distribution, but if it's too old+(version 3.4 or later is required) you can download the whole current+compilation suite as described at https://www.haskell.org/downloads/.+Get the Allure of the Stars package from Hackage[3] as follows      cabal update     cabal run Allure  For a newer, unofficial version, clone the game source from github[5],-clone a matching LambdaHack library snapshot into ../LambdaHack-and run `cabal run` from the main directory.+clone a matching LambdaHack library snapshot into ../LambdaHack and run++    cabal run --project-file=cabal.project.LH.dir+ Alternatively, if you'd like to develop in this codebase, the following speeds up the turn-around a lot      cp cabal.project.local.development cabal.project.local-    cabal install cabal-plan -and then compile with+and then you can compile (and recompile) with      cabal build . @@ -153,68 +156,43 @@      make play -There is a built-in black and white line terminal frontend, suitable-for teletype terminals or a keyboard and a printer (but it's going to use-a lot of paper, unless you disable animations with `--noAnim`). It is used-in CI and for some tests and benchmarks defined in Makefile. To compile-with one of the less rudimentary terminal frontends (in which case you are-on your own regarding font choice and color setup and you won't have-the spiffy colorful squares outlining special positions that exist in SDL2-frontend, but only crude cursor highlights), use Cabal flags, e.g,-to switch to the vty console frontend optimized for screen readers, run--    cabal run -fvty Allure+The SDL2 frontend binary also contains the ANSI terminal frontend+(`--frontendANSI` on commandline) intended for screen readers+and a simplified black and white line terminal frontend (`--frontendTeletype`)+suitable for teletype terminals or a keyboard and a printer (but it's going+to use a lot of paper, unless you disable animations with `--noAnim`).+The teletype frontend is used in CI and for some tests and benchmarks defined+in Makefile. The terminal frontends leave you on your own regarding font+choice and color setup and you won't have the colorful squares outlining+special positions that exist in the SDL2 frontend, but only crude+cursor highlights. The terminal frontends should run on Windows,+but Windows disables console for GUI applications, so they don't.   Testing and debugging --------------------- +Integration tests can be run and displayed with++    cabal test test --enable-tests --test-show-details=direct+ The [Makefile](https://github.com/AllureOfTheStars/Allure/blob/master/Makefile)-contains many sample test commands.+contains many sample automated play test commands. Numerous tests that use the screensaver game modes (AI vs. AI)-and the teletype frontend are gathered in `make test`.-Of these, travis runs `test-travis` on each push to github.+and the teletype frontend are gathered in `make test-locally`.+Some of these are run by CI  on each push to github. Test commands with prefix `frontend` start AI vs. AI games with-the standard, user-friendly frontend and auto-locating the game binary.+the standard SDL2 frontend to view them on.  Run `Allure --help` to see a brief description of all debug options. Of these, the `--sniff` option is very useful (though verbose and initially cryptic), for displaying the traffic between clients-and the server. Some options in the config file may prove useful too,-though they mostly overlap with commandline options (and will be totally-merged at some point).+and the server. Some options in the config file may prove useful+for debugging too, though they mostly overlap with commandline options+(and will be totally merged at some point).  -Coding style------------- -Stylish Haskell is used for slight auto-formatting at buffer save; see-[.stylish-haskell.yaml](https://github.com/AllureOfTheStars/Allure/blob/master/.stylish-haskell.yaml).-As defined in the file, indentation is 2 spaces wide and screen is-80-columns wide. Spaces are used, not tabs. Spurious whitespace avoided.-Spaces around arithmetic operators encouraged.-Generally, relax and try to stick to the style apparent in a file-you are editing. Put big formatting changes in separate commits.--Haddocks are provided for all module headers and for all functions and types-from major modules, in particular for the modules that are interfaces-for a whole directory of modules. Apart of that, only very important-functions and types are distinguished by having a haddock.-If minor ones have comments, they should not be haddocks-and they are permitted to describe implementation details and be out of date.-Prefer assertions instead of comments, unless too verbose.--The 'pointman' from game manual and UI is called 'leader' in the source code-and there are a few more mismatches, though the source code naming-and the UI naming should each be consistent in separation.-If the UI names stick, perhaps source code will be renamed at some point.--This codebase is an experiment in extensive use of states without lens.-So far, it works, doesn't result in much larger files or lots-of repetition and has the added benefits that newcomers don't need-to learn any lens library. Record punning, etc., definitely helps.-- Further information ------------------- @@ -223,6 +201,9 @@ [CREDITS](https://github.com/AllureOfTheStars/Allure/blob/master/CREDITS) and [COPYLEFT](https://github.com/AllureOfTheStars/Allure/blob/master/COPYLEFT). +For developer-focused information (coding style, overview of the codebase),+please see the README of LambdaHack[10].+ Have fun!  @@ -256,11 +237,9 @@ [4]: https://github.com/AllureOfTheStars/Allure/wiki [5]: https://github.com/AllureOfTheStars/Allure [6]: http://allureofthestars.com-[7]: https://www.haskell.org/platform-- [10]: https://github.com/LambdaHack/LambdaHack [11]: https://github.com/AllureOfTheStars/Allure/releases [15]: https://github.com/ghcjs/ghcjs [16]: https://www.npmjs.com/package/google-closure-compiler-[18]: https://ci.appveyor.com/project/Mikolaj/allure/build/artifacts+[18]: https://github.com/AllureOfTheStars/Allure/actions+[19]: https://ci.appveyor.com/project/Mikolaj/allure/build/artifacts
+ cabal.project view
@@ -0,0 +1,7 @@+packages: ./++package sdl2+    flags: +no-linear++package splitmix+    flags: +optimised-mixer
+ cabal.project.LH.dir view
@@ -0,0 +1,8 @@+packages: ./+          ../LambdaHack++package sdl2+    flags: +no-linear++package splitmix+    flags: +optimised-mixer
− test/SessionUIMock.hs
@@ -1,148 +0,0 @@-module SessionUIMock-  ( unwindMacros, unwindMacrosAcc-  ) where--import Prelude ()--import Game.LambdaHack.Core.Prelude--import           Control.Monad.Trans.Class-import           Control.Monad.Trans.State.Lazy-import           Control.Monad.Trans.Writer.Lazy-import           Data.Bifunctor (bimap)-import qualified Data.Map.Strict as M--import qualified Game.LambdaHack.Client.UI.Content.Input as IC-import           Game.LambdaHack.Client.UI.ContentClientUI-import           Game.LambdaHack.Client.UI.FrameM-import           Game.LambdaHack.Client.UI.HandleHumanLocalM-import           Game.LambdaHack.Client.UI.HandleHumanM-import qualified Game.LambdaHack.Client.UI.HumanCmd as HumanCmd-import qualified Game.LambdaHack.Client.UI.Key as K-import           Game.LambdaHack.Client.UI.SessionUI-  (KeyMacro (..), KeyMacroFrame (..), emptyMacroFrame)--data SessionUIMock = SessionUIMock-  { smacroFrame :: KeyMacroFrame-  , smacroStack :: [KeyMacroFrame]-  , sccui       :: CCUI-  , unwindTicks :: Int-  }--type KeyMacroBufferMock = Either String String-type KeyPendingMock = String-type KeyLastMock = String--type BufferTrace = [(KeyMacroBufferMock, KeyPendingMock, KeyLastMock)]-type ActionLog = String--data Op = Looped | HeadEmpty--humanCommandMock :: WriterT [(BufferTrace, ActionLog)] (State SessionUIMock) ()-humanCommandMock = do-  abuffs <- lift $ do-    sess <- get-    return $ renderTrace (smacroFrame sess : smacroStack sess)  -- log session-  abortOrCmd <- lift iterationMock -- do stuff-  -- GC macro stack if there are no actions left to handle,-  -- removing all unnecessary macro frames at once,-  -- but leaving the last one for user's in-game macros.-  lift $ modify $ \sess ->-          let (smacroFrameNew, smacroStackMew) =-                dropEmptyMacroFrames (smacroFrame sess) (smacroStack sess)-          in sess { smacroFrame = smacroFrameNew-                  , smacroStack = smacroStackMew }-  case abortOrCmd of-    Left Looped -> tell [(abuffs, "Macro looped")] >> pure ()-    Left HeadEmpty -> tell [(abuffs, "")] >> pure ()  -- exit loop-    Right Nothing -> tell [(abuffs, "")] >> humanCommandMock-    Right (Just out) -> tell [(abuffs, show out)] >> humanCommandMock--iterationMock :: State SessionUIMock (Either Op (Maybe K.KM))-iterationMock = do-  SessionUIMock _ _ CCUI{coinput=IC.InputContent{bcmdMap}} ticks <- get-  if ticks <= 1000-  then do-    modify $ \sess -> sess {unwindTicks = ticks + 1}-    mkm <- promptGetKeyMock-    case mkm of-      Nothing -> return $ Left HeadEmpty  -- macro finished-      Just km -> case km `M.lookup` bcmdMap of-        Just (_, _, cmd) -> Right <$> cmdSemInCxtOfKMMock km cmd-        _ -> return $ Right $ Just km  -- unknown command; fine for tests-  else return $ Left Looped--cmdSemInCxtOfKMMock :: K.KM -> HumanCmd.HumanCmd-                    -> State SessionUIMock (Maybe K.KM)-cmdSemInCxtOfKMMock km cmd = do-  modify $ \sess ->-    sess {smacroFrame = updateKeyLast km cmd $ smacroFrame sess}-  cmdSemanticsMock km cmd--cmdSemanticsMock :: K.KM -> HumanCmd.HumanCmd-                 -> State SessionUIMock (Maybe K.KM)-cmdSemanticsMock km = \case-  HumanCmd.Macro s -> do-    modify $ \sess ->-      let kms = K.mkKM <$> s-          (smacroFrameNew, smacroStackMew) =-             macroHumanTransition kms (smacroFrame sess) (smacroStack sess)-      in sess { smacroFrame = smacroFrameNew-              , smacroStack = smacroStackMew }-    return Nothing-  HumanCmd.Repeat n -> do-    modify $ \sess ->-      let (smacroFrameNew, smacroStackMew) =-             repeatHumanTransition n (smacroFrame sess) (smacroStack sess)-      in sess { smacroFrame = smacroFrameNew-              , smacroStack = smacroStackMew }-    return Nothing-  HumanCmd.RepeatLast n -> do-    modify $ \sess ->-      sess {smacroFrame = repeatLastHumanTransition n (smacroFrame sess) }-    return Nothing-  HumanCmd.Record -> do-    modify $ \sess ->-      sess {smacroFrame = fst $ recordHumanTransition (smacroFrame sess) }-    return Nothing-  _ -> return $ Just km--promptGetKeyMock :: State SessionUIMock (Maybe K.KM)-promptGetKeyMock = do-  SessionUIMock macroFrame _ CCUI{coinput=IC.InputContent{bcmdMap}} _ <- get-  case keyPending macroFrame of-    KeyMacro (km : kms) -> do-        modify $ \sess ->-          sess {smacroFrame = (smacroFrame sess) {keyPending = KeyMacro kms}}-        modify $ \sess ->-          sess {smacroFrame = addToMacro bcmdMap km $ smacroFrame sess}-        return (Just km)-    KeyMacro [] -> return Nothing--unwindMacros :: IC.InputContent -> KeyMacro -> [(BufferTrace, ActionLog)]-unwindMacros coinput keyPending =-  let initSession = SessionUIMock-        { smacroFrame = emptyMacroFrame {keyPending}-        , smacroStack = []-        , sccui = emptyCCUI {coinput}-        , unwindTicks = 0 }-  in evalState (execWriterT humanCommandMock) initSession--accumulateActions :: [(BufferTrace, ActionLog)] -> [(BufferTrace, ActionLog)]-accumulateActions ba =-  let (buffers, actions) = unzip ba-      actionlog = concat <$> inits actions-  in zip buffers actionlog--unwindMacrosAcc :: IC.InputContent -> KeyMacro -> [(BufferTrace, ActionLog)]-unwindMacrosAcc coinput keyPending =-  accumulateActions $ unwindMacros coinput keyPending--renderTrace :: [KeyMacroFrame] -> BufferTrace-renderTrace macroFrames =-  let buffers = bimap (concatMap K.showKM)-                      (concatMap K.showKM . unKeyMacro)-                . keyMacroBuffer <$> macroFrames-      pendingKeys = concatMap K.showKM . unKeyMacro . keyPending <$> macroFrames-      lastKeys = maybe "" K.showKM . keyLast <$> macroFrames-  in zip3 buffers pendingKeys lastKeys
test/Spec.hs view
@@ -5,18 +5,12 @@  import Game.LambdaHack.Core.Prelude -import qualified Data.Map.Strict as M import qualified Data.Text as T import           Options.Applicative import           System.IO.Unsafe (unsafePerformIO) import           Test.Tasty import           Test.Tasty.HUnit -import qualified Game.LambdaHack.Client.UI.Content.Input as IC-import           Game.LambdaHack.Client.UI.Frontend.Chosen-import qualified Game.LambdaHack.Client.UI.HumanCmd as HumanCmd-import qualified Game.LambdaHack.Client.UI.Key as K-import           Game.LambdaHack.Client.UI.SessionUI import           Game.LambdaHack.Client.UI.UIOptions import           Game.LambdaHack.Client.UI.UIOptionsParse import           Game.LambdaHack.Common.ClientOptions@@ -24,276 +18,14 @@ import           Game.LambdaHack.Server  -- For the case of flattened .cabal file, to avoid ambiguity.-import qualified "Allure" Client.UI.Content.Input as Content.Input import qualified "Allure" Content.RuleKind import           "Allure" TieKnot -import SessionUIMock- main :: IO () main = defaultMain tests  tests :: TestTree-tests = testGroup "Tests" [macroTests, integrationTests]--macroTests :: TestTree-macroTests = testGroup "macroTests" $-  let coinput = IC.makeData Nothing Content.Input.standardKeysAndMouse-      stringToKeyMacro = KeyMacro . map (K.mkKM . (: []))-      listToKeyMacro = KeyMacro . map K.mkKM-      bindInput l input =-        let ltriple = M.fromList $ map (\(k, ks) ->-              (K.mkKM k, ([], "", HumanCmd.Macro $ map (: []) ks))) l-        in input {IC.bcmdMap = M.union ltriple $ IC.bcmdMap input}-  in [ testCase "Macro 1 from PR#192 description" $-         fst <$> unwindMacros coinput (stringToKeyMacro "'j''j'")-         @?= [ [ (Right "",     "'j''j'",  "")  ]-             , [ (Left  "",     "j''j'",   "") ]-             , [ (Left  "j",    "''j'",    "j") ]-             , [ (Right "j",    "'j'",     "j") ]-             , [ (Left  "",     "j'",      "j") ]-             , [ (Left  "j",    "'",       "j") ]-             , [ (Right "j",    "",        "j") ]-             ]-     , testCase "Macro 1 from Issue#189 description" $-         snd (last (unwindMacros (bindInput [ ("a", "'bc'V")-                                            , ("c", "'aaa'V") ] coinput)-                                 (stringToKeyMacro "a")))-         @?= "Macro looped"-     , testCase "Macro 2 from Issue#189 description" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'")] coinput)-                                    (stringToKeyMacro "'a'")))-         @?= "x"-     , testCase "Macro 3 from Issue#189 description" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "'x''x'")))-         @?= "xx"-     , testCase "Macro 4 from Issue#189 description" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "'x''x'V")))-         @?= "xxx"-     , testCase "Macro 5 from Issue#189 description" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "x'x'V")))-         @?= "xxx"-     , testCase "Macro test 10" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "x'y'V")))-         @?= "xyy"-     , testCase "Macro test 11" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "'x''y'V")))-         @?= "xyy"-     , testCase "Macro test 12" $-         snd (last (unwindMacrosAcc coinput-                                    (listToKeyMacro ["x", "C-V"])))-         @?= "x"-     , testCase "Macro test 13" $-         snd (last (unwindMacrosAcc coinput-                                    (listToKeyMacro ["'", "x", "'", "C-V"])))-         @?= "xxxxxxxxxxxxxxxxxxxxxxxxxx"-     , testCase "Macro test 14" $-         snd (last (unwindMacrosAcc coinput-                                    (listToKeyMacro-                                       ["'", "x", "'", "y", "C-V"])))-         @?= "xyxxxxxxxxxxxxxxxxxxxxxxxxx"-     , testCase "Macro test 15" $-         snd (last (unwindMacrosAcc (bindInput [("a", "x")] coinput)-                                    (stringToKeyMacro "'a'V")))-         @?= "xx"-     , testCase "Macro test 16" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'")] coinput)-                                    (stringToKeyMacro "'a'V")))-         @?= "xx"-     , testCase "Macro test 17" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)-                                    (stringToKeyMacro "a")))-         @?= "xx"-     , testCase "Macro test 18" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)-                                    (stringToKeyMacro "'a'")))-         @?= "xx"-     , testCase "Macro test 19" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)-                                    (stringToKeyMacro "'a'V")))-         @?= "xxxx"-     , testCase "Macro test 20" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'bz'V")-                                               , ("c", "'aaa'V") ] coinput)-                                    (stringToKeyMacro "c")))-         @?= "bzbzbzbzbzbzbzbzbzbzbzbz"-     , testCase "RepeatLast test 10" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "x'y'v")))-         @?= "xyy"-     , testCase "RepeatLast test 11" $-         snd (last (unwindMacrosAcc coinput-                                    (stringToKeyMacro "'x'yv")))-         @?= "xyy"-     , testCase "RepeatLast test 12" $-         snd (last (unwindMacrosAcc coinput-                                    (listToKeyMacro ["v", "C-v"])))-         @?= ""-     , testCase "RepeatLast test 13" $-         snd (last (unwindMacrosAcc coinput-                                    (listToKeyMacro ["'", "x", "'", "C-v"])))-         @?= "xxxxxxxxxxxxxxxxxxxxxxxxxx"-     , testCase "RepeatLast test 14" $-         snd (last (unwindMacrosAcc coinput-                                    (listToKeyMacro-                                       ["'", "x", "'", "V", "C-v"])))-         @?= "xxxxxxxxxxxxxxxxxxxxxxxxxxx"-     , testCase "RepeatLast test 15" $-         snd (last (unwindMacrosAcc (bindInput [("a", "x")] coinput)-                                    (stringToKeyMacro "av")))-         @?= "xx"-     , testCase "RepeatLast test 16" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'")] coinput)-                                    (stringToKeyMacro "'a'v")))-         @?= "xx"-     , testCase "RepeatLast test 17" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'v")] coinput)-                                    (stringToKeyMacro "a")))-         @?= "xx"-     , testCase "RepeatLast test 18" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'v")] coinput)-                                    (stringToKeyMacro "'a'")))-         @?= "xx"-     , testCase "RepeatLast test 19" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'v")] coinput)-                                    (stringToKeyMacro "'a'v")))-         @?= "xxxx"-     , testCase "RepeatLast test 20" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'bz'v")-                                         , ("c", "'aaa'v") ] coinput)-                                    (stringToKeyMacro "c")))-         @?= "bzzbzzbzzbzz"-     , testCase "RepeatLast test 21" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)-                                    (stringToKeyMacro "'a'v")))-         @?= "xxxx"-     , testCase "RepeatLast test 22" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'V")] coinput)-                                    (stringToKeyMacro "'aa'v")))-         @?= "xyxyxyxyxyxy"-     , testCase "RepeatLast test 23" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'v")] coinput)-                                    (stringToKeyMacro "'aa'V")))-         @?= "xyyxyyxyyxyy"-     , testCase "RepeatLast test 24" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'vv")] coinput)-                                    (stringToKeyMacro "'aa'vv")))-         @?= "xyyyxyyyxyyyxyyy"-     , testCase "RepeatLast test 25" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'xyv'v")] coinput)-                                    (stringToKeyMacro "'a'a'vv'")))-         @?= "xyyyxyyyxyyyxyyy"-     , testCase "RepeatLast test 26" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'v")-                                               , ("c", "'ab'v") ] coinput)-                                    (stringToKeyMacro "'c'v")))-         @?= "xyyzxyyxyyzxyyxyyxyyzxyyxyyzxyyxyy"-     , testCase "RepeatLast test 27" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'V")-                                               , ("b", "'za'v")-                                               , ("c", "'ab'v") ] coinput)-                                    (stringToKeyMacro "'c'v")))-         @?= "xyxyzxyxyxyxyzxyxyxyxyxyxyzxyxyxyxyzxyxyxyxy"-     , testCase "RepeatLast test 28" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'V")-                                               , ("c", "'ab'v") ] coinput)-                                    (stringToKeyMacro "'c'v")))-         @?= "xyyzxyyzxyyzxyyzxyyxyyzxyyzxyyzxyyzxyy"-     , testCase "RepeatLast test 29" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'V")-                                               , ("c", "'ab'V") ] coinput)-                                    (stringToKeyMacro "'c'v")))-         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"-     , testCase "RepeatLast test 30" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'V")-                                               , ("c", "'ab'V") ] coinput)-                                    (stringToKeyMacro "'c'V")))-         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"-     , testCase "RepeatLast test 31" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'v")-                                               , ("c", "'ab'V") ] coinput)-                                    (stringToKeyMacro "'c'V")))-         @?= "xyyzxyyxyyxyyzxyyxyyxyyzxyyxyyxyyzxyyxyy"-     , testCase "RepeatLast test 32" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'v") ] coinput)-                                    (stringToKeyMacro "'ab'vv")))-         @?= "xyyzxyyxyyzxyyxyyzxyyxyy"-     , testCase "RepeatLast test 33" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'V") ] coinput)-                                    (stringToKeyMacro "a'za'vvv")))-         @?= "xyxyzxyxyxyxyxyxyxyxy"-     , testCase "RepeatLast test 34" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("c", "a'za'Vv") ] coinput)-                                    (stringToKeyMacro "'c'v")))-         @?= "xyyzxyyzxyyzxyyxyyzxyyzxyyzxyy"-     , testCase "RepeatLast test 35" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "'za'V") ] coinput)-                                    (stringToKeyMacro "'ab'Vv")))-         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"-     , testCase "RepeatLast test 36" $-         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")-                                               , ("b", "za'za'") ] coinput)-                                    (stringToKeyMacro "'ab'V'ab'V")))-         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"-     , testCase "RepeatLast test 37" $-         snd (last (unwindMacrosAcc (bindInput [ ("b", "z'xy'vv")-                                               , ("c", "'xyvb'V") ] coinput)-                                    (stringToKeyMacro "'c'V")))-         @?= "xyyzxyyyxyyzxyyyxyyzxyyyxyyzxyyy"-     , testCase "RepeatLast test 38" $-         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xv'V")))-         @?= "xxxx"-     , testCase "RepeatLast test 39" $-         fst <$> unwindMacros coinput (stringToKeyMacro "'xv'V")-         @?= [[(Right   "", "'xv'V",  "")],-              [(Left    "",  "xv'V",  "")],-              [(Left   "x",   "v'V", "x")],-              [(Left   "x",   "x'V", "x")],-              [(Left  "xx",    "'V", "x")],-              [(Right "xx",     "V", "x")],-              [(Right   "",    "xx",  ""), (Right "xx",    "", "V")],-              [(Right   "",     "x", "x"), (Right "xx",    "", "V")],-              [(Right "xx",      "", "V")]]-     , testCase "RepeatLast test 40" $-         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xy'Vv")))-         @?= "xyxyxy"-     , testCase-         "RepeatLast test 41; named macros not referentially transparent" $-         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'V")] coinput)-                                    (stringToKeyMacro "av")))-         @?= "xyxyxyxy"  -- because @a@ repeated; good!-     , testCase "RepeatLast test 42" $-         snd (last (unwindMacrosAcc (bindInput [("a", "xy")] coinput)-                                    (stringToKeyMacro "'a'Vv")))-         @?= "xyxyxy"  -- because @V@ repeated; good!-     , testCase "RepeatLast test 43" $-         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xyV'V")))-         @?= "xyxy"-     , testCase "RepeatLast test 44" $-         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xyV'v")))-         @?= "xyxy"-     , testCase "RepeatLast test 45" $-         snd (last (unwindMacrosAcc (bindInput [("a", "xyV")] coinput)-                                    (stringToKeyMacro "'a'V")))-         @?= "xyxy"-     , testCase "RepeatLast test 46" $-         snd (last (unwindMacrosAcc (bindInput [("a", "xyV")] coinput)-                                    (stringToKeyMacro "'a'v")))-         @?= "xyxy"-     ]+tests = testGroup "Tests" [integrationTests]  integrationTests :: TestTree integrationTests = testGroup "integrationTests" $@@ -304,23 +36,22 @@       serverOptions <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args       tieKnot serverOptions   ]+#ifndef USE_BROWSER   ++   let corule = RK.makeData Content.RuleKind.standardRules       uiOptions = unsafePerformIO $ mkUIOptions corule defClientOptions       testFontset :: (Int, String) -> TestTree       testFontset (n, fontsetName) =-        testCase ("SDL fronted; init only; " ++ fontsetName ++ " fontset") $-          when (frontendName == "sdl") $ do-            -- This test only works when run from the same directory that-            -- the .cabal files is in. And this is what Debian needs, so OK.-            -- The hacky log priority 0 tells SDL frontend to init-            -- and quit at once, for testing on CIs without graphics access.-            let seed = "SMGen " ++ show (13 + 2 * n) ++ " " ++ show (15 + 4 * n)-                args2 = words "--dbgMsgSer --logPriority 0 --newGame 3 --maxFps 100000 --benchmark --stopAfterFrames 5 --automateAll --keepAutomated --gameMode battle"-                        ++ [ "--setDungeonRng", seed, "--setMainRng", seed-                           , "--fontset", fontsetName ]-            serverOptions2 <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args2-            tieKnot serverOptions2-      isUbuntu = ("ubuntu" `T.isPrefixOf`)-  in map testFontset $ zip [0..] $ map T.unpack $ filter (not . isUbuntu)-     $ map fst $ uFontsets uiOptions+        testCase ("SDL fronted; init only; " ++ fontsetName ++ " fontset") $ do+          -- This test only works when run from the same directory that+          -- the .cabal file is in. And this is what Debian needs, so OK.+          -- The hacky log priority 0 tells SDL frontend to init+          -- and quit at once, for testing on CIs without graphics access.+          let seed = "SMGen " ++ show (13 + 2 * n) ++ " " ++ show (15 + 4 * n)+              args2 = words "--dbgMsgSer --logPriority 0 --newGame 3 --maxFps 100000 --benchmark --stopAfterFrames 5 --automateAll --keepAutomated --gameMode battle"+                      ++ [ "--setDungeonRng", seed, "--setMainRng", seed+                         , "--fontset", fontsetName ]+          serverOptions2 <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args2+          tieKnot serverOptions2+  in map testFontset $ zip [0..] $ map T.unpack $ map fst $ uFontsets uiOptions+#endif