packages feed

Allure 0.6.2.0 → 0.7.0.0

raw patch · 20 files changed

+678/−598 lines, 20 filesdep +optparse-applicativedep ~LambdaHack

Dependencies added: optparse-applicative

Dependency ranges changed: LambdaHack

Files

Allure.cabal view
@@ -5,7 +5,7 @@ -- PVP summary:+-+------- breaking API changes --             | | +----- minor or non-breaking API additions --             | | | +--- code changes with no API change-version:       0.6.2.0+version:       0.7.0.0 synopsis:      Near-future Sci-Fi roguelike and tactical squad game description:   Allure of the Stars                is a near-future Sci-Fi roguelike and tactical squad game.@@ -29,7 +29,7 @@ bug-reports:   http://github.com/AllureOfTheStars/Allure/issues license:       AGPL-3 license-file:  LICENSE-tested-with:   GHC >= 8.0 && <= 8.2+tested-with:   GHC==8.0.2, GHC==8.2.1, GHC==8.2.2 data-files:    GameDefinition/config.ui.default,                GameDefinition/fonts/16x16x.fon,                GameDefinition/fonts/8x8xb.fon,@@ -72,7 +72,7 @@                       Content.TileKind,                       TieKnot,                       Paths_Allure-  build-depends:      LambdaHack >= 0.6.2.0 && < 0.6.3.0,+  build-depends:      LambdaHack >= 0.7.0.0 && < 0.7.1.0,                       template-haskell >= 2.6,                        async      >= 2,@@ -80,15 +80,17 @@                       containers >= 0.5.3.0,                       enummapset-th >= 0.6.0.0,                       filepath   >= 1.2.0.1,+                      optparse-applicative >= 0.13,                       random     >= 1.1,                       text       >= 0.11.2.3    default-language:   Haskell2010   default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings                       BangPatterns, RecordWildCards, NamedFieldPuns, MultiWayIf,-                      StrictData+                      LambdaCase, StrictData   other-extensions:   TemplateHaskell-  ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities+  ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints+  ghc-options:        -Wall-missed-specialisations   ghc-options:        -fno-ignore-asserts -fexpose-all-unfoldings -fspecialise-aggressively   ghc-options:        -threaded -rtsopts @@ -133,15 +135,16 @@                       containers >= 0.5.3.0,                       enummapset-th >= 0.6.0.0,                       filepath   >= 1.2.0.1,+                      optparse-applicative >= 0.13,                       random     >= 1.1,                       text       >= 0.11.2.3    default-language:   Haskell2010   default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings                       BangPatterns, RecordWildCards, NamedFieldPuns, MultiWayIf,-                      StrictData+                      LambdaCase, StrictData   other-extensions:   TemplateHaskell-  ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities+  ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints   ghc-options:        -fno-ignore-asserts -fexpose-all-unfoldings -fspecialise-aggressively   ghc-options:        -threaded -rtsopts 
CHANGELOG.md view
@@ -1,5 +1,24 @@-## [v0.6.2.0, aka 'Zoom out'](https://github.com/LambdaHack/LambdaHack/compare/v0.6.1.0...v0.6.2.0)+## [v0.7.0.0, aka 'The dice are cast'](https://github.com/AllureOfTheStars/Allure/compare/v0.6.2.0...v0.7.0.0) +- decouple tile searching from tile alteration+- refrain from identifying items that are not randomized+- switch away from incapacitated leader to let others revive him+- make rescue easier by not going into negative HP the first time+- fix crowd of friends on another level slowing even actors that melee+- fix missing report about items underneath an actor when changing levels+- API breakage: change the syntax of dice in content+- API addition: introduce cave descriptions+- keep all client states in the server and optimize communication with clients+- improve item choice for identification and item polymorphing+- reset embedded items when altering tile+- replace atomic command filtering with exception catching+- reimplement dice as symbolic expressions inducing multiple RNG calls+- switch to optparse-applicative and rewrite cli handling+- add stack and cabal new-build project files+- improve haddocks across the codebase++## [v0.6.2.0, aka 'Zoom out'](https://github.com/AllureOfTheStars/Allure/compare/v0.6.1.0...v0.6.2.0)+ - make fireworks slower and so easier to spot - make rattlesnake deeper but more common - announce no effect of activation@@ -18,7 +37,7 @@ - help players find the info about changing the font size - depend on GHC >= 8.0 and new vector -## [v0.6.1.0, aka 'Breaking one rule at a time'](https://github.com/LambdaHack/LambdaHack/compare/v0.6.0.0...v0.6.1.0)+## [v0.6.1.0, aka 'Breaking one rule at a time'](https://github.com/AllureOfTheStars/Allure/compare/v0.6.0.0...v0.6.1.0)  - major engine bugfix: fix redrawing after window minimized and restored - major engine bugfix: hack around vanishing texture on Windows
CREDITS view
@@ -4,6 +4,8 @@ Andres Loeh Mikolaj Konarski Krzysztof Pławski++ Fonts 16x16x.fon, 8x8x.fon and 8x8xb.fon are are taken from https://github.com/angband/angband, copyrighted by Leon Marrick, Sheldon Simms III and Nick McConnell and released by them under
GameDefinition/Client/UI/Content/KeyKind.hs view
@@ -13,9 +13,9 @@  import Game.LambdaHack.Common.Prelude -import Game.LambdaHack.Client.UI.Content.KeyKind-import Game.LambdaHack.Client.UI.HumanCmd-import Game.LambdaHack.Common.Misc+import           Game.LambdaHack.Client.UI.Content.KeyKind+import           Game.LambdaHack.Client.UI.HumanCmd+import           Game.LambdaHack.Common.Misc import qualified Game.LambdaHack.Content.TileKind as TK  -- | Description of default key-command bindings.@@ -23,233 +23,231 @@ -- In addition to these commands, mouse and keys have a standard meaning -- when navigating various menus. standardKeys :: KeyKind-standardKeys = KeyKind-  { rhumanCommands = map evalKeyDef $-      -- All commands are defined here, except some movement and leader picking-      -- commands. All commands are shown on help screens except debug commands-      -- and macros with empty descriptions.-      -- The order below determines the order on the help screens.-      -- Remember to put commands that show information (e.g., enter aiming-      -- mode) first.+standardKeys = KeyKind $ map evalKeyDef $+  -- All commands are defined here, except some movement and leader picking+  -- commands. All commands are shown on help screens except debug commands+  -- and macros with empty descriptions.+  -- The order below determines the order on the help screens.+  -- Remember to put commands that show information (e.g., enter aiming+  -- mode) first. -      -- Main Menu-      [ ("c", ([CmdMainMenu], "enter challenges menu>", ChallengesMenu))-      , ("n", ([CmdMainMenu], "start new game", GameRestart))-      , ("x", ([CmdMainMenu], "save and exit", GameExit))-      , ("m", ([CmdMainMenu], "enter settings menu>", SettingsMenu))-      , ("a", ([CmdMainMenu], "automate faction", Automate))-      , ("?", ([CmdMainMenu], "see command Help", Help))-      , ("Escape", ([CmdMainMenu], "back to playing", Cancel))+  -- Main Menu+  [ ("e", ([CmdMainMenu], "enter challenges menu>", ChallengesMenu))+  , ("s", ([CmdMainMenu], "start new game", GameRestart))+  , ("x", ([CmdMainMenu], "exit to desktop", GameExit))+  , ("v", ([CmdMainMenu], "visit settings menu>", SettingsMenu))+  , ("a", ([CmdMainMenu], "automate faction", Automate))+  , ("?", ([CmdMainMenu], "see command Help", Help))+  , ("Escape", ([CmdMainMenu], "back to playing", Cancel)) -      -- Item use, 1st part-      , ("g", addCmdCategory CmdMinimal $ grabItems "grab item(s)")-      , ("comma", grabItems "")-      , ("d", dropItems "drop item(s)")-      , ("period", dropItems "")-      , ("f", addCmdCategory CmdItemMenu $ projectA flingTs)-      , ("C-f", addCmdCategory CmdItemMenu-                $ replaceDesc "fling without aiming" $ projectI flingTs)-      , ("a", addCmdCategory CmdItemMenu $ applyI [ApplyItem-                { verb = "apply"-                , object = "consumable"-                , symbol = ' ' }])-      , ("C-a", addCmdCategory CmdItemMenu-                $ replaceDesc "apply and keep choice" $ applyIK [ApplyItem-                  { verb = "apply"-                  , object = "consumable"-                  , symbol = ' ' }])+  -- Item use, 1st part+  , ("g", addCmdCategory CmdMinimal $ grabItems "grab item(s)")+  , ("comma", grabItems "")+  , ("d", dropItems "drop item(s)")+  , ("period", dropItems "")+  , ("f", addCmdCategory CmdItemMenu $ projectA flingTs)+  , ("C-f", addCmdCategory CmdItemMenu+            $ replaceDesc "fling without aiming" $ projectI flingTs)+  , ("a", addCmdCategory CmdItemMenu $ applyI [ApplyItem+            { verb = "apply"+            , object = "consumable"+            , symbol = ' ' }])+  , ("C-a", addCmdCategory CmdItemMenu+            $ replaceDesc "apply and keep choice" $ applyIK [ApplyItem+              { verb = "apply"+              , object = "consumable"+              , symbol = ' ' }]) -      -- Terrain exploration and alteration-      , ("semicolon", ( [CmdMove]-                      , "go to x-hair for 25 steps"-                      , Macro ["C-semicolon", "C-/", "C-V"] ))-      , ("colon", ( [CmdMove]-                  , "run to x-hair collectively for 25 steps"-                  , Macro ["C-colon", "C-/", "C-V"] ))-      , ("x", ( [CmdMove]-              , "explore nearest unknown spot"-              , autoexploreCmd ))-      , ("X", ( [CmdMove]-              , "autoexplore 25 times"-              , autoexplore25Cmd ))-      , ("R", ([CmdMove], "rest (wait 25 times)", Macro ["KP_5", "C-V"]))-      , ("C-R", ( [CmdMove], "lurk (wait 0.1 turns 100 times)"-                , Macro ["C-KP_5", "V"] ))-      , ("c", ( [CmdMove, CmdMinimal]-              , descTs closeDoorTriggers-              , AlterDir closeDoorTriggers ))+  -- Terrain exploration and alteration+  , ("semicolon", ( [CmdMove]+                  , "go to x-hair for 25 steps"+                  , Macro ["C-semicolon", "C-/", "C-V"] ))+  , ("colon", ( [CmdMove]+              , "run to x-hair collectively for 25 steps"+              , Macro ["C-colon", "C-/", "C-V"] ))+  , ("x", ( [CmdMove]+          , "explore nearest unknown spot"+          , autoexploreCmd ))+  , ("X", ( [CmdMove]+          , "autoexplore 25 times"+          , autoexplore25Cmd ))+  , ("R", ([CmdMove], "rest (wait 25 times)", Macro ["KP_5", "C-V"]))+  , ("C-R", ( [CmdMove], "lurk (wait 0.1 turns 100 times)"+            , Macro ["C-KP_5", "V"] ))+  , ("c", ( [CmdMove, CmdMinimal]+          , descTs closeDoorTriggers+          , AlterDir closeDoorTriggers )) -      -- Item use, continued-      , ("^", ( [CmdItem], "sort items by kind and stats", SortSlots))-      , ("p", moveItemTriple [CGround, CEqp, CSha] CInv-                             "item" False)-      , ("e", moveItemTriple [CGround, CInv, CSha] CEqp-                             "item" False)-      , ("s", moveItemTriple [CGround, CInv, CEqp] CSha-                             "and share item" False)-      , ("P", ( [CmdMinimal, CmdItem]-              , "manage item pack of the leader"-              , ChooseItemMenu (MStore CInv) ))-      , ("G", ( [CmdItem]-              , "manage items on the ground"-              , ChooseItemMenu (MStore CGround) ))-      , ("E", ( [CmdItem]-              , "manage equipment of the leader"-              , ChooseItemMenu (MStore CEqp) ))-      , ("S", ( [CmdItem]-              , "manage the shared party stash"-              , ChooseItemMenu (MStore CSha) ))-      , ("A", ( [CmdItem]-              , "manage all owned items"-              , ChooseItemMenu MOwned ))-      , ("@", ( [CmdItem]-              , "describe organs of the leader"-              , ChooseItemMenu (MStore COrgan) ))-      , ("#", ( [CmdItem]-              , "show stat summary of the leader"-              , ChooseItemMenu MStats ))-      , ("~", ( [CmdItem]-              , "display known lore"-              , ChooseItemMenu MLoreItem ))-      , ("q", addCmdCategory CmdItem $ applyI [ApplyItem-                { verb = "quaff"-                , object = "drink"-                , symbol = '!' }])-      , ("r", addCmdCategory CmdItem $ applyI [ApplyItem-                { verb = "read"-                , object = "chip"-                , symbol = '?' }])+  -- Item use, continued+  , ("^", ( [CmdItem], "sort items by kind and stats", SortSlots))+  , ("p", moveItemTriple [CGround, CEqp, CSha] CInv+                         "item" False)+  , ("e", moveItemTriple [CGround, CInv, CSha] CEqp+                         "item" False)+  , ("s", moveItemTriple [CGround, CInv, CEqp] CSha+                         "and share item" False)+  , ("P", ( [CmdMinimal, CmdItem]+          , "manage item pack of the leader"+          , ChooseItemMenu (MStore CInv) ))+  , ("G", ( [CmdItem]+          , "manage items on the ground"+          , ChooseItemMenu (MStore CGround) ))+  , ("E", ( [CmdItem]+          , "manage equipment of the leader"+          , ChooseItemMenu (MStore CEqp) ))+  , ("S", ( [CmdItem]+          , "manage the shared party stash"+          , ChooseItemMenu (MStore CSha) ))+  , ("A", ( [CmdItem]+          , "manage all owned items"+          , ChooseItemMenu MOwned ))+  , ("@", ( [CmdItem]+          , "describe organs of the leader"+          , ChooseItemMenu (MStore COrgan) ))+  , ("#", ( [CmdItem]+          , "show stat summary of the leader"+          , ChooseItemMenu MStats ))+  , ("~", ( [CmdItem]+          , "display known lore"+          , ChooseItemMenu MLoreItem ))+  , ("q", addCmdCategory CmdItem $ applyI [ApplyItem+            { verb = "quaff"+            , object = "drink"+            , symbol = '!' }])+  , ("r", addCmdCategory CmdItem $ applyI [ApplyItem+            { verb = "read"+            , object = "chip"+            , symbol = '?' }]) -      , ("t", addCmdCategory CmdItem $ projectA-                [ ApplyItem { verb = "throw"-                            , object = "missile"-                            , symbol = '{' }-                , ApplyItem { verb = "throw"-                            , object = "missile"-                            , symbol = '}' } ])---      , ("z", projectA [ApplyItem { verb = "zap"---                                  , object = "wand"---                                  , symbol = '-' }])+  , ("t", addCmdCategory CmdItem $ projectA+            [ ApplyItem { verb = "throw"+                        , object = "missile"+                        , symbol = '{' }+            , ApplyItem { verb = "throw"+                        , object = "missile"+                        , symbol = '}' } ])+--  , ("z", projectA [ApplyItem { verb = "zap"+--                              , object = "wand"+--                              , symbol = '-' }]) -      -- Aiming-      , ("KP_Multiply", ( [CmdAim, CmdMinimal]-                        , "cycle x-hair among enemies", AimEnemy ))-          -- not really minimal, because flinging from Item Menu enters aiming-          -- mode, first screen mentions aiming mode not in fling context-      , ("!", ([CmdAim], "", AimEnemy))-      , ("KP_Divide", ([CmdAim], "cycle x-hair among items", AimItem))-      , ("/", ([CmdAim], "", AimItem))-      , ("\\", ([CmdAim], "cycle aiming modes", AimFloor))-      , ("+", ([CmdAim, CmdMinimal], "swerve the aiming line", EpsIncr True))-      , ("-", ([CmdAim], "unswerve the aiming line", EpsIncr False))-      , ("C-?", ( [CmdAim]-                , "set x-hair to nearest unknown spot"-                , XhairUnknown ))-      , ("C-I", ( [CmdAim]-                , "set x-hair to nearest item"-                , XhairItem ))-      , ("C-{", ( [CmdAim]-                , "set x-hair to nearest upstairs"-                , XhairStair True ))-      , ("C-}", ( [CmdAim]-                , "set x-hair to nearest downstairs"-                , XhairStair False ))-      , ("<", ([CmdAim], "move aiming one level higher" , AimAscend 1))-      , ("C-<", ( [CmdNoHelp], "move aiming 10 levels higher"-                , AimAscend 10) )-      , (">", ([CmdAim], "move aiming one level lower", AimAscend (-1)))-      , ("C->", ( [CmdNoHelp], "move aiming 10 levels lower"-                , AimAscend (-10)) )-      , ("BackSpace" , ( [CmdAim]-                     , "clear chosen item and target"-                     , ComposeUnlessError ItemClear TgtClear ))-      , ("Escape", ( [CmdAim, CmdMinimal]-                   , "cancel aiming/open Main Menu"-                   , ByAimMode {exploration = MainMenu, aiming = Cancel} ))-      , ("Return", ( [CmdAim, CmdMinimal]-                   , "accept target/open Help"-                   , ByAimMode {exploration = Help, aiming = Accept} ))+  -- Aiming+  , ("KP_Multiply", ( [CmdAim, CmdMinimal]+                    , "cycle x-hair among enemies", AimEnemy ))+      -- not really minimal, because flinging from Item Menu enters aiming+      -- mode, first screen mentions aiming mode not in fling context+  , ("!", ([CmdAim], "", AimEnemy))+  , ("KP_Divide", ([CmdAim], "cycle x-hair among items", AimItem))+  , ("/", ([CmdAim], "", AimItem))+  , ("\\", ([CmdAim], "cycle aiming modes", AimFloor))+  , ("+", ([CmdAim, CmdMinimal], "swerve the aiming line", EpsIncr True))+  , ("-", ([CmdAim], "unswerve the aiming line", EpsIncr False))+  , ("C-?", ( [CmdAim]+            , "set x-hair to nearest unknown spot"+            , XhairUnknown ))+  , ("C-I", ( [CmdAim]+            , "set x-hair to nearest item"+            , XhairItem ))+  , ("C-{", ( [CmdAim]+            , "set x-hair to nearest upstairs"+            , XhairStair True ))+  , ("C-}", ( [CmdAim]+            , "set x-hair to nearest downstairs"+            , XhairStair False ))+  , ("<", ([CmdAim], "move aiming one level higher" , AimAscend 1))+  , ("C-<", ( [CmdNoHelp], "move aiming 10 levels higher"+            , AimAscend 10) )+  , (">", ([CmdAim], "move aiming one level lower", AimAscend (-1)))+  , ("C->", ( [CmdNoHelp], "move aiming 10 levels lower"+            , AimAscend (-10)) )+  , ("BackSpace" , ( [CmdAim]+                 , "clear chosen item and target"+                 , ComposeUnlessError ItemClear TgtClear ))+  , ("Escape", ( [CmdAim, CmdMinimal]+               , "cancel aiming/open Main Menu"+               , ByAimMode {exploration = MainMenu, aiming = Cancel} ))+  , ("Return", ( [CmdAim, CmdMinimal]+               , "accept target/open Help"+               , ByAimMode {exploration = Help, aiming = Accept} )) -      -- Assorted-      , ("space", ( [CmdMinimal, CmdMeta]-                  , "clear messages/display history", Clear ))-      , ("?", ([CmdMeta], "display Help", Help))-      , ("F1", ([CmdMeta], "", Help))-      , ("Tab", ( [CmdMeta]-                , "cycle among party members on the level"-                , MemberCycle ))-      , ("BackTab", ( [CmdMeta, CmdMinimal]-                  , "cycle among all party members"-                  , MemberBack ))-      , ("=", ( [CmdMinimal, CmdMeta]-              , "select (or deselect) party member", SelectActor) )-      , ("_", ([CmdMeta], "deselect (or select) all on the level", SelectNone))-      , ("v", ([CmdMeta], "voice again the recorded commands", Repeat 1))-      , ("V", repeatTriple 100)-      , ("C-v", repeatTriple 1000)-      , ("C-V", repeatTriple 25)-      , ("'", ([CmdMeta], "start recording commands", Record))+  -- Assorted+  , ("space", ( [CmdMinimal, CmdMeta]+              , "clear messages/display history", Clear ))+  , ("?", ([CmdMeta], "display Help", Help))+  , ("F1", ([CmdMeta], "", Help))+  , ("Tab", ( [CmdMeta]+            , "cycle among party members on the level"+            , MemberCycle ))+  , ("BackTab", ( [CmdMeta, CmdMinimal]+              , "cycle among all party members"+              , MemberBack ))+  , ("=", ( [CmdMinimal, CmdMeta]+          , "select (or deselect) party member", SelectActor) )+  , ("_", ([CmdMeta], "deselect (or select) all on the level", SelectNone))+  , ("v", ([CmdMeta], "voice again the recorded commands", Repeat 1))+  , ("V", repeatTriple 100)+  , ("C-v", repeatTriple 1000)+  , ("C-V", repeatTriple 25)+  , ("'", ([CmdMeta], "start recording commands", Record)) -      -- Mouse-      , ("LeftButtonRelease", mouseLMB)-      , ("RightButtonRelease", mouseRMB)-      , ("C-LeftButtonRelease", replaceDesc "" mouseRMB)  -- Mac convention-      , ( "C-RightButtonRelease"-        , ( [CmdMouse]-          , "open or close door"-          , AlterWithPointer $ closeDoorTriggers ++ openDoorTriggers ) )-      , ("MiddleButtonRelease", mouseMMB)-      , ("WheelNorth", ([CmdMouse], "swerve the aiming line", Macro ["+"]))-      , ("WheelSouth", ([CmdMouse], "unswerve the aiming line", Macro ["-"]))+  -- Mouse+  , ("LeftButtonRelease", mouseLMB)+  , ("RightButtonRelease", mouseRMB)+  , ("C-LeftButtonRelease", replaceDesc "" mouseRMB)  -- Mac convention+  , ( "C-RightButtonRelease"+    , ( [CmdMouse]+      , "open or close door"+      , AlterWithPointer $ closeDoorTriggers ++ openDoorTriggers ) )+  , ("MiddleButtonRelease", mouseMMB)+  , ("WheelNorth", ([CmdMouse], "swerve the aiming line", Macro ["+"]))+  , ("WheelSouth", ([CmdMouse], "unswerve the aiming line", Macro ["-"])) -      -- Debug and others not to display in help screens-      , ("C-S", ([CmdDebug], "save game", GameSave))-      , ("C-semicolon", ( [CmdNoHelp]-                        , "move one step towards the x-hair"-                        , MoveOnceToXhair ))-      , ("C-colon", ( [CmdNoHelp]-                    , "run collectively one step towards the x-hair"-                    , RunOnceToXhair ))-      , ("C-/", ( [CmdNoHelp]-                , "continue towards the x-hair"-                , ContinueToXhair ))-      , ("C-comma", ([CmdNoHelp], "run once ahead", RunOnceAhead))-      , ("safe1", ( [CmdInternal]-                  , "go to pointer for 25 steps"-                  , goToCmd ))-      , ("safe2", ( [CmdInternal]-                  , "run to pointer collectively"-                  , runToAllCmd ))-      , ("safe3", ( [CmdInternal]-                  , "pick new leader on screen"-                  , PickLeaderWithPointer ))-      , ("safe4", ( [CmdInternal]-                  , "select party member on screen"-                  , SelectWithPointer ))-      , ("safe5", ( [CmdInternal]-                  , "set x-hair to enemy"-                  , AimPointerEnemy ))-      , ("safe6", ( [CmdInternal]-                  , "fling at enemy under pointer"-                  , aimFlingCmd ))-      , ("safe7", ( [CmdInternal]-                  , "open Main Menu"-                  , MainMenu ))-      , ("safe8", ( [CmdInternal]-                  , "cancel aiming"-                  , Cancel ))-      , ("safe9", ( [CmdInternal]-                  , "accept target"-                  , Accept ))-      , ("safe10", ( [CmdInternal]-                   , "wait a turn, bracing for impact"-                   , Wait ))-      , ("safe11", ( [CmdInternal]-                   , "wait 0.1 of a turn"-                   , Wait10 ))-      ]-      ++ map defaultHeroSelect [0..6]-  }+  -- Debug and others not to display in help screens+  , ("C-S", ([CmdDebug], "save game", GameSave))+  , ("C-semicolon", ( [CmdNoHelp]+                    , "move one step towards the x-hair"+                    , MoveOnceToXhair ))+  , ("C-colon", ( [CmdNoHelp]+                , "run collectively one step towards the x-hair"+                , RunOnceToXhair ))+  , ("C-/", ( [CmdNoHelp]+            , "continue towards the x-hair"+            , ContinueToXhair ))+  , ("C-comma", ([CmdNoHelp], "run once ahead", RunOnceAhead))+  , ("safe1", ( [CmdInternal]+              , "go to pointer for 25 steps"+              , goToCmd ))+  , ("safe2", ( [CmdInternal]+              , "run to pointer collectively"+              , runToAllCmd ))+  , ("safe3", ( [CmdInternal]+              , "pick new leader on screen"+              , PickLeaderWithPointer ))+  , ("safe4", ( [CmdInternal]+              , "select party member on screen"+              , SelectWithPointer ))+  , ("safe5", ( [CmdInternal]+              , "set x-hair to enemy"+              , AimPointerEnemy ))+  , ("safe6", ( [CmdInternal]+              , "fling at enemy under pointer"+              , aimFlingCmd ))+  , ("safe7", ( [CmdInternal]+              , "open Main Menu"+              , MainMenu ))+  , ("safe8", ( [CmdInternal]+              , "cancel aiming"+              , Cancel ))+  , ("safe9", ( [CmdInternal]+              , "accept target"+              , Accept ))+  , ("safe10", ( [CmdInternal]+               , "wait a turn, bracing for impact"+               , Wait ))+  , ("safe11", ( [CmdInternal]+               , "wait 0.1 of a turn"+               , Wait10 ))+  ]+  ++ map defaultHeroSelect [0..6]  closeDoorTriggers :: [Trigger] closeDoorTriggers =
GameDefinition/Content/CaveKind.hs view
@@ -3,7 +3,7 @@ -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. ----- | Cave layouts.+-- | Cave properties. module Content.CaveKind   ( cdefs   ) where@@ -38,21 +38,21 @@                     , ("caveRogue", 1) ]   , cxsize        = fst normalLevelBound + 1   , cysize        = snd normalLevelBound + 1-  , cgrid         = DiceXY (3 * d 2) 4-  , cminPlaceSize = DiceXY (2 * d 2 + 4) 5+  , cgrid         = DiceXY (3 `d` 2) 4+  , cminPlaceSize = DiceXY (2 `d` 2 + 4) 5   , cmaxPlaceSize = DiceXY 15 10-  , cdarkChance   = d 54 + dl 20+  , cdarkChance   = 1 `d` 54 + 1 `dl` 20   , cnightChance  = 51  -- always night   , cauxConnects  = 1%2   , cmaxVoid      = 1%6   , cminStairDist = 15-  , cextraStairs  = 1 + d 2+  , cextraStairs  = 1 + 1 `d` 2   , cdoorChance   = 3%4   , copenChance   = 1%5   , chidden       = 7   , cactorCoeff   = 130  -- the maze requires time to explore   , cactorFreq    = [("monster", 50), ("animal", 25), ("robot", 25)]-  , citemNum      = 6 * d 5+  , citemNum      = 6 `d` 5   , citemFreq     = [("useful", 40), ("treasure", 60)]   , cplaceFreq    = [("rogue", 100)]   , cpassable     = False@@ -65,64 +65,68 @@   , clegendLitTile  = "legendLit"   , cescapeGroup    = Nothing   , cstairFreq      = [("staircase lift", 100)]+  , cdesc         = ""   } rogue2 = rogue   { cfreq         = [("deep random", 20)]   , cname         = "Dark storage area"   , cdarkChance   = 51  -- all rooms dark   , cnightChance  = 0  -- always day+  , cdesc         = "The lights had gone out."   } arena = rogue   { csymbol       = 'A'   , cname         = "Recreational deck"   , cfreq         = [("default random", 40), ("caveArena", 1)]-  , cgrid         = DiceXY (2 + d 2) (d 3)-  , cminPlaceSize = DiceXY (2 * d 2 + 4) 6+  , cgrid         = DiceXY (2 + 1 `d` 2) (1 `d` 3)+  , cminPlaceSize = DiceXY (2 `d` 2 + 4) 6   , cmaxPlaceSize = DiceXY 16 12-  , cdarkChance   = 49 + d 10  -- almost all rooms dark (1 in 10 lit)+  , cdarkChance   = 49 + 1 `d` 10  -- almost all rooms dark (1 in 10 lit)   -- Light is not too deadly, because not many obstructions and so   -- foes visible from far away and few foes have ranged combat   -- at shallow depth.   , cnightChance  = 0  -- always day   , cauxConnects  = 1   , cmaxVoid      = 1%8-  , cextraStairs  = d 3+  , cextraStairs  = 1 `d` 3   , chidden       = 0   , cactorCoeff   = 100   , cactorFreq    = [("monster", 25), ("animal", 70), ("robot", 5)]-  , citemNum      = 5 * d 5  -- few rooms+  , citemNum      = 5 `d` 5  -- few rooms   , citemFreq     = [("useful", 20), ("treasure", 40), ("any scroll", 40)]   , cplaceFreq    = [("arena", 100)]   , cpassable     = True   , cdefTile      = "arenaSetLit"   , cdarkCorTile  = "trailLit"  -- let trails give off light   , clitCorTile   = "trailLit"+  , cdesc         = ""   } arena2 = arena   { cname         = "Casino"   , cfreq         = [("deep random", 30)]-  , cdarkChance   = 41 + d 10  -- almost all rooms lit (1 in 10 dark)+  , cdarkChance   = 41 + 1 `d` 10  -- almost all rooms lit (1 in 10 dark)   -- Trails provide enough light for fun stealth.   , cnightChance  = 51  -- always night-  , citemNum      = 7 * d 5  -- rare, so make it exciting+  , citemNum      = 7 `d` 5  -- rare, so make it exciting   , citemFreq     = [("useful", 20), ("treasure", 80)] -- lives up to the name   , cdefTile      = "arenaSetDark"+  , cdesc         = ""   } laboratory = arena2   { csymbol       = 'L'   , cname         = "Laboratory"   , cfreq         = [("deep random", 20), ("caveLaboratory", 1)]-  , cgrid         = DiceXY (2 * d 2 + 7) 3-  , cminPlaceSize = DiceXY (3 * d 2 + 4) 5-  , cdarkChance   = d 54 + dl 20  -- most rooms lit, to compensate for corridors+  , cgrid         = DiceXY (2 `d` 2 + 7) 3+  , cminPlaceSize = DiceXY (3 `d` 2 + 4) 5+  , cdarkChance   = 1 `d` 54 + 1 `dl` 20  -- most rooms lit, to compensate for corridors   , cnightChance  = 0  -- always day   , cauxConnects  = 1%10   , cmaxVoid      = 1%10-  , cextraStairs  = d 2+  , cextraStairs  = 1 `d` 2   , cdoorChance   = 1   , copenChance   = 1%2   , chidden       = 7-  , citemNum      = 7 * d 5  -- reward difficulty+  , citemNum      = 7 `d` 5  -- reward difficulty   , citemFreq     = [("useful", 20), ("treasure", 40), ("any vial", 40)]   , cplaceFreq    = [("laboratory", 100)]   , cpassable     = False@@ -130,6 +134,7 @@   , cdarkCorTile  = "labTrailLit"  -- let lab smoke give off light always   , clitCorTile   = "labTrailLit"   , cstairFreq    = [("staircase lift", 100)]+  , cdesc         = "An experiment (or was it manufacturing?) had gone wrong here."   } empty = rogue   { csymbol       = 'E'@@ -138,30 +143,31 @@   , cgrid         = DiceXY 1 1   , cminPlaceSize = DiceXY 12 12   , cmaxPlaceSize = DiceXY 48 32  -- favour large rooms-  , cdarkChance   = d 100 + dl 100+  , cdarkChance   = 1 `d` 100 + 1 `dl` 100   , cnightChance  = 0  -- always day   , cauxConnects  = 3%2   , cminStairDist = 30   , cmaxVoid      = 0  -- too few rooms to have void and fog common anyway-  , cextraStairs  = d 2+  , cextraStairs  = 1 `d` 2   , cdoorChance   = 0   , copenChance   = 0   , chidden       = 0   , cactorCoeff   = 80   , cactorFreq    = [("monster", 25), ("animal", 5), ("robot", 70)]-  , citemNum      = 5 * d 5  -- few rooms+  , citemNum      = 5 `d` 5  -- few rooms   , cplaceFreq    = [("empty", 100)]   , cpassable     = True   , cdefTile      = "emptySet"   , cdarkCorTile  = "floorArenaDark"   , clitCorTile   = "floorArenaLit"   , cstairFreq    = [("staircase", 50), ("staircase lift", 50)]+  , cdesc         = "Not much to see here yet."   } noise = rogue   { csymbol       = 'N'   , cname         = "Flight hardware hub"   , cfreq         = [("caveNoise", 1)]-  , cgrid         = DiceXY (2 + d 3) 3+  , cgrid         = DiceXY (2 + 1 `d` 3) 3   , cminPlaceSize = DiceXY 8 5   , cmaxPlaceSize = DiceXY 20 10   , cdarkChance   = 51@@ -170,26 +176,28 @@   , cnightChance  = 0  -- harder variant, but looks cheerful   , cauxConnects  = 1%10   , cmaxVoid      = 1%100-  , cextraStairs  = d 4+  , cextraStairs  = 1 `d` 4   , cdoorChance   = 1  -- to enable the doorlessWall hack and have no lit tiles   , chidden       = 0   , cactorCoeff   = 160  -- the maze requires time to explore   , cactorFreq    = [("monster", 70), ("animal", 15), ("robot", 15)]-  , citemNum      = 7 * d 5  -- an incentive to explore the labyrinth+  , citemNum      = 7 `d` 5  -- an incentive to explore the labyrinth   , cpassable     = True   , cplaceFreq    = [("noise", 100)]   , cdefTile      = "noiseSet"   , couterFenceTile = "noise fence"  -- ensures no cut-off parts from collapsed   , cdarkCorTile  = "floorArenaDark"   , clitCorTile   = "floorArenaLit"+  , cdesc         = ""   } noise2 = noise   { cname         = "Power distribution hub"   , cfreq         = [("caveNoise2", 1)]   , cnightChance  = 51  -- easier variant, but looks sinister-  , citemNum      = 13 * d 5  -- an incentive to explore the final labyrinth+  , citemNum      = 13 `d` 5  -- an incentive to explore the final labyrinth   , cplaceFreq    = [("noise", 1), ("mine", 99)]   , cstairFreq    = [("gated staircase", 100)]+  , cdesc         = ""   } bridge = rogue   { csymbol       = 'B'@@ -199,32 +207,36 @@   , cextraStairs  = 1   , cactorCoeff   = 200  -- it's quite deep already, so spawn slowly   , cactorFreq    = [("animal", 100)]-  , citemNum      = 9 * d 5  -- lure them in with loot+  , citemNum      = 9 `d` 5  -- lure them in with loot   , citemFreq     = filter ((/= "treasure") . fst) $ citemFreq rogue+  , cdesc         = "The bridge is gutted out and deserted. There are animal cries down below and ominous silence up above."   } shallow2rogue = rogue   { cfreq         = [("shallow random 2", 50)]   , cactorCoeff   = cactorCoeff rogue `div` 2  -- more difficult   , cactorFreq    = filter ((/= "monster") . fst) $ cactorFreq rogue   , citemFreq     = filter ((/= "treasure") . fst) $ citemFreq rogue+  , cdesc         = ""   } shallow2arena = arena   { cfreq         = [("shallow random 2", 100)]   , cactorCoeff   = cactorCoeff arena `div` 2   , cactorFreq    = filter ((/= "monster") . fst) $ cactorFreq empty   , citemFreq     = filter ((/= "treasure") . fst) $ citemFreq empty+  , cdesc         = ""   } shallow2empty = empty   { cfreq         = [("shallow random 2", 10)]   , cactorCoeff   = cactorCoeff empty `div` 2   , cactorFreq    = filter ((/= "monster") . fst) $ cactorFreq empty   , citemFreq     = filter ((/= "treasure") . fst) $ citemFreq empty+  , cdesc         = ""   } shallow1empty = shallow2empty  -- TODO: replace some rooms with oriels?   { cname         = "Outermost deck"   , cfreq         = [("outermost", 100)]-  , cactorCoeff   = 4  -- shallower than LH, so less immediate actors, so boost-  , cactorFreq    = [("animal", 5), ("robot", 5), ("immobile robot", 90)]+  , cactorCoeff   = 4  -- shallower than LH, so fewer immediate actors, so boost+  , cactorFreq    = [("animal", 3), ("robot", 2), ("immobile robot", 95)]       -- The medbot faucets on lvl 1 act like HP resets. Needed to avoid       -- cascading failure, if the particular starting conditions were       -- very hard. Items are not reset, even if they are bad, which provides@@ -232,13 +244,15 @@       -- abused, because they spawn less and less often and also HP doesn't       -- effectively accumulate over max.   , couterFenceTile = "oriels fence"+  , cdesc         = "The black sky outside can be seen through the oriels."   } emptyExit = empty   { cname         = "Shuttle servicing area"-  , cfreq = [("caveEmptyExit", 1)]+  , cfreq         = [("caveEmptyExit", 1)]   , cdarkCorTile  = "trailLit"  -- flavour   , couterFenceTile = "noise fence"  -- for flavour-  , cescapeGroup = Just "escape spaceship down"+  , cescapeGroup  = Just "escape spaceship down"+  , cdesc         = "Empty husks and strewn entrails of small craft litter the hangar among neglected cranes and airlocks."   } raid = rogue   { csymbol       = 'S'@@ -248,9 +262,10 @@   , cmaxVoid      = 1%10   , cactorCoeff   = 500  -- deep level with no kit, so slow spawning   , cactorFreq    = [("animal", 50), ("robot", 50)]-  , citemNum      = 6 * d 8  -- just one level, hard enemies, treasure+  , citemNum      = 6 `d` 8  -- just one level, hard enemies, treasure   , citemFreq     = [("useful", 33), ("gem", 33), ("currency", 33)]   , cescapeGroup  = Just "escape up"+  , cdesc         = ""   } brawl = rogue  -- many random solid tiles, to break LOS, since it's a day                -- and this scenario is not focused on ranged combat;@@ -258,7 +273,7 @@   { csymbol       = 'S'   , cname         = "Woodland biosphere"   , cfreq         = [("caveBrawl", 1)]-  , cgrid         = DiceXY (2 * d 2 + 2) 3+  , cgrid         = DiceXY (2 `d` 2 + 2) 3   , cminPlaceSize = DiceXY 3 3   , cmaxPlaceSize = DiceXY 7 5   , cdarkChance   = 51@@ -268,13 +283,14 @@   , cextraStairs  = 1   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 * d 8+  , citemNum      = 5 `d` 8   , citemFreq     = [("useful", 100)]   , cplaceFreq    = [("brawl", 60), ("rogue", 40)]   , cpassable     = True   , cdefTile      = "brawlSetLit"   , cdarkCorTile  = "floorArenaLit"   , clitCorTile   = "floorArenaLit"+  , cdesc         = ""   } shootout = rogue  -- a scenario with strong missiles;                   -- few solid tiles, but only translucent tiles or walkable@@ -284,7 +300,7 @@   { csymbol       = 'S'   , cname         = "Hydroponic farm"  -- still a neutral, offcial wording   , cfreq         = [("caveShootout", 1)]-  , cgrid         = DiceXY (d 2 + 7) 3+  , cgrid         = DiceXY (1 `d` 2 + 7) 3   , cminPlaceSize = DiceXY 3 3   , cmaxPlaceSize = DiceXY 3 4   , cdarkChance   = 51@@ -294,14 +310,14 @@   , cextraStairs  = 1   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 * d 16+  , citemNum      = 5 `d` 16                       -- less items in inventory, more to be picked up,                       -- to reward explorer and aggressor and punish camper   , citemFreq     = [ ("useful", 30)                     , ("any arrow", 400), ("harpoon", 300)                     , ("any vial", 60) ]                       -- Many consumable buffs are needed in symmetric maps-                      -- so that aggresor prepares them in advance and camper+                      -- so that aggressor prepares them in advance and camper                       -- needs to waste initial turns to buff for the defence.   , cplaceFreq    = [("shootout", 100)]   , cpassable     = True@@ -316,8 +332,8 @@   { csymbol       = 'E'   , cname         = "Red Collar Bros den"  -- tension rises; non-official name   , cfreq         = [("caveEscape", 1)]-  , cgrid         = DiceXY -- (2 * d 2 + 3) 4  -- park, so lamps in lines-                           (2 * d 2 + 6) 3   -- for now, to fit larger places+  , cgrid         = DiceXY -- (2 `d` 2 + 3) 4  -- park, so lamps in lines+                           (2 `d` 2 + 6) 3   -- for now, to fit larger places   , cminPlaceSize = DiceXY 3 3   , cmaxPlaceSize = DiceXY 9 9  -- bias towards larger lamp areas   , cdarkChance   = 51  -- colonnade rooms should always be dark@@ -327,7 +343,7 @@   , cextraStairs  = 1   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 * d 8+  , citemNum      = 5 `d` 8   , citemFreq     = [ ("useful", 30), ("treasure", 30), ("gem", 100)                     , ("weak arrow", 500), ("harpoon", 400) ]   , cplaceFreq    = [("park", 100)]@@ -336,12 +352,13 @@   , cdarkCorTile  = "trailLit"  -- let trails give off light   , clitCorTile   = "trailLit"   , cescapeGroup  = Just "escape outdoor down"+  , cdesc         = ""   } zoo = rogue  -- few lights and many solids, to help the less numerous heroes   { csymbol       = 'Z'   , cname         = "Municipal zoo in flames"  -- non-official adjective   , cfreq         = [("caveZoo", 1)]-  , cgrid         = DiceXY (2 * d 2 + 6) 3+  , cgrid         = DiceXY (2 `d` 2 + 6) 3   , cminPlaceSize = DiceXY 4 4   , cmaxPlaceSize = DiceXY 12 12   , cdarkChance   = 51  -- always dark rooms@@ -353,13 +370,14 @@   , cextraStairs  = 1   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 7 * d 8+  , citemNum      = 7 `d` 8   , citemFreq     = [("useful", 100), ("light source", 1000)]   , cplaceFreq    = [("zoo", 50)]   , cpassable     = True   , cdefTile      = "zooSet"   , cdarkCorTile  = "trailLit"  -- let trails give off light   , clitCorTile   = "trailLit"+  , cdesc         = ""   } ambush = rogue  -- a scenario with strong missiles;                 -- dark, so solid obstacles are to hide from missiles,@@ -373,8 +391,8 @@   { csymbol       = 'M'   , cname         = "Ravaged spaceport"  -- non-official adjective   , cfreq         = [("caveAmbush", 1)]-  , cgrid         = DiceXY -- (2 * d 2 + 3) 4  -- spaceport, so lamps in lines-                           (2 * d 2 + 5) 3   -- for now, to fit larger places+  , cgrid         = DiceXY -- (2 `d` 2 + 3) 4  -- spaceport, so lamps in lines+                           (2 `d` 2 + 5) 3   -- for now, to fit larger places   , cminPlaceSize = DiceXY 3 3   , cmaxPlaceSize = DiceXY 15 15  -- allow hangars   , cdarkChance   = 51@@ -384,19 +402,20 @@   , cextraStairs  = 1   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 * d 8+  , citemNum      = 5 `d` 8   , citemFreq     = [("useful", 30), ("any arrow", 400), ("harpoon", 300)]   , cplaceFreq    = [("ambush", 100)]   , cpassable     = True   , cdefTile      = "ambushSet"   , cdarkCorTile  = "trailLit"  -- let trails give off light   , clitCorTile   = "trailLit"+  , cdesc         = ""   } battle = rogue  -- few lights and many solids, to help the less numerous heroes   { csymbol       = 'B'   , cname         = "Burning public park"   , cfreq         = [("caveBattle", 1)]-  , cgrid         = DiceXY (2 * d 2 + 1) 3+  , cgrid         = DiceXY (2 `d` 2 + 1) 3   , cminPlaceSize = DiceXY 4 4   , cmaxPlaceSize = DiceXY 9 7   , cdarkChance   = 0@@ -408,7 +427,7 @@   , cextraStairs  = 1   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 * d 8+  , citemNum      = 5 `d` 8   , citemFreq     = [("useful", 100), ("light source", 200)]   , cplaceFreq    = [("battle", 50), ("rogue", 50)]   , cpassable     = True@@ -416,20 +435,25 @@   , cdarkCorTile  = "trailLit"  -- let trails give off light   , clitCorTile   = "trailLit"   , couterFenceTile = "noise fence"  -- ensures no cut-off parts from collapsed+  , cdesc         = ""   } safari1 = brawl-  { cname = "Hunam habitat"-  , cfreq = [("caveSafari1", 1)]-  , cescapeGroup = Nothing-  , cstairFreq = [("staircase outdoor", 1)]+  { cname         = "Hunam habitat"+  , cfreq         = [("caveSafari1", 1)]+  , cescapeGroup  = Nothing+  , cstairFreq    = [("staircase outdoor", 1)]+  , cdesc         = "\"DLC 1. Hunams scavenge in a forest in their usual disgusting way.\""   }-safari2 = ambush-  { cname = "Hunting grounds"-  , cfreq = [("caveSafari2", 1)]-  , cstairFreq = [("staircase outdoor", 1)]+safari2 = ambush  -- lamps instead of trees, but ok, it's only a simulation+  { cname         = "Deep into the jungle"+  , cfreq         = [("caveSafari2", 1)]+  , cstairFreq    = [("staircase outdoor", 1)]+  , cdesc         = "\"DLC 2. In the dark pure heart of the jungle noble animals roam freely.\""   }-safari3 = zoo-  { cfreq = [("caveSafari3", 1)]-  , cescapeGroup = Just "escape outdoor down"-  , cstairFreq = [("staircase outdoor", 1)]+safari3 = zoo  -- glass rooms, but ok, it's only a simulation+  { cname         = "Jungle in flames"+  , cfreq         = [("caveSafari3", 1)]+  , cescapeGroup  = Just "escape outdoor down"+  , cstairFreq    = [("staircase outdoor", 1)]+  , cdesc         = "\"DLC 3. Jealous hunams set jungle on fire and flee.\""   }
GameDefinition/Content/ItemKind.hs view
@@ -3,7 +3,7 @@ -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. ----- | Item and treasure definitions.+-- | Item definitions. module Content.ItemKind   ( cdefs, items, otherItemContent   ) where@@ -32,7 +32,7 @@   , getFreq = ifreq   , validateSingle = validateSingleItemKind   , validateAll = validateAllItemKind-  , content = contentFromList []  -- filled out later on+  , content = contentFromList $ items ++ otherItemContent   }  otherItemContent :: [ItemKind]@@ -83,12 +83,12 @@   , iname    = "ceramic foam splinter"   , ifreq    = [("sandstone rock", 1), ("weak arrow", 10)]   , iflavour = zipPlain [Green]-  , icount   = 1 * d 2+  , icount   = 1 `d` 2   , irarity  = [(1, 50), (10, 1)]   , iverbHit = "hit"   , iweight  = 300-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [AddHurtMelee (-16 |*| 5)]+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [AddHurtMelee $ -16 * 5]   , ieffects = []   , ifeature = [toVelocity 70, Fragile, Identified]  -- not dense, irregular   , idesc    = "A light, irregular lump of ceramic foam used in construction."@@ -99,12 +99,13 @@   , iname    = "billiard ball"   , ifreq    = [("useful", 100), ("any arrow", 50), ("weak arrow", 50)]   , iflavour = zipPlain [BrWhite]-  , icount   = 4 * d 3+  , icount   = 4 `d` 3   , irarity  = [(1, 20), (10, 10)]   , iverbHit = "strike"   , iweight  = 170-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [AddHurtMelee (-14 + d 2 + dl 4 |*| 5)]  -- only leather-piercing+  , idamage  = [(98, 1 `d` 1), (2, 2 `d` 1)]+  , iaspects = [AddHurtMelee $ (-14 + 1 `d` 2 + 1 `dl` 4) * 5]+                 -- only leather-piercing   , ieffects = []   , ifeature = [Identified]   , idesc    = "Ideal shape, size and weight for throwing."@@ -115,12 +116,13 @@   , iname    = "steak knife"   , ifreq    = [("useful", 100), ("any arrow", 50), ("weak arrow", 50)]   , iflavour = zipPlain [Cyan]-  , icount   = 4 * d 3+  , icount   = 4 `d` 3   , irarity  = [(1, 10), (10, 20)]   , iverbHit = "nick"   , iweight  = 100-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [AddHurtMelee (-10 + d 2 + dl 4 |*| 5)]  -- heavy vs armor+  , idamage  = [(98, 2 `d` 1), (2, 4 `d` 1)]+  , iaspects = [AddHurtMelee $ (-10 + 1 `d` 2 + 1 `dl` 4) * 5]+                 -- heavy vs armor   , ieffects = [ Explode "single spark"  -- when hitting enemy                , OnSmash (Explode "single spark") ]  -- at wall hit   , ifeature = [toVelocity 70, Identified]  -- hitting with tip costs speed@@ -132,12 +134,13 @@   , iname    = "steel hex nut"   , ifreq    = [("useful", 5), ("any arrow", 100)]   , iflavour = zipPlain [Blue]-  , icount   = 3 * d 3+  , icount   = 3 `d` 3   , irarity  = [(1, 1), (10, 20)]   , iverbHit = "hit"   , iweight  = 200-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [AddHurtMelee (-10 + d 2 + dl 4 |*| 5)]  -- heavy vs armor+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [AddHurtMelee $ (-10 + 1 `d` 2 + 1 `dl` 4) * 5]+                 -- heavy vs armor   , ieffects = [ Explode "single spark"  -- when hitting enemy                , OnSmash (Explode "single spark") ]  -- at wall hit   , ifeature = [toVelocity 150, Identified]@@ -149,12 +152,13 @@   , iname    = "bearing ball"   , ifreq    = [("useful", 5), ("any arrow", 100)]   , iflavour = zipPlain [White]-  , icount   = 6 * d 3+  , icount   = 6 `d` 3   , irarity  = [(1, 1), (10, 15)]   , iverbHit = "hit"   , iweight  = 28-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [AddHurtMelee (-17 + d 2 + dl 4 |*| 5)]  -- not armor-piercing+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [AddHurtMelee $ (-17 + 1 `d` 2 + 1 `dl` 4) * 5]+                 -- not armor-piercing   , ieffects = []   , ifeature = [toVelocity 200, Identified]   , idesc    = "Small but heavy bearing ball. Due to its size and shape, it securely fits in the makeshift sling's pouch and doesn't snag when released."@@ -169,13 +173,13 @@   , iname    = "can"   , ifreq    = [("useful", 100), ("can of sticky foam", 1)]   , iflavour = zipPlain [Magenta]-  , icount   = dl 3+  , icount   = 1 `dl` 3   , irarity  = [(5, 5), (10, 20)]   , iverbHit = "glue"   , iweight  = 1000-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [AddHurtMelee (-14 |*| 5)]-  , ieffects = [ ELabel "of sticky foam", Paralyze (10 + d 12)+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [AddHurtMelee $ -14 * 5]+  , ieffects = [ ELabel "of sticky foam", Paralyze (10 + 1 `d` 12)                , OnSmash (Explode "glue") ]   , ifeature = [toVelocity 70, Identified, Lobable, Fragile]  -- unwieldy   , idesc    = "A can of liquid, fast-setting construction foam."@@ -186,12 +190,12 @@   , iname    = "harpoon"   , ifreq    = [("useful", 100), ("harpoon", 100)]   , iflavour = zipPlain [Brown]-  , icount   = dl 5+  , icount   = 1 `dl` 5   , irarity  = [(5, 5), (10, 5)]   , iverbHit = "hook"   , iweight  = 750-  , idamage  = [(99, 5 * d 1), (1, 10 * d 1)]-  , iaspects = [AddHurtMelee (-10 + d 2 + dl 4 |*| 5)]+  , idamage  = [(99, 5 `d` 1), (1, 10 `d` 1)]+  , iaspects = [AddHurtMelee $ (-10 + 1 `d` 2 + 1 `dl` 4) * 5]   , ieffects = [PullActor (ThrowMod 200 50)]   , ifeature = [Identified]   , idesc    = "A display piece harking back to the Earth's oceanic tourism hayday. The cruel, barbed head lodges in its victim so painfully that the weakest tug of the thin line sends the victim flying."@@ -202,13 +206,13 @@   , iname    = "net"   , ifreq    = [("useful", 100)]   , iflavour = zipPlain [White]-  , icount   = dl 3+  , icount   = 1 `dl` 3   , irarity  = [(3, 5), (10, 4)]   , iverbHit = "entangle"   , iweight  = 1000-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [AddHurtMelee (-14 |*| 5)]-  , ieffects = [ toOrganGameTurn "slowed" (3 + d 3)+  , idamage  = toDmg $ 2 `d` 1+  , iaspects = [AddHurtMelee $ -14 * 5]+  , ieffects = [ toOrganGameTurn "slowed" (3 + 1 `d` 3)                , DropItem maxBound 1 CEqp "torso armor" ]   , ifeature = [Identified]   , idesc    = "A large synthetic fibre net with weights affixed along the edges. Entangles armor and restricts movement."@@ -222,7 +226,7 @@   , iname    = "torch"   , ifreq    = [("useful", 100), ("light source", 100), ("wooden torch", 1)]   , iflavour = zipPlain [Brown]-  , icount   = d 2+  , icount   = 1 `d` 2   , irarity  = [(1, 15)]   , iverbHit = "scorch"   , iweight  = 1000@@ -243,7 +247,7 @@   , irarity  = [(6, 7)]   , iverbHit = "burn"   , iweight  = 1500-  , idamage  = toDmg $ 1 * d 1+  , idamage  = toDmg $ 1 `d` 1   , iaspects = [AddShine 3, AddSight (-1)]   , ieffects = [ Burn 1, Paralyze 6, OnSmash (Explode "burning oil 3")                , EqpSlot EqpSlotLightSource ]@@ -261,7 +265,7 @@   , iverbHit = "snag"   , iweight  = 3000   , idamage  = toDmg 0-  , iaspects = [AddShine 4, AddArmorRanged $ - d 3]  -- noise and distraction+  , iaspects = [AddShine 4, AddArmorRanged $ - 1 `d` 3]  -- noise and distraction   , ieffects = [EqpSlot EqpSlotLightSource]   , ifeature = [Identified, Equipable]   , idesc    = "Powerful, wide-beam spotlight, powered by a hand-crank. Requires noisy two-handed recharging every few minutes."@@ -318,34 +322,34 @@ flask1 = flask   { irarity  = [(10, 4)]   , ieffects = [ ELabel "of strength renewal brew"-               , toOrganActorTurn "strengthened" (20 + d 5)+               , toOrganActorTurn "strengthened" (20 + 1 `d` 5)                , toOrganNone "regenerating"                , OnSmash (Explode "dense shower") ]   } flask2 = flask   { ieffects = [ ELabel "of weakness brew"-               , toOrganGameTurn "weakened" (20 + d 5)+               , toOrganGameTurn "weakened" (20 + 1 `d` 5)                , OnSmash (Explode "sparse shower") ]   } flask3 = flask   { ieffects = [ ELabel "of melee protective balm"-               , toOrganActorTurn "protected from melee" (20 + d 5)+               , toOrganActorTurn "protected from melee" (20 + 1 `d` 5)                , OnSmash (Explode "melee protective balm") ]   } flask4 = flask   { ieffects = [ ELabel "of ranged protective balm"-               , toOrganActorTurn "protected from ranged" (20 + d 5)+               , toOrganActorTurn "protected from ranged" (20 + 1 `d` 5)                , OnSmash (Explode "ranged protective balm") ]   } flask5 = flask   { ieffects = [ ELabel "of red paint"-               , toOrganGameTurn "painted red" (20 + d 5)+               , toOrganGameTurn "painted red" (20 + 1 `d` 5)                , OnSmash (Explode "red paint") ]   } flask6 = flask   { irarity  = [(10, 9)]   , ieffects = [ ELabel "of resolution"-               , toOrganActorTurn "resolute" (200 + d 50)+               , toOrganActorTurn "resolute" (200 + 1 `d` 50)                    -- long, for scouting and has to recharge                , RefillCalm 60  -- not to make it a drawback, via @calmEnough@                , OnSmash (Explode "resolution dust") ]@@ -353,14 +357,14 @@ flask7 = flask   { irarity  = [(10, 4)]   , ieffects = [ ELabel "of haste brew"-               , toOrganActorTurn "hasted" (20 + d 5)+               , toOrganActorTurn "hasted" (20 + 1 `d` 5)                , OnSmash (Explode "blast 20")                , OnSmash (Explode "haste spray") ]   } flask8 = flask   { irarity  = [(1, 14), (10, 4)]   , ieffects = [ ELabel "of lethargy brew"-               , toOrganGameTurn "slowed" (20 + d 5)+               , toOrganGameTurn "slowed" (20 + 1 `d` 5)                , toOrganNone "regenerating", toOrganNone "regenerating"  -- x2                , RefillCalm 5                , OnSmash (Explode "slowness mist") ]@@ -368,32 +372,32 @@ flask9 = flask   { irarity  = [(10, 4)]   , ieffects = [ ELabel "of eye drops"-               , toOrganActorTurn "far-sighted" (40 + d 10)+               , toOrganActorTurn "far-sighted" (40 + 1 `d` 10)                , OnSmash (Explode "eye drop") ]   } flask10 = flask   { irarity  = [(10, 2)]   , ieffects = [ ELabel "of smelly concoction"-               , toOrganActorTurn "keen-smelling" (40 + d 10)+               , toOrganActorTurn "keen-smelling" (40 + 1 `d` 10)                , DetectActor 10                , OnSmash (Explode "smelly droplet") ]   } flask11 = flask   { irarity  = [(10, 4)]   , ieffects = [ ELabel "of cat tears"-               , toOrganActorTurn "shiny-eyed" (40 + d 10)+               , toOrganActorTurn "shiny-eyed" (40 + 1 `d` 10)                , OnSmash (Explode "eye shine") ]   } flask12 = flask   { irarity  = [(1, 14), (10, 10)]   , ieffects = [ ELabel "of whiskey"-               , toOrganActorTurn "drunk" (20 + d 5)+               , toOrganActorTurn "drunk" (20 + 1 `d` 5)                , Burn 1, RefillHP 3                , OnSmash (Explode "whiskey spray") ]   } flask13 = flask   { ieffects = [ ELabel "of bait cocktail"-               , toOrganActorTurn "drunk" (20 + d 5)+               , toOrganActorTurn "drunk" (20 + 1 `d` 5)                , Burn 1, RefillHP 3                , Summon "mobile animal" 1                , OnSmash (Summon "mobile animal" 1)@@ -436,14 +440,14 @@   } flask19 = flask   { ieffects = [ ELabel "of blindness"-               , toOrganGameTurn "blind" (40 + d 10)+               , toOrganGameTurn "blind" (40 + 1 `d` 10)                , OnSmash (Explode "iron filing") ]   } flask20 = flask   { ieffects = [ ELabel "of calamity"                , toOrganNone "poisoned"-               , toOrganGameTurn "weakened" (20 + d 5)-               , toOrganGameTurn "painted red" (20 + d 5)+               , toOrganGameTurn "weakened" (20 + 1 `d` 5)+               , toOrganGameTurn "painted red" (20 + 1 `d` 5)                , OnSmash (Explode "poison cloud") ]   } @@ -489,7 +493,8 @@ potion5 = potion  -- needs to be common to show at least a portion of effects   { irarity  = [(1, 30), (10, 15)]   , ieffects = [ OneOf [ RefillHP 10, RefillHP 5, Burn 5-                       , toOrganActorTurn "strengthened" (20 + d 5) ]+                       , DropItem 1 maxBound COrgan "poisoned"+                       , toOrganActorTurn "strengthened" (20 + 1 `d` 5) ]                , OnSmash (OneOf [ Explode "dense shower"                                 , Explode "sparse shower"                                 , Explode "melee protective balm"@@ -502,7 +507,8 @@   , ieffects = [ Impress                , OneOf [ RefillCalm (-60)                        , RefillHP 20, RefillHP 10, Burn 10-                       , toOrganActorTurn "hasted" (20 + d 5) ]+                       , DropItem 1 maxBound COrgan "poisoned"+                       , toOrganActorTurn "hasted" (20 + 1 `d` 5) ]                , OnSmash (OneOf [ Explode "healing mist 2"                                 , Explode "wounding mist"                                 , Explode "distressing odor"@@ -558,11 +564,12 @@   { ifreq    = [("treasure", 100)]   , irarity  = [(5, 9), (10, 9)]  -- mixed blessing, so available early   , ieffects = [ Unique, ELabel "of Reckless Beacon"-               , Summon "hero" 1, Summon "mobile animal" (2 + d 2) ]+               , Summon "hero" 1, Summon "mobile animal" (2 + 1 `d` 2) ]+  , idesc    = "This ihdustrial wide-spectrum alarm broadcaster, if over-amped for a single powerful blast, should be able to cut through the interference and reach any lost crew members, giving them enough positional information to locate us."   } scroll2 = scroll   { irarity  = [(1, 2)]-  , ieffects = [ ELabel "of greed", Teleport 20, DetectItem 20+  , ieffects = [ ELabel "of greed", DetectItem 20, Teleport 20                , RefillCalm (-100) ]   } scroll3 = scroll@@ -590,21 +597,25 @@   } scroll8 = scroll   { irarity  = [(10, 2)]-  , ieffects = [InsertMove $ 1 + d 2 + dl 2]+  , ieffects = [InsertMove $ 1 + 1 `d` 2 + 1 `dl` 2]   } scroll9 = scroll   { irarity  = [(1, 30)]-  , ieffects = [ELabel "of scientific explanation", Identify]+  , ieffects = [ ELabel "of scientific explanation"+               , Composite [Identify, RefillCalm 10] ]+      -- your most pressing existential concerns are answered scientifitically,+      -- hence the calming effect   } scroll10 = scroll   { irarity  = [(10, 20)]   , ieffects = [ ELabel "molecular reconfiguration"-               , PolyItem, Explode "firecracker 7" ]+               , Composite [PolyItem, Explode "firecracker 7"] ]   } scroll11 = scroll   { ifreq    = [("treasure", 100)]   , irarity  = [(6, 9), (10, 9)]   , ieffects = [Unique, ELabel "of Prisoner Release", Summon "hero" 1]+  , idesc    = "This lock chip opens a nearby closet containing one of our lost crew members."   } scroll12 = scroll   { irarity  = [(1, 9), (10, 4)]@@ -626,7 +637,7 @@   , iverbHit = "prod"   , iweight  = 10000   , idamage  = toDmg 0-  , iaspects = [Timeout $ d 2 + 2 - dl 2 |*| 10]+  , iaspects = [Timeout $ (1 `d` 2 + 2 - 1 `dl` 2) * 10]   , ieffects = [Recharging (toOrganActorTurn "hasted" 1)]   , ifeature = [Durable, Applicable, Identified]   , idesc    = "Makes you vulnerable at take-off, but then you are free like a bird."@@ -642,7 +653,7 @@   , iverbHit = "smack"   , iweight  = 400   , idamage  = toDmg 0-  , iaspects = [AddHurtMelee $ d 10 |*| 3]+  , iaspects = [AddHurtMelee $ (1 `d` 10) * 3]   , ieffects = [EqpSlot EqpSlotAddHurtMelee]   , ifeature = [Identified, Equipable]   , idesc    = "Originally used for realigning the bent or buckled edges of kitchen knives in the local bars. Now it saves lives by letting you fix your weapons between or even during fights, without the need to set up camp, fish out tools and assemble a proper sharpening workshop."@@ -659,7 +670,7 @@   , iweight  = 500   , idamage  = toDmg 0   , iaspects = [ AddSight 10, AddMaxCalm 30, AddShine 2-               , Timeout $ 1 + d 2 ]+               , Timeout $ 1 + 1 `d` 2 ]   , ieffects = [ Periodic                , Recharging (toOrganNone "poisoned")                , Recharging (Summon "mobile robot" 1) ]@@ -678,7 +689,7 @@   , iweight  = 1000   , idamage  = toDmg 0   , iaspects = [ AddNocto 1-               , AddArmorMelee (dl 5 - 10), AddArmorRanged (dl 5 - 10) ]+               , AddArmorMelee (1 `dl` 5 - 10), AddArmorRanged (1 `dl` 5 - 10) ]   , ieffects = [EqpSlot EqpSlotMiscBonus]   , ifeature = [Identified, Equipable]   , idesc    = "Handheld underwater echolocator overdriven to scan dark corridors at the cost of emitting loud pings."@@ -697,9 +708,9 @@   , iverbHit = "whip"   , iweight  = 30   , idamage  = toDmg 0-  , iaspects = [ Timeout $ 1 + d 2-               , AddArmorMelee $ 2 + d 3-               , AddArmorRanged $ d 3 ]+  , iaspects = [ Timeout $ 1 + 1 `d` 2+               , AddArmorMelee $ 2 + 1 `d` 3+               , AddArmorRanged $ 1 `d` 3 ]   , ieffects = [ Unique, Periodic                , Recharging (RefillCalm 1), EqpSlot EqpSlotMiscBonus ]   , ifeature = [Durable, Precious, Identified, Equipable]@@ -726,7 +737,7 @@ necklace1 = necklace   { ifreq    = [("treasure", 100)]   , irarity  = [(3, 0), (4, 1), (10, 2)]  -- prevents camping on lvl 3-  , iaspects = [Timeout $ d 3 + 4 - dl 3 |*| 10]+  , iaspects = [Timeout $ (1 `d` 3 + 4 - 1 `dl` 3) * 10]   , ieffects = [ Unique, ELabel "of Trickle Life", EqpSlot EqpSlotMiscBonus                , Recharging (RefillHP 1) ]                ++ ieffects necklace@@ -735,57 +746,57 @@ necklace2 = necklace   { ifreq    = [("treasure", 100)]  -- just too nasty to call it useful   , irarity  = [(1, 1)]-  , iaspects = [Timeout $ d 3 + 3 - dl 3 |*| 10]-  , ieffects = [ Recharging (Summon "mobile animal" $ 1 + dl 2)+  , iaspects = [Timeout $ (1 `d` 3 + 3 - 1 `dl` 3) * 10]+  , ieffects = [ Recharging (Summon "mobile animal" $ 1 + 1 `dl` 2)                , Recharging (Explode "waste")                , Recharging Impress                , Recharging (DropItem 1 maxBound COrgan "temporary condition") ]                ++ ieffects necklace   } necklace3 = necklace-  { iaspects = [Timeout $ d 3 + 4 - dl 3 |*| 5]+  { iaspects = [Timeout $ (1 `d` 3 + 4 - 1 `dl` 3) * 5]   , ieffects = [ ELabel "of fearful listening"                , Recharging (DetectActor 20)                , Recharging (RefillCalm (-20)) ]                ++ ieffects necklace   } necklace4 = necklace-  { iaspects = [Timeout $ d 4 + 4 - dl 4 |*| 2]-  , ieffects = [Recharging (Teleport $ d 2 * 3)]+  { iaspects = [Timeout $ (1 `d` 4 + 4 - 1 `dl` 4) * 2]+  , ieffects = [Recharging (Teleport $ 3 `d` 2)]                ++ ieffects necklace   } necklace5 = necklace-  { iaspects = [Timeout $ d 3 + 4 - dl 3 |*| 10]+  { iaspects = [Timeout $ (1 `d` 3 + 4 - 1 `dl` 3) * 10]   , ieffects = [ ELabel "of escape"-               , Recharging (Teleport $ 14 + d 3 * 3)+               , Recharging (Teleport $ 14 + 3 `d` 3)                , Recharging (DetectExit 20)                , Recharging (RefillHP (-2)) ]  -- prevent micromanagement                ++ ieffects necklace   } necklace6 = necklace-  { iaspects = [Timeout $ d 3 + 1 |*| 2]+  { iaspects = [Timeout $ (1 `d` 3 + 1) * 2]   , ieffects = [Recharging (PushActor (ThrowMod 100 50))]                ++ ieffects necklace   } necklace7 = necklace   { ifreq    = [("treasure", 100)]-  , iaspects = [ AddMaxHP $ 10 + d 10+  , iaspects = [ AddMaxHP $ 10 + 1 `d` 10                , AddArmorMelee 20, AddArmorRanged 10-               , Timeout $ d 2 + 5 - dl 3 ]+               , Timeout $ 1 `d` 2 + 5 - 1 `dl` 3 ]   , ieffects = [ Unique, ELabel "of Overdrive", EqpSlot EqpSlotAddSpeed-               , Recharging (InsertMove $ 1 + d 2)+               , Recharging (InsertMove $ 1 + 1 `d` 2)                , Recharging (RefillHP (-1))                , Recharging (RefillCalm (-1)) ]  -- fake "hears something" :)                ++ ieffects necklace   , ifeature = Durable : ifeature necklace   } necklace8 = necklace-  { iaspects = [Timeout $ d 3 + 3 - dl 3 |*| 5]+  { iaspects = [Timeout $ (1 `d` 3 + 3 - 1 `dl` 3) * 5]   , ieffects = [Recharging $ Explode "spark"]                ++ ieffects necklace   } necklace9 = necklace-  { iaspects = [Timeout $ d 3 + 3 - dl 3 |*| 5]+  { iaspects = [Timeout $ (1 `d` 3 + 3 - 1 `dl` 3) * 5]   , ieffects = [Recharging $ Explode "fragrance"]                ++ ieffects necklace   }@@ -802,7 +813,7 @@   , iverbHit = "rattle"   , iweight  = 700   , idamage  = toDmg 0-  , iaspects = [AddNocto 1, AddSight (-1), AddArmorMelee $ 1 + dl 3 |*| 3]+  , iaspects = [AddNocto 1, AddSight (-1), AddArmorMelee $ (1 + 1 `dl` 3) * 3]   , ieffects = [EqpSlot EqpSlotMiscBonus]   , ifeature = [Precious, Identified, Durable, Equipable]   , idesc    = "Sturdy antique night vision goggles of unknown origin. Wired to run on modern micro-cells."@@ -818,7 +829,7 @@   , iverbHit = "rap"   , iweight  = 50   , idamage  = toDmg 0-  , iaspects = [AddSight $ 1 + d 2, AddHurtMelee $ d 2 |*| 3]+  , iaspects = [AddSight $ 1 + 1 `d` 2, AddHurtMelee $ (1 `d` 2) * 3]   , ieffects = [EqpSlot EqpSlotAddSight]   , ifeature = [Precious, Identified, Durable, Equipable]   , idesc    = "Zooms on any movement, distant or close. Requires some getting used to. Never needs to be taken off."@@ -856,29 +867,30 @@   } ring1 = ring   { irarity  = [(10, 2)]-  , iaspects = [AddSpeed $ 1 + d 2, AddMaxHP $ dl 7 - 7 - d 7]+  , iaspects = [AddSpeed $ 1 + 1 `d` 2, AddMaxHP $ 1 `dl` 7 - 7 - 1 `d` 7]   , ieffects = [ Explode "distortion"  -- high power                , EqpSlot EqpSlotAddSpeed ]   } ring2 = ring-  { irarity  = [(10, 5)]-  , iaspects = [AddMaxHP $ 10 + dl 10, AddMaxCalm $ dl 5 - 20 - d 5]+  { irarity  = [(10, 8)]+  , iaspects = [AddMaxHP $ 10 + 1 `dl` 10, AddMaxCalm $ 1 `dl` 5 - 20 - 1 `d` 5]   , ieffects = [Explode "blast 20", EqpSlot EqpSlotAddMaxHP]   } ring3 = ring-  { irarity  = [(10, 5)]-  , iaspects = [AddMaxCalm $ 29 + dl 10]+  { irarity  = [(10, 3)]+  , iaspects = [AddMaxCalm $ 29 + 1 `dl` 10]   , ieffects = [Explode "blast 20", EqpSlot EqpSlotMiscBonus]   , idesc    = "Cold, solid to the touch, perfectly round, engraved with solemn, strangely comforting, worn out words."   } ring4 = ring   { irarity  = [(3, 3), (10, 5)]-  , iaspects = [AddHurtMelee $ d 5 + dl 5 |*| 3, AddMaxHP $ dl 3 - 5 - d 3]+  , iaspects = [ AddHurtMelee $ (1 `d` 5 + 1 `dl` 5) * 3+               , AddMaxHP $ 1 `dl` 3 - 5 - 1 `d` 3 ]   , ieffects = [Explode "blast 20", EqpSlot EqpSlotAddHurtMelee]   } ring5 = ring  -- by the time it's found, probably no space in eqp   { irarity  = [(5, 0), (10, 2)]-  , iaspects = [AddShine $ d 2]+  , iaspects = [AddShine $ 1 `d` 2]   , ieffects = [ Explode "distortion"  -- high power                , EqpSlot EqpSlotLightSource ]   , idesc    = "A sturdy ring with a large, shining stone."@@ -886,8 +898,8 @@ ring6 = ring   { ifreq    = [("treasure", 100)]   , irarity  = [(10, 2)]-  , iaspects = [ AddSpeed $ 3 + d 4-               , AddMaxCalm $ - 20 - d 20, AddMaxHP $ - 20 - d 20 ]+  , iaspects = [ AddSpeed $ 3 + 1 `d` 4+               , AddMaxCalm $ - 20 - 1 `d` 20, AddMaxHP $ - 20 - 1 `d` 20 ]   , ieffects = [ Unique, ELabel "of Rush"  -- no explosion, because Durable                , EqpSlot EqpSlotAddSpeed ]   , ifeature = Durable : ifeature ring@@ -922,8 +934,8 @@   , iweight  = 7000   , idamage  = toDmg 0   , iaspects = [ AddHurtMelee (-2)-               , AddArmorMelee $ 1 + d 2 + dl 2 |*| 5-               , AddArmorRanged $ dl 3 |*| 3 ]+               , AddArmorMelee $ (1 + 1 `d` 2 + 1 `dl` 2) * 5+               , AddArmorRanged $ (1 `dl` 3) * 3 ]   , ieffects = [EqpSlot EqpSlotAddArmorMelee]   , ifeature = [Durable, Identified, Equipable]   , idesc    = "A hard-shell torso segment cut from a disposed off spacesuit."@@ -937,8 +949,8 @@   , iweight  = 12000   , idamage  = toDmg 0   , iaspects = [ AddHurtMelee (-3)-               , AddArmorMelee $ 1 + d 2 + dl 2 |*| 5-               , AddArmorRanged $ 2 + d 2 + dl 3 |*| 3 ]+               , AddArmorMelee $ (1 + 1 `d` 2 + 1 `dl` 2) * 5+               , AddArmorRanged $ (2 + 1 `d` 2 + 1 `dl` 3) * 3 ]   , ieffects = [EqpSlot EqpSlotAddArmorRanged]   , ifeature = [Durable, Identified, Equipable]   , idesc    = "A civilian bulletproof vest. Discourages foes from attacking your torso, making it harder for them to land a blow."@@ -952,9 +964,9 @@   , irarity  = [(5, 9), (10, 9)]   , iverbHit = "flap"   , iweight  = 100-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [ AddHurtMelee $ d 2 + dl 7 |*| 3-               , AddArmorRanged $ dl 2 |*| 3 ]+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [ AddHurtMelee $ (1 `d` 2 + 1 `dl` 7) * 3+               , AddArmorRanged $ (1 `dl` 2) * 3 ]   , ieffects = [EqpSlot EqpSlotAddHurtMelee]   , ifeature = [ toVelocity 50  -- flaps and flutters                , Durable, Identified, Equipable ]@@ -967,9 +979,9 @@   , iflavour = zipPlain [BrCyan]   , irarity  = [(1, 9), (10, 3)]   , iweight  = 300-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [ AddArmorMelee $ 2 + dl 2 |*| 5-               , AddArmorRanged $ dl 1 |*| 3 ]+  , idamage  = toDmg $ 2 `d` 1+  , iaspects = [ AddArmorMelee $ (2 + 1 `dl` 2) * 5+               , AddArmorRanged $ (1 `dl` 1) * 3 ]   , ieffects = [EqpSlot EqpSlotAddArmorMelee]   , idesc    = "A piece of a hull maintenance spacesuit, padded and reinforced with carbon fibre."   }@@ -979,10 +991,10 @@   , iflavour = zipFancy [BrRed]   , irarity  = [(1, 3), (10, 3)]   , iweight  = 1000-  , idamage  = toDmg $ 3 * d 1-  , iaspects = [ AddHurtMelee $ dl 4 - 6 |*| 3-               , AddArmorMelee $ 2 + d 2 + dl 2 |*| 5-               , AddArmorRanged $ dl 2 |*| 3 ]+  , idamage  = toDmg $ 3 `d` 1+  , iaspects = [ AddHurtMelee $ (1 `dl` 4 - 6) * 3+               , AddArmorMelee $ (2 + 1 `d` 2 + 1 `dl` 2) * 5+               , AddArmorRanged $ (1 `dl` 2) * 3 ]   , ieffects = [Unique, EqpSlot EqpSlotAddArmorMelee]   , idesc    = "Rigid, bulky handgear embedding a welding equipment, complete with an affixed small shield and a darkened visor. Awe-inspiring."   }@@ -1004,10 +1016,10 @@   , irarity  = [(4, 6)]   , iverbHit = "bash"   , iweight  = 2000-  , idamage  = [(96, 2 * d 1), (3, 4 * d 1), (1, 8 * d 1)]+  , idamage  = [(96, 2 `d` 1), (3, 4 `d` 1), (1, 8 `d` 1)]   , iaspects = [ AddArmorMelee 40  -- not enough to compensate; won't be in eqp                , AddHurtMelee (-30)  -- too harmful; won't be wielded as weapon-               , Timeout $ d 3 + 3 - dl 3 |*| 2 ]+               , Timeout $ (1 `d` 3 + 3 - 1 `dl` 3) * 2 ]   , ieffects = [ Recharging (PushActor (ThrowMod 200 50))                , EqpSlot EqpSlotAddArmorMelee ]   , ifeature = [ toVelocity 50  -- unwieldy to throw@@ -1020,10 +1032,10 @@   , irarity  = [(8, 3)]   , iflavour = zipPlain [Green]   , iweight  = 3000-  , idamage  = [(96, 4 * d 1), (3, 8 * d 1), (1, 16 * d 1)]+  , idamage  = [(96, 4 `d` 1), (3, 8 `d` 1), (1, 16 `d` 1)]   , iaspects = [ AddArmorMelee 80  -- not enough to compensate; won't be in eqp                , AddHurtMelee (-70)  -- too harmful; won't be wielded as weapon-               , Timeout $ d 6 + 6 - dl 6 |*| 2 ]+               , Timeout $ (1 `d` 6 + 6 - 1 `dl` 6) * 2 ]   , ieffects = [ Recharging (PushActor (ThrowMod 400 50))                , EqpSlot EqpSlotAddArmorMelee ]   , ifeature = [ toVelocity 50  -- unwieldy to throw@@ -1042,9 +1054,9 @@   , irarity  = [(3 * 10/12, 50), (4 * 10/12, 1)]   , iverbHit = "stab"   , iweight  = 1000-  , idamage  = toDmg $ 6 * d 1-  , iaspects = [ AddHurtMelee $ d 3 + dl 3 |*| 3-               , AddArmorMelee $ d 2 |*| 5 ]+  , idamage  = toDmg $ 6 `d` 1+  , iaspects = [ AddHurtMelee $ (1 `d` 3 + 1 `dl` 3) * 3+               , AddArmorMelee $ (1 `d` 2) * 5 ]   , ieffects = [EqpSlot EqpSlotWeapon]   , ifeature = [ toVelocity 40  -- ensuring it hits with the tip costs speed                , Durable, Identified, Meleeable ]@@ -1063,7 +1075,7 @@   -- If the effect is very powerful and so the timeout has to be significant,   -- let's make it really large, for the effect to occur only once in a fight:   -- as soon as the item is equipped, or just on the first strike.-  , iaspects = iaspects dagger ++ [Timeout $ d 3 + 4 - dl 3 |*| 2]+  , iaspects = iaspects dagger ++ [Timeout $ (1 `d` 3 + 4 - 1 `dl` 3) * 2]   , ieffects = ieffects dagger                ++ [ Unique                   , Recharging DropBestWeapon, Recharging $ RefillCalm (-3) ]@@ -1079,8 +1091,8 @@                  -- don't make it too common on lvl 3   , iverbHit = "club"   , iweight  = 1600-  , idamage  = [(96, 8 * d 1), (3, 12 * d 1), (1, 16 * d 1)]-  , iaspects = [AddHurtMelee $ d 2 + dl 2 |*| 3]+  , idamage  = [(96, 8 `d` 1), (3, 12 `d` 1), (1, 16 `d` 1)]+  , iaspects = [AddHurtMelee $ (1 `d` 2 + 1 `dl` 2) * 3]   , ieffects = [EqpSlot EqpSlotWeapon]   , ifeature = [ toVelocity 40  -- ensuring it hits with the tip costs speed                , Durable, Identified, Meleeable ]@@ -1091,16 +1103,17 @@   { iname    = "Concussion Hammer"   , ifreq    = [("treasure", 20)]   , irarity  = [(5, 1), (10, 6)]-  , idamage  = toDmg $ 8 * d 1-  , iaspects = iaspects hammer ++ [Timeout $ d 2 + 3 - dl 2 |*| 2]+  , idamage  = toDmg $ 8 `d` 1+  , iaspects = iaspects hammer ++ [Timeout $ (1 `d` 2 + 3 - 1 `dl` 2) * 2]   , ieffects = ieffects hammer ++ [Unique, Recharging $ Paralyze 10]   } hammerSpark = hammer   { iname    = "Grand Smithhammer"   , ifreq    = [("treasure", 20)]   , irarity  = [(5, 1), (10, 6)]-  , idamage  = toDmg $ 8 * d 1-  , iaspects = iaspects hammer ++ [AddShine 3, Timeout $ d 4 + 4 - dl 4 |*| 2]+  , idamage  = toDmg $ 12 `d` 1+  , iaspects = iaspects hammer+               ++ [AddShine 3, Timeout $ (1 `d` 4 + 4 - 1 `dl` 4) * 2]   , ieffects = ieffects hammer ++ [Unique, Recharging $ Explode "spark"]   } sword = ItemKind@@ -1112,7 +1125,7 @@   , irarity  = [(4, 1), (5, 15)]   , iverbHit = "slash"   , iweight  = 2000-  , idamage  = toDmg $ 10 * d 1+  , idamage  = toDmg $ 10 `d` 1   , iaspects = []   , ieffects = [EqpSlot EqpSlotWeapon]   , ifeature = [ toVelocity 40  -- ensuring it hits with the tip costs speed@@ -1125,16 +1138,15 @@   , iname    = "Master's Sword"   , ifreq    = [("treasure", 20)]   , irarity  = [(5, 1), (10, 6)]-  , iaspects = [Timeout $ d 4 + 5 - dl 4 |*| 2]-  , ieffects = ieffects sword-               ++ [Unique, Recharging Impress, Recharging (DetectActor 5)]+  , iaspects = [Timeout $ (1 `d` 4 + 5 - 1 `dl` 4) * 2]+  , ieffects = ieffects sword ++ [Unique, Recharging Impress]   } swordNullify = sword   { isymbol  = symbolEdged   , iname    = "Gutting Sword"   , ifreq    = [("treasure", 20)]   , irarity  = [(5, 1), (10, 6)]-  , iaspects = [Timeout $ d 4 + 5 - dl 4 |*| 2]+  , iaspects = [Timeout $ (1 `d` 4 + 5 - 1 `dl` 4) * 2]   , ieffects = ieffects sword                ++ [ Unique                   , Recharging $ DropItem 1 maxBound COrgan@@ -1150,9 +1162,9 @@   , irarity  = [(8, 1), (9, 40)]   , iverbHit = "impale"   , iweight  = 3000-  , idamage  = [(96, 12 * d 1), (3, 18 * d 1), (1, 24 * d 1)]+  , idamage  = [(96, 12 `d` 1), (3, 18 `d` 1), (1, 24 `d` 1)]   , iaspects = [ AddHurtMelee (-20)  -- just benign enough to be used-               , AddArmorMelee $ 1 + dl 3 |*| 5 ]+               , AddArmorMelee $ (1 + 1 `dl` 3) * 5 ]   , ieffects = [EqpSlot EqpSlotWeapon]   , ifeature = [ toVelocity 20  -- not balanced                , Durable, Identified, Meleeable ]@@ -1163,8 +1175,8 @@   { iname    = "Swiss Halberd"   , ifreq    = [("treasure", 20)]   , irarity  = [(8, 1), (9, 20)]-  , idamage  = toDmg $ 12 * d 1-  , iaspects = iaspects halberd ++ [Timeout $ d 5 + 5 - dl 5 |*| 2]+  , idamage  = toDmg $ 12 `d` 1+  , iaspects = iaspects halberd ++ [Timeout $ (1 `d` 5 + 5 - 1 `dl` 5) * 2]   , ieffects = ieffects halberd                ++ [Unique, Recharging (PushActor (ThrowMod 400 25))]   , idesc    = "A perfect replica made for a reenactor troupe, missing only some sharpening. Versatile, with great reach and leverage. Foes are held at a distance."@@ -1211,8 +1223,8 @@   , iaspects = [AddShine 1, AddSpeed (-1)]                  -- reflects strongly, distracts; so it glows in the dark,                  -- is visible on dark floor, but not too tempting to wear-  , ieffects = []-  , ifeature = [Precious]  -- no @Identified@ and no effects, so never ided+  , ieffects = [RefillCalm (-1)]  -- minor effect to ensure no id-on-pickup+  , ifeature = [Precious]  -- no @Identified@, so kind not known   , idesc    = "Precious, though useless. Worth around 100 gold grains."   , ikit     = []   }@@ -1243,7 +1255,7 @@   , iname    = "gold grain"   , ifreq    = [("treasure", 100), ("currency", 100)]   , iflavour = zipPlain [BrYellow]-  , icount   = 10 + d 20 + dl 20+  , icount   = 10 + 1 `d` 20 + 1 `dl` 20   , irarity  = [(1, 25), (10, 10)]   , iverbHit = "tap"   , iweight  = 1@@ -1262,12 +1274,12 @@   , iname    = "needle"   , ifreq    = [("needle", 1)]  -- special; TODO: fast when fired, not thrown   , iflavour = zipPlain [BrBlue]-  , icount   = 9 * d 3+  , icount   = 9 `d` 3   , irarity  = [(1, 1)]   , iverbHit = "prick"   , iweight  = 3-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [AddHurtMelee (-10 |*| 5)]+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [AddHurtMelee $ -10 * 5]   , ieffects = []   , ifeature = [toVelocity 70, Fragile]   , idesc    = "A long hypodermic needle ending in a dried out micro-syringe. It's too light to throw hard, but it penetrates deeply, causing intense pain on movement."@@ -1279,7 +1291,7 @@   , iflavour = zipPlain [BrRed]   , irarity  = [(1, 1)]   , iaspects = []-  , ieffects = [Summon "construction robot" $ 1 + dl 2]+  , ieffects = [Summon "construction robot" $ 1 + 1 `dl` 2]   , ifeature = Identified : ifeature scroll   , idesc    = "The single-use electronic overdrive hooter that construction robots use to warn about danger and call help in extreme emergency."   , ikit     = []
GameDefinition/Content/ItemKindActor.hs view
@@ -305,14 +305,15 @@   , iverbHit = "thud"   , iweight  = 80000   , idamage  = toDmg 0-  , iaspects = [ AddMaxHP 20, AddMaxCalm 30, AddSpeed 20, AddNocto 2+  , iaspects = [ AddMaxHP 10, AddMaxCalm 30, AddSpeed 20, AddNocto 2                , AddAbility AbAlter (-2) ]  -- can't use stairs nor doors   , ieffects = []   , ifeature = [Durable, Identified]   , idesc    = ""   , ikit     = [ ("hooked claw", COrgan), ("snout", COrgan)-               , ("armored skin", COrgan), ("nostril", COrgan)-               , ("eye 3", COrgan), ("animal brain", COrgan) ]+               , ("armored skin", COrgan), ("armored skin", COrgan)+               , ("nostril", COrgan), ("eye 3", COrgan)+               , ("animal brain", COrgan) ]   } gilaMonster = ItemKind   { isymbol  = 'g'@@ -586,7 +587,7 @@ medbotFaucet = ItemKind   { isymbol  = 'f'   , iname    = "nano medbot faucet"-  , ifreq    = [("robot", 10), ("immobile robot", 90)]+  , ifreq    = [("robot", 10), ("immobile robot", 110)]   , iflavour = zipPlain [BrYellow]   , icount   = 1   , irarity  = [(1, 10), (5 * 10/12, 10)]
GameDefinition/Content/ItemKindBlast.hs view
@@ -80,13 +80,13 @@   , iname    = "firecracker"   , ifreq    = [(toGroupName $ "firecracker" <+> tshow n, 1)]   , iflavour = zipPlain [brightCol !! (n `mod` length brightCol)]-  , icount   = intToDice (n `div` 6) + d (n `div` 2)+  , icount   = intToDice (n `div` 6) + 1 `d` (n `div` 2)   , irarity  = [(1, 1)]   , iverbHit = "crack"   , iweight  = 1   , idamage  = toDmg 0   , iaspects = [AddShine $ intToDice $ n `div` 2]-  , ieffects = [ RefillCalm (-1) | n >= 5 ]+  , ieffects = [ RefillCalm (3 - n) | n >= 5 ]                ++ [ DropBestWeapon | n >= 5]                ++ [ OnSmash $ Explode                     $ toGroupName $ "firecracker" <+> tshow (n - 1)@@ -229,7 +229,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [Teleport $ 15 + d 10]+  , ieffects = [Teleport $ 15 + 1 `d` 10]   , ifeature = [toLinger 10, Fragile, Identified]  -- 2 steps, 1 turn   , idesc    = ""   , ikit     = []@@ -348,7 +348,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "strengthened" (3 + d 3)]+  , ieffects = [toOrganActorTurn "strengthened" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -364,7 +364,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganGameTurn "weakened" (3 + d 3)]+  , ieffects = [toOrganGameTurn "weakened" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -380,7 +380,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "protected from melee" (3 + d 3)]+  , ieffects = [toOrganActorTurn "protected from melee" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -396,7 +396,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "protected from ranged" (3 + d 3)]+  , ieffects = [toOrganActorTurn "protected from ranged" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -412,7 +412,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganGameTurn "painted red" (3 + d 3)]+  , ieffects = [toOrganGameTurn "painted red" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -428,7 +428,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "resolute" (3 + d 3)]+  , ieffects = [toOrganActorTurn "resolute" (3 + 1 `d` 3)]                  -- short enough duration that @calmEnough@ not a big problem   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""@@ -445,7 +445,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "hasted" (3 + d 3)]+  , ieffects = [toOrganActorTurn "hasted" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -461,7 +461,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganGameTurn "slowed" (3 + d 3)]+  , ieffects = [toOrganGameTurn "slowed" (3 + 1 `d` 3)]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn, mist   , idesc    = ""   , ikit     = []@@ -477,7 +477,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "far-sighted" (3 + d 3)]+  , ieffects = [toOrganActorTurn "far-sighted" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -493,7 +493,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "blind" (10 + d 10)]+  , ieffects = [toOrganActorTurn "blind" (10 + 1 `d` 10)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -509,7 +509,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "keen-smelling" (3 + d 3)]+  , ieffects = [toOrganActorTurn "keen-smelling" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -525,7 +525,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "shiny-eyed" (3 + d 3)]+  , ieffects = [toOrganActorTurn "shiny-eyed" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -544,7 +544,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [toOrganActorTurn "drunk" (3 + d 3)]+  , ieffects = [toOrganActorTurn "drunk" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []@@ -560,7 +560,7 @@   , iweight  = 1   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [Burn (-1)]+  , ieffects = [Burn 1]   , ifeature = [toLinger 10, Fragile, Identified]   , idesc    = ""   , ikit     = []
GameDefinition/Content/ItemKindEmbed.hs view
@@ -185,7 +185,7 @@ staircaseTrapDown = staircaseTrapUp   { ifreq    = [("staircase trap down", 1)]   , ieffects = [ Temporary "tumble down the stairwell"-               , toOrganActorTurn "drunk" (20 + d 5) ]+               , toOrganActorTurn "drunk" (20 + 1 `d` 5) ]   } doorwayTrap = ItemKind   { isymbol  = '^'@@ -198,9 +198,9 @@   , iweight  = 10000   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [OneOf [ RefillCalm (-20)-                      , toOrganActorTurn "slowed" (20 + d 5)-                      , toOrganActorTurn "weakened" (20 + d 5) ]]+  , ieffects = [OneOf [ toOrganActorTurn "blind" (20 + 1 `d` 5)+                      , toOrganActorTurn "slowed" (20 + 1 `d` 5)+                      , toOrganActorTurn "weakened" (20 + 1 `d` 5) ]]   , ifeature = [Identified]  -- not Durable, springs at most once   , idesc    = ""   , ikit     = []@@ -219,7 +219,7 @@   , ieffects = [ Temporary "enter unexplainable rage at a glimpse of the inscrutable graffiti"                , RefillCalm (-20)                , Recharging $ OneOf-                   [ toOrganActorTurn "strengthened" (3 + d 3)+                   [ toOrganActorTurn "strengthened" (3 + 1 `d` 3)                    , CreateItem CInv "sandstone rock" TimerNone ] ]   , ifeature = [Identified, Durable]   , idesc    = ""@@ -238,8 +238,8 @@   , iaspects = [Timeout 7]   , ieffects = [ Temporary "be entranced by the subtle mural"                , RefillCalm 2-               , Recharging $ toOrganActorTurn "far-sighted" (3 + d 3)-               , Recharging $ toOrganActorTurn "keen-smelling" (3 + d 3) ]+               , Recharging $ toOrganActorTurn "far-sighted" (3 + 1 `d` 3)+               , Recharging $ toOrganActorTurn "keen-smelling" (3 + 1 `d` 3) ]   , ifeature = [Identified, Durable]   , idesc    = ""   , ikit     = []@@ -271,10 +271,10 @@   , iweight  = 10000   , idamage  = toDmg 0   , iaspects = []-  , ieffects = [ CreateItem CInv "any scroll" TimerNone+  , ieffects = [ CreateItem CGround "any scroll" TimerNone                , Detect 20                , Paralyze 20-               , toOrganActorTurn "drunk" (20 + d 5) ]+               , toOrganActorTurn "drunk" (20 + 1 `d` 5) ]   , ifeature = [Identified]  -- not Durable, springs at most once   , idesc    = ""   , ikit     = []
GameDefinition/Content/ItemKindOrgan.hs view
@@ -3,7 +3,7 @@ -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. ----- | Organ definitions.+-- | Actor organ definitions. module Content.ItemKindOrgan   ( organs   ) where@@ -42,7 +42,7 @@   , irarity  = [(1, 1)]   , iverbHit = "punch"   , iweight  = 2000-  , idamage  = toDmg $ 4 * d 1+  , idamage  = toDmg $ 4 `d` 1   , iaspects = []   , ieffects = []   , ifeature = [Durable, Identified, Meleeable]@@ -53,7 +53,7 @@   { iname    = "foot"   , ifreq    = [("foot", 50)]   , iverbHit = "kick"-  , idamage  = toDmg $ 4 * d 1+  , idamage  = toDmg $ 4 `d` 1   , idesc    = ""   } @@ -64,8 +64,8 @@   , ifreq    = [("hooked claw", 50)]   , icount   = 2  -- even if more, only the fore claws used for fighting   , iverbHit = "hook"-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [Timeout $ 4 + d 4]+  , idamage  = toDmg $ 2 `d` 1+  , iaspects = [Timeout $ 4 + 1 `d` 4]   , ieffects = [Recharging (toOrganGameTurn "slowed" 2)]   , idesc    = ""   }@@ -73,7 +73,7 @@   { iname    = "small claw"   , ifreq    = [("small claw", 50)]   , iverbHit = "slash"-  , idamage  = toDmg $ 2 * d 1+  , idamage  = toDmg $ 2 `d` 1   , idesc    = ""   } snout = fist@@ -81,7 +81,7 @@   , ifreq    = [("snout", 10)]   , icount   = 1   , iverbHit = "bite"-  , idamage  = toDmg $ 2 * d 1+  , idamage  = toDmg $ 2 `d` 1   , idesc    = ""   } smallJaw = fist@@ -89,7 +89,7 @@   , ifreq    = [("small jaw", 20)]   , icount   = 1   , iverbHit = "rip"-  , idamage  = toDmg $ 3 * d 1+  , idamage  = toDmg $ 3 `d` 1   , idesc    = ""   } jaw = fist@@ -97,7 +97,7 @@   , ifreq    = [("jaw", 20)]   , icount   = 1   , iverbHit = "rip"-  , idamage  = toDmg $ 5 * d 1+  , idamage  = toDmg $ 5 `d` 1   , idesc    = ""   } largeJaw = fist@@ -105,7 +105,7 @@   , ifreq    = [("large jaw", 100)]   , icount   = 1   , iverbHit = "crush"-  , idamage  = toDmg $ 10 * d 1+  , idamage  = toDmg $ 10 `d` 1   , idesc    = ""   } horn = fist@@ -113,7 +113,7 @@   , ifreq    = [("horn", 20)]   , icount   = 2   , iverbHit = "impale"-  , idamage  = toDmg $ 6 * d 1+  , idamage  = toDmg $ 6 `d` 1   , iaspects = [AddHurtMelee 20]   , idesc    = ""   }@@ -125,40 +125,40 @@   , ifreq    = [("tentacle", 50)]   , icount   = 4   , iverbHit = "slap"-  , idamage  = toDmg $ 4 * d 1+  , idamage  = toDmg $ 4 `d` 1   , idesc    = ""   } thorn = fist   { iname    = "thorn"   , ifreq    = [("thorn", 100)]-  , icount   = 2 + d 3+  , icount   = 2 + 1 `d` 3   , iverbHit = "impale"-  , idamage  = toDmg $ 1 * d 3+  , idamage  = toDmg $ 1 `d` 3   , ifeature = [Identified, Meleeable]  -- not Durable   , idesc    = ""   } boilingFissure = fist   { iname    = "fissure"   , ifreq    = [("boiling fissure", 100)]-  , icount   = 5 + d 5+  , icount   = 5 + 1 `d` 5   , iverbHit = "hiss at"-  , idamage  = toDmg $ 1 * d 1+  , idamage  = toDmg $ 1 `d` 1   , iaspects = [AddHurtMelee 20]  -- decreasing as count decreases-  , ieffects = [InsertMove $ 1 * d 3]+  , ieffects = [InsertMove $ 1 `d` 3]   , ifeature = [Identified, Meleeable]  -- not Durable   , idesc    = ""   } arsenicFissure = boilingFissure   { iname    = "fissure"   , ifreq    = [("biogas fissure", 100)]-  , icount   = 3 + d 3-  , idamage  = toDmg $ 2 * d 1-  , ieffects = [toOrganGameTurn "weakened" (2 + d 2)]+  , icount   = 3 + 1 `d` 3+  , idamage  = toDmg $ 2 `d` 1+  , ieffects = [toOrganGameTurn "weakened" (2 + 1 `d` 2)]   } sulfurFissure = boilingFissure   { iname    = "fissure"   , ifreq    = [("medbot fissure", 100)]-  , icount   = 2 + d 2+  , icount   = 2 + 1 `d` 2   , idamage  = toDmg 0   , ieffects = [RefillHP 5]   }@@ -178,8 +178,8 @@   , ifreq    = [("sting", 100)]   , icount   = 1   , iverbHit = "sting"-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [Timeout $ 1 + d 5, AddHurtMelee 40]+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [Timeout $ 1 + 1 `d` 5, AddHurtMelee 40]   , ieffects = [Recharging (Paralyze 4)]   , idesc    = "Painful, debilitating and harmful."   }@@ -188,9 +188,9 @@   , ifreq    = [("venom tooth", 100)]   , icount   = 2   , iverbHit = "bite"-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [Timeout $ 5 + d 3]-  , ieffects = [Recharging (toOrganGameTurn "slowed" (3 + d 3))]+  , idamage  = toDmg $ 2 `d` 1+  , iaspects = [Timeout $ 5 + 1 `d` 3]+  , ieffects = [Recharging (toOrganGameTurn "slowed" (3 + 1 `d` 3))]   , idesc    = ""   } venomFang = fist@@ -198,8 +198,8 @@   , ifreq    = [("venom fang", 100)]   , icount   = 2   , iverbHit = "bite"-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [Timeout $ 7 + d 5]+  , idamage  = toDmg $ 2 `d` 1+  , iaspects = [Timeout $ 7 + 1 `d` 5]   , ieffects = [Recharging (toOrganNone "poisoned")]   , idesc    = ""   }@@ -208,9 +208,9 @@   , ifreq    = [("screeching beak", 100)]   , icount   = 1   , iverbHit = "peck"-  , idamage  = toDmg $ 2 * d 1-  , iaspects = [Timeout $ 5 + d 5]-  , ieffects = [Recharging $ Summon "scavenger" $ 1 + dl 2]+  , idamage  = toDmg $ 2 `d` 1+  , iaspects = [Timeout $ 5 + 1 `d` 5]+  , ieffects = [Recharging $ Summon "scavenger" $ 1 + 1 `dl` 2]   , idesc    = ""   } largeTail = fist@@ -218,8 +218,8 @@   , ifreq    = [("large tail", 50)]   , icount   = 1   , iverbHit = "knock"-  , idamage  = toDmg $ 6 * d 1-  , iaspects = [Timeout $ 1 + d 3, AddHurtMelee 20]+  , idamage  = toDmg $ 6 `d` 1+  , iaspects = [Timeout $ 1 + 1 `d` 3, AddHurtMelee 20]   , ieffects = [Recharging (PushActor (ThrowMod 400 25))]   , idesc    = ""   }@@ -295,7 +295,7 @@   { iname    = "insect mortality"   , ifreq    = [("insect mortality", 100)]   , iverbHit = "age"-  , iaspects = [Timeout $ 40 + d 10]+  , iaspects = [Timeout $ 40 + 1 `d` 10]   , ieffects = [Periodic, Recharging (RefillHP (-1))]   , idesc    = ""   }@@ -336,7 +336,7 @@   { iname    = "scent gland"   , ifreq    = [("scent gland", 100)]   , iverbHit = "spray at"-  , iaspects = [Timeout $ 10 + d 2 |*| 5 ]+  , iaspects = [Timeout $ (10 + 1 `d` 2) * 5 ]   , ieffects = [ Periodic, Recharging (Explode "distressing odor")                , Recharging ApplyPerfume ]   , idesc    = ""@@ -346,7 +346,7 @@   , ifreq    = [("boiling vent", 100)]   , iflavour = zipPlain [BrBlue]   , iverbHit = "menace"-  , iaspects = [Timeout $ 2 + d 2 |*| 5]+  , iaspects = [Timeout $ (2 + 1 `d` 2) * 5]   , ieffects = [Periodic                , Recharging (Explode "boiling water")                , Recharging (RefillHP 2) ]@@ -357,7 +357,7 @@   , ifreq    = [("biogas vent", 100)]   , iflavour = zipPlain [BrGreen]   , iverbHit = "menace"-  , iaspects = [Timeout $ 2 + d 2 |*| 5]+  , iaspects = [Timeout $ (2 + 1 `d` 2) * 5]   , ieffects = [ Periodic                , Recharging (Explode "sparse shower")                , Recharging (RefillHP 2) ]@@ -368,7 +368,7 @@   , ifreq    = [("medbot vent", 100)]   , iflavour = zipPlain [BrYellow]   , iverbHit = "menace"-  , iaspects = [Timeout $ 2 + d 2 |*| 5]+  , iaspects = [Timeout $ (2 + 1 `d` 2) * 5]   , ieffects = [ Periodic                , Recharging (Explode "dense shower")                , Recharging (RefillHP 2) ]@@ -390,9 +390,9 @@ razor = fist   { iname    = "razor"   , ifreq    = [("razor", 100)]-  , icount   = 2 + d 5+  , icount   = 2 + 1 `d` 5   , iverbHit = "slice"-  , idamage  = toDmg $ 2 * d 1+  , idamage  = toDmg $ 2 `d` 1   , idesc    = ""   } liveWire = fist@@ -400,8 +400,8 @@   , ifreq    = [("live wire", 100)]   , icount   = 1   , iverbHit = "shock"-  , idamage  = toDmg $ 1 * d 1-  , iaspects = [Timeout $ 3 + d 3, AddHurtMelee 20]+  , idamage  = toDmg $ 1 `d` 1+  , iaspects = [Timeout $ 3 + 1 `d` 3, AddHurtMelee 20]   , ieffects = [ Recharging (DropItem 1 maxBound COrgan "temporary condition")                , Recharging $ RefillHP (-2)                ]@@ -419,7 +419,7 @@   { iname    = "waste container"   , ifreq    = [("waste container", 100)]   , iverbHit = "spill over"-  , iaspects = [Timeout $ 5 + d 5 |*| 10]+  , iaspects = [Timeout $ (5 + 1 `d` 5) * 10]   , ieffects = [ Periodic                , Recharging (Summon "mobile animal" 1)                , Recharging (RefillHP 1)
GameDefinition/Content/ItemKindTemporary.hs view
@@ -71,22 +71,22 @@                          ] tmpRegenerating =   let tmp = tmpAs "regenerating" []-  in tmp { icount = 4 + d 2+  in tmp { icount = 4 + 1 `d` 2          , ieffects = Recharging (RefillHP 1) : ieffects tmp          } tmpPoisoned =   let tmp = tmpAs "poisoned" []-  in tmp { icount = 4 + d 2+  in tmp { icount = 4 + 1 `d` 2          , ieffects = Recharging (RefillHP (-1)) : ieffects tmp          } tmpSlow10Resistant =   let tmp = tmpAs "slow resistant" []-  in tmp { icount = 8 + d 4+  in tmp { icount = 8 + 1 `d` 4          , ieffects = Recharging (DropItem 1 1 COrgan "slowed") : ieffects tmp          } tmpPoisonResistant =   let tmp = tmpAs "poison resistant" []-  in tmp { icount = 8 + d 4+  in tmp { icount = 8 + 1 `d` 4          , ieffects = Recharging (DropItem 1 maxBound COrgan "poisoned")                       : ieffects tmp          }
GameDefinition/Content/ModeKind.hs view
@@ -27,9 +27,9 @@   , validateSingle = validateSingleModeKind   , validateAll = validateAllModeKind   , content = contentFromList-      [raid, brawl, shootout, escape, zoo, ambush, exploration, explorationSurvival, safari, safariSurvival, battle, battleSurvival, defense, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverExploration, screensaverSafari]+      [raid, brawl, shootout, escape, zoo, ambush, crawl, crawlSurvival, safari, safariSurvival, battle, battleSurvival, defense, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverCrawl, screensaverSafari]   }-raid,        brawl, shootout, escape, zoo, ambush, exploration, explorationSurvival, safari, safariSurvival, battle, battleSurvival, defense, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverExploration, screensaverSafari :: ModeKind+raid,        brawl, shootout, escape, zoo, ambush, crawl, crawlSurvival, safari, safariSurvival, battle, battleSurvival, defense, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverCrawl, screensaverSafari :: ModeKind  -- What other symmetric (two only-one-moves factions) and asymmetric vs crowd -- scenarios make sense (e.g., are good for a tutorial or for standalone@@ -114,13 +114,13 @@   , mdesc   = "Not even the unexplained ruin of the largest and tightest security of Neptune's spaceports will prevent you from claiming your prize. After all, you didn't take to the space to let others decide your fate. Onward!"   } -exploration = ModeKind+crawl = ModeKind   { msymbol = 'c'   , mname   = "crawl (long)"-  , mfreq   = [("crawl", 1), ("exploration", 1), ("campaign scenario", 1)]-  , mroster = rosterExploration-  , mcaves  = cavesExploration-  , mdesc   = "You get stranded while looting, with utmost satisfaction, the blasted bridge of an old and extravagantly luxurious cruise liner. The inert spaceship, supposedly long deserted and barely able to sustain life support, suddenly releases the shuttle you came in, lights up its ion engines, manoeuvres deftly off Triton orbit and heads purposefully away from Neptune. Your plan of battle is to break through the dilapidated decks to the auxiliary engineering and docking hub somewhere among the giant spaceship's uppermost levels. You are ready to fight and determined not to leave the ship without taking of its wealth what is rightfully yours. There are animal cries down below and ominous silence up above."+  , mfreq   = [("crawl", 1), ("campaign scenario", 1)]+  , mroster = rosterCrawl+  , mcaves  = cavesCrawl+  , mdesc   = "You get stranded while looting, with utmost satisfaction, the blasted bridge of an old and extravagantly luxurious cruise liner. The inert spaceship, supposedly long deserted and barely able to sustain life support, suddenly releases the shuttle you came in, lights up ion engines, manoeuvres deftly off Triton orbit and heads purposefully away from Neptune. There is static on all communication channels. Your plan of battle is to scour the dilapidated decks, gather all missing squad members and break through to the auxiliary engineering and docking hub somewhere among the giant spaceship's uppermost levels. You are determined to fight for the lives and freedom of your crew and not to leave the ship without taking of its wealth what is rightfully yours."   }  safari = ModeKind  -- easter egg available only via screensaver@@ -134,12 +134,12 @@  -- * Testing modes -explorationSurvival = ModeKind+crawlSurvival = ModeKind   { msymbol = 'd'   , mname   = "crawl survival"   , mfreq   = [("crawl survival", 1)]-  , mroster = rosterExplorationSurvival-  , mcaves  = cavesExploration+  , mroster = rosterCrawlSurvival+  , mcaves  = cavesCrawl   , mdesc   = "Lure the human intruders deeper and deeper."   } @@ -175,7 +175,7 @@   , mname   = "defense"   , mfreq   = [("defense", 1)]   , mroster = rosterDefense-  , mcaves  = cavesExploration+  , mcaves  = cavesCrawl   , mdesc   = "Don't let the half-witted humans derail your operation and flee, like the puny, naked, tentacle-less beasts that they are!"   } @@ -224,10 +224,10 @@   , mroster = screensave (AutoLeader False False) rosterAmbush   } -screensaverExploration = exploration+screensaverCrawl = crawl   { mname   = "auto-crawl (long)"   , mfreq   = [("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterExploration+  , mroster = screensave (AutoLeader False False) rosterCrawl   }  screensaverSafari = safari@@ -237,7 +237,7 @@               screensave (AutoLeader False True) rosterSafari   } -rosterRaid, rosterBrawl, rosterShootout, rosterEscape, rosterZoo, rosterAmbush, rosterExploration, rosterExplorationSurvival, rosterSafari, rosterSafariSurvival, rosterBattle, rosterBattleSurvival, rosterDefense :: Roster+rosterRaid, rosterBrawl, rosterShootout, rosterEscape, rosterZoo, rosterAmbush, rosterCrawl, rosterCrawlSurvival, rosterSafari, rosterSafariSurvival, rosterBattle, rosterBattleSurvival, rosterDefense :: Roster  rosterRaid = Roster   { rosterList = [ ( playerHero {fhiCondPoly = hiRaid}@@ -329,7 +329,7 @@                   , ("Gray Off-world Mercenary", "Horror Den") ]   , rosterAlly = [] } -rosterExploration = Roster+rosterCrawl = Roster   { rosterList = [ ( playerHero                    , [(3, 3, "hero")] )                  , ( playerMonster@@ -346,7 +346,7 @@                  , ("Alien Hierarchy", "Robot Anarchy")                  , ("Robot Anarchy", "Animal Kingdom") ] } -rosterExplorationSurvival = rosterExploration+rosterCrawlSurvival = rosterCrawl   { rosterList = [ ( playerHero { fleaderMode =                                     LeaderAI $ AutoLeader True False                                 , fhasUI = False }@@ -355,7 +355,7 @@                    , [] )                  , ( playerAnimal {fhasUI = True}                    , -- Fun from the start to avoid empty initial level:-                     [ (3, 5 + d 2, "animal")  -- many, because no spawning+                     [ (3, 5 + 1 `d` 2, "animal")  -- many, because no spawning                      -- Optional huge battle at the end:                      , (12, 100, "mobile animal") ] )                  , ( playerRobot@@ -431,20 +431,20 @@                  , ( playerRobot {fneverEmpty = True}                    , [(5, 15, "mobile robot")] ) ] } -rosterDefense = rosterExploration+rosterDefense = rosterCrawl   { rosterList = [ ( playerAntiHero                    , [(3, 3, "hero")] )                  , ( playerAntiMonster                    , [] )                  , ( playerAnimal                    , -- Fun from the start to avoid empty initial level:-                     [ (3, 5 + d 2, "animal")  -- many, because no spawning+                     [ (3, 5 + 1 `d` 2, "animal")  -- many, because no spawning                      -- Optional huge battle at the end:                      , (12, 100, "mobile animal") ] )                  , ( playerRobot                    , [] ) ] }  -- gentle introduction -cavesRaid, cavesBrawl, cavesShootout, cavesEscape, cavesZoo, cavesAmbush, cavesExploration, cavesSafari, cavesBattle :: Caves+cavesRaid, cavesBrawl, cavesShootout, cavesEscape, cavesZoo, cavesAmbush, cavesCrawl, cavesSafari, cavesBattle :: Caves  cavesRaid = IM.fromList [(2, "caveRaid")] @@ -458,7 +458,7 @@  cavesAmbush = IM.fromList [(9, "caveAmbush")] -cavesExploration = IM.fromList $+cavesCrawl = IM.fromList $   [(1, "outermost")]   ++ [(2, "shallow random 2")]   ++ [(3, "caveBridge")]
GameDefinition/Content/PlaceKind.hs view
@@ -43,8 +43,7 @@ rect = PlaceKind  -- Valid for any nonempty area, hence low frequency.   { psymbol  = 'r'   , pname    = "room"-  , pfreq    = [ ("rogue", 100), ("arena", 40), ("laboratory", 40)-               , ("zoo", 7) ]+  , pfreq    = [("rogue", 100), ("arena", 40), ("laboratory", 40), ("zoo", 9)]   , prarity  = [(1, 10), (10, 8)]   , pcover   = CStretch   , pfence   = FWall@@ -68,7 +67,7 @@ glasshouse = PlaceKind   { psymbol  = 'g'   , pname    = "glasshouse"-  , pfreq    = [("arena", 40), ("shootout", 8), ("zoo", 12), ("ambush", 7)]+  , pfreq    = [("arena", 40), ("shootout", 8), ("zoo", 9), ("ambush", 7)]   , prarity  = [(1, 10), (10, 8)]   , pcover   = CStretch   , pfence   = FNone
GameDefinition/Content/TileKind.hs view
@@ -153,11 +153,12 @@   , tcolor   = BrCyan   , tcolor2  = Cyan   , talter   = 5-  , tfeature = [ Embed "signboard", Indistinct-               , ConsideredByAI  -- changes after use, so safe for AI-               , RevealAs "signboard" ]  -- to display as hidden+  , tfeature = [ ConsideredByAI  -- changes after use, so safe for AI+               , RevealAs "signboard"  -- to display as hidden+               , Indistinct+               ]   }-signboardRead = TileKind  -- after first use revealed to be this one+signboardRead = TileKind   { tsymbol  = 'O'   , tname    = "signboard"   , tfreq    = [("signboard", 1), ("zooSet", 2), ("ambushSet", 1)]
GameDefinition/Main.hs view
@@ -13,24 +13,34 @@  import Game.LambdaHack.Common.Prelude -import Control.Concurrent.Async+import           Control.Concurrent.Async import qualified Control.Exception as Ex-import System.Environment (getArgs)-import System.Exit+import qualified GHC.IO.Handle as GHC.IO.Handle+import qualified Options.Applicative as OA+import           System.Exit+import qualified System.IO as SIO +import Game.LambdaHack.Server (serverOptionsPI) import TieKnot --- | Tie the LambdaHack engine client, server and frontend code--- with the game-specific content definitions, and run the game.+-- | Parse commandline options, tie the engine, content and clients knot,+-- run the game and handle exit. main :: IO () main = do-  args <- getArgs+  -- For the case when the game is started not on a console.+  isTerminal <- SIO.hIsTerminalDevice SIO.stdout+  unless isTerminal $ do+    fstdout <- SIO.openFile "stdout.txt" SIO.WriteMode+    fstderr <- SIO.openFile "stderr.txt" SIO.WriteMode+    GHC.IO.Handle.hDuplicateTo fstdout SIO.stdout+    GHC.IO.Handle.hDuplicateTo fstderr SIO.stderr+  serverOptions <- OA.execParser serverOptionsPI   -- Avoid the bound thread that would slow down the communication.-  a <- async $ tieKnot args+  a <- async $ tieKnot serverOptions   ex <- waitCatch a   case ex of     Right () -> return ()     Left e -> case Ex.fromException e of       Just ExitSuccess ->-        exitSuccess  -- we are in the main thread, so it really exits+        exitSuccess  -- we are in the main thread, so here it really exits       _ -> Ex.throwIO e
GameDefinition/PLAYING.md view
@@ -268,7 +268,8 @@ The stress of combat drains Calm, gradually limiting the use of items and, if Calm reaches zero and the actor is sufficiently impressed by his foes, making him defect and surrender to their domination.-Whenever the monster's or hero's hit points reach zero, the combatant dies.+Whenever the monster's or hero's hit points reach zero,+the combatant is incapacitated and promptly dies. When the last hero dies or is dominated, the scenario ends in defeat.  
GameDefinition/TieKnot.hs view
@@ -15,12 +15,12 @@  import qualified System.Random as R -import Game.LambdaHack.Common.ContentDef+import           Game.LambdaHack.Common.ContentDef import qualified Game.LambdaHack.Common.Kind as Kind import qualified Game.LambdaHack.Common.Tile as Tile-import Game.LambdaHack.Content.ItemKind-import Game.LambdaHack.SampleImplementation.SampleMonadServer (executorSer)-import Game.LambdaHack.Server+import qualified Game.LambdaHack.Content.ItemKind as IK+import           Game.LambdaHack.SampleImplementation.SampleMonadServer (executorSer)+import           Game.LambdaHack.Server  import qualified Client.UI.Content.KeyKind as Content.KeyKind import qualified Content.CaveKind@@ -33,47 +33,29 @@ -- | Tie the LambdaHack engine client, server and frontend code -- with the game-specific content definitions, and run the game. ----- The action monad types to be used are determined by the 'executorSer'--- and 'executorCli' calls. If other functions are used in their place--- the types are different and so the whole pattern of computation--- is different. Which of the frontends is run inside the UI client+-- The custom monad types to be used are determined by the 'executorSer'+-- call, which in turn calls 'executorCli'. If other functions are used+-- in their place- the types are different and so the whole pattern+-- of computation differs. Which of the frontends is run inside the UI client -- depends on the flags supplied when compiling the engine library.-tieKnot :: [String] -> IO ()-tieKnot args = do-  -- Options for the next game taken from the commandline.-  let sdebug@DebugModeSer{sallClear, sboostRandomItem, sdungeonRng} =-        debugArgs args+-- Similarly for the choice of native vs JS builds.+tieKnot :: ServerOptions -> IO ()+tieKnot options@ServerOptions{sallClear, sboostRandomItem, sdungeonRng} = do   -- This setup ensures the boosting option doesn't affect generating initial   -- RNG for dungeon, etc., and also, that setting dungeon RNG on commandline   -- equal to what was generated last time, ensures the same item boost.   initialGen <- maybe R.getStdGen return sdungeonRng-  let sdebugNxt = sdebug {sdungeonRng = Just initialGen}+  let soptionsNxt = options {sdungeonRng = Just initialGen}+      cotile = Kind.createOps Content.TileKind.cdefs+      boostedItems = IK.boostItemKindList initialGen Content.ItemKind.items+      coitem = Kind.createOps $+        if sboostRandomItem+        then Content.ItemKind.cdefs+               {content = contentFromList+                          $ boostedItems ++ Content.ItemKind.otherItemContent}+        else Content.ItemKind.cdefs       -- Common content operations, created from content definitions.       -- Evaluated fully to discover errors ASAP and to free memory.-      cotile = Kind.createOps Content.TileKind.cdefs-      boostItem :: ItemKind -> ItemKind-      boostItem i =-        let mainlineLabel (label, _) = label `elem` ["useful", "treasure"]-        in if any mainlineLabel (ifreq i)-           then i { ifreq = ("useful", 10000)-                            : filter (not . mainlineLabel) (ifreq i)-                  , ieffects = delete Unique $ ieffects i-                  }-           else i-      boostList :: [ItemKind] -> [ItemKind]-      boostList l | not sboostRandomItem = l-      boostList [] = []-      boostList l =-        let (r, _) = R.randomR (0, length l - 1) initialGen-        in case splitAt r l of-          (pre, i : post) -> pre ++ boostItem i : post-          _ -> error $  "" `showFailure` l-      boostedItems = boostList Content.ItemKind.items-      cdefsItem =-        Content.ItemKind.cdefs-          {content = contentFromList-                     $ boostedItems ++ Content.ItemKind.otherItemContent}-      coitem = Kind.createOps cdefsItem       !cops = Kind.COps         { cocave  = Kind.createOps Content.CaveKind.cdefs         , coitem@@ -88,4 +70,4 @@       !copsClient = Content.KeyKind.standardKeys   -- Wire together game content, the main loops of game clients   -- and the game server loop.-  executorSer cops copsClient sdebugNxt+  executorSer cops copsClient soptionsNxt
Makefile view
@@ -40,8 +40,8 @@ frontendAmbush: 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush -frontendExploration:-	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode exploration+frontendCrawl:+	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl  frontendSafari: 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode safari@@ -60,7 +60,7 @@   benchMemoryAnim:-	dist/build/Allure/Allure --dbgMsgSer --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode exploration --setDungeonRng 120 --setMainRng 47 --frontendNull --noAnim +RTS -s -A1M -RTS+	dist/build/Allure/Allure --dbgMsgSer --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 120 --setMainRng 47 --frontendNull --noAnim +RTS -s -A1M -RTS  benchBattle: 	dist/build/Allure/Allure --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7@@ -71,31 +71,31 @@ benchFrontendBattle: 	dist/build/Allure/Allure --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7 -benchExploration:-	dist/build/Allure/Allure --dbgMsgSer --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode exploration --setDungeonRng 0 --setMainRng 0+benchCrawl:+	dist/build/Allure/Allure --dbgMsgSer --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0 -benchFrontendExploration:-	dist/build/Allure/Allure --dbgMsgSer --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode exploration --setDungeonRng 0 --setMainRng 0+benchFrontendCrawl:+	dist/build/Allure/Allure --dbgMsgSer --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0 -benchNull: benchBattle benchAnimBattle benchExploration+benchNull: benchBattle benchAnimBattle benchCrawl -bench: benchBattle benchAnimBattle benchFrontendBattle benchExploration benchFrontendExploration+bench: benchBattle benchAnimBattle benchFrontendBattle benchCrawl benchFrontendCrawl -nativeBenchExploration:-	dist/build/Allure/Allure		   --dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode exploration --setDungeonRng 0 --setMainRng 0+nativeBenchCrawl:+	dist/build/Allure/Allure		   --dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0  nativeBenchBattle: 	dist/build/Allure/Allure		   --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0 -nativeBench: nativeBenchBattle nativeBenchExploration+nativeBench: nativeBenchBattle nativeBenchCrawl -nodeBenchExploration:-	node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode exploration --setDungeonRng 0 --setMainRng 0+nodeBenchCrawl:+	node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0  nodeBenchBattle: 	node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0 -nodeBench: nodeBenchBattle nodeBenchExploration+nodeBench: nodeBenchBattle nodeBenchCrawl   test-travis-short: test-short@@ -106,7 +106,7 @@  test-short: test-short-new test-short-load -test-medium: testRaid-medium testBrawl-medium testShootout-medium testEscape-medium testZoo-medium testAmbush-medium testExploration-medium testSafari-medium testSafariSurvival-medium testBattle-medium testBattleSurvival-medium+test-medium: testRaid-medium testBrawl-medium testShootout-medium testEscape-medium testZoo-medium testAmbush-medium testCrawl-medium testSafari-medium testSafariSurvival-medium testBattle-medium testBattleSurvival-medium  testRaid-medium: 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log@@ -126,8 +126,8 @@ testAmbush-medium: 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log -testExploration-medium:-	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode exploration 2> /tmp/teletypetest.log+testCrawl-medium:+	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl 2> /tmp/teletypetest.log  testSafari-medium: 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 2 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log@@ -151,7 +151,7 @@ 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix exploration --dumpInitRngs --automateAll --keepAutomated --gameMode exploration --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log@@ -166,12 +166,14 @@ 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix exploration --dumpInitRngs --automateAll --keepAutomated --gameMode exploration --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log 	dist/build/Allure/Allure --dbgMsgSer --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log +#in LambdaHack/+# cabal install --disable-library-profiling --disable-profiling --disable-documentation -f-release  build-binary-common: 	cabal install --disable-library-profiling --disable-profiling --disable-documentation -f-release --only-dependencies@@ -196,3 +198,6 @@ build-binary: build-binary-common 	cp AllureOfTheStarsInstall/bin/Allure AllureOfTheStars 	tar -czf Allure_x_ubuntu-16.04-amd64.tar.gz AllureOfTheStars++new-build-dev:+	cabal new-build --datadir=. --disable-optimization -j1 all
README.md view
@@ -114,7 +114,7 @@ as follows      cabal update-    cabal install Allure --force-reinstalls+    cabal install Allure  For a newer version, install a matching LambdaHack library snapshot from a development branch, download the game source from github[5]@@ -133,13 +133,33 @@ with the standard, user-friendly frontend.  Run `Allure --help` to see a brief description of all debug options.-Of these, `--sniffIn` and `--sniffOut` are very useful (though verbose-and initially cryptic), for monitoring the traffic between clients+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).  +Coding style+------------++Stylish Haskell is used for slight auto-formatting at buffer save; see+[.stylish-haskell.yaml](https://github.com/LambdaHack/LambdaHack/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 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 in place of comments, unless too verbose.++ Further information ------------------- @@ -183,8 +203,6 @@  [10]: http://github.com/LambdaHack/LambdaHack [11]: https://github.com/AllureOfTheStars/Allure/releases/latest-[13]: http://www.haskell.org/haskellwiki/GHC_under_Wine#Code_that_uses_gtk2hs-[14]: http://www.edsko.net/2014/04/27/haskell-including-gtk-on-mavericks [15]: https://github.com/ghcjs/ghcjs [16]: https://www.npmjs.com/package/google-closure-compiler [17]: https://ci.appveyor.com/project/Mikolaj/allure-hpt6r/build/artifacts
test/test.hs view
@@ -1,10 +1,15 @@ import Prelude () +import Options.Applicative+ import Game.LambdaHack.Common.Prelude+import Game.LambdaHack.Server  import TieKnot  main :: IO ()-main =-  tieKnot $ words "--dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 100 --automateAll --keepAutomated --gameMode exploration --setDungeonRng 42 --setMainRng 42"+main = do+  let args = words "--dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 100 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 42 --setMainRng 42"+  serverOptions <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args+  tieKnot serverOptions   -- tieKnot $ words "--dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 100 --automateAll --keepAutomated --gameMode battle --setDungeonRng 42 --setMainRng 42"