-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A roguelike game engine in early and very active development -- -- This is an alpha release of LambdaHack, a game engine library for -- roguelike games of arbitrary theme, size and complexity, packaged -- together with a small example dungeon crawler. When completed, it will -- let you specify content to be procedurally generated, define the AI -- behaviour on top of the generic content-independent rules and compile -- a ready-to-play game binary, using either the supplied or a -- custom-made main loop. Several frontends are available (GTK is the -- default) and many other generic engine components are easily -- overridden, but the fundamental source of flexibility lies in the -- strict and type-safe separation of code and content. -- -- New in this release are missiles flying for three turns (by an old -- kosmikus' idea), visual feedback for targeting and animations of -- combat and individual monster moves. Upcoming new features: improved -- squad combat, player action undo/redo, completely redesigned UI. Long -- term goals are focused around procedural content generation and -- include in-game content creation, auto-balancing, persistent content -- modification based on player behaviour and the improvement of the AI -- monad EDSL, so that rules for synthesising monster behaviour from game -- content are extensible, readable and easy to debug. -- -- A larger game that depends on the LambdaHack library is Allure of the -- Stars, available from -- http://hackage.haskell.org/package/Allure. @package LambdaHack @version 0.2.1 -- | Saving/loading with serialization and compression. module Game.LambdaHack.Utils.File -- | Serialize, compress and save data with an EOF marker. The OK -- is used as an EOF marker to ensure any apparent problems with -- corrupted files are reported to the user ASAP. encodeEOF :: Binary a => FilePath -> a -> IO () -- | Read, decompress and deserialize data with an EOF marker. The -- OK EOF marker ensures any easily detectable file corruption -- is discovered and reported before the function returns. strictDecodeEOF :: Binary a => FilePath -> IO a -- | Hacks that haven't found their home yet. module Game.LambdaHack.Misc -- | Level bounds. TODO: query terminal size instead and scroll view. normalLevelBound :: (Int, Int) -- | Integer division, rounding up. divUp :: Int -> Int -> Int -- | For each group that the kind belongs to, denoted by a String -- name in the first component of a pair, the second component of a pair -- shows how common the kind is within the group. type Freqs = [(String, Int)] -- | The type of kinds of rooms, halls and passages. module Game.LambdaHack.Content.PlaceKind -- | Parameters for the generation of small areas within a dungeon level. data PlaceKind PlaceKind :: Char -> String -> Freqs -> Cover -> Fence -> [String] -> PlaceKind -- | a symbol psymbol :: PlaceKind -> Char -- | short description pname :: PlaceKind -> String -- | frequency within groups pfreq :: PlaceKind -> Freqs -- | how to fill whole place based on the corner pcover :: PlaceKind -> Cover -- | whether to fence the place with solid border pfence :: PlaceKind -> Fence -- | plan of the top-left corner of the place ptopLeft :: PlaceKind -> [String] -- | A method of filling the whole area by transforming a given corner. data Cover -- | reflect every other corner, overlapping 1 row and column CAlternate :: Cover -- | fill symmetrically 4 corners and stretch their borders CStretch :: Cover -- | tile separately and symmetrically quarters of the place CReflect :: Cover -- | The choice of a fence type for the place. data Fence -- | put a solid wall fence around the place FWall :: Fence -- | leave an empty floor space around the place FFloor :: Fence -- | skip the fence and fill all with the place proper FNone :: Fence -- | Filter a list of kinds, passing through only the incorrect ones, if -- any. -- -- Verify that the top-left corner map is rectangular and not empty. pvalidate :: [PlaceKind] -> [PlaceKind] instance Show Cover instance Eq Cover instance Show Fence instance Eq Fence instance Show PlaceKind -- | A game requires the engine provided by the library, perhaps -- customized, and game content, defined completely afresh for the -- particular game. The general type of the content is CDefs and -- it has instances for all content kinds, such as items kinds -- (Game.LambdaHack.Content.ItemKind). The possible kinds are -- fixed in the library and all defined in the same directory. On the -- other hand, game content, that is all elements of CDefs -- instances, are defined in a directory of the game code proper, with -- names corresponding to their kinds. module Game.LambdaHack.Content -- | The general type of a particular game content, e.g., item kinds. data CDefs a CDefs :: (a -> Char) -> (a -> String) -> (a -> Freqs) -> ([a] -> [a]) -> [a] -> CDefs a -- | symbol, e.g., to print on the map getSymbol :: CDefs a -> a -> Char -- | name, e.g., to show to the player getName :: CDefs a -> a -> String -- | frequency within groups getFreq :: CDefs a -> a -> Freqs -- | validate and catch some offenders, if any validate :: CDefs a -> [a] -> [a] -- | all the defined content of this type content :: CDefs a -> [a] -- | Game time and speed. module Game.LambdaHack.Time -- | Game time in ticks. The time dimension. One tick is 1 microsecond (one -- millionth of a second), one turn is 0.5 s. data Time -- | Start of the game time, or zero lenght time interval. timeZero :: Time -- | At least once per clip all moves are resolved and a frame or a frame -- delay is generated. Currently one clip is 0.1 s, but it may change, -- and the code should not depend on this fixed value. timeClip :: Time -- | One turn is 0.5 s. The code may depend on that. Actors at normal speed -- (2 m/s) take one turn to move one tile (1 m by 1 m). timeTurn :: Time -- | Time addition. timeAdd :: Time -> Time -> Time -- | How many time intervals of the latter kind fits in an interval of the -- former kind. timeFit :: Time -> Time -> Int -- | Negate a time interval. Can be used to subtract from a time or to -- reverse the ordering on time. timeNegate :: Time -> Time -- | Scale time by an Int scalar value. timeScale :: Time -> Int -> Time -- | Represent the main 10 thresholds of a time range by digits, given the -- total length of the time range. timeToDigit :: Time -> Time -> Char -- | Speed in meters per 1 million seconds (m/Ms). Actors at normal speed -- (2 m/s) take one time turn (0.5 s) to move one tile (1 m by 1 m). data Speed -- | Constructor for content definitions. toSpeed :: Double -> Speed -- | Normal speed (2 m/s) that suffices to move one tile in one turn. speedNormal :: Speed -- | Scale speed by an Int scalar value. speedScale :: Speed -> Int -> Speed -- | The number of time ticks it takes to walk 1 meter at the given speed. ticksPerMeter :: Speed -> Time -- | Distance in meters (so also in tiles, given the chess metric) traveled -- in a given time by a body with a given speed. traveled :: Speed -> Time -> Int -- | Calculate projectile speed from item weight in grams and speed bonus -- in percents. See -- https://github.com/kosmikus/LambdaHack/wiki/Item-statistics. speedFromWeight :: Int -> Int -> Speed -- | Calculate maximum range in meters of a projectile from its speed. See -- https://github.com/kosmikus/LambdaHack/wiki/Item-statistics. -- With this formula, each projectile flies for exactly one second, that -- is 2 turns, and then drops to the ground. Dividing and multiplying by -- 2 ensures both turns of flight cover the same distance. rangeFromSpeed :: Speed -> Int instance Show Time instance Eq Time instance Ord Time instance Show Speed instance Eq Speed instance Ord Speed instance Binary Speed instance Binary Time -- | Personal game configuration file support. module Game.LambdaHack.Config -- | The content of the configuration file. It's parsed in a case sensitive -- way (unlike by default in ConfigFile). data CP -- | Read the player configuration file and use it to override any default -- config options. Currently we can't unset options, only override. -- -- The default config, passed in argument configDefault, is -- expected to come from the default configuration file included via CPP -- in file ConfigDefault.hs. mkConfig :: String -> IO CP -- | Personal data directory for the game. Depends on the OS and the game, -- e.g., for LambdaHack under Linux it's ~/.LambdaHack/. appDataDir :: IO FilePath -- | A simplified access to an option in a given section, with simple error -- reporting (no internal errors are caught nor hidden). If there is no -- such option, gives Nothing. getOption :: Get_C a => CP -> SectionSpec -> OptionSpec -> Maybe a -- | Simplified access to an option in a given section. Fails if the option -- is not present. get :: Get_C a => CP -> SectionSpec -> OptionSpec -> a -- | An association list corresponding to a section. Fails if no such -- section. getItems :: CP -> SectionSpec -> [(String, String)] -- | Looks up a file path in the config file and makes it absolute. If the -- game's configuration directory exists, the file path is appended to -- it; otherwise, it's appended to the current directory. getFile :: CP -> SectionSpec -> OptionSpec -> IO FilePath -- | Dumps the current configuration to a file. dump :: FilePath -> CP -> IO () -- | Gets a random generator from the config or, if not present, generates -- one and updates the config with it. getSetGen :: CP -> String -> IO (StdGen, CP) instance Show CP instance Binary CP -- | Colours and text attributes. module Game.LambdaHack.Color -- | Colours supported by the major frontends. data Color Black :: Color Red :: Color Green :: Color Brown :: Color Blue :: Color Magenta :: Color Cyan :: Color White :: Color BrBlack :: Color BrRed :: Color BrGreen :: Color BrYellow :: Color BrBlue :: Color BrMagenta :: Color BrCyan :: Color BrWhite :: Color -- | The default colours, to optimize attribute setting. defBG, defFG :: Color -- | A helper for the terminal frontends that display bright via bold. isBright :: Color -> Bool -- | Due to the limitation of the curses library used in the curses -- frontend, only these are legal backgrounds. legalBG :: [Color] -- | Translationg to heavily modified Linux console color RGB values. colorToRGB :: Color -> String -- | Text attributes: foreground and backgroud colors. data Attr Attr :: !Color -> !Color -> Attr -- | foreground colour fg :: Attr -> !Color -- | backgroud color bg :: Attr -> !Color -- | The default attribute, to optimize attribute setting. defaultAttr :: Attr data AttrChar AttrChar :: !Attr -> !Char -> AttrChar acAttr :: AttrChar -> !Attr acChar :: AttrChar -> !Char -- | The data sufficent to draw a single game screen frame. data SingleFrame SingleFrame :: ![[AttrChar]] -> String -> String -> SingleFrame -- | content of the screen, line by line sfLevel :: SingleFrame -> ![[AttrChar]] -- | an extra line to show at the top sfTop :: SingleFrame -> String -- | an extra line to show at the bottom sfBottom :: SingleFrame -> String -- | Animation is a list of frame modifications to play one by one, where -- each modification if a map from locations to level map symbols. type Animation = [IntMap AttrChar] instance Show Color instance Eq Color instance Ord Color instance Enum Color instance Bounded Color instance Show Attr instance Eq Attr instance Ord Attr instance Show AttrChar instance Eq AttrChar instance Eq SingleFrame instance Binary AttrChar instance Binary Attr instance Binary Color -- | The appearance of in-game items, as communicated to the player. module Game.LambdaHack.Flavour -- | The type of item flavours. data Flavour -- | Turn a colour set into a flavour set. zipPlain, zipFancy :: [Color] -> [Flavour] -- | Colour sets. darkCol, stdCol, brightCol :: [Color] -- | The standard full set of flavours. stdFlav :: [Flavour] -- | Get the underlying base colour of a flavour. flavourToColor :: Flavour -> Color -- | Construct the full name of a flavour. flavourToName :: Flavour -> String instance Show Flavour instance Eq Flavour instance Ord Flavour instance Binary Flavour -- | Queues implemented with two stacks to ensure fast writes. module Game.LambdaHack.Utils.LQueue -- | Queues implemented with two stacks. type LQueue a = ([a], [a]) -- | Create a new empty mutable queue. newLQueue :: LQueue a -- | Check if the queue is empty. nullLQueue :: LQueue a -> Bool -- | Remove all but the last written non-Nothing element of the -- queue. trimLQueue :: LQueue (Maybe a) -> LQueue (Maybe a) -- | Try reading a queue. Return Nothing if empty. tryReadLQueue :: LQueue a -> Maybe (a, LQueue a) -- | Write to the queue. Faster than reading. writeLQueue :: LQueue a -> a -> LQueue a -- | Tools for specifying assertions. A step towards contracts. Actually, a -- bunch of hacks wrapping the original assert function, which -- is the only easy way of obtaining source locations. module Game.LambdaHack.Utils.Assert -- | If the first argument evaluates to True, then the result is the -- second argument. Otherwise an AssertionFailed exception is -- raised, containing a String with the source file and line -- number of the call to assert. -- -- Assertions can normally be turned on or off with a compiler flag (for -- GHC, assertions are normally on unless optimisation is turned on with -- -O or the -fignore-asserts option is given). When -- assertions are turned off, the first argument to assert is -- ignored, and the second argument is returned as the result. assert :: Bool -> a -> a -- | If the condition fails, display the value blamed for the failure. Used -- as in -- --
-- assert (c /= 0 `blame` c) $ 10 / c --blame :: Show a => Bool -> a -> Bool -- | Like undefined, but shows the source location and also the -- value to blame for the failure. To be used as in: -- --
-- assert `failure` ((x1, y1), (x2, y2), "designate a vertical line") --failure :: Show a => (Bool -> b -> b) -> a -> b -- | Like all, but if the predicate fails, blame all the list -- elements and especially those for which it fails. To be used as in: -- --
-- assert (allB (>= 0) [yf, xf, y1, x1, y2, x2]) --allB :: Show a => (a -> Bool) -> [a] -> Bool -- | Check that the value returned from a monad action satisfies a -- predicate. Reports source location and the suspects. Drops the value. checkM :: (Show a, Monad m) => (Bool -> m () -> m ()) -> (c -> Bool) -> a -> c -> m () -- | Verifies that the returned value is true (respectively, false). Used -- as in: -- --
-- open newValve >>= assert `trueM` (newValve, "is already opened, not new") --trueM, falseM :: (Show a, Monad m) => (Bool -> m () -> m ()) -> a -> Bool -> m () -- | Basic cartesian geometry operations on 2D points. module Game.LambdaHack.PointXY -- | Spacial dimension for points and vectors. type X = Int -- | Spacial dimension for points and vectors. type Y = Int -- | 2D points in cartesian representation. newtype PointXY PointXY :: (X, Y) -> PointXY -- | A list of all points on a straight vertical or straight horizontal -- line between two points. Fails if no such line exists. fromTo :: PointXY -> PointXY -> [PointXY] -- | Sort the sequence of two points, in the derived lexicographic order. sortPointXY :: (PointXY, PointXY) -> (PointXY, PointXY) -- | Bresenham's line algorithm generalized to arbitrary starting -- eps (eps value of 0 gives the standard BLA). -- Includes the source point and goes through the target point to -- infinity. blaXY :: Int -> PointXY -> PointXY -> [PointXY] instance Eq PointXY instance Ord PointXY instance Show PointXY -- | Game messages displayed on top of the screen for the player to read. module Game.LambdaHack.Msg -- | The type of a single message. type Msg = String -- | The "press something to see more" mark. moreMsg :: Msg -- | The confirmation request message. yesnoMsg :: Msg -- | Add spaces at the message end, for display overlayed over the level -- map. Also trims (does not wrap!) too long lines. padMsg :: X -> String -> String -- | The type of a set of messages to show at the screen at once. data Report -- | Empty set of messages. emptyReport :: Report -- | Test if the set of messages is empty. nullReport :: Report -> Bool -- | Construct a singleton set of messages. singletonReport :: Msg -> Report -- | Add message to the end of report. addMsg :: Report -> Msg -> Report -- | Split a messages into chunks that fit in one line. We assume the width -- of the messages line is the same as of level map. splitReport :: Report -> [String] -- | Render a report as a (possibly very long) string. renderReport :: Report -> String -- | The history of reports. data History -- | Empty history of reports. emptyHistory :: History -- | Construct a singleton history of reports. singletonHistory :: Report -> History -- | Add a report to history, handling repetitions. addReport :: Report -> History -> History -- | Render history as many lines of text, wrapping if necessary. renderHistory :: History -> Overlay -- | Take the given prefix of reports from a history. takeHistory :: Int -> History -> History -- | A screenful of text lines. When displayed, they are trimmed, not -- wrapped and any lines below the lower screen edge are not visible. type Overlay = [String] -- | Split an overlay into overlays that fit on the screen. splitOverlay :: Y -> Overlay -> [Overlay] -- | Returns a function that looks up the characters in the string by -- location. Takes the height of the display plus the string. Returns -- also the message to print at the top and number of screens required to -- display all of the string. stringByLocation :: X -> Y -> Overlay -> (String, PointXY -> Maybe Char) instance Show Report instance Show History instance Binary History instance Binary Report -- | Basic cartesian geometry operations on 2D vectors. module Game.LambdaHack.VectorXY -- | 2D vectors in cartesian representation. newtype VectorXY VectorXY :: (X, Y) -> VectorXY -- | Shift a point by a vector. shiftXY :: PointXY -> VectorXY -> PointXY -- | Vectors of all unit moves in the chessboard metric, clockwise, -- starting north-west. movesXY :: [VectorXY] -- | Vectors of all cardinal direction unit moves, clockwise, starting -- north. movesCardinalXY :: [VectorXY] -- | The lenght of a vector in the chessboard metric, where diagonal moves -- cost 1. chessDistXY :: VectorXY -> Int -- | Squared euclidean length of a vector. euclidDistSqXY :: VectorXY -> Int -- | Reverse an arbirary vector. negXY :: VectorXY -> VectorXY instance Show VectorXY instance Eq VectorXY -- | Rectangular areas of levels and their basic operations. module Game.LambdaHack.Area -- | The type of areas. The bottom left and the top right points. type Area = (X, Y, X, Y) -- | All (8 at most) closest neighbours of a point within an area. vicinityXY :: Area -> PointXY -> [PointXY] -- | All (4 at most) cardinal direction neighbours of a point within an -- area. vicinityCardinalXY :: Area -> PointXY -> [PointXY] -- | Checks that a point belongs to an area. insideXY :: PointXY -> Area -> Bool -- | Sort the corners of an area so that the bottom left is the first -- point. normalizeArea :: Area -> Area -- | Divide uniformly a larger area into the given number of smaller areas. grid :: (X, Y) -> Area -> [(PointXY, Area)] -- | Checks if it's an area with at least one field. validArea :: Area -> Bool -- | Checks if it's an area with exactly one field. trivialArea :: Area -> Bool -- | Enlarge (or shrink) the given area on all fours sides by the amount. expand :: Area -> Int -> Area -- | Basic operations on 2D points represented as linear offsets. module Game.LambdaHack.Point -- | The type of locations on the 2D level map, heavily optimized. -- -- We represent the (level map on the) screen as a linear framebuffer, -- where Point is an Int offset counted from the first -- cell. We do bounds check for the X size whenever we convert between -- representations and each subsequent array access performs another -- check, effectively for Y size. After dungeon is generated (using -- PointXY, not Point), and converted to the -- Point representation, points are used mainly as keys and not -- constructed often, so the performance will improve due to smaller save -- files, the use of IntMap and cheaper array indexing, -- including cheaper bounds checks. We don't defin Point as a -- newtype to avoid the trouble with using EnumMap in place of -- IntMap, etc. type Point = Int -- | Conversion from cartesian coordinates to Point. toPoint :: X -> PointXY -> Point -- | Print a point as a tuple of cartesian coordinates. showPoint :: X -> Point -> String -- | The top-left corner location of the level. origin :: Point -- | The distance between two points in the chessboard metric. chessDist :: X -> Point -> Point -> Int -- | Checks whether two points are adjacent on the map (horizontally, -- vertically or diagonally). adjacent :: X -> Point -> Point -> Bool -- | Returns the 8, or less, surrounding locations of a given location. vicinity :: X -> Y -> Point -> [Point] -- | Returns the 4, or less, surrounding locations in cardinal directions -- from a given location. vicinityCardinal :: X -> Y -> Point -> [Point] -- | Checks that a point belongs to an area. inside :: X -> Point -> Area -> Bool -- | Calculate the displacement vector from a location to another. displacementXYZ :: X -> Point -> Point -> VectorXY -- | Bresenham's line algorithm generalized to arbitrary starting -- eps (eps value of 0 gives the standard BLA). Skips -- the source point and goes through the second point to the edge of the -- level. GIves Nothing if the points are equal. bla :: X -> Y -> Int -> Point -> Point -> Maybe [Point] -- | Basic operations on 2D vectors represented in an efficient, but not -- unique, way. module Game.LambdaHack.Vector -- | 2D vectors represented as offsets in the linear framebuffer indexed by -- Point. -- -- A newtype is used to prevent mixing up the type with Point -- itself. Note that the offset representations of a vector is usually -- not unique. E.g., for vectors of lenth 1 in the chessboard metric, -- used to denote geographical directions, the representations are -- pairwise distinct if and only if the level width and height are at -- least 3. data Vector -- | Converts a vector in cartesian representation into Vector. toVector :: X -> VectorXY -> Vector -- | Translate a point by a vector. -- -- Particularly simple and fast implementation in the linear -- representation. shift :: Point -> Vector -> Point -- | Translate a point by a vector, but only if the result fits in an area. shiftBounded :: X -> Area -> Point -> Vector -> Point -- | Vectors of all unit moves, clockwise, starting north-west. moves :: X -> [Vector] -- | Vectors of all unit moves, clockwise, starting north-west, -- parameterized by level width. movesWidth :: [X -> Vector] -- | Squared euclidean distance between two unit vectors. euclidDistSq :: X -> Vector -> Vector -> Int -- | Checks whether a unit vector is a diagonal direction, as opposed to -- cardinal. diagonal :: X -> Vector -> Bool -- | Reverse an arbirary vector. neg :: Vector -> Vector -- | Given two distinct locations, determine the direction (a unit vector) -- in which one should move from the first in order to get closer to the -- second. Ignores obstacles. Of several equally good directions (in the -- chessboard metric) it picks one of those that visually (in the -- euclidean metric) maximally align with the vector between the two -- points.. towards :: X -> Point -> Point -> Vector -- | A vector from a point to another. We have -- --
-- shift loc1 (displacement loc1 loc2) == loc2 ---- -- Particularly simple and fast implementation in the linear -- representation. displacement :: Point -> Point -> Vector -- | A vector from a point to another. W have displacePath :: [Point] -> [Vector] instance Show Vector instance Eq Vector instance Binary Vector -- | Frontend-independent keyboard input operations. module Game.LambdaHack.Key -- | Frontend-independent datatype to represent keys. data Key Esc :: Key Return :: Key Space :: Key Tab :: Key PgUp :: Key PgDn :: Key Left :: Key Right :: Key Up :: Key Down :: Key End :: Key Begin :: Key Home :: Key -- | a keypad key for a character (digits and operators) KP :: !Char -> Key -- | a single printable character Char :: !Char -> Key -- | an unknown key, registered to warn the user Unknown :: !String -> Key -- | Configurable event handler for the direction keys. Used for directed -- commands such as close door. handleDir :: X -> (Key, Modifier) -> (Vector -> a) -> a -> a dirAllMoveKey :: [Key] -- | Binding of both sets of movement keys. moveBinding :: ((X -> Vector) -> a) -> ((X -> Vector) -> a) -> [((Key, Modifier), (String, Bool, a))] -- | Translate key from a GTK string description to our internal key type. -- To be used, in particular, for the command bindings and macros in the -- config file. keyTranslate :: String -> Key -- | Our own encoding of modifiers. Incomplete. data Modifier Control :: Modifier NoModifier :: Modifier -- | Show a key with a modifier, if any. showKM :: (Key, Modifier) -> String instance Ord Key instance Eq Key instance Ord Modifier instance Eq Modifier instance Show Key -- | Generic binding of keys to commands, procesing macros, printing -- command help. No operation in this module involves the State -- or Action type. module Game.LambdaHack.Binding -- | Bindings and other information about player commands. data Binding a Binding :: Map (Key, Modifier) (String, Bool, a) -> Map Key Key -> [Key] -> [(Key, Modifier)] -> Binding a -- | binding keys to commands kcmd :: Binding a -> Map (Key, Modifier) (String, Bool, a) -- | macro map kmacro :: Binding a -> Map Key Key -- | major, most often used, commands kmajor :: Binding a -> [Key] -- | direction keys for moving and running kdir :: Binding a -> [(Key, Modifier)] -- | Produce the macro map from a macro association list taken from the -- config file. Macros cannot depend on each other. The map is fully -- evaluated to catch errors in macro definitions early. macroKey :: [(String, String)] -> Map Key Key -- | Produce a set of help screens from the key bindings. keyHelp :: Binding a -> [Overlay] -- | Common definitions for the Field of View algorithms. See -- https://github.com/kosmikus/LambdaHack/wiki/Fov-and-los for -- some more context and references. module Game.LambdaHack.FOV.Common -- | Distance from the (0, 0) point where FOV originates. type Distance = Int -- | Progress along an arc with a constant distance from (0, 0). type Progress = Int -- | Rotated and translated coordinates of 2D points, so that the points -- fit in a single quadrant area (e, g., quadrant I for Permissive FOV, -- hence both coordinates positive; adjacent diagonal halves of quadrant -- I and II for Digital FOV, hence y positive). The special coordinates -- are written using the standard mathematical coordinate setup, where -- quadrant I, with x and y positive, is on the upper right. newtype Bump B :: (X, Y) -> Bump -- | Straight line between points. type Line = (Bump, Bump) -- | Convex hull represented as a list of points. type ConvexHull = [Bump] -- | An edge (comprising of a line and a convex hull) of the area to be -- scanned. type Edge = (Line, ConvexHull) -- | The area left to be scanned, delimited by edges. type EdgeInterval = (Edge, Edge) -- | Maximal element of a non-empty list. Prefers elements from the rear, -- which is essential for PFOV, to avoid ill-defined lines. maximal :: (a -> a -> Bool) -> [a] -> a -- | Check if the line from the second point to the first is more steep -- than the line from the third point to the first. This is related to -- the formal notion of gradient (or angle), but hacked wrt signs to work -- fast in this particular setup. Returns True for ill-defined lines. steeper :: Bump -> Bump -> Bump -> Bool -- | Extends a convex hull of bumps with a new bump. Nothing needs to be -- done if the new bump already lies within the hull. The first argument -- is typically steeper, optionally negated, applied to the second -- argument. addHull :: (Bump -> Bump -> Bool) -> Bump -> ConvexHull -> ConvexHull instance Show Bump -- | DFOV (Digital Field of View) implemented according to specification at -- http://roguebasin.roguelikedevelopment.org/index.php?title=Digital_field_of_view_implementation. -- This fast version of the algorithm, based on PFOV, has AFAIK -- never been described nor implemented before. module Game.LambdaHack.FOV.Digital -- | Calculates the list of tiles, in Bump coordinates, visible -- from (0, 0), within the given sight range. scan :: Distance -> (Bump -> Bool) -> [Bump] -- | PFOV (Permissive Field of View) clean-room reimplemented based on the -- algorithm described in -- http://roguebasin.roguelikedevelopment.org/index.php?title=Precise_Permissive_Field_of_View, -- though the general structure is more influenced by recursive shadow -- casting, as implemented in Shadow.hs. In the result, this algorithm is -- much faster than the original algorithm on dense maps, since it does -- not scan areas blocked by shadows. module Game.LambdaHack.FOV.Permissive -- | Calculates the list of tiles, in Bump coordinates, visible -- from (0, 0). scan :: (Bump -> Bool) -> [Bump] -- | A restrictive variant of Recursive Shadow Casting FOV with infinite -- range. It's not designed for dungeons with diagonal walls and so here -- they block visibility, though they don't block movement. The main -- advantage of the algorithm is that it's very simple and fast. module Game.LambdaHack.FOV.Shadow -- | Rotated and translated coordinates of 2D points, so that they fit in -- the same single octant area. type SBump = (Progress, Distance) -- | The area left to be scanned, delimited by fractions of the original -- arc. Interval (0, 1) means the whole 45 degrees arc of the -- processed octant is to be scanned. type Interval = (Rational, Rational) -- | Calculates the list of tiles, in SBump coordinates, visible -- from (0, 0). scan :: (SBump -> Bool) -> Distance -> Interval -> [SBump] -- | A list of items with relative frequencies of appearance. module Game.LambdaHack.Utils.Frequency -- | The frequency distribution type. data Frequency a -- | Uniform discrete frequency distribution. uniformFreq :: String -> [a] -> Frequency a -- | Takes a name and a list of frequencies and items into the frequency -- distribution. toFreq :: String -> [(Int, a)] -> Frequency a -- | Scale frequecy distribution, multiplying it by a positive integer -- constant. scaleFreq :: Show a => Int -> Frequency a -> Frequency a -- | Leave only items that satisfy a predicate. filterFreq :: (a -> Bool) -> Frequency a -> Frequency a -- | Randomly choose an item according to the distribution. rollFreq :: Show a => Frequency a -> StdGen -> (a, StdGen) -- | Test if the frequency distribution is empty. nullFreq :: Frequency a -> Bool -- | give acces to raw frequency values runFrequency :: Frequency a -> [(Int, a)] instance Show a => Show (Frequency a) instance Functor Frequency instance MonadPlus Frequency instance Monad Frequency -- | Representation of probabilities and random computations. module Game.LambdaHack.Random -- | The monad of computations with random generator state. type Rnd a = State StdGen a -- | Get a random object within a range with a uniform distribution. randomR :: Random a => (a, a) -> Rnd a -- | Get any element of a list with equal probability. oneOf :: [a] -> Rnd a -- | Gen an element according to a frequency distribution. frequency :: Show a => Frequency a -> Rnd a -- | Roll a single die. roll :: Int -> Rnd Int -- | Dice: 1d7, 3d3, 1d0, etc. RollDice a b represents a -- rolls of b-sided die. data RollDice RollDice :: Word8 -> Word8 -> RollDice -- | Roll dice and sum the results. rollDice :: RollDice -> Rnd Int -- | Maximal value of dice. maxDice :: RollDice -> Int -- | Minimal value of dice. minDice :: RollDice -> Int -- | Mean value of dice. meanDice :: RollDice -> Rational -- | Dice for rolling a pair of integer parameters pertaining to, -- respectively, the X and Y cartesian 2D coordinates. data RollDiceXY RollDiceXY :: (RollDice, RollDice) -> RollDiceXY -- | Roll the two sets of dice. rollDiceXY :: RollDiceXY -> Rnd (Int, Int) -- | Dice for parameters scaled with current level depth. To the result of -- rolling the first set of dice we add the second, scaled in proportion -- to current depth divided by maximal dungeon depth. type RollDeep = (RollDice, RollDice) -- | Roll dice scaled with current level depth. Note that at the first -- level, the scaled dice are always ignored. rollDeep :: Int -> Int -> RollDeep -> Rnd Int -- | Roll dice scaled with current level depth and return True if -- the results if greater than 50. chanceDeep :: Int -> Int -> RollDeep -> Rnd Bool -- | Generate a RollDeep that always gives a constant integer. intToDeep :: Int -> RollDeep -- | Maximal value of scaled dice. maxDeep :: RollDeep -> Int -- | Fractional chance. type Chance = Rational -- | Give True, with probability determined by the fraction. chance :: Chance -> Rnd Bool instance Eq RollDice instance Ord RollDice instance Show RollDiceXY instance Read RollDice instance Show RollDice -- | Effects of content on other content. No operation in this module -- involves the State or Action type. module Game.LambdaHack.Effect -- | All possible effects, some of them parameterized or dependent on -- outside coefficients, e.g., item power. data Effect NoEffect :: Effect Heal :: Effect Wound :: !RollDice -> Effect Dominate :: Effect SummonFriend :: Effect SummonEnemy :: Effect ApplyPerfume :: Effect Regeneration :: Effect Searching :: Effect Ascend :: Effect Descend :: Effect -- | Suffix to append to a basic content name, if the content causes the -- effect. effectToSuffix :: Effect -> String -- | How much AI benefits from applying the effect. Multipllied by item -- power. Negative means harm to the enemy when thrown at him. Effects -- with zero benefit won't ever be used, neither actively nor passively. effectToBenefit :: Effect -> Int instance Show Effect instance Read Effect instance Eq Effect instance Ord Effect -- | Terrain tile features. module Game.LambdaHack.Feature -- | All possible terrain tile features, some of them parameterized or -- dependent on outside coefficients, e.g., on the tile secrecy value. data Feature -- | triggered by ascending Ascendable :: Feature -- | triggered by descending Descendable :: Feature -- | triggered by opening Openable :: Feature -- | triggered by closing Closable :: Feature -- | triggered when the tile's secrecy becomes zero Hidden :: Feature -- | causes the effect when triggered Cause :: !Effect -> Feature -- | transitions to any tile of the group when triggered ChangeTo :: !String -> Feature -- | actors can walk through Walkable :: Feature -- | actors can see through Clear :: Feature -- | is lit with an ambient shine Lit :: Feature -- | sustains the effect continuously, TODO Aura :: !Effect -> Feature -- | items and stairs can be generated there Boring :: Feature -- | is a (not hidden) door, stair, etc. Exit :: Feature -- | used for distinct paths throughout the level Path :: Feature -- | discovering the secret will require this many turns Secret :: !RollDice -> Feature instance Show Feature instance Read Feature instance Eq Feature instance Ord Feature -- | The type of kinds of terrain tiles. module Game.LambdaHack.Content.TileKind -- | The type of kinds of terrain tiles. See Tile.hs for -- explanation of the absence of a corresponding type Tile that -- would hold particular concrete tiles in the dungeon. data TileKind TileKind :: !Char -> !String -> !Freqs -> !Color -> !Color -> ![Feature] -> TileKind -- | map symbol tsymbol :: TileKind -> !Char -- | short description tname :: TileKind -> !String -- | frequency within groups tfreq :: TileKind -> !Freqs -- | map color tcolor :: TileKind -> !Color -- | map color when not in FOV tcolor2 :: TileKind -> !Color -- | properties tfeature :: TileKind -> ![Feature] -- | Filter a list of kinds, passing through only the incorrect ones, if -- any. -- -- If tiles look the same on the map, the description should be the same, -- too. Otherwise, the player has to inspect manually all the tiles of -- that kind to see if any is special. This is a part of a stronger but -- less precise property that tiles that look the same can't be -- distinguished by player actions (but may behave differently wrt -- dungeon generation, AI preferences, etc.). tvalidate :: [TileKind] -> [TileKind] instance Show TileKind -- | The type of game rule sets and assorted game data. module Game.LambdaHack.Content.RuleKind -- | The type of game rule sets and assorted game data. -- -- For now the rules are immutable througout the game, so there is no -- type Rule to hold any changing parameters, just -- RuleKind for the fixed set. However, in the future, if the -- rules can get changed during gameplay based on data mining of player -- behaviour, we may add such a type and then RuleKind will -- become just a starting template, analogously as for the other content. -- -- The raccessible field holds a predicate that tells whether -- one location is accessible from another. Precondition: the two -- locations are next to each other. data RuleKind RuleKind :: Char -> String -> Freqs -> (X -> Point -> TileKind -> Point -> TileKind -> Bool) -> String -> (FilePath -> IO FilePath) -> Version -> [Char] -> [Char] -> RuleKind -- | a symbol rsymbol :: RuleKind -> Char -- | short description rname :: RuleKind -> String -- | frequency within groups rfreq :: RuleKind -> Freqs raccessible :: RuleKind -> X -> Point -> TileKind -> Point -> TileKind -> Bool -- | the title of the game rtitle :: RuleKind -> String -- | the path to data files rpathsDataFile :: RuleKind -> FilePath -> IO FilePath -- | the version of the game rpathsVersion :: RuleKind -> Version -- | symbols of melee weapons ritemMelee :: RuleKind -> [Char] -- | symbols of items AI can project ritemProject :: RuleKind -> [Char] -- | No specific possible problems for the content of this kind, so far, so -- the validation function always returns the empty list of offending -- kinds. ruvalidate :: [RuleKind] -> [RuleKind] instance Show RuleKind -- | The type of kinds of monsters and heroes. module Game.LambdaHack.Content.ActorKind -- | Actor properties that are fixed for a given kind of actors. data ActorKind ActorKind :: !Char -> !String -> !Freqs -> !Color -> !Speed -> !RollDice -> !Bool -> !Bool -> !Int -> !Int -> ActorKind -- | map symbol asymbol :: ActorKind -> !Char -- | short description aname :: ActorKind -> !String -- | frequency within groups afreq :: ActorKind -> !Freqs -- | map color acolor :: ActorKind -> !Color -- | natural speed in m/s aspeed :: ActorKind -> !Speed -- | encodes initial and maximal hp ahp :: ActorKind -> !RollDice -- | can it see? asight :: ActorKind -> !Bool -- | can it smell? asmell :: ActorKind -> !Bool -- | intelligence aiq :: ActorKind -> !Int -- | number of turns to regenerate 1 HP aregen :: ActorKind -> !Int -- | Filter a list of kinds, passing through only the incorrect ones, if -- any. -- -- Make sure actor kinds can be told apart on the level map. avalidate :: [ActorKind] -> [ActorKind] instance Show ActorKind -- | The type of kinds of weapons and treasure. module Game.LambdaHack.Content.ItemKind -- | Item properties that are fixed for a given kind of items. data ItemKind ItemKind :: !Char -> !String -> !Freqs -> ![Flavour] -> !Effect -> !RollDeep -> !RollDeep -> !String -> !String -> !Int -> !Int -> ItemKind -- | map symbol isymbol :: ItemKind -> !Char -- | generic name iname :: ItemKind -> !String -- | frequency within groups ifreq :: ItemKind -> !Freqs -- | possible flavours iflavour :: ItemKind -> ![Flavour] -- | the effect when activated ieffect :: ItemKind -> !Effect -- | created in that quantify icount :: ItemKind -> !RollDeep -- | created with that power ipower :: ItemKind -> !RollDeep -- | the verb for applying and possibly combat iverbApply :: ItemKind -> !String -- | the verb for projecting iverbProject :: ItemKind -> !String -- | weight in grams iweight :: ItemKind -> !Int -- | percentage bonus or malus to throw speed itoThrow :: ItemKind -> !Int -- | No specific possible problems for the content of this kind, so far, so -- the validation function always returns the empty list of offending -- kinds. ivalidate :: [ItemKind] -> [ItemKind] instance Show ItemKind -- | Operations on the Area type that involve random numbers. module Game.LambdaHack.AreaRnd -- | Pick a random point within an area. xyInArea :: Area -> Rnd PointXY -- | Create a random room according to given parameters. mkRoom :: (X, Y) -> Area -> Rnd Area -- | Create a void room, i.e., a single point area. mkVoidRoom :: Area -> Rnd Area -- | Pick a subset of connections between adjacent areas within a grid -- until there is only one connected component in the graph of all areas. connectGrid :: (X, Y) -> Rnd [(PointXY, PointXY)] -- | Pick a single random connection between adjacent areas within a grid. randomConnection :: (X, Y) -> Rnd (PointXY, PointXY) -- | The coordinates of consecutive fields of a corridor. type Corridor = [PointXY] -- | Try to connect two places with a corridor. Choose entrances at least 4 -- tiles distant from the edges, if possible. connectPlaces :: Area -> Area -> Rnd Corridor -- | The type of cave layout kinds. module Game.LambdaHack.Content.CaveKind -- | Parameters for the generation of dungeon levels. data CaveKind CaveKind :: Char -> String -> Freqs -> X -> Y -> RollDiceXY -> RollDiceXY -> RollDeep -> Rational -> Chance -> Int -> Int -> Chance -> Chance -> Chance -> RollDice -> String -> String -> String -> String -> String -> String -> CaveKind -- | a symbol csymbol :: CaveKind -> Char -- | short description cname :: CaveKind -> String -- | frequency within groups cfreq :: CaveKind -> Freqs -- | X size of the whole cave cxsize :: CaveKind -> X -- | Y size of the whole cave cysize :: CaveKind -> Y -- | the dimensions of the grid of places cgrid :: CaveKind -> RollDiceXY -- | minimal size of places cminPlaceSize :: CaveKind -> RollDiceXY -- | the chance a place is dark cdarkChance :: CaveKind -> RollDeep -- | a proportion of extra connections cauxConnects :: CaveKind -> Rational -- | the chance of not creating a place cvoidChance :: CaveKind -> Chance -- | extra places, may overlap except two cnonVoidMin :: CaveKind -> Int -- | minimal distance between stairs cminStairDist :: CaveKind -> Int -- | the chance of a door in an opening cdoorChance :: CaveKind -> Chance -- | if there's a door, is it open? copenChance :: CaveKind -> Chance -- | if not open, is it hidden? chiddenChance :: CaveKind -> Chance -- | the number of items in the cave citemNum :: CaveKind -> RollDice -- | the default cave tile group name cdefaultTile :: CaveKind -> String -- | the cave corridor tile group name ccorridorTile :: CaveKind -> String -- | the filler wall group name cfillerTile :: CaveKind -> String -- | the dark place plan legend ground name cdarkLegendTile :: CaveKind -> String -- | the lit place plan legend ground name clitLegendTile :: CaveKind -> String -- | the hidden tiles ground name chiddenTile :: CaveKind -> String -- | Filter a list of kinds, passing through only the incorrect ones, if -- any. -- -- Catch caves with not enough space for all the places. Check the size -- of the cave descriptions to make sure they fit on screen. cvalidate :: [CaveKind] -> [CaveKind] instance Show CaveKind -- | General content types and operations. module Game.LambdaHack.Kind -- | Content identifiers for the content type c. data Id c -- | Type family for auxiliary data structures for speeding up content -- operations. -- | Content operations for the content of type a. data Ops a Ops :: (Id a -> Char) -> (Id a -> String) -> (Id a -> a) -> (String -> Id a) -> (String -> (a -> Bool) -> Rnd (Id a)) -> (forall b. (Id a -> a -> b -> b) -> b -> b) -> (Id a, Id a) -> Speedup a -> Ops a -- | the symbol of a content element at id osymbol :: Ops a -> Id a -> Char -- | the name of a content element at id oname :: Ops a -> Id a -> String -- | the content element at given id okind :: Ops a -> Id a -> a -- | the id of the unique member of a singleton content group ouniqGroup :: Ops a -> String -> Id a -- | pick a random id belonging to a group and satisfying a predicate opick :: Ops a -> String -> (a -> Bool) -> Rnd (Id a) -- | fold over all content elements of a ofoldrWithKey :: Ops a -> forall b. (Id a -> a -> b -> b) -> b -> b -- | bounds of identifiers of content a obounds :: Ops a -> (Id a, Id a) -- | auxiliary speedup components ospeedup :: Ops a -> Speedup a -- | Operations for all content types, gathered together. data COps COps :: !Ops ActorKind -> !Ops CaveKind -> !Ops ItemKind -> !Ops PlaceKind -> !Ops RuleKind -> !Ops TileKind -> COps coactor :: COps -> !Ops ActorKind cocave :: COps -> !Ops CaveKind coitem :: COps -> !Ops ItemKind coplace :: COps -> !Ops PlaceKind corule :: COps -> !Ops RuleKind cotile :: COps -> !Ops TileKind -- | Create content operations for type a from definition of -- content of type a. createOps :: Show a => CDefs a -> Ops a -- | The standard ruleset used for level operations. stdRuleset :: Ops RuleKind -> RuleKind -- | Arrays, indexed by type i of content identifiers pointing to -- content type c, where the identifiers are represented as -- Word8 (and so content of type c can have at most 256 -- elements). data Array i c -- | Content identifiers array lookup. (!) :: Ix i => Array i c -> i -> Id c -- | Construct a content identifiers array updated with the association -- list. (//) :: Ix i => Array i c -> [(i, Id c)] -> Array i c -- | Create a content identifiers array from a list of elements. listArray :: Ix i => (i, i) -> [Id c] -> Array i c -- | Create a content identifiers array from an association list. array :: Ix i => (i, i) -> [(i, Id c)] -> Array i c -- | Content identifiers array bounds. bounds :: Ix i => Array i c -> (i, i) instance Show (Id c) instance Eq (Id c) instance Ord (Id c) instance Ix (Id c) instance (Show i, Ix i) => Show (Array i c) instance (Ix i, Binary i) => Binary (Array i c) instance Show COps instance Binary (Id c) -- | Actors in the game: monsters and heroes. No operation in this module -- involves the State or Action type. module Game.LambdaHack.Actor -- | A unique identifier of an actor in a dungeon. type ActorId = Int -- | Find a hero name in the config file, or create a stock name. findHeroName :: CP -> Int -> String -- | Chance that a new monster is generated. Currently depends on the -- number of monsters already present, and on the level. In the future, -- the strength of the character and the strength of the monsters present -- could further influence the chance, and the chance could also affect -- which monster is generated. How many and which monsters are generated -- will also depend on the cave kind used to build the level. monsterGenChance :: Int -> Int -> Rnd Bool -- | The type of party identifiers. data PartyId -- | All supported party identifiers. Animals and projectiles move every -- turn. Projectiles don't recognize friends and foes, animals turn -- friedly or hostile, depending on various factors. heroParty, animalProjectiles, enemyProjectiles, heroProjectiles, animalParty, enemyParty :: PartyId -- | The list of parties that represent projectiles. allProjectiles :: [PartyId] -- | Actor properties that are changing throughout the game. If they are -- dublets of properties from ActorKind, they are usually -- modified temporarily, but tend to return to the original value from -- ActorKind over time. E.g., HP. data Actor Actor :: !Id ActorKind -> !Maybe Char -> !Maybe String -> !Maybe Color -> !Maybe Speed -> !Int -> !Maybe (Vector, Int) -> Target -> !Point -> !Char -> !Time -> !PartyId -> Actor -- | the kind of the actor bkind :: Actor -> !Id ActorKind -- | individual map symbol bsymbol :: Actor -> !Maybe Char -- | individual name bname :: Actor -> !Maybe String -- | individual map color bcolor :: Actor -> !Maybe Color -- | individual speed bspeed :: Actor -> !Maybe Speed -- | current hit points bhp :: Actor -> !Int -- | direction and distance of running bdir :: Actor -> !Maybe (Vector, Int) -- | target for ranged attacks and AI btarget :: Actor -> Target -- | current location bloc :: Actor -> !Point -- | next inventory letter bletter :: Actor -> !Char -- | absolute time of next action btime :: Actor -> !Time -- | to which party the actor belongs bparty :: Actor -> !PartyId -- | A template for a new actor. The initial target is invalid to force a -- reset ASAP. template :: Id ActorKind -> Maybe Char -> Maybe String -> Int -> Point -> Time -> PartyId -> Actor -- | Increment current hit points of an actor. addHp :: Ops ActorKind -> Int -> Actor -> Actor -- | Checks for the presence of actors in a location. Does not check if the -- tile is walkable. unoccupied :: [Actor] -> Point -> Bool -- | The unique kind of heroes. heroKindId :: Ops ActorKind -> Id ActorKind -- | The unique kind of projectiles. projectileKindId :: Ops ActorKind -> Id ActorKind -- | Access actor speed, individual or, otherwise, stock. actorSpeed :: Ops ActorKind -> Actor -> Speed -- | The type of na actor target. data Target -- | target an actor with its last seen location TEnemy :: ActorId -> Point -> Target -- | target a given location TLoc :: Point -> Target -- | target the list of locations one after another TPath :: [Vector] -> Target -- | target current position of the cursor; default TCursor :: Target instance Show PartyId instance Eq PartyId instance Ord PartyId instance Show Target instance Eq Target instance Show Actor instance Binary Target instance Binary Actor instance Binary PartyId -- | Weapons, treasure and all the other items in the game. No operation in -- this module involves the State or Action type. TODO: -- Document after it's rethought and rewritten wrt separating inventory -- manangement and items proper. module Game.LambdaHack.Item -- | Game items in inventories or strewn around the dungeon. data Item Item :: !Id ItemKind -> !Int -> Maybe Char -> !Int -> Item -- | kind of the item jkind :: Item -> !Id ItemKind -- | power of the item jpower :: Item -> !Int -- | inventory symbol jletter :: Item -> Maybe Char -- | inventory count jcount :: Item -> !Int -- | Generate an item. newItem :: Ops ItemKind -> Int -> Int -> Rnd Item -- | Represent an item on the map. viewItem :: Ops ItemKind -> Id ItemKind -> FlavourMap -> (Char, Color) -- | Price an item, taking count into consideration. itemPrice :: Ops ItemKind -> Item -> Int strongestSearch :: Ops ItemKind -> [Item] -> Maybe Item strongestSword :: COps -> [Item] -> Maybe Item strongestRegen :: Ops ItemKind -> [Item] -> Maybe Item -- | Adds an item to a list of items, joining equal items. Also returns the -- joined item. joinItem :: Item -> [Item] -> (Item, [Item]) removeItemByLetter :: Item -> [Item] -> [Item] equalItemIdentity :: Item -> Item -> Bool removeItemByIdentity :: Item -> [Item] -> [Item] -- | Assigns a letter to an item, for inclusion in the inventory of a hero. -- Takes a remembered letter and a starting letter. assignLetter :: Maybe Char -> Char -> [Item] -> Maybe Char letterLabel :: Maybe Char -> String cmpLetterMaybe :: Maybe Char -> Maybe Char -> Ordering maxLetter :: Char -> Char -> Char letterRange :: [Char] -> String -- | Flavours assigned to items in this game. type FlavourMap = Map (Id ItemKind) Flavour getFlavour :: Ops ItemKind -> FlavourMap -> Id ItemKind -> Flavour -- | Randomly chooses flavour for all item kinds for this game. dungeonFlavourMap :: Ops ItemKind -> Rnd FlavourMap -- | The type of already discovered items. type Discoveries = Set (Id ItemKind) instance Show Item instance Binary Item -- | Operations concerning dungeon level tiles. -- -- Unlike for many other content types, there is no type Tile, -- of particular concrete tiles in the dungeon, corresponding to -- TileKind (the type of kinds of terrain tiles). This is because -- the tiles are too numerous and there's not enough storage space for a -- well-rounded Tile type, on one hand, and on the other hand, -- tiles are accessed too often in performance critical code to try to -- compress their representation and/or recompute them. Instead, of -- defining a Tile type, we express various properties of -- concrete tiles by arrays or sparse IntMaps, as appropriate. -- -- Actors at normal speed (2 m/s) take one turn to move one tile (1 m by -- 1 m). module Game.LambdaHack.Tile -- | The time interval needed to discover a given secret, e.g., a hidden -- terrain tile, e.g., a hidden door. type SecretTime = Time -- | The last time a hero left a smell in a given tile. To be used by -- monsters that hunt by smell. type SmellTime = Time -- | Whether a tile kind has the given feature. kindHasFeature :: Feature -> TileKind -> Bool -- | Whether a tile kind has all features of the first set and no features -- of the second. kindHas :: [Feature] -> [Feature] -> TileKind -> Bool -- | Whether a tile kind (specified by its id) has the given feature. hasFeature :: Ops TileKind -> Feature -> Id TileKind -> Bool -- | Whether a tile does not block vision. Essential for efficiency of -- FOV, hence tabulated. isClear :: Ops TileKind -> Id TileKind -> Bool -- | Whether a tile is lit on its own. Essential for efficiency of -- Perception, hence tabulated. isLit :: Ops TileKind -> Id TileKind -> Bool -- | The player can't tell one tile from the other. similar :: TileKind -> TileKind -> Bool -- | The player can't tell if the tile is hidden or not. canBeHidden :: Ops TileKind -> TileKind -> Bool -- | Inhabited dungeon levels and the operations to query and change them -- as the game progresses. module Game.LambdaHack.Level -- | All actors on the level, indexed by actor identifier. type ActorDict = IntMap Actor -- | Items carried by actors, indexed by actor identifier. type InvDict = IntMap [Item] -- | Current smell on map tiles. type SmellMap = IntMap SmellTime -- | Current secrecy value on map tiles. type SecretMap = IntMap SecretTime -- | Actual and remembered item lists on map tiles. type ItemMap = IntMap ([Item], [Item]) -- | Tile kinds on the map. type TileMap = Array Point TileKind -- | A single, inhabited dungeon level. data Level Level :: ActorDict -> InvDict -> X -> Y -> SmellMap -> SecretMap -> ItemMap -> TileMap -> TileMap -> String -> String -> (Point, Point) -> Time -> Level -- | all actors on the level lactor :: Level -> ActorDict -- | items belonging to actors linv :: Level -> InvDict -- | width of the level lxsize :: Level -> X -- | height of the level lysize :: Level -> Y -- | smells lsmell :: Level -> SmellMap -- | secrecy values lsecret :: Level -> SecretMap -- | items on the ground litem :: Level -> ItemMap -- | map tiles lmap :: Level -> TileMap -- | remembered map tiles lrmap :: Level -> TileMap -- | level description for the player ldesc :: Level -> String -- | debug information from cave generation lmeta :: Level -> String -- | destination of the (up, down) stairs lstairs :: Level -> (Point, Point) -- | date of the last activity on the level ltime :: Level -> Time -- | Update the hero and monster maps. updateActorDict :: (ActorDict -> ActorDict) -> Level -> Level -- | Update the hero items and monster items maps. updateInv :: (InvDict -> InvDict) -> Level -> Level -- | Update the smell map. updateSmell :: (SmellMap -> SmellMap) -> Level -> Level -- | Update the items on the ground map. updateIMap :: (ItemMap -> ItemMap) -> Level -> Level -- | Update the tile and remembered tile maps. updateLMap, updateLRMap :: (TileMap -> TileMap) -> Level -> Level -- | Place all items on the list at a location on the level. dropItemsAt :: [Item] -> Point -> Level -> Level -- | Query for actual and remembered tile kinds on the map. at, rememberAt :: Level -> Point -> Id TileKind -- | Query for actual and remembered items on the ground. atI, rememberAtI :: Level -> Point -> [Item] -- | Check whether one location is accessible from another, using the -- formula from the standard ruleset. accessible :: COps -> Level -> Point -> Point -> Bool -- | Check whether the location contains a door of secrecy lower than -- k and that can be opened according to the standard ruleset. openable :: Ops TileKind -> Level -> SecretTime -> Point -> Bool -- | Find a random location on the map satisfying a predicate. findLoc :: TileMap -> (Point -> Id TileKind -> Bool) -> Rnd Point -- | Try to find a random location on the map satisfying the conjunction of -- the list of predicates. If the premitted number of attempts is not -- enough, try again the same number of times without the first -- predicate, then without the first two, etc., until only one predicate -- remains, at which point try as many times, as needed. findLocTry :: Int -> TileMap -> [Point -> Id TileKind -> Bool] -> Rnd Point instance Show Level instance Binary Level -- | The game arena comprised of levels. No operation in this module -- involves the State, COps, Config or -- Action type. module Game.LambdaHack.Dungeon -- | Level ids are, for now, ordered linearly by depth. data LevelId -- | Depth of a level. levelNumber :: LevelId -> Int -- | Default level for a given depth. levelDefault :: Int -> LevelId -- | The complete dungeon is a map from level names to levels. data Dungeon -- | Create a dungeon from a list of levels and maximum depth. The depth is -- only a danger indicator; there may potentially be multiple levels with -- the same depth. fromList :: [(LevelId, Level)] -> Int -> Dungeon -- | Association list corresponding to the dungeon. Starts at the supplied -- level id (usually the current level) to try to speed up the searches -- and keep the dungeon lazy. currentFirst :: LevelId -> Dungeon -> [(LevelId, Level)] -- | Adjust the level at a given id. adjust :: (Level -> Level) -> LevelId -> Dungeon -> Dungeon -- | Adjust the level at a given id. mapDungeon :: (Level -> Level) -> Dungeon -> Dungeon -- | Find a level with the given id. (!) :: Dungeon -> LevelId -> Level -- | Try to look up a level with the given id. lookup :: LevelId -> Dungeon -> Maybe Level -- | Maximum depth of the dungeon. depth :: Dungeon -> Int instance Show LevelId instance Eq LevelId instance Ord LevelId instance Show Dungeon instance Binary Dungeon instance Binary LevelId -- | High score table operations. module Game.LambdaHack.HighScore -- | Current result of the game. data Status -- | the player lost the game on the given level Killed :: !LevelId -> Status -- | game is supended Camping :: Status -- | the player won Victor :: Status -- | A single score record. Records are ordered in the highscore table, -- from the best to the worst, in lexicographic ordering wrt the fields -- below. data ScoreRecord ScoreRecord :: !Int -> !Time -> !ClockTime -> !Status -> ScoreRecord -- | the score points :: ScoreRecord -> !Int -- | game time spent (negated, so less better) negTime :: ScoreRecord -> !Time -- | date of the last game interruption date :: ScoreRecord -> !ClockTime -- | reason of the game interruption status :: ScoreRecord -> !Status -- | Take care of saving a new score to the table and return a list of -- messages to display. register :: CP -> Bool -> ScoreRecord -> IO (String, [Overlay]) instance Show Status instance Eq Status instance Ord Status instance Eq ScoreRecord instance Ord ScoreRecord instance Binary ScoreRecord instance Binary Status -- | Field Of View scanning with a variety of algorithms. See -- https://github.com/kosmikus/LambdaHack/wiki/Fov-and-los for -- discussion. module Game.LambdaHack.FOV -- | Field Of View scanning mode. data FovMode -- | restrictive shadow casting Shadow :: FovMode -- | permissive FOV Permissive :: FovMode -- | digital FOV with the given radius Digital :: Int -> FovMode -- | only feeling out adjacent tiles by touch Blind :: FovMode -- | Perform a full scan for a given location. Returns the locations that -- are currently in the field of view. The Field of View algorithm to -- use, passed in the second argument, is set in the config file. fullscan :: Ops TileKind -> FovMode -> Point -> Level -> [Point] instance Show FovMode -- | Game state and persistent player diary types and operations. module Game.LambdaHack.State -- | The state of a single game that can be save and restored. In practice, -- we maintain some extra state, but it's temporary for a single turn or -- relevant only to the current session. data State State :: ActorId -> Cursor -> FlavourMap -> Discoveries -> Dungeon -> LevelId -> Int -> StdGen -> CP -> Bool -> Maybe (Bool, Status) -> DebugMode -> State -- | represents the player-controlled actor splayer :: State -> ActorId -- | cursor location and level to return to scursor :: State -> Cursor -- | association of flavour to items sflavour :: State -> FlavourMap -- | items (kinds) that have been discovered sdisco :: State -> Discoveries -- | all dungeon levels sdungeon :: State -> Dungeon -- | identifier of the current level slid :: State -> LevelId -- | stores next actor index scounter :: State -> Int -- | current random generator srandom :: State -> StdGen -- | game config sconfig :: State -> CP -- | last command unexpectedly took no time snoTime :: State -> Bool -- | cause of game shutdown squit :: State -> Maybe (Bool, Status) -- | debugging mode sdebug :: State -> DebugMode -- | Current targeting mode of the player. data TgtMode -- | not in targeting mode TgtOff :: TgtMode -- | the player requested targeting mode explicitly TgtExplicit :: TgtMode -- | the mode was entered (and will be exited) automatically TgtAuto :: TgtMode -- | Current targeting cursor parameters. data Cursor Cursor :: TgtMode -> LevelId -> Point -> LevelId -> Int -> Cursor -- | targeting mode ctargeting :: Cursor -> TgtMode -- | cursor level clocLn :: Cursor -> LevelId -- | cursor coordinates clocation :: Cursor -> Point -- | the level current player resides on creturnLn :: Cursor -> LevelId -- | a parameter of the tgt digital line ceps :: Cursor -> Int -- | Get current level from the dungeon data. slevel :: State -> Level -- | Get current time from the dungeon data. stime :: State -> Time -- | Initial game state. defaultState :: CP -> FlavourMap -> Dungeon -> LevelId -> Point -> StdGen -> State -- | Update cursor parameters within state. updateCursor :: (Cursor -> Cursor) -> State -> State -- | Update time within state. updateTime :: (Time -> Time) -> State -> State -- | Update item discoveries within state. updateDiscoveries :: (Discoveries -> Discoveries) -> State -> State -- | Update level data within state. updateLevel :: (Level -> Level) -> State -> State -- | Update dungeon data within state. updateDungeon :: (Dungeon -> Dungeon) -> State -> State -- | The diary contains all the player data that carries over from game to -- game. That includes the last message, previous messages and otherwise -- recorded history of past games. This can be used for calculating -- player achievements, unlocking advanced game features and general data -- mining. data Diary Diary :: Report -> History -> Diary sreport :: Diary -> Report shistory :: Diary -> History -- | Initial player diary. defaultDiary :: IO Diary data DebugMode DebugMode :: Maybe FovMode -> Bool -> DebugMode smarkVision :: DebugMode -> Maybe FovMode somniscient :: DebugMode -> Bool cycleMarkVision :: State -> State toggleOmniscient :: State -> State instance Show TgtMode instance Eq TgtMode instance Show Cursor instance Show DebugMode instance Show State instance Binary TgtMode instance Binary Cursor instance Binary State instance Binary Diary -- | Saving and restoring games and player diaries. module Game.LambdaHack.Save -- | Save a simple serialized version of the current state. Protected by a -- lock to avoid corrupting the file. saveGameFile :: State -> IO () -- | Restore a saved game, if it exists. Initialize directory structure, if -- needed. restoreGame :: (FilePath -> IO FilePath) -> CP -> String -> IO (Either (State, Diary) (String, Diary)) -- | Remove the backup of the savegame and save the player diary. Should be -- called before any non-error exit from the game. Sometimes the backup -- file does not exist and it's OK. We don't bother reporting any other -- removal exceptions, either, because the backup file is relatively -- unimportant. We wait on the mvar, because saving the diary at game -- shutdown is important. rmBkpSaveDiary :: State -> Diary -> IO () -- | Save the diary and a backup of the save game file, in case of crashes. -- This is only a backup, so no problem is the game is shut down before -- saving finishes, so we don't wait on the mvar. However, if a previous -- save is already in progress, we skip this save. saveGameBkp :: State -> Diary -> IO () -- | Construct English sentences from content. module Game.LambdaHack.Grammar -- | The type of verbs. type Verb = String -- | The grammatical object type. type Object = String -- | Capitalize a string. capitalize :: Object -> Object pluralise :: Object -> Object -- | Add the indefinite article (a, an) to a word -- (h is too hard). addIndefinite :: Object -> Object -- | How to refer to an item in object position of a sentence. If cheating -- is allowed, full identity of the object is revealed together with its -- flavour (e.g. at game over screen). objectItemCheat :: Ops ItemKind -> Bool -> State -> Item -> Object -- | How to refer to an item in object position of a sentence. objectItem :: Ops ItemKind -> State -> Item -> Object -- | How to refer to an actor in object position of a sentence. objectActor :: Ops ActorKind -> Actor -> Object -- | Capitalized actor object. capActor :: Ops ActorKind -> Actor -> Object -- | Sentences such as "Dog barks loudly." actorVerb :: Ops ActorKind -> Actor -> Verb -> String -> String -- | Sentences such as "Dog quaffs a red potion fast." actorVerbItem :: COps -> State -> Actor -> Verb -> Item -> String -> String -- | Sentences such as "Dog bites goblin furiously." actorVerbActor :: Ops ActorKind -> Actor -> Verb -> Actor -> String -> String -- | Sentences such as "Dog gulps down a red potion fast." actorVerbExtraItem :: COps -> State -> Actor -> Verb -> String -> Item -> String -> String -- | Produces a textual description of the terrain and items at an already -- explored location. Mute for unknown locations. The detailed variant is -- for use in the targeting mode. lookAt :: COps -> Bool -> Bool -> State -> Level -> Point -> String -> String -- | Operations on the Actor type that need the State type, -- but not the Action type. TODO: Add an export list and -- document after it's rewritten according to #17. module Game.LambdaHack.ActorState -- | Checks whether an actor identifier represents a hero. isAHero :: State -> ActorId -> Bool -- | Checks whether an actor identifier represents a monster. isAMonster :: State -> ActorId -> Bool -- | How long until an actor's smell vanishes from a tile. smellTimeout :: State -> Time -- | Finds an actor body on any level. Fails if not found. findActorAnyLevel :: ActorId -> State -> (LevelId, Actor, [Item]) -- | Tries to finds an actor body satisfying a predicate on any level. tryFindActor :: State -> (Actor -> Bool) -> Maybe (ActorId, Actor) getPlayerBody :: State -> Actor getPlayerItem :: State -> [Item] -- | The list of actors and their levels for all heroes in the dungeon. allHeroesAnyLevel :: State -> [ActorId] updateAnyActorBody :: ActorId -> (Actor -> Actor) -> State -> State updateAnyActorItem :: ActorId -> ([Item] -> [Item]) -> State -> State updateAnyLevel :: (Level -> Level) -> LevelId -> State -> State -- | Calculate the location of player's target. targetToLoc :: IntSet -> State -> Point -> Maybe Point -- | Checks if the actor is present on the current level. The order of -- argument here and in other functions is set to allow -- --
-- b <- gets (memActor a) --memActor :: ActorId -> State -> Bool -- | Gets actor body from the current level. Error if not found. getActor :: ActorId -> State -> Actor -- | Gets actor's items from the current level. Empty list, if not found. getActorItem :: ActorId -> State -> [Item] -- | Removes the actor, if present, from the current level. deleteActor :: ActorId -> State -> State -- | Add actor to the current level. insertActor :: ActorId -> Actor -> State -> State -- | Removes a player from the current level. deletePlayer :: State -> State heroAssocs, allButHeroesAssocs, friendlyAssocs, dangerousAssocs, hostileAssocs :: Level -> [(ActorId, Actor)] heroList, friendlyList, dangerousList, hostileList :: State -> [Actor] -- | Finds an actor at a location on the current level. Perception -- irrelevant. locToActor :: Point -> State -> Maybe ActorId locToActors :: Point -> State -> [ActorId] nearbyFreeLoc :: Ops TileKind -> Point -> State -> Point -- | Calculate loot's worth for heroes on the current level. calculateTotal :: Ops ItemKind -> State -> ([Item], Int) tryFindHeroK :: State -> Int -> Maybe ActorId -- | Create a new hero on the current level, close to the given location. addHero :: COps -> Point -> State -> State -- | Create a set of initial heroes on the current level, at location ploc. initialHeroes :: COps -> Point -> State -> State -- | Create a new monster in the level, at a given position and with a -- given actor kind and HP. addMonster :: Ops TileKind -> Id ActorKind -> Int -> Point -> State -> State -- | Create a projectile actor containing the given missile. addProjectile :: COps -> Item -> Point -> PartyId -> [Point] -> Time -> State -> State -- | Actors perceiving other actors and the dungeon level. module Game.LambdaHack.Perception -- | The type representing the perception for all levels in the dungeon. type DungeonPerception = [(LevelId, Perception)] -- | The type representing the perception of all actors on the level. -- -- Note: Heroes share visibility and only have separate reachability. The -- pplayer field must be void on all levels except where he resides. -- Right now, the field is used only for player-controlled monsters. data Perception -- | The set of tiles visible by at least one hero. totalVisible :: Perception -> IntSet -- | For debug only: the set of tiles reachable (would be visible if lit) -- by at least one hero. debugTotalReachable :: Perception -> IntSet -- | Calculate the perception of all actors on the level. dungeonPerception :: COps -> State -> DungeonPerception -- | Check whether a location is within the visually reachable area of the -- given actor (disregarding lighting). Defaults to false if the actor is -- not player-controlled (monster or hero). actorReachesLoc :: ActorId -> Point -> Perception -> Maybe ActorId -> Bool -- | Check whether an actor is within the visually reachable area of the -- given actor (disregarding lighting). Not quite correct if FOV not -- symmetric (e.g., Shadow). Defaults to false if neither actor -- is player-controlled. actorReachesActor :: ActorId -> ActorId -> Point -> Point -> Perception -> Maybe ActorId -> Bool -- | Whether a monster can see a hero (False if the target has no -- perceptions, e.g., not a hero or a hero without perception, due to -- being spawned on the same turn by a monster and then seen by another). -- An approximation, to avoid computing FOV for the monster. monsterSeesHero :: Ops TileKind -> Perception -> Level -> ActorId -> ActorId -> Point -> Point -> Bool -- | Display game data on the screen using one of the available frontends -- (determined at compile time with cabal flags). module Game.LambdaHack.Draw -- | Color mode for the display. data ColorMode -- | normal, with full colours ColorFull :: ColorMode -- | black+white only ColorBW :: ColorMode -- | Draw the whole screen: level map, status area and, at most, a single -- page overlay of text divided into lines. draw :: ColorMode -> COps -> Perception -> State -> Overlay -> SingleFrame -- | Render animations on top of the current screen frame. animate :: State -> Diary -> COps -> Perception -> Animation -> [Maybe SingleFrame] -- | Generation of places from place kinds. module Game.LambdaHack.Place -- | The map of tile kinds in a place (and generally anywhere in a cave). -- The map is sparse. The default tile that eventually fills the empty -- spaces is specified in the cave kind specification with -- cdefaultTile. type TileMapXY = Map PointXY (Id TileKind) -- | The parameters of a place. Most are immutable and set at the time when -- a place is generated. data Place Place :: !Id PlaceKind -> !Area -> !Bool -> !String -> !Id TileKind -> !Id TileKind -> Place qkind :: Place -> !Id PlaceKind qarea :: Place -> !Area qseen :: Place -> !Bool qlegend :: Place -> !String qsolidFence :: Place -> !Id TileKind qhollowFence :: Place -> !Id TileKind -- | For CAlternate tiling, require the place be comprised of an -- even number of whole corners, with exactly one square overlap between -- consecutive coners and no trimming. For other tiling methods, check -- that the area is large enough for tiling the corner twice in each -- direction, with a possible one row/column overlap. placeValid :: Area -> PlaceKind -> Bool -- | Construct a fence around an area, with the given tile kind. buildFence :: Id TileKind -> Area -> TileMapXY -- | Given a few parameters, roll and construct a Place -- datastructure and fill a cave section acccording to it. buildPlace :: COps -> CaveKind -> Id TileKind -> Int -> Int -> Area -> Rnd (TileMapXY, Place) instance Show Place instance Binary Place -- | Generation of caves (not yet inhabited dungeon levels) from cave -- kinds. module Game.LambdaHack.Cave -- | The map of tile kinds in a cave. The map is sparse. The default tile -- that eventually fills the empty spaces is specified in the cave kind -- specification with cdefaultTile. type TileMapXY = TileMapXY -- | The map of starting secrecy strength of tiles in a cave. The map is -- sparse. Unspecified tiles have secrecy strength of 0. type SecretMapXY = Map PointXY SecretTime -- | The map of starting items in tiles of a cave. The map is sparse. -- Unspecified tiles have no starting items. type ItemMapXY = Map PointXY Item -- | The type of caves (not yet inhabited dungeon levels). data Cave Cave :: !Id CaveKind -> TileMapXY -> SecretMapXY -> ItemMapXY -> String -> [Place] -> Cave -- | the kind of the cave dkind :: Cave -> !Id CaveKind -- | tile kinds in the cave dmap :: Cave -> TileMapXY -- | secrecy strength of cave tiles dsecret :: Cave -> SecretMapXY -- | starting items in the cave ditem :: Cave -> ItemMapXY -- | debug information about the cave dmeta :: Cave -> String -- | places generated in the cave dplaces :: Cave -> [Place] -- | Cave generation by an algorithm inspired by the original Rogue, buildCave :: COps -> Int -> Int -> Id CaveKind -> Rnd Cave instance Show Cave -- | Dungeon operations that require State, COps or -- Config type. module Game.LambdaHack.DungeonState -- | Freshly generated and not yet populated dungeon. data FreshDungeon FreshDungeon :: LevelId -> Point -> Dungeon -> FreshDungeon -- | starting level for the party entryLevel :: FreshDungeon -> LevelId -- | starting location for the party entryLoc :: FreshDungeon -> Point -- | level maps freshDungeon :: FreshDungeon -> Dungeon -- | Generate the dungeon for a new game. generate :: COps -> CP -> Rnd FreshDungeon -- | Compute the level identifier and starting location on the level, after -- a level change. whereTo :: State -> Int -> Maybe (LevelId, Point) -- | AI strategies to direct actors not controlled by the player. No -- operation in this module involves the State or -- Action type. module Game.LambdaHack.Strategy -- | A strategy is a choice of (non-empty) frequency tables of possible -- actions. newtype Strategy a Strategy :: [Frequency a] -> Strategy a runStrategy :: Strategy a -> [Frequency a] -- | Strategy where only the actions from the given single frequency table -- can be picked. liftFrequency :: Frequency a -> Strategy a -- | Strategy with the actions from both argument strategies, with original -- frequencies. (.|) :: Strategy a -> Strategy a -> Strategy a -- | Strategy with no actions at all. reject :: Strategy a -- | Conditionally accepted strategy. (.=>) :: Bool -> Strategy a -> Strategy a -- | Strategy with all actions not satisfying the predicate removed. The -- remaining action keep their original relative frequency values. only :: (a -> Bool) -> Strategy a -> Strategy a instance Show a => Show (Strategy a) instance MonadPlus Strategy instance Monad Strategy -- | Text frontend based on Gtk. module Game.LambdaHack.Display.Gtk -- | Session data maintained by the frontend. data FrontendSession -- | Add a frame to be drawn. display :: FrontendSession -> Bool -> Bool -> Maybe SingleFrame -> IO () -- | Input key via the frontend. Fail if there is no frame to show to the -- player as a prompt for the keypress. nextEvent :: FrontendSession -> Maybe Bool -> IO (Key, Modifier) -- | Display a prompt, wait for any of the specified keys (for any key, if -- the list is empty). Repeat if an unexpected key received. Starts in -- Push or None mode, stop in None mode. Spends most time waiting for a -- key, so not performance critical, so does not need optimization. promptGetKey :: FrontendSession -> [(Key, Modifier)] -> SingleFrame -> IO (Key, Modifier) -- | The name of the frontend. frontendName :: String -- | Spawns the gtk input and output thread, which spawns all the other -- required threads. We create a separate thread for gtk to minimize -- communication with the heavy main thread. The other threads have to be -- spawned after gtk is initialized, because they call -- postGUIAsync, and need sview and stags. startup :: String -> (FrontendSession -> IO ()) -> IO () -- | Shuts down the frontend cleanly. shutdown :: FrontendSession -> IO () instance Eq GtkFrame -- | Display game data on the screen using one of the available frontends -- (determined at compile time with cabal flags). module Game.LambdaHack.Display -- | Session data maintained by the frontend. data FrontendSession -- | Spawns the gtk input and output thread, which spawns all the other -- required threads. We create a separate thread for gtk to minimize -- communication with the heavy main thread. The other threads have to be -- spawned after gtk is initialized, because they call -- postGUIAsync, and need sview and stags. startup :: String -> (FrontendSession -> IO ()) -> IO () -- | Shuts down the frontend cleanly. shutdown :: FrontendSession -> IO () -- | The name of the frontend. frontendName :: String -- | Input key via the frontend. Fail if there is no frame to show to the -- player as a prompt for the keypress. nextEvent :: FrontendSession -> Maybe Bool -> IO (Key, Modifier) -- | Display a prompt, wait for any of the specified keys (for any key, if -- the list is empty). Repeat if an unexpected key received. Starts in -- Push or None mode, stop in None mode. Spends most time waiting for a -- key, so not performance critical, so does not need optimization. promptGetKey :: FrontendSession -> [(Key, Modifier)] -> SingleFrame -> IO (Key, Modifier) -- | Push a frame or a single frame's worth of delay to the frame queue. displayFrame :: FrontendSession -> Bool -> Maybe SingleFrame -> IO () -- | Game action monad and basic building blocks for player and monster -- actions. module Game.LambdaHack.Action -- | The type of the function inside any action. (Separated from the -- Action type to document each argument with haddock.) type ActionFun r a = Session -> DungeonPerception -> (State -> Diary -> a -> IO r) -> (Msg -> IO r) -> State -> Diary -> IO r -- | Actions of player-controlled characters and of any other actors. data Action a -- | Run an action, with a given session, state and diary, in the -- IO monad. handlerToIO :: Session -> State -> Diary -> Action () -> IO () -- | Invoke pseudo-random computation with the generator kept in the state. rndToAction :: Rnd a -> Action a -- | Actions and screen frames, including delays, resulting from performing -- the actions. type ActionFrame a = Action (a, [Maybe SingleFrame]) -- | Return the value with an empty set of screen frames. returnNoFrame :: a -> ActionFrame a -- | As the when monad operation, but on type ActionFrame -- (). whenFrame :: Bool -> ActionFrame () -> ActionFrame () -- | Inject action into actions with screen frames. inFrame :: Action () -> ActionFrame () -- | The constant session information, not saved to the game save file. data Session Session :: FrontendSession -> COps -> Binding (ActionFrame ()) -> Session -- | frontend session information sfs :: Session -> FrontendSession -- | game content scops :: Session -> COps -- | binding of keys to commands skeyb :: Session -> Binding (ActionFrame ()) -- | Get the content operations. getCOps :: Action COps -- | Get the key binding. getBinding :: Action (Binding (ActionFrame ())) -- | Reset the state and resume from the last backup point, i.e., invoke -- the failure continuation. abort :: Action a -- | Abort with the given message. abortWith :: Msg -> Action a -- | Abort and print the given msg if the condition is true. abortIfWith :: Bool -> Msg -> Action a -- | Abort and conditionally print the fixed message. neverMind :: Bool -> Action a -- | Set the current exception handler. First argument is the handler, -- second is the computation the handler scopes over. tryWith :: (Msg -> Action a) -> Action a -> Action a -- | Set the current exception handler. Apart of executing it, draw and -- pass along a frame with the abort message, if any. tryWithFrame :: Action a -> ActionFrame a -> ActionFrame a -- | Take a handler and a computation. If the computation fails, the -- handler is invoked and then the computation is retried. tryRepeatedlyWith :: (Msg -> Action ()) -> Action () -> Action () -- | Try the given computation and silently catch failure. tryIgnore :: Action () -> Action () -- | Try the given computation and silently catch failure, returning empty -- set of screen frames. tryIgnoreFrame :: ActionFrame () -> ActionFrame () -- | Get the current diary. getDiary :: Action Diary -- | Add a message to the current report. msgAdd :: Msg -> Action () -- | Store current report in the history and reset report. recordHistory :: Action () -- | Wait for a player command. getKeyCommand :: Maybe Bool -> Action (Key, Modifier) -- | Wait for a player keypress. getKeyChoice :: [(Key, Modifier)] -> SingleFrame -> Action (Key, Modifier) -- | A series of confirmations for all overlays. getOverConfirm :: [SingleFrame] -> Action Bool -- | Display a msg with a more prompt. Return value indicates if -- the player tried to cancel/escape. displayMore :: ColorMode -> Msg -> Action Bool -- | Print a yes/no question and return the player's answer. Use black and -- white colours to turn player's attention to the choice. displayYesNo :: Msg -> Action Bool -- | Print a msg and several overlays, one per page. All frames require -- confirmations. Raise abort if the players presses ESC. displayOverAbort :: Msg -> [Overlay] -> Action () -- | Print a msg and several overlays, one per page. The last frame does -- not expect a confirmation. displayOverlays :: Msg -> [Overlay] -> ActionFrame () -- | Print a prompt and an overlay and wait for a player keypress. If many -- overlays, scroll screenfuls with SPACE. Do not wrap screenfuls (in -- some menus ? cycles views, so the user can restart from the -- top). displayChoiceUI :: Msg -> [Overlay] -> [(Key, Modifier)] -> Action (Key, Modifier) -- | Push a frame or a single frame's worth of delay to the frame queue. displayFramePush :: Maybe SingleFrame -> Action () -- | Draw the current level. The prompt is displayed, but not added to -- history. The prompt is appended to the current message and only the -- first screenful of the resulting overlay is displayed. drawPrompt :: ColorMode -> Msg -> Action SingleFrame -- | Initialize perception, etc., display level and run the action. startClip :: Action () -> Action () -- | Update player memory. remember :: Action () -- | Update player at the given list of locations.. rememberList :: [Point] -> Action () -- | Get the current perception. getPerception :: Action Perception -- | Update actor stats. Works for actors on other levels, too. updateAnyActor :: ActorId -> (Actor -> Actor) -> Action () -- | Update player-controlled actor stats. updatePlayerBody :: (Actor -> Actor) -> Action () -- | Obtains the current date and time. currentDate :: Action ClockTime -- | Save the diary and a backup of the save game file, in case of crashes. -- -- See saveGameBkp. saveGameBkp :: State -> Diary -> Action () -- | Dumps the current configuration to a file. -- -- See dump. dumpCfg :: FilePath -> CP -> Action () -- | End the game, shutting down the frontend. The boolean argument tells -- if ending screens should be shown, the other arguments describes the -- cause of shutdown. shutGame :: (Bool, Status) -> Action () -- | Debugging. debug :: String -> Action () instance MonadState State Action instance Functor Action instance Monad Action instance Show (Action a) -- | The effectToAction function and all it depends on. This file should -- not depend on Actions.hs nor ItemAction.hs. TODO: Add an export list -- and document after it's rewritten according to #17. module Game.LambdaHack.EffectAction -- | The source actor affects the target actor, with a given effect and -- power. The second argument is verbosity of the resulting message. Both -- actors are on the current level and can be the same actor. The first -- bool result indicates if the effect was spectacular enough for the -- actors to identify it (and the item that caused it, if any). The -- second bool tells if the effect was seen by or affected the party. effectToAction :: Effect -> Int -> ActorId -> ActorId -> Int -> Action (Bool, Bool) -- | The boolean part of the result says if the ation was interesting and -- the string part describes how the target reacted (not what the source -- did). eff :: Effect -> Int -> ActorId -> ActorId -> Int -> Action (Bool, String) nullEffect :: Action (Bool, String) squashActor :: ActorId -> ActorId -> Action () effLvlGoUp :: Int -> Action () -- | Change level and reset it's time and update the times of all actors. -- The player may be added to lactor of the new level only after -- this operation is executed. switchLevel :: LevelId -> Action () -- | The player leaves the dungeon. fleeDungeon :: Action () -- | The source actor affects the target actor, with a given item. If the -- event is seen, the item may get identified. itemEffectAction :: Int -> ActorId -> ActorId -> Item -> Action () -- | Make the item known to the player. discover :: Item -> Action () -- | Make the actor controlled by the player. Focus on the actor if level -- changes. False, if nothing to do. selectPlayer :: ActorId -> Action Bool focusIfOurs :: ActorId -> Action Bool summonHeroes :: Int -> Point -> Action () summonMonsters :: Int -> Point -> Action () -- | Remove dead heroes (or dead dominated monsters). Check if game is -- over. For now we only check the selected hero and at current level, -- but if poison, etc. is implemented, we'd need to check all heroes on -- any level. checkPartyDeath :: Action () -- | End game, showing the ending screens, if requested. gameOver :: Bool -> Action () -- | Create a list of item names, split into many overlays. itemOverlay :: Bool -> Bool -> [Item] -> Action [Overlay] stopRunning :: Action () -- | Perform look around in the current location of the cursor. doLook :: ActionFrame () gameVersion :: Action () -- | The game action stuff that is independent from ItemAction.hs (both -- depend on EffectAction.hs). TODO: Add an export list and document -- after it's rewritten according to #17. module Game.LambdaHack.Actions saveGame :: Action () quitGame :: Action () moveCursor :: Vector -> Int -> ActionFrame () move :: Vector -> ActionFrame () ifRunning :: ((Vector, Int) -> Action a) -> Action a -> Action a -- | Guess and report why the bump command failed. guessBump :: Ops TileKind -> Feature -> Id TileKind -> Action () -- | Player tries to trigger a tile using a feature. bumpTile :: Point -> Feature -> Action () -- | Perform the action specified for the tile in case it's triggered. triggerTile :: Point -> Action () -- | Ask for a direction and trigger a tile, if possible. playerTriggerDir :: Feature -> Verb -> Action () -- | Player tries to trigger a tile in a given direction. playerBumpDir :: Feature -> Vector -> Action () -- | Player tries to trigger the tile he's standing on. playerTriggerTile :: Feature -> Action () -- | An actor opens a door: player (hero or controlled monster) or enemy. actorOpenDoor :: ActorId -> Vector -> Action () -- | Change the displayed level in targeting mode to (at most) k levels -- shallower. Enters targeting mode, if not already in one. tgtAscend :: Int -> ActionFrame () -- | Switches current hero to the next hero on the level, if any, wrapping. -- We cycle through at most 10 heroes (@, 0--9). cycleHero :: Action () -- | Search for hidden doors. search :: Action () -- | This function performs a move (or attack) by any actor, i.e., it can -- handle monsters, heroes and both. moveOrAttack :: Bool -> ActorId -> Vector -> Action () -- | Resolves the result of an actor moving into another. Usually this -- involves melee attack, but with two heroes it just changes focus. -- Actors on blocked locations can be attacked without any restrictions. -- For instance, an actor embedded in a wall can be attacked from an -- adjacent position. This function is analogous to projectGroupItem, but -- for melee and not using up the weapon. actorAttackActor :: ActorId -> ActorId -> Action () -- | Resolves the result of an actor running (not walking) into another. -- This involves switching positions of the two actors. actorRunActor :: ActorId -> ActorId -> Action () -- | Create a new monster in the level, at a random position. rollMonster :: COps -> Perception -> State -> Rnd State -- | Generate a monster, possibly. generateMonster :: Action () -- | Possibly regenerate HP for all actors on the current level. regenerateLevelHP :: Action () -- | Display command help. displayHelp :: ActionFrame () displayHistory :: ActionFrame () dumpConfig :: Action () redraw :: Action () -- | Add new smell traces to the level. Only humans leave a strong scent. addSmell :: Action () -- | Running and disturbance. module Game.LambdaHack.Running -- | Start running in the given direction and with the given number of -- tiles already traversed (usually 0). The first turn of running -- succeeds much more often than subsequent turns, because most of the -- disturbances are ignored, since the player is aware of them and still -- explicitly requests a run. run :: (Vector, Int) -> ActionFrame () -- | This function implements the actual logic of running. It checks if we -- have to stop running because something interesting cropped up, it -- ajusts the direction given by the vector if we reached a corridor's -- corner (we never change direction except in corridors) and it -- increments the counter of traversed tiles. continueRun :: (Vector, Int) -> Action () -- | Item UI code with the Action type and everything it depends on -- that is not already in Action.hs and EffectAction.hs. This file should -- not depend on Actions.hs. TODO: Add an export list and document after -- it's rewritten according to #17. module Game.LambdaHack.ItemAction -- | Display inventory inventory :: ActionFrame () -- | Let the player choose any item with a given group name. Note that this -- does not guarantee the chosen item belongs to the group, as the player -- can override the choice. getGroupItem :: [Item] -> Object -> [Char] -> String -> String -> Action Item applyGroupItem :: ActorId -> Verb -> Item -> Action () playerApplyGroupItem :: Verb -> Object -> [Char] -> Action () projectGroupItem :: ActorId -> Point -> Verb -> Item -> Action () playerProjectGroupItem :: Verb -> Object -> [Char] -> ActionFrame () playerProjectGI :: Verb -> Object -> [Char] -> ActionFrame () -- | Start the monster targeting mode. Cycle between monster targets. targetMonster :: TgtMode -> ActionFrame () -- | Start the floor targeting mode or reset the cursor location to the -- player. targetFloor :: TgtMode -> ActionFrame () -- | Set, activate and display cursor information. setCursor :: TgtMode -> ActionFrame () -- | Tweak the eps parameter of the targeting digital line. epsIncr :: Bool -> Action () -- | End targeting mode, accepting the current location or not. endTargeting :: Bool -> Action () endTargetingMsg :: Action () -- | Cancel something, e.g., targeting mode, resetting the cursor to the -- position of the player. Chosen target is not invalidated. cancelCurrent :: Action () -- | Accept something, e.g., targeting mode, keeping cursor where it was. -- Or perform the default action, if nothing needs accepting. acceptCurrent :: ActionFrame () -> ActionFrame () -- | Drop a single item. dropItem :: Action () removeOnlyFromInventory :: ActorId -> Item -> Point -> Action () -- | Remove given item from an actor's inventory or floor. TODO: this is -- subtly wrong: if identical items are on the floor and in inventory, -- the floor one will be chosen, regardless of player intention. TODO: -- right now it ugly hacks (with the ploc) around removing items of dead -- heros/monsters. The subtle incorrectness helps here a lot, because -- items of dead heroes land on the floor, so we use them up in -- inventory, but remove them after use from the floor. removeFromInventory :: ActorId -> Item -> Point -> Action () -- | Remove given item from the given location. Tell if successful. removeFromLoc :: Item -> Point -> Action Bool actorPickupItem :: ActorId -> Action () pickupItem :: Action () allObjectsName :: String -- | Let the player choose any item from a list of items. getAnyItem :: String -> [Item] -> String -> Action Item data ItemDialogState INone :: ItemDialogState ISuitable :: ItemDialogState IAll :: ItemDialogState -- | Let the player choose a single, preferably suitable, item from a list -- of items. getItem :: String -> (Item -> Bool) -> String -> [Item] -> String -> Action Item instance Eq ItemDialogState -- | Abstract syntax of player commands and their semantics. module Game.LambdaHack.Command -- | Abstract syntax of player commands. The type is abstract, but the -- values are created outside this module via the Read class (from config -- file) . data Cmd -- | Major commands land on the first page of command help. majorCmd :: Cmd -> Bool -- | Time cosuming commands are marked as such in help and cannot be -- invoked in targeting mode on a remote level (level different than the -- level of the selected hero). timedCmd :: Cmd -> Bool -- | The semantics of player commands in terms of the Action -- monad. cmdSemantics :: Cmd -> ActionFrame () -- | Description of player commands. cmdDescription :: Cmd -> String instance Show Cmd instance Read Cmd -- | Binding of keys to commands implemented with the Action monad. module Game.LambdaHack.BindingAction -- | Binding of keys to movement and other standard commands, as well as -- commands defined in the config file. stdBinding :: CP -> (Cmd -> ActionFrame ()) -> (Cmd -> String) -> Binding (ActionFrame ()) -- | AI strategy operations implemented with the Action monad. module Game.LambdaHack.StrategyAction -- | Monster AI strategy based on monster sight, smell, intelligence, etc. strategy :: COps -> ActorId -> State -> Perception -> Strategy (Action ()) -- | A strategy to always just wait. wait :: Strategy (Action ()) -- | The main loop of the game, processing player and AI moves turn by -- turn. module Game.LambdaHack.Turn -- | Start a clip (a part of a turn for which one or more frames will be -- generated). Do whatever has to be done every fixed number of time -- units, e.g., monster generation. Run the player and other actors -- moves. Eventually advance the time and repeat. handleTurn :: Action () -- | Setting up game data and restoring or starting a game. module Game.LambdaHack.Start -- | Either restore a saved game, or setup a new game. Then call the main -- game loop. start :: CP -> Session -> IO ()