summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikolajKonarski <>2017-12-18 20:02:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2017-12-18 20:02:00 (GMT)
commitb0e07a8377d405f17c3e582c90476195a65a631d (patch)
tree479acc62d50b957858903341b182e2de0f95531d
parent25d0f8df895d2995024a157ef300a22924da29e9 (diff)
version 0.7.1.00.7.1.0
-rw-r--r--CHANGELOG.md7
-rw-r--r--CREDITS1
-rw-r--r--Game/LambdaHack/Client/AI/PickActorM.hs2
-rw-r--r--Game/LambdaHack/Client/ClientOptions.hs3
-rw-r--r--Game/LambdaHack/Client/UI/DisplayAtomicM.hs14
-rw-r--r--Game/LambdaHack/Client/UI/Frontend/Dom.hs4
-rw-r--r--Game/LambdaHack/Client/UI/Frontend/Sdl.hs412
-rw-r--r--Game/LambdaHack/Client/UI/HandleHumanLocalM.hs1
-rw-r--r--Game/LambdaHack/Client/UI/Key.hs4
-rw-r--r--Game/LambdaHack/Common/Save.hs1
-rw-r--r--Game/LambdaHack/Common/Thread.hs3
-rw-r--r--Game/LambdaHack/Server/Commandline.hs17
-rw-r--r--Game/LambdaHack/Server/DebugM.hs1
-rw-r--r--Game/LambdaHack/Server/HandleRequestM.hs2
-rw-r--r--Game/LambdaHack/Server/PeriodicM.hs21
-rw-r--r--GameDefinition/Content/CaveKind.hs20
-rw-r--r--GameDefinition/Content/ItemKind.hs83
-rw-r--r--GameDefinition/Content/ItemKindActor.hs74
-rw-r--r--GameDefinition/Content/ItemKindBlast.hs66
-rw-r--r--GameDefinition/Content/ItemKindEmbed.hs30
-rw-r--r--GameDefinition/Content/ItemKindOrgan.hs38
-rw-r--r--GameDefinition/Content/ItemKindTemporary.hs2
-rw-r--r--GameDefinition/Main.hs9
-rw-r--r--GameDefinition/MainMenu.ascii6
-rw-r--r--LICENSE2
-rw-r--r--LambdaHack.cabal2
-rw-r--r--Makefile125
-rw-r--r--README.md15
28 files changed, 568 insertions, 397 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc722d4..bd60bcc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/CREDITS b/CREDITS
index e22888a..4bf019e 100644
--- a/CREDITS
+++ b/CREDITS
@@ -5,6 +5,7 @@ Andres Loeh
Mikolaj Konarski
Tuukka Turto
Veronika Romashkina
+@Peritract
Fonts 16x16x.fon, 8x8x.fon and 8x8xb.fon are are taken from
diff --git a/Game/LambdaHack/Client/AI/PickActorM.hs b/Game/LambdaHack/Client/AI/PickActorM.hs
index 0fe7269..2ea5596 100644
--- a/Game/LambdaHack/Client/AI/PickActorM.hs
+++ b/Game/LambdaHack/Client/AI/PickActorM.hs
@@ -85,7 +85,7 @@ pickActorToMove maidToAvoid = do
-- 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
diff --git a/Game/LambdaHack/Client/ClientOptions.hs b/Game/LambdaHack/Client/ClientOptions.hs
index 8f32d77..4e5d2ae 100644
--- a/Game/LambdaHack/Client/ClientOptions.hs
+++ b/Game/LambdaHack/Client/ClientOptions.hs
@@ -25,6 +25,8 @@ data ClientOptions = ClientOptions
-- ^ 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 @@ defClientOptions = ClientOptions
, sdlFonSizeAdd = Nothing
, sfontSize = Nothing
, scolorIsBold = Nothing
+ , slogPriority = Nothing
, smaxFps = Nothing
, sdisableAutoYes = False
, snoAnim = Nothing
diff --git a/Game/LambdaHack/Client/UI/DisplayAtomicM.hs b/Game/LambdaHack/Client/UI/DisplayAtomicM.hs
index f246840..4657c3b 100644
--- a/Game/LambdaHack/Client/UI/DisplayAtomicM.hs
+++ b/Game/LambdaHack/Client/UI/DisplayAtomicM.hs
@@ -758,7 +758,9 @@ quitFactionUI fid toSt = do
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 @@ quitFactionUI fid toSt = do
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]
diff --git a/Game/LambdaHack/Client/UI/Frontend/Dom.hs b/Game/LambdaHack/Client/UI/Frontend/Dom.hs
index 072149e..a00061b 100644
--- a/Game/LambdaHack/Client/UI/Frontend/Dom.hs
+++ b/Game/LambdaHack/Client/UI/Frontend/Dom.hs
@@ -94,7 +94,7 @@ runWeb soptions@ClientOptions{..} rfMVar = do
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 @@ handleMouse rf (cell, _) cx cy = do
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
diff --git a/Game/LambdaHack/Client/UI/Frontend/Sdl.hs b/Game/LambdaHack/Client/UI/Frontend/Sdl.hs
index 88dc72a..c780b35 100644
--- a/Game/LambdaHack/Client/UI/Frontend/Sdl.hs
+++ b/Game/LambdaHack/Client/UI/Frontend/Sdl.hs
@@ -4,7 +4,7 @@ module Game.LambdaHack.Client.UI.Frontend.Sdl
#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 Prelude ()
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 System.FilePath
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 @@ data FrontendSession = FrontendSession
, 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 @@ frontendName = "sdl"
-- | 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 @@ startupFun soptions@ClientOptions{..} rfMVar = do
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 @@ startupFun soptions@ClientOptions{..} rfMVar = do
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 @@ startupFun soptions@ClientOptions{..} rfMVar = do
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 @@ forceShutdown sess@FrontendSession{..} = do
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 @@ displayNoLock ClientOptions{..} FrontendSession{..} curFrame = do
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 @@ displayNoLock ClientOptions{..} FrontendSession{..} curFrame = do
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 @@ displayNoLock ClientOptions{..} FrontendSession{..} curFrame = do
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 @@ modTranslate m =
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
diff --git a/Game/LambdaHack/Client/UI/HandleHumanLocalM.hs b/Game/LambdaHack/Client/UI/HandleHumanLocalM.hs
index e3a4a76..624e377 100644
--- a/Game/LambdaHack/Client/UI/HandleHumanLocalM.hs
+++ b/Game/LambdaHack/Client/UI/HandleHumanLocalM.hs
@@ -755,6 +755,7 @@ doLook = do
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
diff --git a/Game/LambdaHack/Client/UI/Key.hs b/Game/LambdaHack/Client/UI/Key.hs
index 7e9274a..fe17239 100644
--- a/Game/LambdaHack/Client/UI/Key.hs
+++ b/Game/LambdaHack/Client/UI/Key.hs
@@ -281,7 +281,7 @@ mkKP c = KM NoModifier $ KP c
-- 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 [c] = Char c
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
diff --git a/Game/LambdaHack/Common/Save.hs b/Game/LambdaHack/Common/Save.hs
index 304fe8f..adf6045 100644
--- a/Game/LambdaHack/Common/Save.hs
+++ b/Game/LambdaHack/Common/Save.hs
@@ -52,6 +52,7 @@ loopSave cops stateToFileName toSave =
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.
diff --git a/Game/LambdaHack/Common/Thread.hs b/Game/LambdaHack/Common/Thread.hs
index 8bd5611..35e6865 100644
--- a/Game/LambdaHack/Common/Thread.hs
+++ b/Game/LambdaHack/Common/Thread.hs
@@ -10,7 +10,8 @@ import Game.LambdaHack.Common.Prelude
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
diff --git a/Game/LambdaHack/Server/Commandline.hs b/Game/LambdaHack/Server/Commandline.hs
index 993680f..787e370 100644
--- a/Game/LambdaHack/Server/Commandline.hs
+++ b/Game/LambdaHack/Server/Commandline.hs
@@ -12,8 +12,11 @@ module Game.LambdaHack.Server.Commandline
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 @@ import Game.LambdaHack.Server.ServerOptions
-- | 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 @@ serverOptionsP = do
sfontSize <- fontSizeP
sfontDir <- fontDirP
scolorIsBold <- noColorIsBoldP
+ slogPriority <- logPriorityP
smaxFps <- maxFpsP
sdisableAutoYes <- disableAutoYesP
snoAnim <- noAnimP
@@ -229,6 +238,12 @@ maxFpsP = optional $ max 1 <$>
<> 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 =
switch ( long "disableAutoYes"
diff --git a/Game/LambdaHack/Server/DebugM.hs b/Game/LambdaHack/Server/DebugM.hs
index 7ad6921..8b09e47 100644
--- a/Game/LambdaHack/Server/DebugM.hs
+++ b/Game/LambdaHack/Server/DebugM.hs
@@ -40,6 +40,7 @@ debugResponse :: MonadServer m => FactionId -> Response -> m ()
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{} ->
diff --git a/Game/LambdaHack/Server/HandleRequestM.hs b/Game/LambdaHack/Server/HandleRequestM.hs
index 11e4840..e2dee0e 100644
--- a/Game/LambdaHack/Server/HandleRequestM.hs
+++ b/Game/LambdaHack/Server/HandleRequestM.hs
@@ -85,7 +85,7 @@ setBWait :: MonadServerAtomic m
{-# 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) $
diff --git a/Game/LambdaHack/Server/PeriodicM.hs b/Game/LambdaHack/Server/PeriodicM.hs
index b041e7d..9df2918 100644
--- a/Game/LambdaHack/Server/PeriodicM.hs
+++ b/Game/LambdaHack/Server/PeriodicM.hs
@@ -160,13 +160,13 @@ advanceTime aid percent = do
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 @@ advanceTime aid percent = do
-- 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).
diff --git a/GameDefinition/Content/CaveKind.hs b/GameDefinition/Content/CaveKind.hs
index 253b090..25e5390 100644
--- a/GameDefinition/Content/CaveKind.hs
+++ b/GameDefinition/Content/CaveKind.hs
@@ -60,7 +60,7 @@ rogue = CaveKind
, 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 @@ arena = rogue
, 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 @@ arena2 = arena
, 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 @@ laboratory = arena2
, 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 @@ empty = rogue
, 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 @@ noise = rogue
, 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 @@ noise2 = noise
, 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 @@ shallow1rogue = shallow2rogue
, 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 @@ brawl = rogue -- many random solid tiles, to break LOS, since it's a day
, 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
diff --git a/GameDefinition/Content/ItemKind.hs b/GameDefinition/Content/ItemKind.hs
index f02cb3d..22983d0 100644
--- a/GameDefinition/Content/ItemKind.hs
+++ b/GameDefinition/Content/ItemKind.hs
@@ -35,9 +35,9 @@ otherItemContent = embeds ++ actors ++ organs ++ blasts ++ temporaries
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 @@ blanket = ItemKind
, 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 @@ blanket = ItemKind
-- 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 @@ potion2 = potion
, 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 @@ potion10 = potion
, Impress, RefillCalm (-60)
, OnSmash (Explode "healing mist 2")
, OnSmash (Explode "pheromone") ]
+ -- , idesc = ""
}
-- * Non-exploding consumables, not specifically designed for throwing
@@ -782,6 +855,7 @@ necklace7 = necklace
, 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 @@ ring6 = ring
, 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 @@ hammerParalyze = hammer
, 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 @@ hammerSpark = hammer
, 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
diff --git a/GameDefinition/Content/ItemKindActor.hs b/GameDefinition/Content/ItemKindActor.hs
index 69b20e8..ed63147 100644
--- a/GameDefinition/Content/ItemKindActor.hs
+++ b/GameDefinition/Content/ItemKindActor.hs
@@ -22,7 +22,6 @@ warrior, warrior2, warrior3, warrior4, warrior5, scout, ranger, escapist, amb
geyserBoiling, geyserArsenic, geyserSulfur :: ItemKind
-- * Hunams
-
warrior = ItemKind
{ isymbol = '@'
, iname = "warrior" -- modified if initial actors in hero faction
@@ -39,18 +38,26 @@ warrior = ItemKind
, 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 @@ scout = warrior
++ [ ("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 @@ escapist = warrior
, ("flask", CInv)
, ("light source", CInv)
, ("blanket", CInv) ]
+ -- , idesc = ""
}
ambusher = warrior
{ iname = "ambusher"
@@ -83,25 +93,37 @@ ambusher = warrior
++ [ ("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 @@ goldenJackal = ItemKind -- basically a much smaller and slower hyena
, 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 @@ griffonVulture = ItemKind
-- 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 @@ skunk = ItemKind
, 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 @@ armadillo = ItemKind
, 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 @@ gilaMonster = ItemKind
, 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 @@ rattlesnake = ItemKind
, 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 @@ komodoDragon = ItemKind -- bad hearing; regeneration makes it very powerful
, 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 @@ hyena = ItemKind
, 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 @@ alligator = ItemKind
, 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 @@ beeSwarm = ItemKind
, 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 @@ hornetSwarm = ItemKind
, 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 @@ thornbush = ItemKind
, 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 @@ geyserBoiling = ItemKind
, 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 @@ geyserArsenic = ItemKind
, 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 @@ geyserSulfur = ItemKind
, 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)]
}
diff --git a/GameDefinition/Content/ItemKindBlast.hs b/GameDefinition/Content/ItemKindBlast.hs
index a49365a..2522d14 100644
--- a/GameDefinition/Content/ItemKindBlast.hs
+++ b/GameDefinition/Content/ItemKindBlast.hs
@@ -87,7 +87,7 @@ firecracker n = ItemKind
$ 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 @@ fragrance = ItemKind
-- 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 @@ pheromone = ItemKind
, 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 @@ mistCalming = ItemKind -- unused
, 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 @@ odorDistressing = ItemKind
, 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 @@ mistHealing = ItemKind
, 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 @@ mistHealing2 = ItemKind
, 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 @@ mistWounding = ItemKind
, 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 @@ distortion = ItemKind
, 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 @@ glassPiece = ItemKind -- when blowing up windows
, 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 @@ smoke = ItemKind -- when stuff burns out -- unused
, 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 @@ boilingWater = ItemKind
, 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 @@ glue = ItemKind
, 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 @@ singleSpark = ItemKind
, 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 @@ spark = ItemKind
, 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 @@ denseShower = ItemKind
, 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 @@ sparseShower = ItemKind
, 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 @@ protectingBalmMelee = ItemKind
, 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 @@ protectingBalmRanged = ItemKind
, 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 @@ vulnerabilityBalm = ItemKind
, 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 @@ resolutionDust = ItemKind
, 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 @@ hasteSpray = ItemKind
, 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 @@ slownessMist = ItemKind
, 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 @@ eyeDrop = ItemKind
, 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 @@ ironFiling = ItemKind
, 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 @@ smellyDroplet = ItemKind
, 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 @@ eyeShine = ItemKind
, 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 @@ whiskeySpray = ItemKind
, 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 @@ waste = ItemKind
, iaspects = []
, ieffects = [Burn 1]
, ifeature = [toLinger 10, Fragile, Identified]
- , idesc = ""
+ , idesc = "Sodden and foul-smelling."
, ikit = []
}
youthSprinkle = ItemKind
@@ -573,7 +573,7 @@ youthSprinkle = ItemKind
, iaspects = []
, ieffects = [toOrganNone "regenerating"]
, ifeature = [toLinger 10, Fragile, Identified]
- , idesc = ""
+ , idesc = "Bright and smelling of the Spring."
, ikit = []
}
poisonCloud = ItemKind
@@ -589,7 +589,7 @@ poisonCloud = ItemKind
, 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 @@ mistAntiSlow = ItemKind
, 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 @@ mistAntidote = ItemKind
, iaspects = []
, ieffects = [DropItem 1 maxBound COrgan "poisoned"]
, ifeature = [toVelocity 5, Fragile, Identified] -- 1 step, 1 turn
- , idesc = ""
+ , idesc = "Washes away death's dew."
, ikit = []
}
diff --git a/GameDefinition/Content/ItemKindEmbed.hs b/GameDefinition/Content/ItemKindEmbed.hs
index 35ccd39..5a07e87 100644
--- a/GameDefinition/Content/ItemKindEmbed.hs
+++ b/GameDefinition/Content/ItemKindEmbed.hs
@@ -32,7 +32,7 @@ stairsUp = ItemKind
, iaspects = []
, ieffects = [Ascend True]
, ifeature = [Identified, Durable]
- , idesc = ""
+ , idesc = "Stairs that rise towards escape."
, ikit = []
}
stairsDown = stairsUp
@@ -40,12 +40,14 @@ stairsDown = stairsUp
, 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 @@ terrainCache = stairsUp
, 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 @@ terrainCacheTrap = ItemKind
, 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 @@ signboardExit = ItemKind
, iaspects = []
, ieffects = [DetectExit 100]
, ifeature = [Identified, Durable]
- , idesc = ""
+ , idesc = "A battered sign, carved by unknown hands."
, ikit = []
}
signboardMap = signboardExit
@@ -107,7 +110,7 @@ fireSmall = ItemKind
, 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 @@ fireBig = fireSmall
, 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 @@ frost = ItemKind
, 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 @@ rubble = ItemKind
, ELabel "", ELabel "", ELabel ""
, ELabel "", ELabel "", ELabel "" ]]
, ifeature = [Identified, Durable]
- , idesc = ""
+ , idesc = "Broken chunks of rock and glass."
, ikit = []
}
staircaseTrapUp = ItemKind
@@ -171,7 +174,7 @@ staircaseTrapUp = ItemKind
, 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 @@ staircaseTrapDown = staircaseTrapUp
{ 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 @@ doorwayTrap = ItemKind
, 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 @@ obscenePictograms = ItemKind
[ 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 @@ subtleFresco = ItemKind
, 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 @@ scratchOnWall = ItemKind
, 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 @@ pulpit = ItemKind
, 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 = []
}
diff --git a/GameDefinition/Content/ItemKindOrgan.hs b/GameDefinition/Content/ItemKindOrgan.hs
index 8bf22a3..f0d414f 100644
--- a/GameDefinition/Content/ItemKindOrgan.hs
+++ b/GameDefinition/Content/ItemKindOrgan.hs
@@ -41,7 +41,7 @@ fist = ItemKind
, iaspects = []
, ieffects = []
, ifeature = [Durable, Identified, Meleeable]
- , idesc = ""
+ , idesc = "Simple but effective."
, ikit = []
}
foot = fist
@@ -49,7 +49,7 @@ foot = fist
, 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 @@ hookedClaw = fist
, 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 @@ snout = fist
, icount = 1
, iverbHit = "bite"
, idamage = toDmg $ 2 `d` 1
- , idesc = ""
+ , idesc = "Sensitive and wide-nostrilled."
}
smallJaw = fist
{ iname = "small jaw"
@@ -85,7 +85,7 @@ smallJaw = fist
, icount = 1
, iverbHit = "rip"
, idamage = toDmg $ 3 `d` 1
- , idesc = ""
+ , idesc = "Filled with small, even teeth."
}
jaw = fist
{ iname = "jaw"
@@ -93,7 +93,7 @@ jaw = fist
, icount = 1
, iverbHit = "rip"
, idamage = toDmg $ 5 `d` 1
- , idesc = ""
+ , idesc = "Delivers a powerful bite."
}
largeJaw = fist
{ iname = "large jaw"
@@ -101,7 +101,7 @@ largeJaw = fist
, 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 @@ horn = fist
, 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 @@ tentacle = fist
, icount = 4
, iverbHit = "slap"
, idamage = toDmg $ 4 `d` 1
- , idesc = ""
+ , idesc = "Damp and dextrous."
}
thorn = fist
{ iname = "thorn"
@@ -130,7 +130,7 @@ thorn = fist
, 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 @@ boilingFissure = fist
, 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 @@ arsenicFissure = boilingFissure
, 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 @@ sulfurFissure = boilingFissure
, icount = 2 + 1 `d` 2
, idamage = toDmg 0
, ieffects = [RefillHP 5]
+ , idesc = ""
}
beeSting = fist
{ iname = "bee sting"
@@ -186,7 +188,7 @@ venomTooth = fist
, 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 @@ venomFang = fist
, 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 @@ screechingBeak = fist
, 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 @@ largeTail = fist
, 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 @@ armoredSkin = ItemKind
, iaspects = [AddArmorMelee 30, AddArmorRanged 15]
, ieffects = []
, ifeature = [Durable, Identified]
- , idesc = ""
+ , idesc = "Homemade armour is just as good."
, ikit = []
}
@@ -249,7 +251,7 @@ eye n = armoredSkin
, icount = 2
, iverbHit = "glare at"
, iaspects = [AddSight (intToDice n)]
- , idesc = ""
+ , idesc = "A piercing stare."
}
eye2 = eye 2
eye3 = eye 3
diff --git a/GameDefinition/Content/ItemKindTemporary.hs b/GameDefinition/Content/ItemKindTemporary.hs
index a473d57..6f58a20 100644
--- a/GameDefinition/Content/ItemKindTemporary.hs
+++ b/GameDefinition/Content/ItemKindTemporary.hs
@@ -42,7 +42,7 @@ tmpAs name aspects = ItemKind
, Recharging $ tmpNoLonger name
, OnSmash $ tmpNoLonger name ]
, ifeature = [Identified, Fragile, Durable] -- hack: destroy on drop
- , idesc = ""
+ , idesc = "" -- no description needed; stats are enough
, ikit = []
}
diff --git a/GameDefinition/Main.hs b/GameDefinition/Main.hs
index ff6f35d..7a7c476 100644
--- a/GameDefinition/Main.hs
+++ b/GameDefinition/Main.hs
@@ -13,8 +13,11 @@ import qualified Control.Exception as Ex
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 @@ main = do
-- 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
diff --git a/GameDefinition/MainMenu.ascii b/GameDefinition/MainMenu.ascii
index 0b8840d..19988d4 100644
--- a/GameDefinition/MainMenu.ascii
+++ b/GameDefinition/MainMenu.ascii
@@ -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
diff --git a/LICENSE b/LICENSE
index 75fd716..36a44c4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -14,7 +14,7 @@ GameDefinition/fonts/LICENSE.Fix15Mono-Bold
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.
diff --git a/LambdaHack.cabal b/LambdaHack.cabal
index 1e68b32..9eca6aa 100644
--- a/LambdaHack.cabal
+++ b/LambdaHack.cabal
@@ -5,7 +5,7 @@ name: LambdaHack
-- 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,
diff --git a/Makefile b/Makefile
index 1336b59..df80e01 100644
--- a/Makefile
+++ b/Makefile
@@ -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 @@ minific:
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,69 +101,72 @@ test-short: test-short-new test-short-load
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
@@ -189,7 +190,9 @@ build-binary-common:
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
diff --git a/README.md b/README.md
index 9fde748..7726853 100644
--- a/README.md
+++ b/README.md
@@ -75,14 +75,16 @@ e.g., when it's closed while the game is still saving progress
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 @@ Have fun!
[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