packages feed

LambdaHack 0.7.0.0 → 0.7.1.0

raw patch · 28 files changed

+567/−396 lines, 28 filesdep ~randomPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: random

API changes (from Hackage documentation)

- Game.LambdaHack.Client.UI.Frontend.Sdl: [sdisplayPermitted] :: FrontendSession -> MVar Bool
- Game.LambdaHack.Client.UI.Frontend.Sdl: displayNoLock :: ClientOptions -> FrontendSession -> SingleFrame -> IO ()
+ Game.LambdaHack.Client.ClientOptions: [slogPriority] :: ClientOptions -> Maybe Int
+ Game.LambdaHack.Client.UI.Frontend.Sdl: [scontinueSdlLoop] :: FrontendSession -> IORef Bool
+ Game.LambdaHack.Client.UI.Frontend.Sdl: [sframeDrawn] :: FrontendSession -> MVar ()
+ Game.LambdaHack.Client.UI.Frontend.Sdl: [sframeQueue] :: FrontendSession -> MVar SingleFrame
+ Game.LambdaHack.Client.UI.Frontend.Sdl: drawFrame :: ClientOptions -> FrontendSession -> SingleFrame -> IO ()
- Game.LambdaHack.Client.ClientOptions: ClientOptions :: Maybe Text -> Maybe Text -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> Bool -> Maybe Bool -> Bool -> Bool -> Maybe Text -> Maybe FilePath -> String -> Bool -> Bool -> Bool -> Bool -> Maybe Int -> Maybe Int -> ClientOptions
+ Game.LambdaHack.Client.ClientOptions: ClientOptions :: Maybe Text -> Maybe Text -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> Maybe Int -> Bool -> Maybe Bool -> Bool -> Bool -> Maybe Text -> Maybe FilePath -> String -> Bool -> Bool -> Bool -> Bool -> Maybe Int -> Maybe Int -> ClientOptions
- Game.LambdaHack.Client.UI.Frontend.Sdl: FrontendSession :: Window -> Renderer -> Font -> IORef FontAtlas -> IORef Texture -> IORef SingleFrame -> IORef Bool -> MVar Bool -> FrontendSession
+ Game.LambdaHack.Client.UI.Frontend.Sdl: FrontendSession :: Window -> Renderer -> Font -> IORef FontAtlas -> IORef Texture -> IORef SingleFrame -> IORef Bool -> IORef Bool -> MVar SingleFrame -> MVar () -> FrontendSession
- Game.LambdaHack.Client.UI.Frontend.Sdl: display :: ClientOptions -> FrontendSession -> SingleFrame -> IO ()
+ Game.LambdaHack.Client.UI.Frontend.Sdl: display :: FrontendSession -> SingleFrame -> IO ()

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+## [v0.7.1.0, aka 'Ancient troubles'](https://github.com/LambdaHack/LambdaHack/compare/v0.7.0.0...v0.7.1.0)++- add amazing cave and item (actor, blast, organ) descriptions+- package for Windows as an installer and also as zip archives+- fix a crash from SDL frontend under some OpenGL drivers (no thread-safety)+- add WWW address to the Main Menu, for other sites that may run our JS blob+ ## [v0.7.0.0, aka 'The dice are cast'](https://github.com/LambdaHack/LambdaHack/compare/v0.6.2.0...v0.7.0.0)  - decouple tile searching from tile alteration
CREDITS view
@@ -5,6 +5,7 @@ Mikolaj Konarski Tuukka Turto Veronika Romashkina+@Peritract   Fonts 16x16x.fon, 8x8x.fon and 8x8xb.fon are are taken from
Game/LambdaHack/Client/AI/PickActorM.hs view
@@ -85,7 +85,7 @@               -- because he is likely to be still stuck.               Just ((aid, b), tgt)             Just aidToAvoid | aid /= aidToAvoid ->-              -- Not an attempted leader stuck this turn/+              -- Not an attempted leader stuck this turn.               Just ((aid, b), tgt)             _ -> Nothing       oursTgtRaw <- mapM refresh ours
Game/LambdaHack/Client/ClientOptions.hs view
@@ -25,6 +25,8 @@       -- ^ Font size to use for the main game window.   , scolorIsBold      :: Maybe Bool       -- ^ Whether to use bold attribute for colorful characters.+  , slogPriority      :: Maybe Int+      -- ^ How much to log (e.g., from SDL). 1 is all, 5 is errors, the default.   , smaxFps           :: Maybe Int       -- ^ Maximal frames per second.       -- This is better low and fixed, to avoid jerkiness and delays@@ -67,6 +69,7 @@   , sdlFonSizeAdd = Nothing   , sfontSize = Nothing   , scolorIsBold = Nothing+  , slogPriority = Nothing   , smaxFps = Nothing   , sdisableAutoYes = False   , snoAnim = Nothing
Game/LambdaHack/Client/UI/DisplayAtomicM.hs view
@@ -758,7 +758,9 @@   case (toSt, partingPart) of     (Just status, Just pp) -> do       isNoConfirms <- isNoConfirmsGame-      go <- if isNoConfirms then return False else displaySpaceEsc ColorFull ""+      go <- if isNoConfirms && fmap stOutcome toSt /= Just Camping+            then return False+            else displaySpaceEsc ColorFull ""       when (side == fid) recordHistory         -- we are going to exit or restart, so record and clear, but only once       when go $ do@@ -820,10 +822,12 @@                     if go2 then viewItems pointer2 else return True         go3 <- viewItems 2         when go3 $ do-          -- Show score for any UI client after any kind of game exit,-          -- even though it is saved only for human UI clients at game over.-          scoreSlides <- scoreToSlideshow total status-          void $ getConfirms ColorFull [K.spaceKM, K.escKM] scoreSlides+          unless isNoConfirms $ do+            -- Show score for any UI client after any kind of game exit,+            -- even though it is saved only for human UI clients at game over+            -- (that is not a noConfirms or benchmark game).+            scoreSlides <- scoreToSlideshow total status+            void $ getConfirms ColorFull [K.spaceKM, K.escKM] scoreSlides           -- The last prompt stays onscreen during shutdown, etc.           promptAdd pp           partingSlide <- reportToSlideshow [K.spaceKM, K.escKM]
Game/LambdaHack/Client/UI/Frontend/Dom.hs view
@@ -94,7 +94,7 @@   tableElem <- unsafeCastTo HTMLTableElement tableElemRaw   appendChild_ divBlock tableElem   scharStyle <- getStyle tableElem-  -- Speed: http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout+  -- Speed: <http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout>   setProp scharStyle "table-layout" "fixed"   setProp scharStyle "font-family" "lambdaHackFont"   setProp scharStyle "font-size" $ tshow (fromJust sfontSize) <> "px"@@ -187,7 +187,7 @@         maybe (return ())               (\key -> IO.liftIO $ saveKMP rf modifier key pointer) mkey       saveMouse = do-        -- https://hackage.haskell.org/package/ghcjs-dom-0.2.1.0/docs/GHCJS-DOM-EventM.html+        -- <https://hackage.haskell.org/package/ghcjs-dom-0.2.1.0/docs/GHCJS-DOM-EventM.html>         but <- mouseButton         modifier <- readMod         let key = case but of
Game/LambdaHack/Client/UI/Frontend/Sdl.hs view
@@ -4,7 +4,7 @@ #ifdef EXPOSE_INTERNAL     -- * Internal operations   , FontAtlas, FrontendSession(..), startupFun, shutdown, forceShutdown-  , display, displayNoLock, modTranslate, keyTranslate, colorToRGBA+  , display, drawFrame, modTranslate, keyTranslate, colorToRGBA #endif   ) where @@ -13,7 +13,6 @@ import Game.LambdaHack.Common.Prelude hiding (Alt)  import           Control.Concurrent-import           Control.Concurrent.Async import qualified Data.Char as Char import qualified Data.EnumMap.Strict as EM import           Data.IORef@@ -28,6 +27,7 @@ import qualified SDL import qualified SDL.Font as TTF import           SDL.Input.Keyboard.Codes+import qualified SDL.Raw.Basic as SDL (logSetAllPriority) import qualified SDL.Vect as Vect  import           Game.LambdaHack.Client.ClientOptions@@ -50,7 +50,9 @@   , stexture          :: IORef SDL.Texture   , spreviousFrame    :: IORef SingleFrame   , sforcedShutdown   :: IORef Bool-  , sdisplayPermitted :: MVar Bool+  , scontinueSdlLoop  :: IORef Bool+  , sframeQueue       :: MVar SingleFrame+  , sframeDrawn       :: MVar ()   }  -- | The name of the frontend.@@ -60,14 +62,22 @@ -- | Set up and start the main loop providing input and output. -- -- Apparently some SDL backends are not thread-safe--- (https://wiki.libsdl.org/FAQDevelopment),--- so we stick to main thread.+-- (<https://wiki.libsdl.org/FAQDevelopment>;+-- "this should only be run in the thread that initialized the video subsystem,+-- and for extra safety, you should consider only doing those things+-- on the main thread in any case")+-- so we stick to a single bound thread (but not to the main thread;+-- enough is enough and at least in the case of OpenGL all bound threads+-- are supposed to be as good as the main thread). startup :: ClientOptions -> IO RawFrontend startup soptions = startupBound $ startupFun soptions  startupFun :: ClientOptions -> MVar RawFrontend -> IO () startupFun soptions@ClientOptions{..} rfMVar = do   SDL.initialize [SDL.InitVideo, SDL.InitEvents]+  -- lowest: pattern SDL_LOG_PRIORITY_VERBOSE = (1) :: LogPriority+  -- our default: pattern SDL_LOG_PRIORITY_ERROR = (5) :: LogPriority+  SDL.logSetAllPriority $ toEnum $ fromMaybe 5 slogPriority   let title = fromJust stitle       fontFileName = T.unpack (fromJust sdlFontFile)       fontFile | isRelative fontFileName = fromJust sfontDir </> fontFileName@@ -86,7 +96,10 @@       screenV2 = SDL.V2 (toEnum $ xsize * boxSize)                         (toEnum $ ysize * boxSize)       windowConfig = SDL.defaultWindow {SDL.windowInitialSize = screenV2}-      rendererConfig = SDL.defaultRenderer {SDL.rendererTargetTexture = True}+      rendererConfig = SDL.RendererConfig+        { rendererType          = SDL.AcceleratedVSyncRenderer+        , rendererTargetTexture = True+        }   swindow <- SDL.createWindow title windowConfig   srenderer <- SDL.createRenderer swindow (-1) rendererConfig   let initTexture = do@@ -104,78 +117,46 @@   stexture <- newIORef texture   spreviousFrame <- newIORef blankSingleFrame   sforcedShutdown <- newIORef False-  sdisplayPermitted <- newMVar True+  scontinueSdlLoop <- newIORef True+  sframeQueue <- newEmptyMVar+  sframeDrawn <- newEmptyMVar   let sess = FrontendSession{..}-  rf <- createRawFrontend (display soptions sess) (shutdown sess)+  rf <- createRawFrontend (display sess) (shutdown sess)   putMVar rfMVar rf   let pointTranslate :: forall i. (Enum i) => Vect.Point Vect.V2 i -> Point       pointTranslate (SDL.P (SDL.V2 x y)) =         Point (fromEnum x `div` boxSize) (fromEnum y `div` boxSize)       redraw = do-        displayPermitted <- takeMVar sdisplayPermitted-        when displayPermitted $ do-          -- Textures may be trashed and even invalid, especially on Windows.-          atlas <- readIORef satlas-          writeIORef satlas EM.empty-          oldTexture <- readIORef stexture-          newTexture <- initTexture-          mapM_ SDL.destroyTexture $ EM.elems atlas-          SDL.destroyTexture oldTexture-          writeIORef stexture newTexture-          prevFrame <- readIORef spreviousFrame-          writeIORef spreviousFrame blankSingleFrame-          displayNoLock soptions sess prevFrame-        putMVar sdisplayPermitted displayPermitted-      storeKeys :: IO ()-      storeKeys = do-        e <- SDL.waitEvent  -- blocks here, so no polling-        case SDL.eventPayload e of-          SDL.KeyboardEvent keyboardEvent-            | SDL.keyboardEventKeyMotion keyboardEvent == SDL.Pressed -> do-              let sym = SDL.keyboardEventKeysym keyboardEvent-                  ksm = SDL.keysymModifier sym-                  shiftPressed = SDL.keyModifierLeftShift ksm-                                 || SDL.keyModifierRightShift ksm-                  key = keyTranslate shiftPressed $ SDL.keysymKeycode sym-                  modifier = modTranslate ksm-              p <- SDL.getAbsoluteMouseLocation-              when (key == K.Esc) $ resetChanKey (fchanKey rf)-              saveKMP rf modifier key (pointTranslate p)-          SDL.MouseButtonEvent mouseButtonEvent-            | SDL.mouseButtonEventMotion mouseButtonEvent == SDL.Released -> do-              md <- modTranslate <$> SDL.getModState-              let key = case SDL.mouseButtonEventButton mouseButtonEvent of-                    SDL.ButtonLeft -> K.LeftButtonRelease-                    SDL.ButtonMiddle -> K.MiddleButtonRelease-                    SDL.ButtonRight -> K.RightButtonRelease-                    _ -> K.LeftButtonRelease  -- any other is spare left-                  modifier = if md == K.Shift then K.NoModifier else md-                  p = SDL.mouseButtonEventPos mouseButtonEvent-              saveKMP rf modifier key (pointTranslate p)-          SDL.MouseWheelEvent mouseWheelEvent -> do-            md <- modTranslate <$> SDL.getModState-            let SDL.V2 _ y = SDL.mouseWheelEventPos mouseWheelEvent-                mkey = case (compare y 0, SDL.mouseWheelEventDirection-                                            mouseWheelEvent) of-                  (EQ, _) -> Nothing-                  (LT, SDL.ScrollNormal) -> Just K.WheelSouth-                  (GT, SDL.ScrollNormal) -> Just K.WheelNorth-                  (LT, SDL.ScrollFlipped) -> Just K.WheelSouth-                  (GT, SDL.ScrollFlipped) -> Just K.WheelNorth-                modifier = if md == K.Shift then K.NoModifier else md-            p <- SDL.getAbsoluteMouseLocation-            maybe (return ())-                  (\key -> saveKMP rf modifier key (pointTranslate p)) mkey-          SDL.WindowClosedEvent{} -> forceShutdown sess-          SDL.QuitEvent -> forceShutdown sess-          SDL.WindowRestoredEvent{} -> redraw-          SDL.WindowExposedEvent{} -> redraw  -- needed on Windows-          -- Probably not needed, because textures nor their content not lost:-          -- SDL.WindowShownEvent{} -> redraw-          _ -> return ()-        displayPermitted <- readMVar sdisplayPermitted-        if displayPermitted-        then storeKeys+        -- Textures may be trashed and even invalid, especially on Windows.+        atlas <- readIORef satlas+        writeIORef satlas EM.empty+        oldTexture <- readIORef stexture+        newTexture <- initTexture+        mapM_ SDL.destroyTexture $ EM.elems atlas+        SDL.destroyTexture oldTexture+        writeIORef stexture newTexture+        prevFrame <- readIORef spreviousFrame+        writeIORef spreviousFrame blankSingleFrame+        drawFrame soptions sess prevFrame+      loopSDL :: IO ()+      loopSDL = do+        me <- SDL.pollEvent  -- events take precedence over frames+        case me of+          Nothing -> do+            mfr <- tryTakeMVar sframeQueue+            case mfr of+              Just fr -> do+                -- Some SDL2 (OpenGL) backends are very thread-unsafe,+                -- so we need to ensure we draw on the same (bound) OS thread+                -- that initialized SDL, hence we have to poll frames.+                drawFrame soptions sess fr+                putMVar sframeDrawn ()  -- signal that drawing ended+              Nothing -> threadDelay 15000+                           -- 60 polls per second, so keyboard snappy enough+          Just e -> handleEvent e+        continueSdlLoop <- readIORef scontinueSdlLoop+        if continueSdlLoop+        then loopSDL         else do           TTF.free sfont           TTF.quit@@ -185,10 +166,54 @@           forcedShutdown <- readIORef sforcedShutdown           when forcedShutdown             exitSuccess  -- not in the main thread, so no exit yet, see "Main"-  storeKeys+      handleEvent e = case SDL.eventPayload e of+        SDL.KeyboardEvent keyboardEvent+          | SDL.keyboardEventKeyMotion keyboardEvent == SDL.Pressed -> do+            let sym = SDL.keyboardEventKeysym keyboardEvent+                ksm = SDL.keysymModifier sym+                shiftPressed = SDL.keyModifierLeftShift ksm+                               || SDL.keyModifierRightShift ksm+                key = keyTranslate shiftPressed $ SDL.keysymKeycode sym+                modifier = modTranslate ksm+            p <- SDL.getAbsoluteMouseLocation+            when (key == K.Esc) $ resetChanKey (fchanKey rf)+            saveKMP rf modifier key (pointTranslate p)+        SDL.MouseButtonEvent mouseButtonEvent+          | SDL.mouseButtonEventMotion mouseButtonEvent == SDL.Released -> do+            md <- modTranslate <$> SDL.getModState+            let key = case SDL.mouseButtonEventButton mouseButtonEvent of+                  SDL.ButtonLeft -> K.LeftButtonRelease+                  SDL.ButtonMiddle -> K.MiddleButtonRelease+                  SDL.ButtonRight -> K.RightButtonRelease+                  _ -> K.LeftButtonRelease  -- any other is spare left+                modifier = if md == K.Shift then K.NoModifier else md+                p = SDL.mouseButtonEventPos mouseButtonEvent+            saveKMP rf modifier key (pointTranslate p)+        SDL.MouseWheelEvent mouseWheelEvent -> do+          md <- modTranslate <$> SDL.getModState+          let SDL.V2 _ y = SDL.mouseWheelEventPos mouseWheelEvent+              mkey = case (compare y 0, SDL.mouseWheelEventDirection+                                          mouseWheelEvent) of+                (EQ, _) -> Nothing+                (LT, SDL.ScrollNormal) -> Just K.WheelSouth+                (GT, SDL.ScrollNormal) -> Just K.WheelNorth+                (LT, SDL.ScrollFlipped) -> Just K.WheelSouth+                (GT, SDL.ScrollFlipped) -> Just K.WheelNorth+              modifier = if md == K.Shift then K.NoModifier else md+          p <- SDL.getAbsoluteMouseLocation+          maybe (return ())+                (\key -> saveKMP rf modifier key (pointTranslate p)) mkey+        SDL.WindowClosedEvent{} -> forceShutdown sess+        SDL.QuitEvent -> forceShutdown sess+        SDL.WindowRestoredEvent{} -> redraw+        SDL.WindowExposedEvent{} -> redraw  -- needed on Windows+        -- Probably not needed, because textures nor their content not lost:+        -- SDL.WindowShownEvent{} -> redraw+        _ -> return ()+  loopSDL  shutdown :: FrontendSession -> IO ()-shutdown FrontendSession{..} = void $ swapMVar sdisplayPermitted False+shutdown FrontendSession{..} = writeIORef scontinueSdlLoop False  forceShutdown :: FrontendSession -> IO () forceShutdown sess@FrontendSession{..} = do@@ -196,33 +221,31 @@   shutdown sess  -- | Add a frame to be drawn.-display :: ClientOptions-        -> FrontendSession  -- ^ frontend session data+display :: FrontendSession  -- ^ frontend session data         -> SingleFrame      -- ^ the screen frame to draw         -> IO ()-display soptions sess@FrontendSession{..} curFrame = do-  displayPermitted <- takeMVar sdisplayPermitted-  if displayPermitted then do-    -- Apparently some SDL backends are not thread-safe, so keep to main thread:-    a <- asyncBound $ displayNoLock soptions sess curFrame-    wait a-    putMVar sdisplayPermitted displayPermitted+display FrontendSession{..} curFrame = do+  continueSdlLoop <- readIORef scontinueSdlLoop+  if continueSdlLoop then do+    putMVar sframeQueue curFrame+    -- Wait until the frame is drawn.+    takeMVar sframeDrawn   else do-    putMVar sdisplayPermitted displayPermitted     forcedShutdown <- readIORef sforcedShutdown     when forcedShutdown $       -- When there's a forced shutdown, ignore displaying one frame       -- and don't occupy the CPU creating new ones and moving on with the game       -- (possibly also saving the new game state, surprising the player),-      -- but give time for SDL to clean up and exit via @exitSuccess@+      -- but delay the server and client thread(s) for a long time+      -- and let the SDL-init thread clean up and exit via @exitSuccess@       -- to avoid exiting via "thread blocked".       threadDelay 50000 -displayNoLock :: ClientOptions-              -> FrontendSession  -- ^ frontend session data-              -> SingleFrame      -- ^ the screen frame to draw-              -> IO ()-displayNoLock ClientOptions{..} FrontendSession{..} curFrame = do+drawFrame :: ClientOptions+          -> FrontendSession  -- ^ frontend session data+          -> SingleFrame      -- ^ the screen frame to draw+          -> IO ()+drawFrame ClientOptions{..} FrontendSession{..} curFrame = do   let isFonFile = "fon" `isSuffixOf` T.unpack (fromJust sdlFontFile)       sdlSizeAdd = fromJust $ if isFonFile then sdlFonSizeAdd else sdlTtfSizeAdd   boxSize <- (+ sdlSizeAdd) <$> TTF.height sfont@@ -234,7 +257,8 @@         let rect = SDL.Rectangle (vp (x * boxSize) (y * boxSize))                                  (Vect.V2 (toEnum boxSize) (toEnum boxSize))         SDL.drawRect srenderer $ Just rect-        SDL.rendererDrawColor srenderer SDL.$= colorToRGBA Color.Black  -- reset back to black+        SDL.rendererDrawColor srenderer SDL.$= colorToRGBA Color.Black+          -- reset back to black       setChar :: Int -> Word32 -> Word32 -> IO ()       setChar i w wPrev = unless (w == wPrev) $ do         atlas <- readIORef satlas@@ -251,7 +275,7 @@               Color.HighlightGrey -> normalizeAc Color.BrBlack               Color.HighlightWhite -> normalizeAc Color.White               Color.HighlightMagenta -> normalizeAc Color.BrMagenta-        -- https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_42.html#SEC42+        -- <https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_42.html#SEC42>         textTexture <- case EM.lookup ac atlas of           Nothing -> do             -- Make all visible floors bold (no bold fold variant for 16x16x,@@ -264,7 +288,8 @@                                          else 8901  -- 0x22c5                          else acCharRaw             textSurface <--              TTF.shadedGlyph sfont (colorToRGBA fg) (colorToRGBA Color.Black) acChar+              TTF.shadedGlyph sfont (colorToRGBA fg)+                                    (colorToRGBA Color.Black) acChar             textTexture <- SDL.createTextureFromSurface srenderer textSurface             SDL.freeSurface textSurface             writeIORef satlas $ EM.insert ac textTexture atlas  -- not @acRaw@@@ -309,107 +334,106 @@     False  keyTranslate :: Bool -> SDL.Keycode -> K.Key-keyTranslate shiftPressed n =-  case n of-    KeycodeEscape     -> K.Esc-    KeycodeReturn     -> K.Return-    KeycodeBackspace  -> K.BackSpace-    KeycodeTab        -> if shiftPressed then K.BackTab else K.Tab-    KeycodeSpace      -> K.Space-    KeycodeExclaim -> K.Char '!'-    KeycodeQuoteDbl -> K.Char '"'-    KeycodeHash -> K.Char '#'-    KeycodePercent -> K.Char '%'-    KeycodeDollar -> K.Char '$'-    KeycodeAmpersand -> K.Char '&'-    KeycodeQuote -> if shiftPressed then K.Char '"' else K.Char '\''-    KeycodeLeftParen -> K.Char '('-    KeycodeRightParen -> K.Char ')'-    KeycodeAsterisk -> K.Char '*'-    KeycodePlus -> K.Char '+'-    KeycodeComma -> if shiftPressed then K.Char '<' else K.Char ','-    KeycodeMinus -> if shiftPressed then K.Char '_' else K.Char '-'-    KeycodePeriod -> if shiftPressed then K.Char '>' else K.Char '.'-    KeycodeSlash -> if shiftPressed then K.Char '?' else K.Char '/'-    Keycode1 -> if shiftPressed then K.Char '!' else K.Char '1'-    Keycode2 -> if shiftPressed then K.Char '@' else K.Char '2'-    Keycode3 -> if shiftPressed then K.Char '#' else K.Char '3'-    Keycode4 -> if shiftPressed then K.Char '$' else K.Char '4'-    Keycode5 -> if shiftPressed then K.Char '%' else K.Char '5'-    Keycode6 -> if shiftPressed then K.Char '^' else K.Char '6'-    Keycode7 -> if shiftPressed then K.Char '&' else K.Char '7'-    Keycode8 -> if shiftPressed then K.Char '*' else K.Char '8'-    Keycode9 -> if shiftPressed then K.Char '(' else K.Char '9'-    Keycode0 -> if shiftPressed then K.Char ')' else K.Char '0'-    KeycodeColon -> K.Char ':'-    KeycodeSemicolon -> if shiftPressed then K.Char ':' else K.Char ';'-    KeycodeLess -> K.Char '<'-    KeycodeEquals -> if shiftPressed then K.Char '+' else K.Char '='-    KeycodeGreater -> K.Char '>'-    KeycodeQuestion -> K.Char '?'-    KeycodeAt -> K.Char '@'-    KeycodeLeftBracket -> if shiftPressed then K.Char '{' else K.Char '['-    KeycodeBackslash -> if shiftPressed then K.Char '|' else K.Char '\\'-    KeycodeRightBracket -> if shiftPressed then K.Char '}' else K.Char ']'-    KeycodeCaret -> K.Char '^'-    KeycodeUnderscore -> K.Char '_'-    KeycodeBackquote -> if shiftPressed then K.Char '~' else K.Char '`'-    KeycodeUp         -> K.Up-    KeycodeDown       -> K.Down-    KeycodeLeft       -> K.Left-    KeycodeRight      -> K.Right-    KeycodeHome       -> K.Home-    KeycodeEnd        -> K.End-    KeycodePageUp     -> K.PgUp-    KeycodePageDown   -> K.PgDn-    KeycodeInsert     -> K.Insert-    KeycodeDelete     -> K.Delete-    KeycodeKPDivide   -> K.KP '/'-    KeycodeKPMultiply -> K.KP '*'-    KeycodeKPMinus    -> K.Char '-'  -- KP and normal are merged here-    KeycodeKPPlus     -> K.Char '+'  -- KP and normal are merged here-    KeycodeKPEnter    -> K.Return-    KeycodeKPEquals   -> K.Return  -- in case of some funny layouts-    KeycodeKP1 -> if shiftPressed then K.KP '1' else K.End-    KeycodeKP2 -> if shiftPressed then K.KP '2' else K.Down-    KeycodeKP3 -> if shiftPressed then K.KP '3' else K.PgDn-    KeycodeKP4 -> if shiftPressed then K.KP '4' else K.Left-    KeycodeKP5 -> if shiftPressed then K.KP '5' else K.Begin-    KeycodeKP6 -> if shiftPressed then K.KP '6' else K.Right-    KeycodeKP7 -> if shiftPressed then K.KP '7' else K.Home-    KeycodeKP8 -> if shiftPressed then K.KP '8' else K.Up-    KeycodeKP9 -> if shiftPressed then K.KP '9' else K.PgUp-    KeycodeKP0 -> if shiftPressed then K.KP '0' else K.Insert-    KeycodeKPPeriod -> K.Char '.'  -- dot and comma are merged here-    KeycodeKPComma  -> K.Char '.'  -- to sidestep national standards-    KeycodeF1       -> K.Fun 1-    KeycodeF2       -> K.Fun 2-    KeycodeF3       -> K.Fun 3-    KeycodeF4       -> K.Fun 4-    KeycodeF5       -> K.Fun 5-    KeycodeF6       -> K.Fun 6-    KeycodeF7       -> K.Fun 7-    KeycodeF8       -> K.Fun 8-    KeycodeF9       -> K.Fun 9-    KeycodeF10      -> K.Fun 10-    KeycodeF11      -> K.Fun 11-    KeycodeF12      -> K.Fun 12-    KeycodeLCtrl    -> K.DeadKey-    KeycodeLShift   -> K.DeadKey-    KeycodeLAlt     -> K.DeadKey-    KeycodeLGUI     -> K.DeadKey-    KeycodeRCtrl    -> K.DeadKey-    KeycodeRShift   -> K.DeadKey-    KeycodeRAlt     -> K.DeadKey-    KeycodeRGUI     -> K.DeadKey-    KeycodeMode     -> K.DeadKey-    KeycodeNumLockClear -> K.DeadKey-    KeycodeUnknown  -> K.Unknown "KeycodeUnknown"-    _ -> let i = fromEnum $ unwrapKeycode n-         in if | 97 <= i && i <= 122-                 && shiftPressed -> K.Char $ Char.chr $ i - 32-               | 32 <= i && i <= 126 -> K.Char $ Char.chr i-               | otherwise -> K.Unknown $ show n+keyTranslate shiftPressed n = case n of+  KeycodeEscape     -> K.Esc+  KeycodeReturn     -> K.Return+  KeycodeBackspace  -> K.BackSpace+  KeycodeTab        -> if shiftPressed then K.BackTab else K.Tab+  KeycodeSpace      -> K.Space+  KeycodeExclaim -> K.Char '!'+  KeycodeQuoteDbl -> K.Char '"'+  KeycodeHash -> K.Char '#'+  KeycodePercent -> K.Char '%'+  KeycodeDollar -> K.Char '$'+  KeycodeAmpersand -> K.Char '&'+  KeycodeQuote -> if shiftPressed then K.Char '"' else K.Char '\''+  KeycodeLeftParen -> K.Char '('+  KeycodeRightParen -> K.Char ')'+  KeycodeAsterisk -> K.Char '*'+  KeycodePlus -> K.Char '+'+  KeycodeComma -> if shiftPressed then K.Char '<' else K.Char ','+  KeycodeMinus -> if shiftPressed then K.Char '_' else K.Char '-'+  KeycodePeriod -> if shiftPressed then K.Char '>' else K.Char '.'+  KeycodeSlash -> if shiftPressed then K.Char '?' else K.Char '/'+  Keycode1 -> if shiftPressed then K.Char '!' else K.Char '1'+  Keycode2 -> if shiftPressed then K.Char '@' else K.Char '2'+  Keycode3 -> if shiftPressed then K.Char '#' else K.Char '3'+  Keycode4 -> if shiftPressed then K.Char '$' else K.Char '4'+  Keycode5 -> if shiftPressed then K.Char '%' else K.Char '5'+  Keycode6 -> if shiftPressed then K.Char '^' else K.Char '6'+  Keycode7 -> if shiftPressed then K.Char '&' else K.Char '7'+  Keycode8 -> if shiftPressed then K.Char '*' else K.Char '8'+  Keycode9 -> if shiftPressed then K.Char '(' else K.Char '9'+  Keycode0 -> if shiftPressed then K.Char ')' else K.Char '0'+  KeycodeColon -> K.Char ':'+  KeycodeSemicolon -> if shiftPressed then K.Char ':' else K.Char ';'+  KeycodeLess -> K.Char '<'+  KeycodeEquals -> if shiftPressed then K.Char '+' else K.Char '='+  KeycodeGreater -> K.Char '>'+  KeycodeQuestion -> K.Char '?'+  KeycodeAt -> K.Char '@'+  KeycodeLeftBracket -> if shiftPressed then K.Char '{' else K.Char '['+  KeycodeBackslash -> if shiftPressed then K.Char '|' else K.Char '\\'+  KeycodeRightBracket -> if shiftPressed then K.Char '}' else K.Char ']'+  KeycodeCaret -> K.Char '^'+  KeycodeUnderscore -> K.Char '_'+  KeycodeBackquote -> if shiftPressed then K.Char '~' else K.Char '`'+  KeycodeUp         -> K.Up+  KeycodeDown       -> K.Down+  KeycodeLeft       -> K.Left+  KeycodeRight      -> K.Right+  KeycodeHome       -> K.Home+  KeycodeEnd        -> K.End+  KeycodePageUp     -> K.PgUp+  KeycodePageDown   -> K.PgDn+  KeycodeInsert     -> K.Insert+  KeycodeDelete     -> K.Delete+  KeycodeKPDivide   -> K.KP '/'+  KeycodeKPMultiply -> K.KP '*'+  KeycodeKPMinus    -> K.Char '-'  -- KP and normal are merged here+  KeycodeKPPlus     -> K.Char '+'  -- KP and normal are merged here+  KeycodeKPEnter    -> K.Return+  KeycodeKPEquals   -> K.Return  -- in case of some funny layouts+  KeycodeKP1 -> if shiftPressed then K.KP '1' else K.End+  KeycodeKP2 -> if shiftPressed then K.KP '2' else K.Down+  KeycodeKP3 -> if shiftPressed then K.KP '3' else K.PgDn+  KeycodeKP4 -> if shiftPressed then K.KP '4' else K.Left+  KeycodeKP5 -> if shiftPressed then K.KP '5' else K.Begin+  KeycodeKP6 -> if shiftPressed then K.KP '6' else K.Right+  KeycodeKP7 -> if shiftPressed then K.KP '7' else K.Home+  KeycodeKP8 -> if shiftPressed then K.KP '8' else K.Up+  KeycodeKP9 -> if shiftPressed then K.KP '9' else K.PgUp+  KeycodeKP0 -> if shiftPressed then K.KP '0' else K.Insert+  KeycodeKPPeriod -> K.Char '.'  -- dot and comma are merged here+  KeycodeKPComma  -> K.Char '.'  -- to sidestep national standards+  KeycodeF1       -> K.Fun 1+  KeycodeF2       -> K.Fun 2+  KeycodeF3       -> K.Fun 3+  KeycodeF4       -> K.Fun 4+  KeycodeF5       -> K.Fun 5+  KeycodeF6       -> K.Fun 6+  KeycodeF7       -> K.Fun 7+  KeycodeF8       -> K.Fun 8+  KeycodeF9       -> K.Fun 9+  KeycodeF10      -> K.Fun 10+  KeycodeF11      -> K.Fun 11+  KeycodeF12      -> K.Fun 12+  KeycodeLCtrl    -> K.DeadKey+  KeycodeLShift   -> K.DeadKey+  KeycodeLAlt     -> K.DeadKey+  KeycodeLGUI     -> K.DeadKey+  KeycodeRCtrl    -> K.DeadKey+  KeycodeRShift   -> K.DeadKey+  KeycodeRAlt     -> K.DeadKey+  KeycodeRGUI     -> K.DeadKey+  KeycodeMode     -> K.DeadKey+  KeycodeNumLockClear -> K.DeadKey+  KeycodeUnknown  -> K.Unknown "KeycodeUnknown"+  _ -> let i = fromEnum $ unwrapKeycode n+       in if | 97 <= i && i <= 122+               && shiftPressed -> K.Char $ Char.chr $ i - 32+             | 32 <= i && i <= 126 -> K.Char $ Char.chr i+             | otherwise -> K.Unknown $ show n   sDL_ALPHA_OPAQUE :: Word8
Game/LambdaHack/Client/UI/HandleHumanLocalM.hs view
@@ -755,6 +755,7 @@                       in "Originally of" <+> gname tfact                          <> ", now fighting for" <+> dominatedBy <> "."                     _ | bfid body == side -> ""  -- just one of us+                    _ | bproj body -> "Launched by" <+> gname bfact <> "."                     _ -> "One of" <+> gname bfact <> "."                   idesc = case itemDisco $ itemToF (btrunk body) (1, []) of                     Nothing -> ""  -- no details, only show the name
Game/LambdaHack/Client/UI/Key.hs view
@@ -281,7 +281,7 @@ -- To be used, in particular, for the command bindings and macros -- in the config file. ----- See https://github.com/twobob/gtk-/blob/master/gdk/keyname-table.h+-- See <https://github.com/twobob/gtk-/blob/master/gdk/keyname-table.h> keyTranslate :: String -> Key keyTranslate "less"          = Char '<' keyTranslate "greater"       = Char '>'@@ -426,7 +426,7 @@ keyTranslate s               = Unknown s  -- | Translate key from a Web API string description--- (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#Key_values)+-- (<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#Key_values>) -- to our internal key type. To be used in web frontends. -- The argument says whether Shift is pressed. keyTranslateWeb :: String -> Bool -> Key
Game/LambdaHack/Common/Save.hs view
@@ -52,6 +52,7 @@         dataDir <- appDataDir         tryCreateDir (dataDir </> "saves")         let fileName = stateToFileName s+        yield  -- minimize UI lag due to saving         encodeEOF (dataDir </> "saves" </> fileName) (vExevLib cops, s)         -- Wait until the save finished. During that time, the mvar         -- is continually updated to newest state values.
Game/LambdaHack/Common/Thread.hs view
@@ -10,7 +10,8 @@ import Control.Concurrent.Async import Control.Concurrent.MVar --- Swiped from http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent.html. Ported to Async to link exceptions, to let travis tests fail.+-- Swiped from <http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent.html>+-- Ported to Async to link exceptions, to let travis tests fail.  forkChild :: MVar [Async ()] -> IO () -> IO () forkChild children io = do
Game/LambdaHack/Server/Commandline.hs view
@@ -12,8 +12,11 @@ import Prelude ()  import Game.LambdaHack.Common.Prelude+-- Cabal+import qualified Paths_LambdaHack as Self (version)  import qualified Data.Text as T+import           Data.Version import           Options.Applicative import qualified System.Random as R @@ -29,10 +32,15 @@  -- | Parser for server options from commandline arguments. serverOptionsPI :: ParserInfo ServerOptions-serverOptionsPI =  info ( helper <*> serverOptionsP )+serverOptionsPI =  info (serverOptionsP <**> helper <**> version)                $  fullDesc                <> progDesc "Configure debug options here, gameplay options in configuration file." +version :: Parser (a -> a)+version = infoOption (showVersion Self.version)+  (  long "version"+  <> help "Print engine version information" )+ serverOptionsP :: Parser ServerOptions serverOptionsP = do   ~(snewGameSer, scurChalSer)@@ -60,6 +68,7 @@   sfontSize         <- fontSizeP   sfontDir          <- fontDirP   scolorIsBold      <- noColorIsBoldP+  slogPriority      <- logPriorityP   smaxFps           <- maxFpsP   sdisableAutoYes   <- disableAutoYesP   snoAnim           <- noAnimP@@ -228,6 +237,12 @@   option auto (  long "maxFps"               <> metavar "N"               <> help "Display at most N frames per second" )++logPriorityP :: Parser (Maybe Int)+logPriorityP = optional $ max 1 <$>+  option auto (  long "logPriority"+              <> metavar "N"+              <> help "Log only messages of priority at least N, where 1 (all) is the lowest and 5 (errors only) is the default." )  disableAutoYesP :: Parser Bool disableAutoYesP =
Game/LambdaHack/Server/DebugM.hs view
@@ -40,6 +40,7 @@ debugResponse fid resp = case resp of   RespUpdAtomic _ cmd@UpdPerception{} -> debugPlain fid "RespUpdAtomic" cmd   RespUpdAtomic _ cmd@UpdResume{} -> debugPlain fid "RespUpdAtomic" cmd+  RespUpdAtomic _ cmd@UpdRestart{} -> debugPlain fid "RespUpdAtomic" cmd   RespUpdAtomic _ cmd@UpdSpotTile{} -> debugPlain fid "RespUpdAtomic" cmd   RespUpdAtomic _ cmd -> debugPretty fid "RespUpdAtomic" cmd   RespUpdAtomicNoState cmd@UpdPerception{} ->
Game/LambdaHack/Server/HandleRequestM.hs view
@@ -85,7 +85,7 @@ {-# INLINE setBWait #-} setBWait cmd aid b = do   let mwait = case cmd of-        ReqWait -> Just True  -- true wait, with bracind, no overhead, etc.+        ReqWait -> Just True  -- true wait, with bracing, no overhead, etc.         ReqWait10 -> Just False  -- false wait, only one clip at a time         _ -> Nothing   when ((mwait == Just True) /= bwait b) $
Game/LambdaHack/Server/PeriodicM.hs view
@@ -160,13 +160,13 @@   modifyServer $ \ser ->     ser {sactorTime = ageActor (bfid b) (blid b) aid t $ sactorTime ser} --- Add communication overhead time delta to all non-projectile, non-dying--- faction's actors (except the leader). Effectively, this limits moves+-- | Add communication overhead time delta to all non-projectile, non-dying+-- faction's actors, except the leader. Effectively, this limits moves -- of a faction on a level to 10, regardless of the number of actors -- and their speeds. To avoid animals suddenly acting extremely sluggish -- whenever monster's leader visits a distant arena that has a crowd -- of animals, overhead applies only to actors on the same level.--- Since the number of active arenas is limited, this bounds the total moves+-- Since the number of active levels is limited, this bounds the total moves -- per turn of each faction as well. -- -- Leader is immune from overhead and so he is faster than other faction@@ -176,22 +176,21 @@ -- having very long UI turns, introducing UI lag. overheadActorTime :: MonadServerAtomic m => FactionId -> LevelId -> m () overheadActorTime fid lid = do-  actorTime <- getsServer $ (EM.! fid) . sactorTime+  actorTimeFid <- getsServer $ (EM.! fid) . sactorTime+  let actorTimeLid = actorTimeFid EM.! lid   s <- getState   mleader <- getsState $ gleader . (EM.! fid) . sfactionD-  arenas <- getsServer sarenas-  let f !lid2 !_aid !time | lid2 /= lid = time-      f _ aid time =+  let f !aid !time =         let body = getActorBody aid s         in if isNothing (btrajectory body)-              && bhp body > 0+              && bhp body > 0  -- speed up all-move-at-once carcass removal               && Just aid /= mleader  -- leader fast, for UI to be fast            then timeShift time (Delta timeClip)            else time-      g !acc !lid2 = EM.adjust (EM.mapWithKey $ f lid2) lid acc-      actorTimeNew = foldl' g actorTime arenas+      actorTimeLid2 = EM.mapWithKey f actorTimeLid+      actorTimeFid2 = EM.insert lid actorTimeLid2 actorTimeFid   modifyServer $ \ser ->-    ser {sactorTime = EM.insert fid actorTimeNew $ sactorTime ser}+    ser {sactorTime = EM.insert fid actorTimeFid2 $ sactorTime ser}  -- | Swap the relative move times of two actors (e.g., when switching -- a UI leader).
GameDefinition/Content/CaveKind.hs view
@@ -60,7 +60,7 @@   , clegendLitTile  = "legendLit"   , cescapeGroup    = Nothing   , cstairFreq      = [("staircase", 100)]-  , cdesc         = ""+  , cdesc         = "Winding tunnels stretch into the dark."   }  -- no lit corridor alternative, because both lit # and . look bad here arena = rogue   { csymbol       = 'A'@@ -87,7 +87,7 @@   , cdefTile      = "arenaSetLit"   , cdarkCorTile  = "trailLit"  -- let trails give off light   , clitCorTile   = "trailLit"-  , cdesc         = ""+  , cdesc         = "The shelves groan with dusty books and tattered scrolls."   } arena2 = arena   { cname         = "Smoking rooms"@@ -98,7 +98,7 @@   , citemNum      = 7 `d` 5  -- rare, so make it exciting   , citemFreq     = [("useful", 20), ("treasure", 40), ("any vial", 40)]   , cdefTile      = "arenaSetDark"-  , cdesc         = ""+  , cdesc         = "Velvet couches exude the strong smell of tobacco."   } laboratory = arena2   { csymbol       = 'L'@@ -121,7 +121,7 @@   , cdefTile      = "fillerWall"   , cdarkCorTile  = "labTrailLit"  -- let lab smoke give off light always   , clitCorTile   = "labTrailLit"-  , cdesc         = "An experiment (or was it manufacturing?) had gone wrong here."+  , cdesc         = "Shattered glassware and the sharp scent of spilt chemicals show that something terrible happened here."   } empty = rogue   { csymbol       = 'E'@@ -153,7 +153,7 @@   , cdefTile      = "emptySet"   , cdarkCorTile  = "floorArenaDark"   , clitCorTile   = "floorArenaLit"-  , cdesc         = ""+  , cdesc         = "Swirls of warm fog fill the air, the hiss of geysers sounding all around."   } noise = rogue   { csymbol       = 'N'@@ -180,7 +180,7 @@   , couterFenceTile = "noise fence"  -- ensures no cut-off parts from collapsed   , cdarkCorTile  = "floorArenaDark"   , clitCorTile   = "floorArenaLit"-  , cdesc         = ""+  , cdesc         = "Soon, these passages will be swallowed up by the mud."   } noise2 = noise   { cname         = "Frozen derelict mine"@@ -189,12 +189,12 @@   , citemNum      = 13 `d` 5  -- an incentive to explore the final labyrinth   , cplaceFreq    = [("noise", 1), ("mine", 99)]   , cstairFreq    = [("gated staircase", 100)]-  , cdesc         = ""+  , cdesc         = "Pillars of shining ice create a frozen labyrinth."   } shallow2rogue = rogue   { cfreq         = [("shallow random 2", 100)]   , cextraStairs  = 1  -- ensure heroes meet initial monsters and their loot-  , cdesc         = ""+  , cdesc         = "The snorts and grunts of savage beasts can be clearly heard."   } shallow1rogue = shallow2rogue   { csymbol       = 'B'@@ -206,7 +206,7 @@   , citemNum      = 8 `d` 5  -- lure them in with loot   , citemFreq     = filter ((/= "treasure") . fst) $ citemFreq rogue   , cescapeGroup  = Just "escape up"-  , cdesc         = ""+  , cdesc         = "This close to the surface, the sunlight still illuminates the dungeon."   } raid = rogue   { csymbol       = 'T'@@ -244,7 +244,7 @@   , cdefTile      = "brawlSetLit"   , cdarkCorTile  = "floorArenaLit"   , clitCorTile   = "floorArenaLit"-  , cdesc         = ""+  , cdesc         = "Sunlight falls through the trees and dapples on the ground."   } shootout = rogue  -- a scenario with strong missiles;                   -- few solid tiles, but only translucent tiles or walkable
GameDefinition/Content/ItemKind.hs view
@@ -35,9 +35,9 @@  items :: [ItemKind] items =-  [sandstoneRock, dart, spike, slingStone, slingBullet, paralizingProj, harpoon, net, light1, light2, light3, blanket, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, flask18, flask19, flask20, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, imageItensifier, sightSharpening, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorMail, gloveFencing, gloveGauntlet, gloveJousting, buckler, shield, dagger, daggerDropBestWeapon, hammer, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberdPushActor, wand1, wand2, gem1, gem2, gem3, gem4, gem5, currency]+  [sandstoneRock, dart, spike, slingStone, slingBullet, paralizingProj, harpoon, net, light1, light2, light3, blanket, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, flask18, flask19, flask20, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, imageItensifier, sightSharpening, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorMail, gloveFencing, gloveGauntlet, gloveJousting, buckler, shield, dagger, daggerDropBestWeapon, hammer, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberdPushActor, wand1, wand2, gem1, gem2, gem3, gem4, gem5, currency, explosive, firecracker] -sandstoneRock,    dart, spike, slingStone, slingBullet, paralizingProj, harpoon, net, light1, light2, light3, blanket, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, flask18, flask19, flask20, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, imageItensifier, sightSharpening, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorMail, gloveFencing, gloveGauntlet, gloveJousting, buckler, shield, dagger, daggerDropBestWeapon, hammer, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberdPushActor, wand1, wand2, gem1, gem2, gem3, gem4, gem5, currency :: ItemKind+sandstoneRock,    dart, spike, slingStone, slingBullet, paralizingProj, harpoon, net, light1, light2, light3, blanket, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, flask18, flask19, flask20, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, imageItensifier, sightSharpening, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorMail, gloveFencing, gloveGauntlet, gloveJousting, buckler, shield, dagger, daggerDropBestWeapon, hammer, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberdPushActor, wand1, wand2, gem1, gem2, gem3, gem4, gem5, currency, explosive, firecracker :: ItemKind  necklace, ring, potion, flask, scroll, wand, gem :: ItemKind  -- generic templates @@ -276,7 +276,7 @@                , AddArmorMelee 1, AddMaxCalm 2 ]   , ieffects = []   , ifeature = [Lobable, Identified, Equipable]  -- not Fragile; reusable douse-  , idesc    = ""+  , idesc    = "Warm, comforting, and concealing, woven from soft wool."   , ikit     = []   } @@ -293,6 +293,77 @@ -- all aspects. -- -- No flask nor temporary organ of Calm depletion, since Calm reduced often.++-- All the explosive items are currently disabled (empty rarity),+-- so that we can brainstorm them without breaking game balance.+explosive = ItemKind+  { isymbol = symbolFlask+      -- if it's, in fact, a flask of laboratory-produced highly explosive+      -- liquid, symbolFlask makes sense; it also suggests it can be lobbed,+      -- which is very handy for an explosive thrown weapon;+      -- but symbolProjectile makes sense, too, if it's essentially a small+      -- grenade, specifically designed to be thrown, or symbolTool+      -- if it's a heavy mining explosive+  , iname = "explosive"  -- we will need something more specific, even it it's+                         -- only a generic template item+  , ifreq = [("useful", 100)]+  , iflavour = zipLiquid [BrRed]+      -- this ensures the object will be bright red on the screen+      -- and will be described as "red-speckled"; we are also free+      -- to manually set both the colour and the description+  , icount = 1+  , irarity = []  -- [(1, 7), (10, 4)] means it's quite common (7) first level+                  -- and less common (4) on the deepest level of the dungeon,+                  -- linearly interpolating in-between+  , iverbHit = "blast"+  , iweight = 500  -- flavour and affects the speed when thrown;+                   -- 250g and below is full speed+  , idamage = toDmg 0+  , iaspects = []+  , ieffects = [Explode "blast 20", OnSmash (Explode "blast 20")]+      -- this is tricky: @OnSmash@ causes the explosion when the item+      -- hits a tile (including floor), so a well aimed item can successfully+      -- ungulf many enemies (or a single hidden or fast-moving enemy)+      -- in an explosion; however the lone @Explode "blast 20"@+      -- activates only when an actor is hit (or applies the item)+      -- and makes the actor the centre of the explosion and so keeps him+      -- unharmed (from the initial explosion; fireckrackers cause chain+      -- explosion so the secondary blasts can harm him);+      -- this is why "oil lamp", which is the closest to a grenade+      -- we currently have, doesn't have a lone Exlode effect,+      -- but instead causes a couple of unplesant effects directly:+      -- slight burning, before the fire is doused, and paralysis+      -- from slipping on the oil and slippery grip of items,+      -- until the oil can be wiped out; it also causes a tiny impact damage;+      -- all this suggests that we should define "blastI" item+      -- that not only blasts outward, but also inward, by first creating+      -- particles that move 1 (or 2?) tle away outward and then starting+      -- a standard "blast" explosion in each (firecracker does that,+      -- but with many, not just 2 levels of explosions)+  , ifeature = [Lobable, Fragile, Identified, toVelocity 50]+      -- not Applicable, because won't be applied, only thrown;+      -- Fragile, because we want it to explode at target tile, even if+      -- it hits no actor nor wall when it gets there;+      -- Identified, because the player recognizes it from the description+      -- already (unless we design granades that all look alike, but some+      -- explode with blast, others with fireckracker, etc. and the player+      -- needs to experiment to learn to recognize them);+      -- @toVelocity 50@ means will have 50% of the speed of an ideally shaped+      -- item of the same weight+  , idesc = "The practical application of science."+  , ikit = []+  }++firecracker = explosive+  { iname = "firecracker bag/stick?"+     -- remove the revealing name if we go the "10 kinds of grenades" route,+     -- but make sure it's not too similar to the current "20 kinds of flasks+     -- and potions that also explode" fun+  , iverbHit = "crack"  -- a pun, matches the verb from ItemKindBlast.hs+  , ieffects = [Explode "firecracker 5", OnSmash (Explode "firecracker 5")]+  , idesc = "String and paper, concealing a deadly surprise."+  }+ flask = ItemKind   { isymbol  = symbolFlask   , iname    = "flask"@@ -473,6 +544,7 @@   , irarity  = [(6, 9), (10, 9)]   , ieffects = [ Unique, ELabel "of Attraction", Impress, RefillCalm (-20)                , OnSmash (Explode "pheromone") ]+  -- , idesc    = ""   } potion3 = potion   { ieffects = [ RefillHP 5, DropItem 1 maxBound COrgan "poisoned"@@ -532,6 +604,7 @@                , Impress, RefillCalm (-60)                , OnSmash (Explode "healing mist 2")                , OnSmash (Explode "pheromone") ]+  -- , idesc    = ""   }  -- * Non-exploding consumables, not specifically designed for throwing@@ -782,6 +855,7 @@                , Recharging (RefillCalm (-1)) ]  -- fake "hears something" :)                ++ ieffects necklace   , ifeature = Durable : ifeature necklace+  -- , idesc    = ""   } necklace8 = necklace   { iaspects = [Timeout $ (1 `d` 3 + 3 - 1 `dl` 3) * 5]@@ -896,6 +970,7 @@   , ieffects = [ Unique, ELabel "of Rush"  -- no explosion, because Durable                , EqpSlot EqpSlotAddSpeed ]   , ifeature = Durable : ifeature ring+  -- , idesc    = ""   } ring7 = ring   { ifreq    = [("useful", 10), ("ring of opportunity sniper", 1) ]@@ -1098,6 +1173,7 @@   , idamage  = toDmg $ 8 `d` 1   , iaspects = iaspects hammer ++ [Timeout $ (1 `d` 2 + 3 - 1 `dl` 2) * 2]   , ieffects = ieffects hammer ++ [Unique, Recharging $ Paralyze 10]+  -- , idesc    = ""   } hammerSpark = hammer   { iname    = "Grand Smithhammer"@@ -1106,6 +1182,7 @@   , 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"]+  -- , idesc    = ""   } sword = ItemKind   { isymbol  = symbolEdged
GameDefinition/Content/ItemKindActor.hs view
@@ -22,7 +22,6 @@    geyserBoiling, geyserArsenic, geyserSulfur :: ItemKind  -- * Hunams- warrior = ItemKind   { isymbol  = '@'   , iname    = "warrior"  -- modified if initial actors in hero faction@@ -39,18 +38,26 @@                , AddAbility AbAlter 2 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "A hardened veteran of combat."   , ikit     = [ ("fist", COrgan), ("foot", COrgan), ("eye 6", COrgan)                , ("sapient brain", COrgan) ]   } warrior2 = warrior-  { iname    = "adventurer" }+  { iname    = "adventurer"+  -- , idesc    = ""+  } warrior3 = warrior-  { iname    = "blacksmith" }+  { iname    = "blacksmith"+  -- , idesc    = ""+   } warrior4 = warrior-  { iname    = "forester" }+  { iname    = "forester"+  -- , idesc    = ""+  } warrior5 = warrior-  { iname    = "scientist" }+  { iname    = "scientist"+  -- , idesc    = ""+  }  scout = warrior   { iname    = "scout"@@ -59,11 +66,13 @@                ++ [ ("add sight", CEqp)                   , ("armor ranged", CEqp)                   , ("add nocto 1", CInv) ]+  -- , idesc    = ""   } ranger = warrior   { iname    = "ranger"   , ifreq    = [("ranger hero", 100), ("mobile", 1)]   , ikit     = ikit warrior ++ [("weak arrow", CInv), ("armor ranged", CEqp)]+  -- , idesc    = ""   } escapist = warrior   { iname    = "escapist"@@ -75,6 +84,7 @@                   , ("flask", CInv)                   , ("light source", CInv)                   , ("blanket", CInv) ]+  -- , idesc    = ""   } ambusher = warrior   { iname    = "ambusher"@@ -83,25 +93,37 @@                ++ [ ("ring of opportunity sniper", CEqp)                   , ("light source", CEqp), ("wooden torch", CInv)                   , ("weak arrow", CInv), ("any arrow", CSha), ("flask", CSha) ]+  -- , idesc    = ""   } soldier = warrior   { iname    = "soldier"   , ifreq    = [("soldier hero", 100), ("mobile", 1)]   , ikit     = ikit warrior ++ [("starting weapon", CEqp)]+  -- , idesc    = ""   }  civilian = warrior   { iname    = "clerk"   , ifreq    = [("civilian", 100), ("mobile", 1)]-  , iflavour = zipPlain [BrBlack] }+  , iflavour = zipPlain [BrBlack]+  -- , idesc    = ""+  } civilian2 = civilian-  { iname    = "hairdresser" }+  { iname    = "hairdresser"+  -- , idesc    = ""+  } civilian3 = civilian-  { iname    = "lawyer" }+  { iname    = "lawyer"+  -- , idesc    = ""+  } civilian4 = civilian-  { iname    = "peddler" }+  { iname    = "peddler"+  -- , idesc    = ""+  } civilian5 = civilian-  { iname    = "tax collector" }+  { iname    = "tax collector"+  -- , idesc    = ""+  }  -- * Monsters @@ -238,7 +260,7 @@   , iaspects = [ AddMaxHP 12, AddMaxCalm 70, AddSpeed 24, AddNocto 2 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "An opportunistic predator, feeding on carrion and the weak."   , ikit     = [ ("small jaw", COrgan), ("eye 6", COrgan), ("nostril", COrgan)                , ("animal brain", COrgan) ]   }@@ -261,7 +283,7 @@       -- on either side, are just too frustrating.   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "It soars high above, searching for vulnerable prey."   , ikit     = [ ("screeching beak", COrgan)  -- in reality it grunts and hisses                , ("small claw", COrgan), ("eye 7", COrgan)                , ("animal brain", COrgan) ]@@ -280,7 +302,7 @@                , AddAbility AbAlter (-2) ]  -- can't use stairs nor doors   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Its only defence is the terrible stench."   , ikit     = [ ("scent gland", COrgan)                , ("small claw", COrgan), ("snout", COrgan)                , ("nostril", COrgan), ("eye 3", COrgan)@@ -300,7 +322,7 @@                , AddAbility AbAlter (-2) ]  -- can't use stairs nor doors   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "When threatened, it rolls into a ball."   , ikit     = [ ("hooked claw", COrgan), ("snout", COrgan)                , ("armored skin", COrgan), ("armored skin", COrgan)                , ("nostril", COrgan), ("eye 3", COrgan)@@ -320,7 +342,7 @@                , AddAbility AbAlter (-2) ]  -- can't use stairs nor doors   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Numbing venom ensures that even the fastest prey has no escape."   , ikit     = [ ("venom tooth", COrgan), ("small claw", COrgan)                , ("eye 3", COrgan), ("nostril", COrgan)                , ("animal brain", COrgan) ]@@ -339,7 +361,7 @@                , AddAbility AbAlter (-2) ]  -- can't use stairs nor doors   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Beware its rattle - it serves as a warning of an agonising death."   , ikit     = [ ("venom fang", COrgan)                , ("eye 4", COrgan), ("nostril", COrgan)                , ("animal brain", COrgan) ]@@ -357,7 +379,7 @@   , iaspects = [ AddMaxHP 41, AddMaxCalm 60, AddSpeed 18, AddNocto 2 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Larger and more aggressive than any other lizard."   , ikit     = [ ("large tail", COrgan), ("jaw", COrgan)                , ("hooked claw", COrgan), ("speed gland 4", COrgan)                , ("armored skin", COrgan), ("eye 3", COrgan)@@ -377,7 +399,7 @@   , iaspects = [ AddMaxHP 20, AddMaxCalm 70, AddSpeed 32, AddNocto 2 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Skulking in the shadows, waiting for easy prey."   , ikit     = [ ("jaw", COrgan), ("eye 6", COrgan), ("nostril", COrgan)                , ("animal brain", COrgan) ]   }@@ -394,7 +416,7 @@   , iaspects = [ AddMaxHP 41, AddMaxCalm 70, AddSpeed 18, AddNocto 2 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "An armored predator from the dawn of time."   , ikit     = [ ("large jaw", COrgan), ("large tail", COrgan)                , ("small claw", COrgan)                , ("armored skin", COrgan), ("eye 6", COrgan)@@ -438,7 +460,7 @@                , AddAbility AbAlter (-2) ]  -- can't use stairs nor doors   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Every bee would die for the queen."   , ikit     = [ ("bee sting", COrgan), ("vision 6", COrgan)                , ("insect mortality", COrgan), ("animal brain", COrgan) ]   }@@ -457,7 +479,7 @@                , AddArmorMelee 80, AddArmorRanged 40 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "A vicious cloud of stings and hate."   , ikit     = [ ("sting", COrgan), ("vision 8", COrgan)                , ("insect mortality", COrgan), ("animal brain", COrgan) ]   }@@ -475,7 +497,7 @@                , AddAbility AbWait 1, AddAbility AbMelee 1 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Each branch bears long, curved thorns."   , ikit     = [("thorn", COrgan), ("armored skin", COrgan)]   } geyserBoiling = ItemKind@@ -493,7 +515,7 @@                , AddArmorMelee 40, AddArmorRanged 20 ]  -- hard material   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "A jet of acidic water, hot enough to melt flesh."   , ikit     = [("boiling vent", COrgan), ("boiling fissure", COrgan)]   } geyserArsenic = ItemKind@@ -511,7 +533,7 @@                , AddAbility AbWait 1, AddAbility AbMelee 1 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "The sharp scent betrays the poison within the spray."   , ikit     = [("arsenic vent", COrgan), ("arsenic fissure", COrgan)]   } geyserSulfur = ItemKind@@ -529,6 +551,6 @@                , AddAbility AbWait 1, AddAbility AbMelee 1 ]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "The pool boils and bubbles, stinking of rotten eggs. Despite the smell, these waters purify and strengthen."   , ikit     = [("sulfur vent", COrgan), ("sulfur fissure", COrgan)]   }
GameDefinition/Content/ItemKindBlast.hs view
@@ -87,7 +87,7 @@                     $ toGroupName $ "firecracker" <+> tshow (n - 1)                   | n > 2 ]   , ifeature = [toVelocity 5, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Scraps of burnt paper, covering little pockets of black powder, buffeted by colorful explosions."   , ikit     = []   } firecracker7 = firecracker 7@@ -114,7 +114,7 @@   -- Linger 10, because sometimes it takes 2 turns due to starting just   -- before actor turn's end (e.g., via a necklace).   , ifeature = [toLinger 10, Fragile, Identified]  -- 2 steps, 1 turn-  , idesc    = ""+  , idesc    = "A pleasant scent."   , ikit     = []   } pheromone = ItemKind@@ -130,7 +130,7 @@   , iaspects = []   , ieffects = [Impress, RefillCalm (-10)]   , ifeature = [toVelocity 10, Fragile, Identified]  -- 2 steps, 2 turns-  , idesc    = ""+  , idesc    = "A sharp, strong scent."   , ikit     = []   } mistCalming = ItemKind  -- unused@@ -146,7 +146,7 @@   , iaspects = []   , ieffects = [RefillCalm 2]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "A soothing, gentle cloud."   , ikit     = []   } odorDistressing = ItemKind@@ -162,7 +162,7 @@   , iaspects = []   , ieffects = [RefillCalm (-20)]   , ifeature = [toLinger 10, Fragile, Identified]  -- 2 steps, 1 turn-  , idesc    = ""+  , idesc    = "It turns the stomach."   , ikit     = []   } mistHealing = ItemKind@@ -178,7 +178,7 @@   , iaspects = [AddShine 1]   , ieffects = [RefillHP 2]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "It fills the air with light and life."   , ikit     = []   } mistHealing2 = ItemKind@@ -194,7 +194,7 @@   , iaspects = [AddShine 2]   , ieffects = [RefillHP 4]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "At its touch, wounds close and bruises fade."   , ikit     = []   } mistWounding = ItemKind@@ -210,7 +210,7 @@   , iaspects = []   , ieffects = [RefillHP (-2)]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "The air itself stings and itches."   , ikit     = []   } distortion = ItemKind@@ -226,7 +226,7 @@   , iaspects = []   , ieffects = [Teleport $ 15 + 1 `d` 10]   , ifeature = [toLinger 10, Fragile, Identified]  -- 2 steps, 1 turn-  , idesc    = ""+  , idesc    = "The air shifts oddly, as though light is being warped."   , ikit     = []   } glassPiece = ItemKind  -- when blowing up windows@@ -242,7 +242,7 @@   , iaspects = []   , ieffects = [RefillHP (-1)]  -- high velocity, so can't do idamage   , ifeature = [toLinger 20, Fragile, Identified]  -- 4 steps, 1 turn-  , idesc    = ""+  , idesc    = "Swift, sharp edges."   , ikit     = []   } smoke = ItemKind  -- when stuff burns out  -- unused@@ -258,7 +258,7 @@   , iaspects = []   , ieffects = []   , ifeature = [toVelocity 20, Fragile, Identified]  -- 4 steps, 2 turns-  , idesc    = ""+  , idesc    = "Twirling clouds of grey smoke."   , ikit     = []   } boilingWater = ItemKind@@ -274,7 +274,7 @@   , iaspects = []   , ieffects = [Burn 1]   , ifeature = [toVelocity 30, Fragile, Identified]  -- 6 steps, 2 turns-  , idesc    = ""+  , idesc    = "It bubbles and hisses."   , ikit     = []   } glue = ItemKind@@ -290,7 +290,7 @@   , iaspects = []   , ieffects = [Paralyze 10]   , ifeature = [toVelocity 20, Fragile, Identified]  -- 4 steps, 2 turns-  , idesc    = ""+  , idesc    = "Thick and clinging."   , ikit     = []   } singleSpark = ItemKind@@ -306,7 +306,7 @@   , iaspects = [AddShine 4]   , ieffects = []   , ifeature = [toLinger 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "A glowing ember."   , ikit     = []   } spark = ItemKind@@ -322,7 +322,7 @@   , iaspects = [AddShine 4]   , ieffects = [Burn 1]   , ifeature = [toLinger 10, Fragile, Identified]  -- 2 steps, 1 turn-  , idesc    = ""+  , idesc    = "A flash of fire."   , ikit     = []   } @@ -345,7 +345,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "strengthened" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "A thick rain of droplets."   , ikit     = []   } sparseShower = ItemKind@@ -361,7 +361,7 @@   , iaspects = []   , ieffects = [toOrganGameTurn "weakened" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Light droplets that cling to clothing."   , ikit     = []   } protectingBalmMelee = ItemKind@@ -377,7 +377,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "protected from melee" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "A thick ointment that hardens the skin."   , ikit     = []   } protectingBalmRanged = ItemKind@@ -393,7 +393,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "protected from ranged" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Grease that protects from flying death."   , ikit     = []   } vulnerabilityBalm = ItemKind@@ -409,7 +409,7 @@   , iaspects = []   , ieffects = [toOrganGameTurn "defenseless" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Only the most learned make use of this."   , ikit     = []   } resolutionDust = ItemKind@@ -426,7 +426,7 @@   , ieffects = [toOrganActorTurn "resolute" (3 + 1 `d` 3)]                  -- short enough duration that @calmEnough@ not a big problem   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "A handful of honest earth, to strengthen the soul."   , ikit     = []   } hasteSpray = ItemKind@@ -442,7 +442,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "hasted" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "A quick spurt."   , ikit     = []   } slownessMist = ItemKind@@ -458,7 +458,7 @@   , iaspects = []   , ieffects = [toOrganGameTurn "slowed" (3 + 1 `d` 3)]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn, mist-  , idesc    = ""+  , idesc    = "Clammy fog, making each movement an effort."   , ikit     = []   } eyeDrop = ItemKind@@ -474,7 +474,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "far-sighted" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Not to be taken orally."   , ikit     = []   } ironFiling = ItemKind@@ -490,7 +490,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "blind" (10 + 1 `d` 10)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "A shaving of bright metal."   , ikit     = []   } smellyDroplet = ItemKind@@ -506,7 +506,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "keen-smelling" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "A viscous lump that stains the skin."   , ikit     = []   } eyeShine = ItemKind@@ -522,7 +522,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "shiny-eyed" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "They almost glow in the dark."   , ikit     = []   } @@ -541,7 +541,7 @@   , iaspects = []   , ieffects = [toOrganActorTurn "drunk" (3 + 1 `d` 3)]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "It burns in the best way."   , ikit     = []   } waste = ItemKind@@ -557,7 +557,7 @@   , iaspects = []   , ieffects = [Burn 1]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Sodden and foul-smelling."   , ikit     = []   } youthSprinkle = ItemKind@@ -573,7 +573,7 @@   , iaspects = []   , ieffects = [toOrganNone "regenerating"]   , ifeature = [toLinger 10, Fragile, Identified]-  , idesc    = ""+  , idesc    = "Bright and smelling of the Spring."   , ikit     = []   } poisonCloud = ItemKind@@ -589,7 +589,7 @@   , iaspects = []   , ieffects = [toOrganNone "poisoned"]   , ifeature = [toVelocity 10, Fragile, Identified]  -- 2 steps, 2 turns-  , idesc    = ""+  , idesc    = "Choking gas that stings the eyes."   , ikit     = []   } mistAntiSlow = ItemKind@@ -605,7 +605,7 @@   , iaspects = []   , ieffects = [DropItem 1 1 COrgan "slowed"]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "A cleansing rain."   , ikit     = []   } mistAntidote = ItemKind@@ -621,6 +621,6 @@   , iaspects = []   , ieffects = [DropItem 1 maxBound COrgan "poisoned"]   , ifeature = [toVelocity 5, Fragile, Identified]  -- 1 step, 1 turn-  , idesc    = ""+  , idesc    = "Washes away death's dew."   , ikit     = []   }
GameDefinition/Content/ItemKindEmbed.hs view
@@ -32,7 +32,7 @@   , iaspects = []   , ieffects = [Ascend True]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "Stairs that rise towards escape."   , ikit     = []   } stairsDown = stairsUp@@ -40,12 +40,14 @@   , iname    = "staircase down"   , ifreq    = [("staircase down", 1)]   , ieffects = [Ascend False]+  , idesc    = ""   } escape = stairsUp   { iname    = "escape"   , ifreq    = [("escape", 1)]   , iflavour = zipPlain [BrYellow]   , ieffects = [Escape]+  , idesc    = ""   } terrainCache = stairsUp   { isymbol  = 'O'@@ -53,6 +55,7 @@   , ifreq    = [("terrain cache", 1)]   , iflavour = zipPlain [BrYellow]   , ieffects = [CreateItem CGround "useful" TimerNone]+  , idesc    = "Glittering gold, just waiting to be taken."   } terrainCacheTrap = ItemKind   { isymbol  = '^'@@ -70,7 +73,7 @@                       , ELabel "", ELabel "", ELabel ""                       , ELabel "", ELabel "" ]]   , ifeature = [Identified]  -- not Durable, springs at most once-  , idesc    = ""+  , idesc    = "It's a trap!"   , ikit     = []   } signboardExit = ItemKind@@ -86,7 +89,7 @@   , iaspects = []   , ieffects = [DetectExit 100]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "A battered sign, carved by unknown hands."   , ikit     = []   } signboardMap = signboardExit@@ -107,7 +110,7 @@   , iaspects = []   , ieffects = [Burn 1, Explode "single spark"]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "A few small logs, burning brightly."   , ikit     = []   } fireBig = fireSmall@@ -117,7 +120,7 @@   , ieffects = [ Burn 2, Explode "spark"                , CreateItem CInv "wooden torch" TimerNone ]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "Glowing with light and warmth."   , ikit     = []   } frost = ItemKind@@ -135,7 +138,7 @@                , RefillCalm 20  -- cold reason                , PushActor (ThrowMod 200 50) ]  -- slippery ice   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "Intricate patterns of shining ice."   , ikit     = []   } rubble = ItemKind@@ -155,7 +158,7 @@                       , ELabel "", ELabel "", ELabel ""                       , ELabel "", ELabel "", ELabel "" ]]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "Broken chunks of rock and glass."   , ikit     = []   } staircaseTrapUp = ItemKind@@ -171,7 +174,7 @@   , iaspects = []   , ieffects = [Temporary "be caught in an updraft", Teleport 5]   , ifeature = [Identified]  -- not Durable, springs at most once-  , idesc    = ""+  , idesc    = "A hidden spring, to help the unwary soar."   , ikit     = []   } -- Needs to be separate from staircaseTrapUp, to make sure the item is@@ -181,6 +184,7 @@   { ifreq    = [("staircase trap down", 1)]   , ieffects = [ Temporary "tumble down the stairwell"                , toOrganActorTurn "drunk" (20 + 1 `d` 5) ]+  , idesc    = "A treacherous slab, to teach those who are too proud."   } doorwayTrap = ItemKind   { isymbol  = '^'@@ -197,7 +201,7 @@                       , toOrganActorTurn "slowed" (20 + 1 `d` 5)                       , toOrganActorTurn "weakened" (20 + 1 `d` 5) ]]   , ifeature = [Identified]  -- not Durable, springs at most once-  , idesc    = ""+  , idesc    = "Just turn the handle..."   , ikit     = []   } obscenePictograms = ItemKind@@ -217,7 +221,7 @@                    [ toOrganActorTurn "strengthened" (3 + 1 `d` 3)                    , CreateItem CInv "sandstone rock" TimerNone ] ]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "They aren't even anatomically possible."   , ikit     = []   } subtleFresco = ItemKind@@ -236,7 +240,7 @@                , Recharging $ toOrganActorTurn "far-sighted" (3 + 1 `d` 3)                , Recharging $ toOrganActorTurn "keen-smelling" (3 + 1 `d` 3) ]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "Expensive yet tasteful."   , ikit     = []   } scratchOnWall = ItemKind@@ -252,7 +256,7 @@   , iaspects = []   , ieffects = [Temporary "start making sense of the scratches", DetectHidden 3]   , ifeature = [Identified, Durable]-  , idesc    = ""+  , idesc    = "A seemingly random series of scratches, carved deep into the wall."   , ikit     = []   } pulpit = ItemKind@@ -270,6 +274,6 @@                , toOrganGameTurn "defenseless" (20 + 1 `d` 5)                , Explode "PhD defense question" ]   , ifeature = [Identified]  -- not Durable, springs at most once-  , idesc    = ""+  , idesc    = "A dark wood stand, where strange priests once preached."   , ikit     = []   }
GameDefinition/Content/ItemKindOrgan.hs view
@@ -41,7 +41,7 @@   , iaspects = []   , ieffects = []   , ifeature = [Durable, Identified, Meleeable]-  , idesc    = ""+  , idesc    = "Simple but effective."   , ikit     = []   } foot = fist@@ -49,7 +49,7 @@   , ifreq    = [("foot", 50)]   , iverbHit = "kick"   , idamage  = toDmg $ 4 `d` 1-  , idesc    = ""+  , idesc    = "A weapon you can still use if disarmed."   }  -- * Universal weapon organs@@ -62,14 +62,14 @@   , idamage  = toDmg $ 2 `d` 1   , iaspects = [Timeout $ 4 + 1 `d` 4]   , ieffects = [Recharging (toOrganGameTurn "slowed" 2)]-  , idesc    = ""+  , idesc    = "A curved talon."   } smallClaw = fist   { iname    = "small claw"   , ifreq    = [("small claw", 50)]   , iverbHit = "slash"   , idamage  = toDmg $ 2 `d` 1-  , idesc    = ""+  , idesc    = "A pearly spike."   } snout = fist   { iname    = "snout"@@ -77,7 +77,7 @@   , icount   = 1   , iverbHit = "bite"   , idamage  = toDmg $ 2 `d` 1-  , idesc    = ""+  , idesc    = "Sensitive and wide-nostrilled."   } smallJaw = fist   { iname    = "small jaw"@@ -85,7 +85,7 @@   , icount   = 1   , iverbHit = "rip"   , idamage  = toDmg $ 3 `d` 1-  , idesc    = ""+  , idesc    = "Filled with small, even teeth."   } jaw = fist   { iname    = "jaw"@@ -93,7 +93,7 @@   , icount   = 1   , iverbHit = "rip"   , idamage  = toDmg $ 5 `d` 1-  , idesc    = ""+  , idesc    = "Delivers a powerful bite."   } largeJaw = fist   { iname    = "large jaw"@@ -101,7 +101,7 @@   , icount   = 1   , iverbHit = "crush"   , idamage  = toDmg $ 10 `d` 1-  , idesc    = ""+  , idesc    = "Enough to swallow anything in a single gulp."   } horn = fist   { iname    = "horn"@@ -110,7 +110,7 @@   , iverbHit = "impale"   , idamage  = toDmg $ 6 `d` 1   , iaspects = [AddHurtMelee 20]-  , idesc    = ""+  , idesc    = "Sharp and solid, for defence or attack."   }  -- * Special weapon organs@@ -121,7 +121,7 @@   , icount   = 4   , iverbHit = "slap"   , idamage  = toDmg $ 4 `d` 1-  , idesc    = ""+  , idesc    = "Damp and dextrous."   } thorn = fist   { iname    = "thorn"@@ -130,7 +130,7 @@   , iverbHit = "impale"   , idamage  = toDmg $ 1 `d` 3   , ifeature = [Identified, Meleeable]  -- not Durable-  , idesc    = ""+  , idesc    = "Sharp yet brittle."   } boilingFissure = fist   { iname    = "fissure"@@ -141,7 +141,7 @@   , iaspects = [AddHurtMelee 20]  -- decreasing as count decreases   , ieffects = [InsertMove $ 1 `d` 3]   , ifeature = [Identified, Meleeable]  -- not Durable-  , idesc    = ""+  , idesc    = "A deep crack to the underworld."   } arsenicFissure = boilingFissure   { iname    = "fissure"@@ -149,6 +149,7 @@   , icount   = 3 + 1 `d` 3   , idamage  = toDmg $ 2 `d` 1   , ieffects = [toOrganGameTurn "weakened" (2 + 1 `d` 2)]+  , idesc    = ""   } sulfurFissure = boilingFissure   { iname    = "fissure"@@ -156,6 +157,7 @@   , icount   = 2 + 1 `d` 2   , idamage  = toDmg 0   , ieffects = [RefillHP 5]+  , idesc    = ""   } beeSting = fist   { iname    = "bee sting"@@ -186,7 +188,7 @@   , idamage  = toDmg $ 2 `d` 1   , iaspects = [Timeout $ 5 + 1 `d` 3]   , ieffects = [Recharging (toOrganGameTurn "slowed" (3 + 1 `d` 3))]-  , idesc    = ""+  , idesc    = "A chilling numbness spreads from its bite."   } venomFang = fist   { iname    = "venom fang"@@ -196,7 +198,7 @@   , idamage  = toDmg $ 2 `d` 1   , iaspects = [Timeout $ 7 + 1 `d` 5]   , ieffects = [Recharging (toOrganNone "poisoned")]-  , idesc    = ""+  , idesc    = "Dripping with deadly venom."   } screechingBeak = fist   { iname    = "screeching beak"@@ -206,7 +208,7 @@   , idamage  = toDmg $ 2 `d` 1   , iaspects = [Timeout $ 5 + 1 `d` 5]   , ieffects = [Recharging $ Summon "scavenger" $ 1 + 1 `dl` 2]-  , idesc    = ""+  , idesc    = "Both a weapon and a beacon, calling more scavengers to the meal."   } largeTail = fist   { iname    = "large tail"@@ -216,7 +218,7 @@   , idamage  = toDmg $ 6 `d` 1   , iaspects = [Timeout $ 1 + 1 `d` 3, AddHurtMelee 20]   , ieffects = [Recharging (PushActor (ThrowMod 400 25))]-  , idesc    = ""+  , idesc    = "Slow but heavy."   }  -- Non-weapons@@ -236,7 +238,7 @@   , iaspects = [AddArmorMelee 30, AddArmorRanged 15]   , ieffects = []   , ifeature = [Durable, Identified]-  , idesc    = ""+  , idesc    = "Homemade armour is just as good."   , ikit     = []   } @@ -249,7 +251,7 @@   , icount   = 2   , iverbHit = "glare at"   , iaspects = [AddSight (intToDice n)]-  , idesc    = ""+  , idesc    = "A piercing stare."   } eye2 = eye 2 eye3 = eye 3
GameDefinition/Content/ItemKindTemporary.hs view
@@ -42,7 +42,7 @@                , Recharging $ tmpNoLonger name                , OnSmash $ tmpNoLonger name ]   , ifeature = [Identified, Fragile, Durable]  -- hack: destroy on drop-  , idesc    = ""+  , idesc    = ""  -- no description needed; stats are enough   , ikit     = []   } 
GameDefinition/Main.hs view
@@ -13,8 +13,11 @@ import qualified GHC.IO.Handle as GHC.IO.Handle import qualified Options.Applicative as OA import           System.Exit+import           System.FilePath import qualified System.IO as SIO +import Game.LambdaHack.Common.File (tryCreateDir)+import Game.LambdaHack.Common.Misc import Game.LambdaHack.Server (serverOptionsPI) import TieKnot @@ -25,8 +28,10 @@   -- 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+    dataDir <- appDataDir+    tryCreateDir dataDir+    fstdout <- SIO.openFile (dataDir </> "stdout.txt") SIO.WriteMode+    fstderr <- SIO.openFile (dataDir </> "stderr.txt") SIO.WriteMode     GHC.IO.Handle.hDuplicateTo fstdout SIO.stdout     GHC.IO.Handle.hDuplicateTo fstderr SIO.stderr   serverOptions <- OA.execParser serverOptionsPI
GameDefinition/MainMenu.ascii view
@@ -1,9 +1,9 @@ fffffjjjjtti,:Lft:                tDEGLLGEKEK;   .    .   . .  .iEDEGL.;iiij ... fLLLLfffjjjti;.fL .              GDDLfGDEDEf .    .  .      .    ;LDWEGi,;iit .. LD#WWELfffjjtt;.if.:           .DEGLLGEDLti,   . .    . . .        ,tLKDG ,;itt.-fK#WWW#DLffjjtt;:;j...        tDEGfLDEDGtK.   .LambdaHack           itLEG::;itt.-;K#WWWWW Lfffjjti:.t...      tDEGfLDEDL,   ..  .  .                  tDGj.;itt.- EWWWWKW  GLffjjti: t,      tDEGLGEEDj;  .                            tLDLi,ittj+fK#WWW#DLffjjtt;:;j...        tDEGfLDEDGtK.  . LambdaHack           itLEG::;itt.+;K#WWWWW Lfffjjti:.t...      tDEGfLDEDL, .                           tDGj.;itt.+ EWWWWKW  GLffjjti: t,      tDEGLGEEDj;   <lambdahack.github.io>      tLDLi,ittj  EKWWWEWG  GLffjjti: tt   .tDEGGDEDGi,                                ;fEG :iitj  LEWWWKKK   GLfffjti: tt  tDDLGDEDGf.  {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ;jGG :iitj   DWWWKDWL   DLffjjti: jt.DDLGDEDLD    {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ,jDG .ittj
LICENSE view
@@ -14,7 +14,7 @@ The whole LambdaHack is licensed under the BSD3 licence, as follows.  Copyright (c) 2008--2017 Andres Loeh-Copyright (c) 2010--2017 Mikolaj Konarski+Copyright (c) 2010--2017 Mikolaj Konarski and others (see git history)  All rights reserved. 
LambdaHack.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.7.0.0+version:       0.7.1.0 synopsis:      A game engine library for roguelike dungeon crawlers description:   LambdaHack is a Haskell game engine library for roguelike games                of arbitrary theme, size and complexity,
Makefile view
@@ -1,5 +1,5 @@ play:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --dumpInitRngs+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --dumpInitRngs  configure-debug: 	cabal configure --enable-profiling --profiling-detail=all-functions -fwith_expensive_assertions --disable-optimization@@ -17,83 +17,81 @@ 	ccjs dist/build/LambdaHack/LambdaHack.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --jscomp_off="*" --externs=node > ../lambdahack.github.io/lambdahack.all.js  frontendRaid:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid  frontendBrawl:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode brawl+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode brawl  frontendShootout:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout  frontendEscape:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape  frontendZoo:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo  frontendAmbush:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush  frontendCrawl:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl  frontendSafari:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode safari+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode safari  frontendSafariSurvival:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "safari survival"+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "safari survival"  frontendBattle:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode battle+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode battle  frontendBattleSurvival:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "battle survival"+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "battle survival"  frontendDefense:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense   benchMemoryAnim:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 120 --setMainRng 47 --frontendNull --noAnim +RTS -s -A1M -RTS+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 120 --setMainRng 47 --frontendNull --noAnim +RTS -s -A1M -RTS  benchBattle:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7  benchAnimBattle:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --newGame 3 --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --newGame 3 --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7  benchFrontendBattle:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7  benchCrawl:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0  benchFrontendCrawl:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0  benchNull: benchBattle benchAnimBattle benchCrawl  bench: benchBattle benchAnimBattle benchFrontendBattle benchCrawl benchFrontendCrawl  nativeBenchCrawl:-	dist/build/LambdaHack/LambdaHack		   --dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	dist/build/LambdaHack/LambdaHack		   --dbgMsgSer --logPriority 4 --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0  nativeBenchBattle:-	dist/build/LambdaHack/LambdaHack		   --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0+	dist/build/LambdaHack/LambdaHack		   --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0  nativeBench: nativeBenchBattle nativeBenchCrawl  nodeBenchCrawl:-	node dist/build/LambdaHack/LambdaHack.jsexe/all.js --dbgMsgSer --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	node dist/build/LambdaHack/LambdaHack.jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0  nodeBenchBattle:-	node dist/build/LambdaHack/LambdaHack.jsexe/all.js --dbgMsgSer --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0+	node dist/build/LambdaHack/LambdaHack.jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0  nodeBench: nodeBenchBattle nodeBenchCrawl  -test-travis-short: test-short- test-travis: test-short test-medium benchNull  test: test-short test-medium benchNull@@ -103,70 +101,73 @@ 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/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log  testBrawl-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log  testShootout-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log  testEscape-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log  testZoo-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log  testAmbush-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log  testCrawl-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl 2> /tmp/teletypetest.log  testSafari-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 2 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log  testSafariSurvival-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 8 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 60 --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 8 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 60 --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" 2> /tmp/teletypetest.log  testBattle-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log  testBattleSurvival-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 60 --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 60 --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" 2> /tmp/teletypetest.log  testDefense-medium:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 300 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 300 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log  test-short-new:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log  # "--setDungeonRng 0 --setMainRng 0" is needed for determinism relative to seed # generated before game save test-short-load:-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootouti --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/LambdaHack/LambdaHack --dbgMsgSer --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootouti --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	dist/build/LambdaHack/LambdaHack --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log  +version:+	dist/build/LambdaHack/LambdaHack --version+ build-binary-common: 	cabal install --disable-library-profiling --disable-profiling --disable-documentation -f-release --only-dependencies 	cabal configure --disable-library-profiling --disable-profiling -f-release --prefix=/ --datadir=. --datasubdir=.@@ -189,7 +190,9 @@  build-binary: build-binary-common 	cp LambdaHackTheGameInstall/bin/LambdaHack LambdaHackTheGame-	tar -czf LambdaHack_x_ubuntu-16.04-amd64.tar.gz LambdaHackTheGame+	dist/build/LambdaHack/LambdaHack --version > /dev/null; \+	LH_VERSION=$$(cat ~/.LambdaHack/stdout.txt); \+	tar -czf LambdaHack_$${LH_VERSION}_ubuntu-16.04-amd64.tar.gz LambdaHackTheGame  new-build-dev: 	cabal new-build --datadir=. --disable-optimization -j1
README.md view
@@ -75,14 +75,16 @@ you may prefer to use a native binary for your architecture, if it exists.  Pre-compiled game binaries for some platforms are available through-the release page[11] and from AppVeyor (Windows 32bit[17] and Windows 64bit[18];-note that these no longer work on Windows XP, since Cygwin and MSYS2-dropped support for XP). To use a pre-compiled binary archive,+the release page[11] (and continuously from AppVeyor[18]).+Note that Windows binaries no longer work on Windows XP, since Cygwin+and MSYS2 dropped support for XP). To use a pre-compiled binary archive, unpack it and run the executable in the unpacked directory.+Or use program shortcuts from the installer, if available. -On Linux, make sure you have the SDL2 libraries suite installed on your system-(e.g., libsdl2, libsdl2-ttf). For Windows, the SDL2 and all other needed-libraries are already contained in the game's binary archive.+On Linux, make sure you have the SDL2 libraries installed on your system+(e.g., libsdl2-2.0-0, libsdl2-ttf-2.0-0 on Ubuntu; also libdw1).+For Windows, the SDL2 and all other needed libraries are already contained+in the game's binary archive.   Screen and keyboard configuration@@ -246,5 +248,4 @@ [11]: https://github.com/LambdaHack/LambdaHack/releases/latest [15]: https://github.com/ghcjs/ghcjs [16]: https://www.npmjs.com/package/google-closure-compiler-[17]: https://ci.appveyor.com/project/Mikolaj/lambdahack-4hh0j/build/artifacts [18]: https://ci.appveyor.com/project/Mikolaj/lambdahack/build/artifacts