Restructuring, #9
This commit is contained in:
parent
2055ef234e
commit
dab6fadad4
41 changed files with 941 additions and 680 deletions
10
README.md
10
README.md
|
@ -258,6 +258,16 @@ If we look at the example, all the objects are
|
||||||
|
|
||||||
<mark>TODO</mark>
|
<mark>TODO</mark>
|
||||||
|
|
||||||
|
`RPGEngine` is the main module. It contains the `playRPGEngine` function which bootstraps the whole game. It is also
|
||||||
|
the game loop. From here, `RPGEngine` talks to its submodules.
|
||||||
|
|
||||||
|
These submodules are `Config`, `Data`, `Input`, `Parse` & `Render`. They are all responsible for their own part, either
|
||||||
|
containing the program configuration, data containers, everything needed to handle input, everything needed to parse a
|
||||||
|
source file & everything needed to render the game. However, each of these submodules has their own submodules to
|
||||||
|
divide the work. They are conveniently named after the state of the game that they work with, e.g. the main menu has a
|
||||||
|
module & when the game is playing is a different module. A special one is `Core`, which is kind of like a library for
|
||||||
|
every piece. It contains functions that are regularly used by the other modules.
|
||||||
|
|
||||||
#### Monads/Monad stack
|
#### Monads/Monad stack
|
||||||
|
|
||||||
<mark>TODO</mark>
|
<mark>TODO</mark>
|
||||||
|
|
10
lib/Input.hs
Normal file
10
lib/Input.hs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-- Go to the next stage of the Game
|
||||||
|
-- setNextState :: Game -> Game
|
||||||
|
-- setNextState game = game{ state = newState }
|
||||||
|
-- where newState = nextState $ state game
|
||||||
|
|
||||||
|
-- -- Get the next state based on the current state
|
||||||
|
-- nextState :: State -> State
|
||||||
|
-- nextState Menu {} = defaultLvlSelect
|
||||||
|
-- nextState Pause {} = Playing
|
||||||
|
-- nextState _ = Menu
|
|
@ -5,33 +5,18 @@ module RPGEngine
|
||||||
( playRPGEngine
|
( playRPGEngine
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import RPGEngine.Data.Defaults
|
import RPGEngine.Config ( bgColor, winDimensions, winOffsets )
|
||||||
import RPGEngine.Render
|
import RPGEngine.Render ( initWindow, render, initGame )
|
||||||
import RPGEngine.Input
|
import RPGEngine.Input ( handleAllInput )
|
||||||
|
|
||||||
import Graphics.Gloss (
|
import Graphics.Gloss ( play )
|
||||||
Color(..)
|
|
||||||
, white
|
|
||||||
, play
|
|
||||||
)
|
|
||||||
|
|
||||||
----------------------------- Constants ------------------------------
|
|
||||||
|
|
||||||
-- Dimensions for main window
|
|
||||||
winDimensions :: (Int, Int)
|
|
||||||
winDimensions = (1280, 720)
|
|
||||||
|
|
||||||
-- Offsets for main window
|
|
||||||
winOffsets :: (Int, Int)
|
|
||||||
winOffsets = (0, 0)
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
-- This is the gameloop.
|
-- This is the game loop.
|
||||||
-- It can receive input and update itself. It is rendered by a renderer.
|
-- It can receive input and update itself. It is rendered by a renderer.
|
||||||
playRPGEngine :: String -> Int -> IO()
|
playRPGEngine :: String -> Int -> IO()
|
||||||
playRPGEngine title fps = do
|
playRPGEngine title fps = do
|
||||||
play window bgColor fps initGame render handleInputs step
|
play window bgColor fps initGame render handleAllInput step
|
||||||
where window = initWindow title winDimensions winOffsets
|
where window = initWindow title winDimensions winOffsets
|
||||||
step _ g = g -- TODO Do something with step? Check health etc.
|
step _ g = g -- TODO Do something with step? Check health etc.
|
||||||
handleInputs = handleAllInput
|
|
||||||
|
|
36
lib/RPGEngine/Config.hs
Normal file
36
lib/RPGEngine/Config.hs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
-- This module should ultimately be replaced by a config file parser
|
||||||
|
module RPGEngine.Config
|
||||||
|
-- All entries are exported
|
||||||
|
where
|
||||||
|
|
||||||
|
import Graphics.Gloss
|
||||||
|
|
||||||
|
----------------------- Window configuration -------------------------
|
||||||
|
|
||||||
|
-- Dimensions for main window
|
||||||
|
winDimensions :: (Int, Int)
|
||||||
|
winDimensions = (1280, 720)
|
||||||
|
|
||||||
|
-- Offsets for main window
|
||||||
|
winOffsets :: (Int, Int)
|
||||||
|
winOffsets = (0, 0)
|
||||||
|
|
||||||
|
-- Game background color
|
||||||
|
bgColor :: Color
|
||||||
|
bgColor = white
|
||||||
|
|
||||||
|
-- Default scale
|
||||||
|
zoom :: Float
|
||||||
|
zoom = 5.0
|
||||||
|
|
||||||
|
-- Resolution of the texture
|
||||||
|
resolution :: Float
|
||||||
|
resolution = 16
|
||||||
|
|
||||||
|
-- Location of the assets folder containing all images
|
||||||
|
assetsFolder :: FilePath
|
||||||
|
assetsFolder = "assets/"
|
||||||
|
|
||||||
|
-- Location of the level folder containing all levels
|
||||||
|
levelFolder :: FilePath
|
||||||
|
levelFolder = "levels/"
|
|
@ -1,28 +1,64 @@
|
||||||
module RPGEngine.Data where
|
-- Contains all the data containers of the game.
|
||||||
|
-- Submodules contain accessors for these data containers.
|
||||||
|
module RPGEngine.Data
|
||||||
|
-- All data types are exported
|
||||||
|
where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core
|
||||||
|
import RPGEngine.Render.Core ( Renderer )
|
||||||
|
|
||||||
-------------------------------- Game --------------------------------
|
-------------------------------- Game --------------------------------
|
||||||
|
|
||||||
-- TODO Add more
|
-- A game is the base data container.
|
||||||
data Game = Game {
|
data Game = Game {
|
||||||
-- Current state of the game
|
|
||||||
state :: State,
|
state :: State,
|
||||||
playing :: Level,
|
|
||||||
levels :: [Level],
|
levels :: [Level],
|
||||||
player :: Player
|
player :: Player
|
||||||
}
|
}
|
||||||
|
|
||||||
|
------------------------------- State --------------------------------
|
||||||
|
|
||||||
|
-- Code reusability
|
||||||
|
data StateBase = StateBase {
|
||||||
|
renderer :: Renderer Game,
|
||||||
|
inputHandler :: InputHandler Game
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Main menu
|
||||||
|
data State = Menu { base :: StateBase }
|
||||||
|
-- Select the level you want to play
|
||||||
|
| LevelSelection { base :: StateBase,
|
||||||
|
levelList :: [FilePath],
|
||||||
|
selector :: ListSelector }
|
||||||
|
-- Playing a level
|
||||||
|
| Playing { base :: StateBase,
|
||||||
|
level :: Level }
|
||||||
|
-- Paused while playing a level
|
||||||
|
| Paused { base :: StateBase,
|
||||||
|
level :: Level }
|
||||||
|
-- Won a level
|
||||||
|
| Win { base :: StateBase }
|
||||||
|
-- Lost a level
|
||||||
|
| Lose { base :: StateBase }
|
||||||
|
|
||||||
------------------------------- Level --------------------------------
|
------------------------------- Level --------------------------------
|
||||||
|
|
||||||
data Level = Level {
|
data Level = Level {
|
||||||
layout :: Layout,
|
layout :: Layout,
|
||||||
coordlayout :: [(X, Y, Physical)],
|
-- All Physical pieces but with their coordinates
|
||||||
|
index :: [(X, Y, Physical)],
|
||||||
items :: [Item],
|
items :: [Item],
|
||||||
entities :: [Entity]
|
entities :: [Entity]
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
type X = Int
|
||||||
|
type Y = Int
|
||||||
|
|
||||||
type Layout = [Strip]
|
type Layout = [Strip]
|
||||||
type Strip = [Physical]
|
type Strip = [Physical]
|
||||||
|
|
||||||
|
-- A Physical part of the world. A single tile of the world. A block
|
||||||
|
-- with stuff on it.
|
||||||
data Physical = Void
|
data Physical = Void
|
||||||
| Walkable
|
| Walkable
|
||||||
| Blocked
|
| Blocked
|
||||||
|
@ -30,48 +66,12 @@ data Physical = Void
|
||||||
| Exit
|
| Exit
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------- Player -------------------------------
|
-------------------------------- Item --------------------------------
|
||||||
|
|
||||||
type X = Int
|
|
||||||
type Y = Int
|
|
||||||
|
|
||||||
data Player = Player {
|
|
||||||
playerHp :: Maybe Int,
|
|
||||||
inventory :: [Item],
|
|
||||||
position :: (X, Y)
|
|
||||||
} deriving (Eq, Show)
|
|
||||||
|
|
||||||
instance Living Player where
|
|
||||||
hp = playerHp
|
|
||||||
|
|
||||||
------------------------------- State --------------------------------
|
|
||||||
|
|
||||||
-- Current state of the game.
|
|
||||||
data State = Menu
|
|
||||||
| LvlSelect
|
|
||||||
| Playing
|
|
||||||
| Pause
|
|
||||||
| Win
|
|
||||||
| Lose
|
|
||||||
|
|
||||||
------------------------------- Object -------------------------------
|
|
||||||
|
|
||||||
class Object a where
|
|
||||||
id :: a -> String
|
|
||||||
x :: a -> Int
|
|
||||||
y :: a -> Int
|
|
||||||
name :: a -> String
|
|
||||||
description :: a -> String
|
|
||||||
actions :: a -> [([Condition], Action)]
|
|
||||||
value :: a -> Maybe Int
|
|
||||||
|
|
||||||
class Living a where
|
|
||||||
hp :: a -> Maybe Int
|
|
||||||
|
|
||||||
data Item = Item {
|
data Item = Item {
|
||||||
itemId :: ItemId,
|
itemId :: ItemId,
|
||||||
itemX :: Int,
|
itemX :: X,
|
||||||
itemY :: Int,
|
itemY :: Y,
|
||||||
itemName :: String,
|
itemName :: String,
|
||||||
itemDescription :: String,
|
itemDescription :: String,
|
||||||
itemActions :: [([Condition], Action)],
|
itemActions :: [([Condition], Action)],
|
||||||
|
@ -79,41 +79,37 @@ data Item = Item {
|
||||||
useTimes :: Maybe Int
|
useTimes :: Maybe Int
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
instance Object Item where
|
type ItemId = String
|
||||||
id = itemId
|
|
||||||
x = itemX
|
------------------------------- Entity -------------------------------
|
||||||
y = itemY
|
|
||||||
name = itemName
|
|
||||||
description = itemDescription
|
|
||||||
actions = itemActions
|
|
||||||
value = itemValue
|
|
||||||
|
|
||||||
data Entity = Entity {
|
data Entity = Entity {
|
||||||
entityId :: EntityId,
|
entityId :: EntityId,
|
||||||
entityX :: Int,
|
entityX :: X,
|
||||||
entityY :: Int,
|
entityY :: Y,
|
||||||
entityName :: String,
|
entityName :: String,
|
||||||
entityDescription :: String,
|
entityDescription :: String,
|
||||||
entityActions :: [([Condition], Action)],
|
entityActions :: [([Condition], Action)],
|
||||||
entityValue :: Maybe Int,
|
entityValue :: Maybe Int,
|
||||||
entityHp :: Maybe Int,
|
entityHp :: HP,
|
||||||
direction :: Direction
|
direction :: Direction
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
instance Object Entity where
|
|
||||||
id = entityId
|
|
||||||
x = entityX
|
|
||||||
y = entityY
|
|
||||||
name = entityName
|
|
||||||
description = entityDescription
|
|
||||||
actions = entityActions
|
|
||||||
value = entityValue
|
|
||||||
|
|
||||||
instance Living Entity where
|
|
||||||
hp = entityHp
|
|
||||||
|
|
||||||
type EntityId = String
|
type EntityId = String
|
||||||
type ItemId = String
|
type HP = Maybe Int
|
||||||
|
|
||||||
|
data Direction = North
|
||||||
|
| East
|
||||||
|
| South
|
||||||
|
| West
|
||||||
|
| Stay -- No direction
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
data Player = Player {
|
||||||
|
playerHp :: HP,
|
||||||
|
inventory :: [Item],
|
||||||
|
position :: (X, Y)
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------ Condition -----------------------------
|
------------------------------ Condition -----------------------------
|
||||||
|
|
||||||
|
@ -121,7 +117,7 @@ data Condition = InventoryFull
|
||||||
| InventoryContains ItemId
|
| InventoryContains ItemId
|
||||||
| Not Condition
|
| Not Condition
|
||||||
| AlwaysFalse
|
| AlwaysFalse
|
||||||
deriving (Show, Eq)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------- Action -------------------------------
|
------------------------------- Action -------------------------------
|
||||||
|
|
||||||
|
@ -130,14 +126,5 @@ data Action = Leave
|
||||||
| UseItem ItemId
|
| UseItem ItemId
|
||||||
| DecreaseHp EntityId ItemId
|
| DecreaseHp EntityId ItemId
|
||||||
| IncreasePlayerHp ItemId
|
| IncreasePlayerHp ItemId
|
||||||
| Nothing
|
| DoNothing
|
||||||
deriving (Show, Eq)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------ Direction -----------------------------
|
|
||||||
|
|
||||||
data Direction = North
|
|
||||||
| East
|
|
||||||
| South
|
|
||||||
| West
|
|
||||||
| Center -- Equal to 'stay where you are'
|
|
||||||
deriving (Show, Eq)
|
|
67
lib/RPGEngine/Data/Default.hs
Normal file
67
lib/RPGEngine/Data/Default.hs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
module RPGEngine.Data.Default
|
||||||
|
-- Everything is exported
|
||||||
|
where
|
||||||
|
import RPGEngine.Data (Entity (..), Game (..), Item (..), Layout, Player (..), Level (..), StateBase (..), State (..), Physical (..), Direction (..))
|
||||||
|
import RPGEngine.Input.Core (ListSelector(..))
|
||||||
|
import RPGEngine.Render.LevelSelection (renderLevelSelection)
|
||||||
|
import RPGEngine.Input.Playing (spawnPlayer)
|
||||||
|
import RPGEngine.Render.Menu (renderMenu)
|
||||||
|
|
||||||
|
------------------------------ Defaults ------------------------------
|
||||||
|
|
||||||
|
defaultEntity :: Entity
|
||||||
|
defaultEntity = Entity {
|
||||||
|
entityId = "",
|
||||||
|
entityX = 0,
|
||||||
|
entityY = 0,
|
||||||
|
entityName = "Default",
|
||||||
|
entityDescription = "",
|
||||||
|
entityActions = [],
|
||||||
|
entityValue = Prelude.Nothing,
|
||||||
|
entityHp = Prelude.Nothing,
|
||||||
|
direction = Stay
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultItem :: Item
|
||||||
|
defaultItem = Item {
|
||||||
|
itemId = "",
|
||||||
|
itemX = 0,
|
||||||
|
itemY = 0,
|
||||||
|
itemName = "Default",
|
||||||
|
itemDescription = "",
|
||||||
|
itemActions = [],
|
||||||
|
itemValue = Prelude.Nothing,
|
||||||
|
useTimes = Prelude.Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultLayout :: Layout
|
||||||
|
defaultLayout = [
|
||||||
|
[Blocked, Blocked, Blocked],
|
||||||
|
[Blocked, Entrance, Blocked],
|
||||||
|
[Blocked, Blocked, Blocked]
|
||||||
|
]
|
||||||
|
|
||||||
|
defaultLevel :: Level
|
||||||
|
defaultLevel = Level {
|
||||||
|
layout = defaultLayout,
|
||||||
|
index = [
|
||||||
|
(0, 0, Blocked),
|
||||||
|
(0, 1, Blocked),
|
||||||
|
(0, 2, Blocked),
|
||||||
|
(1, 0, Blocked),
|
||||||
|
(1, 1, Entrance),
|
||||||
|
(1, 2, Blocked),
|
||||||
|
(2, 0, Blocked),
|
||||||
|
(2, 1, Blocked),
|
||||||
|
(2, 2, Blocked)
|
||||||
|
],
|
||||||
|
items = [],
|
||||||
|
entities = []
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultPlayer :: Player
|
||||||
|
defaultPlayer = Player {
|
||||||
|
playerHp = Prelude.Nothing, -- Compares to infinity
|
||||||
|
inventory = [],
|
||||||
|
position = (0, 0)
|
||||||
|
}
|
|
@ -1,65 +0,0 @@
|
||||||
module RPGEngine.Data.Defaults where
|
|
||||||
|
|
||||||
import RPGEngine.Data
|
|
||||||
import RPGEngine.Input.Player (spawnPlayer)
|
|
||||||
import RPGEngine.Input.Level (putCoords)
|
|
||||||
|
|
||||||
defaultEntity :: Entity
|
|
||||||
defaultEntity = Entity {
|
|
||||||
entityId = "",
|
|
||||||
entityX = 0,
|
|
||||||
entityY = 0,
|
|
||||||
entityName = "Default",
|
|
||||||
entityDescription = "",
|
|
||||||
entityActions = [],
|
|
||||||
entityValue = Prelude.Nothing,
|
|
||||||
entityHp = Prelude.Nothing,
|
|
||||||
direction = Center
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Initialize the game
|
|
||||||
initGame :: Game
|
|
||||||
initGame = Game {
|
|
||||||
state = defaultState,
|
|
||||||
playing = defaultLevel,
|
|
||||||
levels = [defaultLevel],
|
|
||||||
player = spawnPlayer defaultLevel defaultPlayer
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultItem :: Item
|
|
||||||
defaultItem = Item {
|
|
||||||
itemId = "",
|
|
||||||
itemX = 0,
|
|
||||||
itemY = 0,
|
|
||||||
itemName = "Default",
|
|
||||||
itemDescription = "",
|
|
||||||
itemActions = [],
|
|
||||||
itemValue = Prelude.Nothing,
|
|
||||||
useTimes = Prelude.Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultLayout :: Layout
|
|
||||||
defaultLayout = [
|
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
|
||||||
[Blocked, Entrance, Walkable, Walkable, Walkable, Walkable, Exit, Blocked],
|
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
|
||||||
]
|
|
||||||
|
|
||||||
defaultLevel :: Level
|
|
||||||
defaultLevel = Level {
|
|
||||||
layout = defaultLayout,
|
|
||||||
coordlayout = putCoords defaultLevel, -- TODO This should go
|
|
||||||
items = [],
|
|
||||||
entities = []
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultPlayer :: Player
|
|
||||||
defaultPlayer = Player {
|
|
||||||
playerHp = Prelude.Nothing, -- Compares to infinity
|
|
||||||
inventory = [],
|
|
||||||
position = (0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Default state of the game, Menu
|
|
||||||
defaultState :: State
|
|
||||||
defaultState = Menu
|
|
22
lib/RPGEngine/Data/Game.hs
Normal file
22
lib/RPGEngine/Data/Game.hs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
module RPGEngine.Data.Game
|
||||||
|
( isLegalMove
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Data
|
||||||
|
( Player(Player, position),
|
||||||
|
Direction,
|
||||||
|
Physical(Exit, Walkable, Entrance),
|
||||||
|
State(Playing, level),
|
||||||
|
Game(Game, state, player) )
|
||||||
|
import RPGEngine.Data.Level (findAt, directionOffsets)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
-- Check if a move is legal by checking what is located at the new position.
|
||||||
|
isLegalMove :: Direction -> Game -> Bool
|
||||||
|
isLegalMove dir g@Game{ state = Playing { level = lvl }, player = p@Player{ position = (x, y) }} = legality
|
||||||
|
where legality = physical `elem` [Walkable, Entrance, Exit]
|
||||||
|
physical = findAt newPos lvl
|
||||||
|
newPos = (x + xD, y + yD)
|
||||||
|
(xD, yD) = directionOffsets dir
|
||||||
|
isLegalMove _ _ = False
|
36
lib/RPGEngine/Data/Level.hs
Normal file
36
lib/RPGEngine/Data/Level.hs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
module RPGEngine.Data.Level
|
||||||
|
-- Everything is exported
|
||||||
|
where
|
||||||
|
|
||||||
|
import GHC.IO (unsafePerformIO)
|
||||||
|
import System.Directory (getDirectoryContents)
|
||||||
|
import RPGEngine.Input.Core (ListSelector(..))
|
||||||
|
import RPGEngine.Data (Level (..), Physical (..), Direction (..), Entity (..), Game (..), Item (..), Player (..), StateBase (..), State (..), X, Y, Layout)
|
||||||
|
import RPGEngine.Config (levelFolder)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
-- Find first position of a Physical
|
||||||
|
-- Graceful exit by giving Nothing if there is nothing found.
|
||||||
|
findFirstOf :: Level -> Physical -> Maybe (X, Y)
|
||||||
|
findFirstOf l@Level{ index = index } physical = try
|
||||||
|
where matches = filter (\(x, y, v) -> v == physical) index
|
||||||
|
try | not (null matches) = Just $ (\(x, y, _) -> (x, y)) $ head matches
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
|
-- What is located at a given position in the level?
|
||||||
|
findAt :: (X, Y) -> Level -> Physical
|
||||||
|
findAt pos lvl@Level{ index = index } = try
|
||||||
|
where matches = map (\(_, _, v) -> v) $ filter (\(x, y, v) -> (x, y) == pos) index
|
||||||
|
try | not (null matches) = head matches
|
||||||
|
| otherwise = Void
|
||||||
|
|
||||||
|
directionOffsets :: Direction -> (X, Y)
|
||||||
|
directionOffsets North = ( 0, 1)
|
||||||
|
directionOffsets East = ( 1, 0)
|
||||||
|
directionOffsets South = ( 0, -1)
|
||||||
|
directionOffsets West = (-1, 0)
|
||||||
|
directionOffsets Stay = ( 0, 0)
|
||||||
|
|
||||||
|
getLevelList :: [FilePath]
|
||||||
|
getLevelList = drop 2 $ unsafePerformIO $ getDirectoryContents levelFolder
|
|
@ -1,22 +0,0 @@
|
||||||
-- Describes the current state of the game,
|
|
||||||
-- e.g. Main menu, game, pause, win or lose
|
|
||||||
-- Allows to easily go to a next state and change rendering accordingly
|
|
||||||
|
|
||||||
module RPGEngine.Data.State
|
|
||||||
( State(..)
|
|
||||||
|
|
||||||
, nextState
|
|
||||||
) where
|
|
||||||
|
|
||||||
import RPGEngine.Data
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Get the next state based on the current state
|
|
||||||
nextState :: State -> State
|
|
||||||
nextState Menu = LvlSelect
|
|
||||||
nextState Playing = Pause
|
|
||||||
nextState Pause = Playing
|
|
||||||
nextState _ = Menu
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
|
@ -1,50 +1,15 @@
|
||||||
-- Input for RPG-Engine
|
-- Implementations for each state can be found in their respective
|
||||||
|
-- submodules.
|
||||||
module RPGEngine.Input
|
module RPGEngine.Input
|
||||||
( handleAllInput
|
( handleAllInput
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import RPGEngine.Data
|
|
||||||
import RPGEngine.Data.State
|
|
||||||
import RPGEngine.Input.Core
|
import RPGEngine.Input.Core
|
||||||
import RPGEngine.Input.Player
|
import RPGEngine.Data
|
||||||
|
|
||||||
import Graphics.Gloss.Interface.IO.Game
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
-- Handle all input of all states of the game.
|
||||||
|
|
||||||
-- Handle all input for RPG-Engine
|
|
||||||
handleAllInput :: InputHandler Game
|
handleAllInput :: InputHandler Game
|
||||||
handleAllInput ev g@Game{ state = Playing } = handlePlayInputs ev g
|
handleAllInput ev g@Game{ state = state } = handleInput ev g
|
||||||
handleAllInput ev g@Game{ state = LvlSelect } = handleLvlSelectInput ev g
|
where handleInput = inputHandler $ base state
|
||||||
handleAllInput ev g = handleAnyKey setNextState ev g
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Input for 'Playing' state
|
|
||||||
handlePlayInputs :: InputHandler Game
|
|
||||||
handlePlayInputs = composeInputHandlers [
|
|
||||||
-- Pause the game
|
|
||||||
handleKey (Char 'p') (\game -> game{ state = Pause }),
|
|
||||||
|
|
||||||
-- Player movement
|
|
||||||
handleKey (SpecialKey KeyUp) $ movePlayer North,
|
|
||||||
handleKey (SpecialKey KeyRight) $ movePlayer East,
|
|
||||||
handleKey (SpecialKey KeyDown) $ movePlayer South,
|
|
||||||
handleKey (SpecialKey KeyLeft) $ movePlayer West,
|
|
||||||
|
|
||||||
handleKey (Char 'w') $ movePlayer North,
|
|
||||||
handleKey (Char 'd') $ movePlayer East,
|
|
||||||
handleKey (Char 's') $ movePlayer South,
|
|
||||||
handleKey (Char 'a') $ movePlayer West
|
|
||||||
]
|
|
||||||
|
|
||||||
-- Input for selection a level to load
|
|
||||||
handleLvlSelectInput :: InputHandler Game
|
|
||||||
handleLvlSelectInput = composeInputHandlers []
|
|
||||||
|
|
||||||
-- Go to the next stage of the Game
|
|
||||||
setNextState :: Game -> Game
|
|
||||||
setNextState game = game{ state = newState }
|
|
||||||
where newState = nextState $ state game
|
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
-- Allows to create a massive inputHandler that can handle anything
|
|
||||||
-- after you specify what you want it to do.
|
|
||||||
|
|
||||||
module RPGEngine.Input.Core
|
module RPGEngine.Input.Core
|
||||||
( InputHandler(..)
|
( InputHandler
|
||||||
|
, ListSelector(..)
|
||||||
|
|
||||||
, composeInputHandlers
|
, composeInputHandlers
|
||||||
, handle
|
, handle
|
||||||
, handleKey
|
, handleKey
|
||||||
, handleAnyKey
|
, handleAnyKey
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Graphics.Gloss.Interface.IO.Game
|
import Graphics.Gloss.Interface.Pure.Game
|
||||||
|
( Event(EventKey), Key(..), KeyState(Down), SpecialKey )
|
||||||
|
|
||||||
----------------------------- Constants ------------------------------
|
----------------------------- Constants ------------------------------
|
||||||
|
|
||||||
type InputHandler a = Event -> (a -> a)
|
type InputHandler a = Event -> (a -> a)
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
data ListSelector = ListSelector {
|
||||||
|
selection :: Int,
|
||||||
|
selected :: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
-- Compose multiple InputHandlers into one InputHandler that handles
|
-- Compose multiple InputHandlers into one InputHandler that handles
|
||||||
-- all of them.
|
-- all of them.
|
||||||
|
@ -26,8 +31,8 @@ composeInputHandlers (ih:ihs) ev a = composeInputHandlers ihs ev (ih ev a)
|
||||||
-- Handle any event
|
-- Handle any event
|
||||||
handle :: Event -> (a -> a) -> InputHandler a
|
handle :: Event -> (a -> a) -> InputHandler a
|
||||||
handle (EventKey key _ _ _) = handleKey key
|
handle (EventKey key _ _ _) = handleKey key
|
||||||
-- handle (EventMotion _) = undefined
|
-- handle (EventMotion _) = undefined -- TODO
|
||||||
-- handle (EventResize _) = undefined
|
-- handle (EventResize _) = undefined -- TODO
|
||||||
handle _ = const (const id)
|
handle _ = const (const id)
|
||||||
|
|
||||||
-- Handle a event by pressing a key
|
-- Handle a event by pressing a key
|
||||||
|
@ -41,7 +46,7 @@ handleAnyKey :: (a -> a) -> InputHandler a
|
||||||
handleAnyKey f (EventKey _ Down _ _) = f
|
handleAnyKey f (EventKey _ Down _ _) = f
|
||||||
handleAnyKey _ _ = id
|
handleAnyKey _ _ = id
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
--------------------------- Help functions ---------------------------
|
||||||
|
|
||||||
handleCharKey :: Char -> (a -> a) -> InputHandler a
|
handleCharKey :: Char -> (a -> a) -> InputHandler a
|
||||||
handleCharKey c1 f (EventKey (Char c2) Down _ _)
|
handleCharKey c1 f (EventKey (Char c2) Down _ _)
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
module RPGEngine.Input.Level
|
|
||||||
( putCoords
|
|
||||||
, findFirst
|
|
||||||
, whatIsAt
|
|
||||||
) where
|
|
||||||
import RPGEngine.Data (Level (..), Y, X, Physical(..))
|
|
||||||
|
|
||||||
-- Map all Physicals onto coordinates
|
|
||||||
putCoords :: Level -> [(X, Y, Physical)]
|
|
||||||
putCoords l@Level{ layout = lay } = concatMap (\(a, bs) -> map (\(b, c) -> (b, a, c)) bs) numberedList
|
|
||||||
where numberedStrips = zip [0::Int .. ] lay
|
|
||||||
numberedList = map (\(x, strip) -> (x, zip [0::Int ..] strip)) numberedStrips
|
|
||||||
|
|
||||||
-- Find first position of a Physical
|
|
||||||
-- Graceful exit by giving Nothing if there is nothing found.
|
|
||||||
findFirst :: Level -> Physical -> Maybe (X, Y)
|
|
||||||
findFirst l@Level{ coordlayout = lay } physical = try
|
|
||||||
where matches = filter (\(x, y, v) -> v == physical) lay
|
|
||||||
try | not (null matches) = Just $ (\(x, y, _) -> (x, y)) $ head matches
|
|
||||||
| otherwise = Nothing
|
|
||||||
|
|
||||||
-- What is located at a given position in the level?
|
|
||||||
whatIsAt :: (X, Y) -> Level -> Physical
|
|
||||||
whatIsAt pos lvl@Level{ coordlayout = lay } = try
|
|
||||||
where matches = map (\(_, _, v) -> v) $ filter (\(x, y, v) -> (x, y) == pos) lay
|
|
||||||
try | not (null matches) = head matches
|
|
||||||
| otherwise = Void
|
|
49
lib/RPGEngine/Input/LevelSelection.hs
Normal file
49
lib/RPGEngine/Input/LevelSelection.hs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
module RPGEngine.Input.LevelSelection
|
||||||
|
( handleInputLevelSelection
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core
|
||||||
|
( composeInputHandlers, handleKey, InputHandler, ListSelector (..) )
|
||||||
|
|
||||||
|
import RPGEngine.Config ( levelFolder )
|
||||||
|
import RPGEngine.Data ( Game (..), Direction (..), State (..), StateBase (..) )
|
||||||
|
|
||||||
|
import Graphics.Gloss.Interface.IO.Game
|
||||||
|
( Key(SpecialKey), SpecialKey(KeySpace) )
|
||||||
|
import Graphics.Gloss.Interface.IO.Interact (SpecialKey(..))
|
||||||
|
import RPGEngine.Render.Playing (renderPlaying)
|
||||||
|
import RPGEngine.Input.Playing (handleInputPlaying)
|
||||||
|
import RPGEngine.Parse (parse)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
handleInputLevelSelection :: InputHandler Game
|
||||||
|
handleInputLevelSelection = composeInputHandlers [
|
||||||
|
handleKey (SpecialKey KeySpace) selectLevel,
|
||||||
|
|
||||||
|
handleKey (SpecialKey KeyUp) $ moveSelector North,
|
||||||
|
handleKey (SpecialKey KeyDown) $ moveSelector South
|
||||||
|
]
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Select a level and load it in
|
||||||
|
selectLevel :: Game -> Game
|
||||||
|
selectLevel game@Game{ state = LevelSelection{ levelList = list, selector = selector }} = newGame
|
||||||
|
where newGame = parse $ levelFolder ++ (list !! index)
|
||||||
|
index = selection selector
|
||||||
|
selectLevel g = g
|
||||||
|
|
||||||
|
-- Move the selector either up or down
|
||||||
|
moveSelector :: Direction -> Game -> Game
|
||||||
|
moveSelector dir game@Game{ state = state@LevelSelection{ levelList = list, selector = selector } } = newGame
|
||||||
|
where newGame = game{ state = newState }
|
||||||
|
newState = state{ selector = newSelector }
|
||||||
|
newSelector | constraint = selector{ selection = newSelection }
|
||||||
|
| otherwise = selector
|
||||||
|
constraint = 0 <= newSelection && newSelection < length list
|
||||||
|
newSelection = selection selector + diff
|
||||||
|
diff | dir == North = -1
|
||||||
|
| dir == South = 1
|
||||||
|
| otherwise = 0
|
||||||
|
moveSelector _ g = g
|
13
lib/RPGEngine/Input/Lose.hs
Normal file
13
lib/RPGEngine/Input/Lose.hs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module RPGEngine.Input.Lose
|
||||||
|
( handleInputLose
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core ( InputHandler )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
handleInputLose :: InputHandler Game
|
||||||
|
handleInputLose = undefined
|
|
@ -1,12 +0,0 @@
|
||||||
module RPGEngine.Input.LvlSelect
|
|
||||||
( getLvlList
|
|
||||||
) where
|
|
||||||
|
|
||||||
import GHC.IO (unsafePerformIO)
|
|
||||||
import System.Directory (getDirectoryContents)
|
|
||||||
|
|
||||||
lvlFolder :: FilePath
|
|
||||||
lvlFolder = "levels"
|
|
||||||
|
|
||||||
getLvlList :: [FilePath]
|
|
||||||
getLvlList = unsafePerformIO $ getDirectoryContents lvlFolder
|
|
36
lib/RPGEngine/Input/Menu.hs
Normal file
36
lib/RPGEngine/Input/Menu.hs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
module RPGEngine.Input.Menu
|
||||||
|
( handleInputMenu
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core ( InputHandler, composeInputHandlers, handleAnyKey, ListSelector (..) )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game (..), State (..), StateBase (..) )
|
||||||
|
import RPGEngine.Render.LevelSelection (renderLevelSelection)
|
||||||
|
import RPGEngine.Input.LevelSelection (handleInputLevelSelection)
|
||||||
|
import RPGEngine.Data.Level (getLevelList)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
handleInputMenu :: InputHandler Game
|
||||||
|
handleInputMenu = composeInputHandlers [
|
||||||
|
handleAnyKey selectLevel
|
||||||
|
]
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
selectLevel :: Game -> Game
|
||||||
|
selectLevel g@Game{ state = state } = g{ state = defaultLevelSelection }
|
||||||
|
|
||||||
|
defaultLevelSelection :: State
|
||||||
|
defaultLevelSelection = LevelSelection { base = base, selector = defaultSelector, levelList = levels }
|
||||||
|
where base = StateBase {
|
||||||
|
renderer = renderLevelSelection,
|
||||||
|
inputHandler = handleInputLevelSelection
|
||||||
|
}
|
||||||
|
levels = getLevelList
|
||||||
|
|
||||||
|
defaultSelector :: ListSelector
|
||||||
|
defaultSelector = ListSelector {
|
||||||
|
selection = 0,
|
||||||
|
selected = False
|
||||||
|
}
|
12
lib/RPGEngine/Input/Paused.hs
Normal file
12
lib/RPGEngine/Input/Paused.hs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module RPGEngine.Input.Paused
|
||||||
|
( handleInputPaused
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core ( InputHandler )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
handleInputPaused :: InputHandler Game
|
||||||
|
handleInputPaused = undefined
|
|
@ -1,44 +0,0 @@
|
||||||
module RPGEngine.Input.Player
|
|
||||||
( spawnPlayer
|
|
||||||
, movePlayer
|
|
||||||
) where
|
|
||||||
|
|
||||||
import RPGEngine.Data (Game(..), Direction(..), Player(..), X, Y, Physical (..), Level(..))
|
|
||||||
import RPGEngine.Input.Level (whatIsAt, findFirst)
|
|
||||||
import Data.Maybe (fromJust, isNothing)
|
|
||||||
|
|
||||||
----------------------------- Constants ------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
diffs :: Direction -> (X, Y)
|
|
||||||
diffs North = ( 0, 1)
|
|
||||||
diffs East = ( 1, 0)
|
|
||||||
diffs South = ( 0, -1)
|
|
||||||
diffs West = (-1, 0)
|
|
||||||
diffs Center = ( 0, 0)
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Set the initial position of the player in a given level.
|
|
||||||
spawnPlayer :: Level -> Player -> Player
|
|
||||||
spawnPlayer l@Level{ layout = lay } p@Player{ position = prevPos } = p{ position = newPos }
|
|
||||||
where try = findFirst l Entrance
|
|
||||||
newPos | isNothing try = prevPos
|
|
||||||
| otherwise = fromJust try
|
|
||||||
|
|
||||||
-- Move a player in a direction if possible.
|
|
||||||
movePlayer :: Direction -> Game -> Game
|
|
||||||
movePlayer dir g@Game{ player = p@Player{ position = (x, y) }} = newGame
|
|
||||||
where newGame = g{ player = newPlayer }
|
|
||||||
newPlayer = p{ position = newCoord }
|
|
||||||
newCoord | isLegalMove dir g = (x + xD, y + yD)
|
|
||||||
| otherwise = (x, y)
|
|
||||||
(xD, yD) = diffs dir
|
|
||||||
|
|
||||||
-- Check if a move is legal by checking what is located at the new position.
|
|
||||||
isLegalMove :: Direction -> Game -> Bool
|
|
||||||
isLegalMove dir g@Game{ playing = lvl, player = p@Player{ position = (x, y) }} = legality
|
|
||||||
where legality = physical `elem` [Walkable, Entrance, Exit]
|
|
||||||
physical = whatIsAt newPos lvl
|
|
||||||
newPos = (x + xD, y + yD)
|
|
||||||
(xD, yD) = diffs dir
|
|
80
lib/RPGEngine/Input/Playing.hs
Normal file
80
lib/RPGEngine/Input/Playing.hs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
module RPGEngine.Input.Playing
|
||||||
|
( handleInputPlaying
|
||||||
|
, spawnPlayer
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core
|
||||||
|
( composeInputHandlers, handleKey, InputHandler )
|
||||||
|
|
||||||
|
import RPGEngine.Data
|
||||||
|
( Player(Player, position),
|
||||||
|
Direction(West, North, East, South),
|
||||||
|
Physical(Entrance),
|
||||||
|
Y,
|
||||||
|
X,
|
||||||
|
Level(Level, layout),
|
||||||
|
State(..),
|
||||||
|
StateBase(StateBase, renderer, inputHandler),
|
||||||
|
Game(Game, state, player) )
|
||||||
|
import RPGEngine.Data.Level ( findFirstOf, directionOffsets )
|
||||||
|
import RPGEngine.Data.Game ( isLegalMove )
|
||||||
|
import RPGEngine.Input.Paused ( handleInputPaused )
|
||||||
|
import RPGEngine.Render.Paused ( renderPaused )
|
||||||
|
import Graphics.Gloss.Interface.IO.Game (Key(..))
|
||||||
|
import Graphics.Gloss.Interface.IO.Interact (SpecialKey(..))
|
||||||
|
import Data.Maybe (isNothing, fromJust)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
handleInputPlaying :: InputHandler Game
|
||||||
|
handleInputPlaying = composeInputHandlers [
|
||||||
|
-- Pause the game
|
||||||
|
handleKey (Char 'p') pauseGame,
|
||||||
|
|
||||||
|
-- Player movement
|
||||||
|
handleKey (SpecialKey KeyUp) $ movePlayer North,
|
||||||
|
handleKey (SpecialKey KeyRight) $ movePlayer East,
|
||||||
|
handleKey (SpecialKey KeyDown) $ movePlayer South,
|
||||||
|
handleKey (SpecialKey KeyLeft) $ movePlayer West,
|
||||||
|
|
||||||
|
handleKey (Char 'w') $ movePlayer North,
|
||||||
|
handleKey (Char 'd') $ movePlayer East,
|
||||||
|
handleKey (Char 's') $ movePlayer South,
|
||||||
|
handleKey (Char 'a') $ movePlayer West
|
||||||
|
]
|
||||||
|
|
||||||
|
-- Set the initial position of the player in a given level.
|
||||||
|
spawnPlayer :: Level -> Player -> Player
|
||||||
|
spawnPlayer l@Level{ layout = lay } p@Player{ position = prevPos } = p{ position = newPos }
|
||||||
|
where try = findFirstOf l Entrance
|
||||||
|
newPos | isNothing try = prevPos
|
||||||
|
| otherwise = fromJust try
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
pauseGame :: Game -> Game
|
||||||
|
pauseGame g@Game{ state = Playing{ level = level } } = pausedGame
|
||||||
|
where pausedGame = g{ state = pausedState }
|
||||||
|
pausedState = Paused{ base = newBase, level = level }
|
||||||
|
newBase = StateBase { renderer = renderPaused, inputHandler = handleInputPaused }
|
||||||
|
|
||||||
|
-- Move a player in a direction if possible.
|
||||||
|
movePlayer :: Direction -> Game -> Game
|
||||||
|
movePlayer dir g@Game{ player = p@Player{ position = (x, y) }} = newGame
|
||||||
|
where newGame = g{ player = newPlayer }
|
||||||
|
newPlayer = p{ position = newCoord }
|
||||||
|
newCoord | isLegalMove dir g = (x + xD, y + yD)
|
||||||
|
| otherwise = (x, y)
|
||||||
|
(xD, yD) = directionOffsets dir
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
goToNextLevel :: Game -> Game
|
||||||
|
goToNextLevel = undefined
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Map all Physicals onto coordinates
|
||||||
|
putCoords :: Level -> [(X, Y, Physical)]
|
||||||
|
putCoords l@Level{ layout = lay } = concatMap (\(a, bs) -> map (\(b, c) -> (b, a, c)) bs) numberedList
|
||||||
|
where numberedStrips = zip [0::Int .. ] lay
|
||||||
|
numberedList = map (\(x, strip) -> (x, zip [0::Int ..] strip)) numberedStrips
|
13
lib/RPGEngine/Input/Win.hs
Normal file
13
lib/RPGEngine/Input/Win.hs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module RPGEngine.Input.Win
|
||||||
|
( handleInputWin
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Input.Core ( InputHandler )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
handleInputWin :: InputHandler Game
|
||||||
|
handleInputWin = undefined
|
|
@ -1,19 +1,16 @@
|
||||||
module RPGEngine.Parse where
|
module RPGEngine.Parse
|
||||||
|
( parse
|
||||||
|
) where
|
||||||
|
|
||||||
import RPGEngine.Data
|
import RPGEngine.Data ( Game )
|
||||||
import RPGEngine.Parse.StructElement
|
import RPGEngine.Parse.StructureToGame ( structureToGame )
|
||||||
import RPGEngine.Parse.Game
|
import GHC.IO (unsafePerformIO)
|
||||||
|
import Text.Parsec.String (parseFromFile)
|
||||||
|
import RPGEngine.Parse.TextToStructure (structure)
|
||||||
|
|
||||||
import Text.Parsec.String
|
------------------------------ Exported ------------------------------
|
||||||
import System.IO.Unsafe
|
|
||||||
|
|
||||||
----------------------------- Constants ------------------------------
|
parse :: FilePath -> Game
|
||||||
|
parse filename = structureToGame struct
|
||||||
type FileName = String
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
parseToGame :: FileName -> Game
|
|
||||||
parseToGame filename = structToGame struct
|
|
||||||
where (Right struct) = unsafePerformIO io
|
where (Right struct) = unsafePerformIO io
|
||||||
io = parseFromFile structElement filename
|
io = parseFromFile structure filename
|
|
@ -1,7 +1,23 @@
|
||||||
module RPGEngine.Parse.Core where
|
module RPGEngine.Parse.Core
|
||||||
|
( parseWith
|
||||||
|
, parseWithRest
|
||||||
|
, ignoreWS
|
||||||
|
) where
|
||||||
|
|
||||||
import Text.Parsec
|
import Text.Parsec
|
||||||
import Text.Parsec.String
|
( ParseError,
|
||||||
|
anyChar,
|
||||||
|
endOfLine,
|
||||||
|
spaces,
|
||||||
|
string,
|
||||||
|
anyToken,
|
||||||
|
choice,
|
||||||
|
eof,
|
||||||
|
manyTill,
|
||||||
|
parse )
|
||||||
|
import Text.Parsec.String ( Parser )
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
-- A wrapper, which takes a parser and some input and returns a
|
-- A wrapper, which takes a parser and some input and returns a
|
||||||
-- parsed output.
|
-- parsed output.
|
||||||
|
@ -14,7 +30,7 @@ parseWithRest :: Parser a -> String -> Either ParseError (a, String)
|
||||||
parseWithRest parser = parse ((,) <$> parser <*> rest) ""
|
parseWithRest parser = parse ((,) <$> parser <*> rest) ""
|
||||||
where rest = manyTill anyToken eof
|
where rest = manyTill anyToken eof
|
||||||
|
|
||||||
-- Ignore all kinds of whitespaces
|
-- Ignore all kinds of whitespace
|
||||||
ignoreWS :: Parser a -> Parser a
|
ignoreWS :: Parser a -> Parser a
|
||||||
ignoreWS parser = choice [skipComment, spaces] >> parser
|
ignoreWS parser = choice [skipComment, spaces] >> parser
|
||||||
where skipComment = do{ string "#"; manyTill anyChar endOfLine; return ()}
|
where skipComment = do{ string "#"; manyTill anyChar endOfLine; return ()}
|
|
@ -1,101 +0,0 @@
|
||||||
module RPGEngine.Parse.Game where
|
|
||||||
|
|
||||||
import RPGEngine.Data
|
|
||||||
import RPGEngine.Data.Defaults
|
|
||||||
import RPGEngine.Parse.StructElement
|
|
||||||
|
|
||||||
-------------------------------- Game --------------------------------
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
structToGame :: StructElement -> Game
|
|
||||||
structToGame = undefined
|
|
||||||
|
|
||||||
------------------------------- Player -------------------------------
|
|
||||||
|
|
||||||
structToPlayer :: StructElement -> Player
|
|
||||||
structToPlayer (Block block) = structToPlayer' block defaultPlayer
|
|
||||||
structToPlayer _ = defaultPlayer
|
|
||||||
|
|
||||||
structToPlayer' :: [StructElement] -> Player -> Player
|
|
||||||
structToPlayer' [] p = p
|
|
||||||
structToPlayer' ((Entry(Tag "hp") val ):es) p = (structToPlayer' es p){ playerHp = structToMaybeInt val }
|
|
||||||
structToPlayer' ((Entry(Tag "inventory") (Block inv)):es) p = (structToPlayer' es p){ inventory = structToItems inv }
|
|
||||||
structToPlayer' _ _ = defaultPlayer
|
|
||||||
|
|
||||||
structToActions :: StructElement -> [([Condition], Action)]
|
|
||||||
structToActions (Block []) = []
|
|
||||||
structToActions (Block block) = structToActions' block []
|
|
||||||
structToActions _ = []
|
|
||||||
|
|
||||||
structToActions' :: [StructElement] -> [([Condition], Action)] -> [([Condition], Action)]
|
|
||||||
structToActions' [] list = list
|
|
||||||
structToActions' ((Entry(ConditionList cs) (Regular (Action a))):as) list = structToActions' as ((cs, a):list)
|
|
||||||
structToActions' _ list = list
|
|
||||||
|
|
||||||
------------------------------- Levels -------------------------------
|
|
||||||
|
|
||||||
structToLevels :: StructElement -> [Level]
|
|
||||||
structToLevels (Block struct) = structToLevel <$> struct
|
|
||||||
structToLevels _ = [defaultLevel]
|
|
||||||
|
|
||||||
structToLevel :: StructElement -> Level
|
|
||||||
structToLevel (Block entries) = structToLevel' entries defaultLevel
|
|
||||||
structToLevel _ = defaultLevel
|
|
||||||
|
|
||||||
structToLevel' :: [StructElement] -> Level -> Level
|
|
||||||
structToLevel' ((Entry(Tag "layout") (Regular (Layout layout))):ls) l = (structToLevel' ls l){ RPGEngine.Data.layout = layout }
|
|
||||||
structToLevel' ((Entry(Tag "items") (Block items) ):ls) l = (structToLevel' ls l){ items = structToItems items }
|
|
||||||
structToLevel' ((Entry(Tag "entities") (Block entities) ):ls) l = (structToLevel' ls l){ entities = structToEntities entities }
|
|
||||||
structToLevel' _ _ = defaultLevel
|
|
||||||
|
|
||||||
------------------------------- Items --------------------------------
|
|
||||||
|
|
||||||
structToItems :: [StructElement] -> [Item]
|
|
||||||
structToItems items = structToItem <$> items
|
|
||||||
|
|
||||||
structToItem :: StructElement -> Item
|
|
||||||
structToItem (Block block) = structToItem' block defaultItem
|
|
||||||
structToItem _ = defaultItem
|
|
||||||
|
|
||||||
structToItem' :: [StructElement] -> Item -> Item
|
|
||||||
structToItem' [] i = i
|
|
||||||
structToItem' ((Entry(Tag "id") (Regular(String id ))):is) i = (structToItem' is i){ itemId = id }
|
|
||||||
structToItem' ((Entry(Tag "x") (Regular(Integer x ))):is) i = (structToItem' is i){ itemX = x }
|
|
||||||
structToItem' ((Entry(Tag "y") (Regular(Integer y ))):is) i = (structToItem' is i){ itemY = y }
|
|
||||||
structToItem' ((Entry(Tag "name") (Regular(String name))):is) i = (structToItem' is i){ itemName = name }
|
|
||||||
structToItem' ((Entry(Tag "description") (Regular(String desc))):is) i = (structToItem' is i){ itemDescription = desc }
|
|
||||||
structToItem' ((Entry(Tag "value") val ):is) i = (structToItem' is i){ itemValue = structToMaybeInt val }
|
|
||||||
structToItem' ((Entry(Tag "actions") actions ):is) i = (structToItem' is i){ itemActions = structToActions actions }
|
|
||||||
structToItem' ((Entry (Tag "useTimes") useTimes ):is) i = (structToItem' is i){ useTimes = structToMaybeInt useTimes }
|
|
||||||
structToItem' _ _ = defaultItem
|
|
||||||
|
|
||||||
------------------------------ Entities ------------------------------
|
|
||||||
|
|
||||||
structToEntities :: [StructElement] -> [Entity]
|
|
||||||
structToEntities entities = structToEntity <$> entities
|
|
||||||
|
|
||||||
structToEntity :: StructElement -> Entity
|
|
||||||
structToEntity (Block block) = structToEntity' block defaultEntity
|
|
||||||
structToEntity _ = defaultEntity
|
|
||||||
|
|
||||||
structToEntity' :: [StructElement] -> Entity -> Entity
|
|
||||||
structToEntity' [] e = e
|
|
||||||
structToEntity' ((Entry(Tag "id") (Regular(String id )) ):es) e = (structToEntity' es e){ entityId = id }
|
|
||||||
structToEntity' ((Entry(Tag "x") (Regular(Integer x )) ):es) e = (structToEntity' es e){ entityX = x }
|
|
||||||
structToEntity' ((Entry(Tag "y") (Regular(Integer y )) ):es) e = (structToEntity' es e){ entityY = y }
|
|
||||||
structToEntity' ((Entry(Tag "name") (Regular(String name)) ):es) e = (structToEntity' es e){ entityName = name }
|
|
||||||
structToEntity' ((Entry(Tag "description") (Regular(String desc)) ):es) e = (structToEntity' es e){ entityDescription = desc }
|
|
||||||
structToEntity' ((Entry(Tag "actions") actions ):es) e = (structToEntity' es e){ entityActions = structToActions actions }
|
|
||||||
structToEntity' ((Entry(Tag "value") val ):es) e = (structToEntity' es e){ entityValue = structToMaybeInt val }
|
|
||||||
structToEntity' ((Entry(Tag "hp") val ):es) e = (structToEntity' es e){ entityHp = structToMaybeInt val }
|
|
||||||
structToEntity' ((Entry(Tag "direction") (Regular(Direction dir))):es) e = (structToEntity' es e){ RPGEngine.Data.direction = dir }
|
|
||||||
structToEntity' _ _ = defaultEntity
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
structToMaybeInt :: StructElement -> Maybe Int
|
|
||||||
structToMaybeInt (Regular (Integer val)) = Just val
|
|
||||||
structToMaybeInt (Regular Infinite) = Prelude.Nothing
|
|
||||||
structToMaybeInt _ = Prelude.Nothing -- TODO
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
120
lib/RPGEngine/Parse/StructureToGame.hs
Normal file
120
lib/RPGEngine/Parse/StructureToGame.hs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
module RPGEngine.Parse.StructureToGame
|
||||||
|
-- Everything is exported for testing
|
||||||
|
where
|
||||||
|
|
||||||
|
import RPGEngine.Data
|
||||||
|
( Action,
|
||||||
|
Condition,
|
||||||
|
Player(playerHp, inventory),
|
||||||
|
Entity(entityId, entityX, entityY, entityName, entityDescription,
|
||||||
|
entityActions, entityValue, entityHp, direction),
|
||||||
|
Item(itemId, itemX, itemY, itemName, itemDescription, itemValue,
|
||||||
|
itemActions, useTimes),
|
||||||
|
Level(layout, items, entities),
|
||||||
|
Game (..), State (..), StateBase (..) )
|
||||||
|
import RPGEngine.Parse.TextToStructure
|
||||||
|
( Value(Infinite, Action, Layout, String, Direction, Integer),
|
||||||
|
Key(Tag, ConditionList),
|
||||||
|
Structure(..) )
|
||||||
|
import RPGEngine.Data.Default (defaultPlayer, defaultLevel, defaultItem, defaultEntity)
|
||||||
|
import RPGEngine.Render.Playing (renderPlaying)
|
||||||
|
import RPGEngine.Input.Playing (handleInputPlaying)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
structureToGame :: Structure -> Game
|
||||||
|
structureToGame (Block [(Entry(Tag "player") playerBlock), (Entry(Tag "levels") levelsBlock)]) = game
|
||||||
|
where game = Game{ state = newState, levels = newLevels, player = newPlayer }
|
||||||
|
newState = Playing{ base = playingBase, level = currentLevel }
|
||||||
|
playingBase = StateBase{ renderer = renderPlaying, inputHandler = handleInputPlaying }
|
||||||
|
newLevels = structureToLevels levelsBlock
|
||||||
|
currentLevel = head newLevels
|
||||||
|
newPlayer = structureToPlayer playerBlock
|
||||||
|
|
||||||
|
------------------------------- Player -------------------------------
|
||||||
|
|
||||||
|
structureToPlayer :: Structure -> Player
|
||||||
|
structureToPlayer (Block block) = structureToPlayer' block defaultPlayer
|
||||||
|
structureToPlayer _ = defaultPlayer
|
||||||
|
|
||||||
|
structureToPlayer' :: [Structure] -> Player -> Player
|
||||||
|
structureToPlayer' [] p = p
|
||||||
|
structureToPlayer' ((Entry(Tag "hp") val ):es) p = (structureToPlayer' es p){ playerHp = structureToMaybeInt val }
|
||||||
|
structureToPlayer' ((Entry(Tag "inventory") (Block inv)):es) p = (structureToPlayer' es p){ inventory = structureToItems inv }
|
||||||
|
structureToPlayer' _ _ = defaultPlayer
|
||||||
|
|
||||||
|
structureToActions :: Structure -> [([Condition], Action)]
|
||||||
|
structureToActions (Block []) = []
|
||||||
|
structureToActions (Block block) = structureToActions' block []
|
||||||
|
structureToActions _ = []
|
||||||
|
|
||||||
|
structureToActions' :: [Structure] -> [([Condition], Action)] -> [([Condition], Action)]
|
||||||
|
structureToActions' [] list = list
|
||||||
|
structureToActions' ((Entry(ConditionList cs) (Regular (Action a))):as) list = structureToActions' as ((cs, a):list)
|
||||||
|
structureToActions' _ list = list
|
||||||
|
|
||||||
|
------------------------------- Levels -------------------------------
|
||||||
|
|
||||||
|
structureToLevels :: Structure -> [Level]
|
||||||
|
structureToLevels (Block struct) = structureToLevel <$> struct
|
||||||
|
structureToLevels _ = [defaultLevel]
|
||||||
|
|
||||||
|
structureToLevel :: Structure -> Level
|
||||||
|
structureToLevel (Block entries) = structureToLevel' entries defaultLevel
|
||||||
|
structureToLevel _ = defaultLevel
|
||||||
|
|
||||||
|
structureToLevel' :: [Structure] -> Level -> Level
|
||||||
|
structureToLevel' ((Entry(Tag "layout") (Regular (Layout layout))):ls) l = (structureToLevel' ls l){ RPGEngine.Data.layout = layout }
|
||||||
|
structureToLevel' ((Entry(Tag "items") (Block items) ):ls) l = (structureToLevel' ls l){ items = structureToItems items }
|
||||||
|
structureToLevel' ((Entry(Tag "entities") (Block entities) ):ls) l = (structureToLevel' ls l){ entities = structureToEntities entities }
|
||||||
|
structureToLevel' _ _ = defaultLevel
|
||||||
|
|
||||||
|
------------------------------- Items --------------------------------
|
||||||
|
|
||||||
|
structureToItems :: [Structure] -> [Item]
|
||||||
|
structureToItems items = structureToItem <$> items
|
||||||
|
|
||||||
|
structureToItem :: Structure -> Item
|
||||||
|
structureToItem (Block block) = structureToItem' block defaultItem
|
||||||
|
structureToItem _ = defaultItem
|
||||||
|
|
||||||
|
structureToItem' :: [Structure] -> Item -> Item
|
||||||
|
structureToItem' [] i = i
|
||||||
|
structureToItem' ((Entry(Tag "id") (Regular(String id ))):is) i = (structureToItem' is i){ itemId = id }
|
||||||
|
structureToItem' ((Entry(Tag "x") (Regular(Integer x ))):is) i = (structureToItem' is i){ itemX = x }
|
||||||
|
structureToItem' ((Entry(Tag "y") (Regular(Integer y ))):is) i = (structureToItem' is i){ itemY = y }
|
||||||
|
structureToItem' ((Entry(Tag "name") (Regular(String name))):is) i = (structureToItem' is i){ itemName = name }
|
||||||
|
structureToItem' ((Entry(Tag "description") (Regular(String desc))):is) i = (structureToItem' is i){ itemDescription = desc }
|
||||||
|
structureToItem' ((Entry(Tag "value") val ):is) i = (structureToItem' is i){ itemValue = structureToMaybeInt val }
|
||||||
|
structureToItem' ((Entry(Tag "actions") actions ):is) i = (structureToItem' is i){ itemActions = structureToActions actions }
|
||||||
|
structureToItem' ((Entry (Tag "useTimes") useTimes ):is) i = (structureToItem' is i){ useTimes = structureToMaybeInt useTimes }
|
||||||
|
structureToItem' _ _ = defaultItem
|
||||||
|
|
||||||
|
------------------------------ Entities ------------------------------
|
||||||
|
|
||||||
|
structureToEntities :: [Structure] -> [Entity]
|
||||||
|
structureToEntities entities = structureToEntity <$> entities
|
||||||
|
|
||||||
|
structureToEntity :: Structure -> Entity
|
||||||
|
structureToEntity (Block block) = structureToEntity' block defaultEntity
|
||||||
|
structureToEntity _ = defaultEntity
|
||||||
|
|
||||||
|
structureToEntity' :: [Structure] -> Entity -> Entity
|
||||||
|
structureToEntity' [] e = e
|
||||||
|
structureToEntity' ((Entry(Tag "id") (Regular(String id )) ):es) e = (structureToEntity' es e){ entityId = id }
|
||||||
|
structureToEntity' ((Entry(Tag "x") (Regular(Integer x )) ):es) e = (structureToEntity' es e){ entityX = x }
|
||||||
|
structureToEntity' ((Entry(Tag "y") (Regular(Integer y )) ):es) e = (structureToEntity' es e){ entityY = y }
|
||||||
|
structureToEntity' ((Entry(Tag "name") (Regular(String name)) ):es) e = (structureToEntity' es e){ entityName = name }
|
||||||
|
structureToEntity' ((Entry(Tag "description") (Regular(String desc)) ):es) e = (structureToEntity' es e){ entityDescription = desc }
|
||||||
|
structureToEntity' ((Entry(Tag "actions") actions ):es) e = (structureToEntity' es e){ entityActions = structureToActions actions }
|
||||||
|
structureToEntity' ((Entry(Tag "value") val ):es) e = (structureToEntity' es e){ entityValue = structureToMaybeInt val }
|
||||||
|
structureToEntity' ((Entry(Tag "hp") val ):es) e = (structureToEntity' es e){ entityHp = structureToMaybeInt val }
|
||||||
|
structureToEntity' ((Entry(Tag "direction") (Regular(Direction dir))):es) e = (structureToEntity' es e){ RPGEngine.Data.direction = dir }
|
||||||
|
structureToEntity' _ _ = defaultEntity
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
structureToMaybeInt :: Structure -> Maybe Int
|
||||||
|
structureToMaybeInt (Regular (Integer val)) = Just val
|
||||||
|
structureToMaybeInt (Regular Infinite) = Prelude.Nothing
|
||||||
|
structureToMaybeInt _ = Prelude.Nothing -- TODO
|
|
@ -1,13 +1,14 @@
|
||||||
module RPGEngine.Parse.StructElement where
|
module RPGEngine.Parse.TextToStructure
|
||||||
|
-- Everything is exported for testing
|
||||||
|
where
|
||||||
|
|
||||||
import RPGEngine.Data (Action(..), Condition(..), Layout, Direction(..), Physical(..), Strip)
|
|
||||||
import RPGEngine.Parse.Core ( ignoreWS )
|
import RPGEngine.Parse.Core ( ignoreWS )
|
||||||
|
|
||||||
|
import RPGEngine.Data (Action (..), Condition (..), Direction (..), Layout, Strip, Physical (..))
|
||||||
|
|
||||||
import Text.Parsec
|
import Text.Parsec
|
||||||
( char,
|
( alphaNum,
|
||||||
many,
|
char,
|
||||||
try,
|
|
||||||
alphaNum,
|
|
||||||
digit,
|
digit,
|
||||||
noneOf,
|
noneOf,
|
||||||
oneOf,
|
oneOf,
|
||||||
|
@ -15,7 +16,9 @@ import Text.Parsec
|
||||||
choice,
|
choice,
|
||||||
many1,
|
many1,
|
||||||
notFollowedBy,
|
notFollowedBy,
|
||||||
sepBy )
|
sepBy,
|
||||||
|
many,
|
||||||
|
try )
|
||||||
import qualified Text.Parsec as P ( string )
|
import qualified Text.Parsec as P ( string )
|
||||||
import Text.Parsec.String ( Parser )
|
import Text.Parsec.String ( Parser )
|
||||||
|
|
||||||
|
@ -23,18 +26,18 @@ import Text.Parsec.String ( Parser )
|
||||||
|
|
||||||
-- See documentation for more details, only a short description is
|
-- See documentation for more details, only a short description is
|
||||||
-- provided here.
|
-- provided here.
|
||||||
data StructElement = Block [StructElement]
|
data Structure = Block [Structure]
|
||||||
| Entry Key StructElement -- Key + Value
|
| Entry Key Structure -- Key + Value
|
||||||
| Regular Value -- Regular value, Integer or String or Infinite
|
| Regular Value -- Regular value, Integer or String or Infinite
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
structElement :: Parser StructElement
|
structure :: Parser Structure
|
||||||
structElement = try $ choice [block, entry, regular]
|
structure = try $ choice [block, entry, regular]
|
||||||
|
|
||||||
-- A list of entries
|
-- A list of entries
|
||||||
block :: Parser StructElement
|
block :: Parser Structure
|
||||||
block = try $ do
|
block = try $ do
|
||||||
open <- ignoreWS $ oneOf openingBrackets
|
open <- ignoreWS $ oneOf openingBrackets
|
||||||
middle <- ignoreWS $ choice [entry, block] `sepBy` char ','
|
middle <- ignoreWS $ choice [entry, block] `sepBy` char ','
|
||||||
|
@ -42,15 +45,15 @@ block = try $ do
|
||||||
ignoreWS $ char closingBracket
|
ignoreWS $ char closingBracket
|
||||||
return $ Block middle
|
return $ Block middle
|
||||||
|
|
||||||
entry :: Parser StructElement
|
entry :: Parser Structure
|
||||||
entry = try $ do
|
entry = try $ do
|
||||||
key <- ignoreWS key
|
key <- ignoreWS key
|
||||||
-- TODO Fix this
|
-- TODO Fix this
|
||||||
oneOf ": " -- Can be left out
|
oneOf ": " -- Can be left out
|
||||||
value <- ignoreWS structElement
|
value <- ignoreWS structure
|
||||||
return $ Entry key value
|
return $ Entry key value
|
||||||
|
|
||||||
regular :: Parser StructElement
|
regular :: Parser Structure
|
||||||
regular = try $ Regular <$> value
|
regular = try $ Regular <$> value
|
||||||
|
|
||||||
--------------------------------- Key --------------------------------
|
--------------------------------- Key --------------------------------
|
||||||
|
@ -108,7 +111,7 @@ data Value = String String
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
value :: Parser Value
|
value :: Parser Value
|
||||||
value = choice [string, integer, infinite, action, direction]
|
value = choice [layout, string, integer, infinite, action, direction]
|
||||||
|
|
||||||
string :: Parser Value
|
string :: Parser Value
|
||||||
string = try $ String <$> between (char '\"') (char '\"') reading
|
string = try $ String <$> between (char '\"') (char '\"') reading
|
||||||
|
@ -134,7 +137,7 @@ action = try $ do
|
||||||
| script == "useItem" = UseItem arg
|
| script == "useItem" = UseItem arg
|
||||||
| script == "decreaseHp" = DecreaseHp first second
|
| script == "decreaseHp" = DecreaseHp first second
|
||||||
| script == "increasePlayerHp" = IncreasePlayerHp arg
|
| script == "increasePlayerHp" = IncreasePlayerHp arg
|
||||||
| otherwise = RPGEngine.Data.Nothing
|
| otherwise = DoNothing
|
||||||
(first, ',':second) = break (== ',') arg
|
(first, ',':second) = break (== ',') arg
|
||||||
return $ Action answer
|
return $ Action answer
|
||||||
|
|
||||||
|
@ -152,12 +155,15 @@ direction = try $ do
|
||||||
make "right" = East
|
make "right" = East
|
||||||
make "down" = South
|
make "down" = South
|
||||||
make "left" = West
|
make "left" = West
|
||||||
make _ = Center
|
make _ = Stay
|
||||||
|
|
||||||
layout :: Parser Value
|
layout :: Parser Value
|
||||||
layout = try $ do
|
layout = try $ do
|
||||||
|
open <- ignoreWS $ oneOf openingBrackets
|
||||||
ignoreWS $ char '|'
|
ignoreWS $ char '|'
|
||||||
list <- ignoreWS strip `sepBy` ignoreWS (char '|')
|
list <- ignoreWS $ ignoreWS strip `sepBy` ignoreWS (char '|')
|
||||||
|
let closing = getMatchingClosingBracket open
|
||||||
|
ignoreWS $ char closing
|
||||||
return $ Layout list
|
return $ Layout list
|
||||||
|
|
||||||
strip :: Parser Strip
|
strip :: Parser Strip
|
||||||
|
@ -180,7 +186,6 @@ physical = try $ do
|
||||||
make 'e' = Exit
|
make 'e' = Exit
|
||||||
make _ = Void
|
make _ = Void
|
||||||
|
|
||||||
|
|
||||||
------------------------------ Brackets ------------------------------
|
------------------------------ Brackets ------------------------------
|
||||||
|
|
||||||
openingBrackets :: [Char]
|
openingBrackets :: [Char]
|
|
@ -1,38 +1,21 @@
|
||||||
-- Allows to render the played game
|
-- Implementation for each state can be found in their respective
|
||||||
|
-- submodules.
|
||||||
module RPGEngine.Render
|
module RPGEngine.Render
|
||||||
( initWindow
|
( initWindow
|
||||||
, bgColor
|
, initGame
|
||||||
|
|
||||||
, render
|
, render
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import RPGEngine.Data
|
import RPGEngine.Render.Core ( Renderer(..) )
|
||||||
( State(..),
|
|
||||||
Game(..), Player (..) )
|
|
||||||
import RPGEngine.Render.Level
|
|
||||||
( renderLevel )
|
|
||||||
import Graphics.Gloss
|
|
||||||
( white,
|
|
||||||
pictures,
|
|
||||||
text,
|
|
||||||
Display(InWindow),
|
|
||||||
Color,
|
|
||||||
Picture,
|
|
||||||
scale,
|
|
||||||
translate )
|
|
||||||
import RPGEngine.Render.Player (renderPlayer, focusPlayer)
|
|
||||||
import RPGEngine.Render.GUI (renderGUI)
|
|
||||||
import Graphics.Gloss.Data.Picture (color)
|
|
||||||
import RPGEngine.Render.Core (overlay)
|
|
||||||
import RPGEngine.Input.LvlSelect (getLvlList)
|
|
||||||
import RPGEngine.Render.LvlSelect (renderLvlList)
|
|
||||||
|
|
||||||
----------------------------- Constants ------------------------------
|
import RPGEngine.Data ( State(..), Game(..), StateBase(..) )
|
||||||
|
import Graphics.Gloss ( Display )
|
||||||
-- Game background color
|
import Graphics.Gloss.Data.Display ( Display(InWindow) )
|
||||||
bgColor :: Color
|
import Graphics.Gloss.Data.Picture (Picture)
|
||||||
bgColor = white
|
import RPGEngine.Data.Default (defaultLevel, defaultPlayer)
|
||||||
|
import RPGEngine.Input.Playing (spawnPlayer)
|
||||||
|
import RPGEngine.Render.Menu (renderMenu)
|
||||||
|
import RPGEngine.Input.Menu (handleInputMenu)
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -40,43 +23,16 @@ bgColor = white
|
||||||
initWindow :: String -> (Int, Int) -> (Int, Int) -> Display
|
initWindow :: String -> (Int, Int) -> (Int, Int) -> Display
|
||||||
initWindow = InWindow
|
initWindow = InWindow
|
||||||
|
|
||||||
-- Render the game
|
-- Initialize the game
|
||||||
|
initGame :: Game
|
||||||
|
initGame = Game {
|
||||||
|
state = Menu{ base = StateBase{ renderer = renderMenu, inputHandler = handleInputMenu }},
|
||||||
|
levels = [defaultLevel],
|
||||||
|
player = spawnPlayer defaultLevel defaultPlayer
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Render all different states
|
||||||
render :: Game -> Picture
|
render :: Game -> Picture
|
||||||
render g@Game{ state = Menu } = renderMenu g
|
render g@Game{ state = state } = renderFunc g
|
||||||
render g@Game{ state = LvlSelect } = renderLevelSelection g
|
where stateBase = base state
|
||||||
render g@Game{ state = Playing } = renderPlaying g
|
renderFunc = renderer stateBase
|
||||||
render g@Game{ state = Pause } = renderPause g
|
|
||||||
render g@Game{ state = Win } = renderWin g
|
|
||||||
render g@Game{ state = Lose } = renderLose g
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
renderMenu :: Game -> Picture
|
|
||||||
renderMenu _ = text "[Press any key to start]"
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
renderLevelSelection :: Game -> Picture
|
|
||||||
renderLevelSelection _ = renderLvlList getLvlList
|
|
||||||
|
|
||||||
renderPlaying :: Game -> Picture
|
|
||||||
renderPlaying g@Game{ playing = lvl, player = player } = pictures [
|
|
||||||
renderLevel lvl,
|
|
||||||
renderPlayer player,
|
|
||||||
renderGUI g
|
|
||||||
]
|
|
||||||
|
|
||||||
renderPause :: Game -> Picture
|
|
||||||
renderPause g = pictures [renderPlaying g, pause]
|
|
||||||
where pause = pictures [
|
|
||||||
overlay,
|
|
||||||
color white $ scale 0.5 0.5 $ text "[Press any key to continue]"
|
|
||||||
]
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
renderWin :: Game -> Picture
|
|
||||||
renderWin _ = text "Win"
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
renderLose :: Game -> Picture
|
|
||||||
renderLose _ = text "Lose"
|
|
|
@ -1,24 +1,21 @@
|
||||||
module RPGEngine.Render.Core where
|
module RPGEngine.Render.Core
|
||||||
|
( Renderer
|
||||||
|
|
||||||
import Graphics.Gloss ( Picture, translate, pictures )
|
, getRender
|
||||||
import GHC.IO (unsafePerformIO)
|
, setRenderPos
|
||||||
import Graphics.Gloss.Juicy (loadJuicyPNG)
|
, overlay
|
||||||
import Data.Maybe (fromJust)
|
) where
|
||||||
import Graphics.Gloss.Data.Picture (scale)
|
|
||||||
import Graphics.Gloss.Data.Bitmap (BitmapData(..))
|
import RPGEngine.Config
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import Graphics.Gloss
|
||||||
|
import GHC.IO
|
||||||
|
import Graphics.Gloss.Juicy
|
||||||
|
|
||||||
----------------------------- Constants ------------------------------
|
----------------------------- Constants ------------------------------
|
||||||
|
|
||||||
-- Default scale
|
type Renderer a = a -> Picture
|
||||||
zoom :: Float
|
|
||||||
zoom = 5.0
|
|
||||||
|
|
||||||
-- Resolution of the texture
|
|
||||||
resolution :: Float
|
|
||||||
resolution = 16
|
|
||||||
|
|
||||||
assetsFolder :: FilePath
|
|
||||||
assetsFolder = "assets/"
|
|
||||||
|
|
||||||
unknownImage :: FilePath
|
unknownImage :: FilePath
|
||||||
unknownImage = "unknown.png"
|
unknownImage = "unknown.png"
|
||||||
|
@ -54,11 +51,7 @@ library = unknown:entities ++ environment ++ gui ++ items
|
||||||
gui = []
|
gui = []
|
||||||
items = map (\(f, s) -> (f, renderPNG (assetsFolder ++ "items/" ++ s))) allItems
|
items = map (\(f, s) -> (f, renderPNG (assetsFolder ++ "items/" ++ s))) allItems
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
-- Turn a path to a .png file into a Picture.
|
|
||||||
renderPNG :: FilePath -> Picture
|
|
||||||
renderPNG path = scale zoom zoom $ fromJust $ unsafePerformIO $ loadJuicyPNG path
|
|
||||||
|
|
||||||
-- Retrieve an image from the library. If the library does not contain
|
-- Retrieve an image from the library. If the library does not contain
|
||||||
-- the requested image, a default is returned.
|
-- the requested image, a default is returned.
|
||||||
|
@ -83,3 +76,9 @@ overlay = setRenderPos offX offY $ pictures voids
|
||||||
width = round $ 7680 / resolution / zoom
|
width = round $ 7680 / resolution / zoom
|
||||||
offX = negate (width `div` 2)
|
offX = negate (width `div` 2)
|
||||||
offY = negate (height `div` 2)
|
offY = negate (height `div` 2)
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Turn a path to a .png file into a Picture.
|
||||||
|
renderPNG :: FilePath -> Picture
|
||||||
|
renderPNG path = scale zoom zoom $ fromJust $ unsafePerformIO $ loadJuicyPNG path
|
|
@ -1,10 +0,0 @@
|
||||||
module RPGEngine.Render.GUI
|
|
||||||
( renderGUI
|
|
||||||
) where
|
|
||||||
|
|
||||||
import RPGEngine.Data (Game)
|
|
||||||
import Graphics.Gloss (Picture, blank)
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
renderGUI :: Game -> Picture
|
|
||||||
renderGUI _ = blank
|
|
33
lib/RPGEngine/Render/LevelSelection.hs
Normal file
33
lib/RPGEngine/Render/LevelSelection.hs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module RPGEngine.Render.LevelSelection
|
||||||
|
( renderLevelSelection
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Config ( resolution, zoom )
|
||||||
|
import RPGEngine.Data ( Game (..), State (..) )
|
||||||
|
import RPGEngine.Data.Level ( getLevelList )
|
||||||
|
import RPGEngine.Render.Core ( Renderer )
|
||||||
|
|
||||||
|
import Graphics.Gloss
|
||||||
|
( pictures, text, translate, blank, Picture, color )
|
||||||
|
import Graphics.Gloss.Data.Picture (scale)
|
||||||
|
import RPGEngine.Input.Core (ListSelector (..))
|
||||||
|
import Graphics.Gloss.Data.Color (red)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
renderLevelSelection :: Renderer Game
|
||||||
|
renderLevelSelection Game{ state = state } = result
|
||||||
|
where result = renderLevelList state
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
renderLevelList :: Renderer State
|
||||||
|
renderLevelList LevelSelection{ levelList = list, selector = selector } = everything
|
||||||
|
where everything = pictures $ map render entries
|
||||||
|
sel = selection selector
|
||||||
|
entries = zip [0::Int .. ] list
|
||||||
|
render (i, path) | i == sel = color red $ scale zoomed zoomed $ translate 0 (offset i) $ text path
|
||||||
|
| otherwise = scale zoomed zoomed $ translate 0 (offset i) $ text path
|
||||||
|
zoomed = 0.1 * zoom
|
||||||
|
offset i = negate (2 * resolution * zoom * fromIntegral i)
|
||||||
|
renderLevelList _ = blank
|
14
lib/RPGEngine/Render/Lose.hs
Normal file
14
lib/RPGEngine/Render/Lose.hs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module RPGEngine.Render.Lose
|
||||||
|
( renderLose
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Render.Core ( Renderer )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
import Graphics.Gloss ( text )
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
renderLose :: Renderer Game
|
||||||
|
renderLose _ = text "Win"
|
|
@ -1,15 +0,0 @@
|
||||||
module RPGEngine.Render.LvlSelect
|
|
||||||
( renderLvlList
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Graphics.Gloss ( Picture, pictures, translate, scale )
|
|
||||||
import Graphics.Gloss.Data.Picture (blank, text)
|
|
||||||
import RPGEngine.Render.Core (resolution, zoom)
|
|
||||||
|
|
||||||
-- Render all level names, under each other.
|
|
||||||
renderLvlList :: [FilePath] -> Picture
|
|
||||||
renderLvlList list = pictures $ map render entries
|
|
||||||
where entries = zip [0::Int .. ] list
|
|
||||||
render (i, path) = scale zoomed zoomed $ translate 0 (offset i) $ text path
|
|
||||||
zoomed = 0.1 * zoom
|
|
||||||
offset i = negate (2 * resolution * zoom * fromIntegral i)
|
|
14
lib/RPGEngine/Render/Menu.hs
Normal file
14
lib/RPGEngine/Render/Menu.hs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module RPGEngine.Render.Menu
|
||||||
|
( renderMenu
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Render.Core ( Renderer )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
import Graphics.Gloss (text)
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
renderMenu :: Renderer Game
|
||||||
|
renderMenu _ = text "[Press any key to start]"
|
20
lib/RPGEngine/Render/Paused.hs
Normal file
20
lib/RPGEngine/Render/Paused.hs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module RPGEngine.Render.Paused
|
||||||
|
( renderPaused
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Render.Core ( Renderer, overlay )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
import Graphics.Gloss ( pictures, scale, text )
|
||||||
|
import RPGEngine.Render.Playing ( renderPlaying )
|
||||||
|
import Graphics.Gloss.Data.Picture (color)
|
||||||
|
import Graphics.Gloss.Data.Color (white)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
renderPaused :: Renderer Game
|
||||||
|
renderPaused g = pictures [renderPlaying g, pause]
|
||||||
|
where pause = pictures [
|
||||||
|
overlay,
|
||||||
|
color white $ scale 0.5 0.5 $ text "[Press any key to continue]"
|
||||||
|
]
|
|
@ -1,17 +0,0 @@
|
||||||
module RPGEngine.Render.Player
|
|
||||||
( renderPlayer
|
|
||||||
, focusPlayer
|
|
||||||
) where
|
|
||||||
|
|
||||||
import RPGEngine.Data (Player(..), Game(..))
|
|
||||||
import Graphics.Gloss (Picture, text)
|
|
||||||
import RPGEngine.Render.Core (getRender, setRenderPos, zoom, resolution)
|
|
||||||
import Graphics.Gloss.Data.Picture (translate)
|
|
||||||
|
|
||||||
renderPlayer :: Player -> Picture
|
|
||||||
renderPlayer Player{ position = (x, y) } = setRenderPos x y $ getRender "player"
|
|
||||||
|
|
||||||
focusPlayer :: Game -> Picture -> Picture
|
|
||||||
focusPlayer Game{ player = Player{ position = (x, y)}} = translate centerX centerY
|
|
||||||
where centerX = resolution * zoom * fromIntegral (negate x)
|
|
||||||
centerY = resolution * zoom * fromIntegral (negate y)
|
|
|
@ -1,12 +1,48 @@
|
||||||
module RPGEngine.Render.Level
|
module RPGEngine.Render.Playing
|
||||||
( renderLevel
|
( renderPlaying
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Graphics.Gloss
|
import RPGEngine.Render.Core ( Renderer, getRender, setRenderPos )
|
||||||
import RPGEngine.Data
|
|
||||||
import RPGEngine.Render.Core (getRender, setRenderPos, zoom, resolution)
|
|
||||||
|
|
||||||
renderLevel :: Level -> Picture
|
import RPGEngine.Data
|
||||||
|
( Player(..),
|
||||||
|
Entity(..),
|
||||||
|
Item(..),
|
||||||
|
Physical(..),
|
||||||
|
Layout,
|
||||||
|
Level(..),
|
||||||
|
State(..),
|
||||||
|
Game(..) )
|
||||||
|
import Graphics.Gloss ( Picture, pictures )
|
||||||
|
import Graphics.Gloss.Data.Picture (translate)
|
||||||
|
import RPGEngine.Config (resolution, zoom)
|
||||||
|
|
||||||
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
renderPlaying :: Renderer Game
|
||||||
|
renderPlaying g@Game{ state = Playing { level = lvl }, player = player } = pictures [
|
||||||
|
renderLevel lvl,
|
||||||
|
renderPlayer player
|
||||||
|
]
|
||||||
|
|
||||||
|
------------------------------- Player -------------------------------
|
||||||
|
|
||||||
|
renderPlayer :: Renderer Player
|
||||||
|
renderPlayer Player{ position = (x, y) } = move picture
|
||||||
|
where move = setRenderPos x y
|
||||||
|
picture = getRender "player"
|
||||||
|
|
||||||
|
-- Center the player in the middle of the screen.
|
||||||
|
-- Not in use at the moment, might be useful later.
|
||||||
|
focusPlayer :: Game -> Picture -> Picture
|
||||||
|
focusPlayer Game{ player = Player{ position = (x, y)}} = move
|
||||||
|
where move = translate centerX centerY
|
||||||
|
centerX = resolution * zoom * fromIntegral (negate x)
|
||||||
|
centerY = resolution * zoom * fromIntegral (negate y)
|
||||||
|
|
||||||
|
------------------------------- Level --------------------------------
|
||||||
|
|
||||||
|
renderLevel :: Renderer Level
|
||||||
renderLevel Level{ layout = l, items = i, entities = e } = level
|
renderLevel Level{ layout = l, items = i, entities = e } = level
|
||||||
where level = pictures [void, layout, items, entities]
|
where level = pictures [void, layout, items, entities]
|
||||||
void = createVoid
|
void = createVoid
|
||||||
|
@ -28,6 +64,18 @@ renderStrip list = pictures physicals
|
||||||
image Exit = pictures [getRender "tile", getRender "exit"]
|
image Exit = pictures [getRender "tile", getRender "exit"]
|
||||||
count = length list - 1
|
count = length list - 1
|
||||||
|
|
||||||
|
createVoid :: Picture
|
||||||
|
createVoid = setRenderPos offX offY $ pictures voids
|
||||||
|
where voids = [setRenderPos x y void | x <- [0 .. width], y <- [0 .. height]]
|
||||||
|
void = getRender "void"
|
||||||
|
intZoom = round zoom :: Int
|
||||||
|
height = round $ 4320 / resolution / zoom
|
||||||
|
width = round $ 7680 / resolution / zoom
|
||||||
|
offX = negate (width `div` 2)
|
||||||
|
offY = negate (height `div` 2)
|
||||||
|
|
||||||
|
-------------------------- Items & Entities --------------------------
|
||||||
|
|
||||||
renderItems :: [Item] -> Picture
|
renderItems :: [Item] -> Picture
|
||||||
renderItems list = pictures $ map renderItem list
|
renderItems list = pictures $ map renderItem list
|
||||||
|
|
||||||
|
@ -41,13 +89,3 @@ renderEntities list = pictures $ map renderEntity list
|
||||||
renderEntity :: Entity -> Picture
|
renderEntity :: Entity -> Picture
|
||||||
renderEntity Entity{ entityId = id, entityX = x, entityY = y} = setRenderPos x y image
|
renderEntity Entity{ entityId = id, entityX = x, entityY = y} = setRenderPos x y image
|
||||||
where image = getRender id
|
where image = getRender id
|
||||||
|
|
||||||
createVoid :: Picture
|
|
||||||
createVoid = setRenderPos offX offY $ pictures voids
|
|
||||||
where voids = [setRenderPos x y void | x <- [0 .. width], y <- [0 .. height]]
|
|
||||||
void = getRender "void"
|
|
||||||
intZoom = round zoom :: Int
|
|
||||||
height = round $ 4320 / resolution / zoom
|
|
||||||
width = round $ 7680 / resolution / zoom
|
|
||||||
offX = negate (width `div` 2)
|
|
||||||
offY = negate (height `div` 2)
|
|
14
lib/RPGEngine/Render/Win.hs
Normal file
14
lib/RPGEngine/Render/Win.hs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
module RPGEngine.Render.Win
|
||||||
|
( renderWin
|
||||||
|
) where
|
||||||
|
|
||||||
|
import RPGEngine.Render.Core ( Renderer )
|
||||||
|
|
||||||
|
import RPGEngine.Data ( Game )
|
||||||
|
import Graphics.Gloss (text)
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
renderWin :: Renderer Game
|
||||||
|
renderWin _ = text "Win"
|
|
@ -14,27 +14,35 @@ library
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
RPGEngine
|
RPGEngine
|
||||||
|
|
||||||
|
RPGEngine.Config
|
||||||
|
|
||||||
RPGEngine.Data
|
RPGEngine.Data
|
||||||
RPGEngine.Data.Defaults
|
RPGEngine.Data.Default
|
||||||
RPGEngine.Data.State
|
RPGEngine.Data.Game
|
||||||
|
RPGEngine.Data.Level
|
||||||
|
|
||||||
RPGEngine.Input
|
RPGEngine.Input
|
||||||
RPGEngine.Input.Core
|
RPGEngine.Input.Core
|
||||||
RPGEngine.Input.Level
|
RPGEngine.Input.LevelSelection
|
||||||
RPGEngine.Input.LvlSelect
|
RPGEngine.Input.Lose
|
||||||
RPGEngine.Input.Player
|
RPGEngine.Input.Menu
|
||||||
|
RPGEngine.Input.Paused
|
||||||
|
RPGEngine.Input.Playing
|
||||||
|
RPGEngine.Input.Win
|
||||||
|
|
||||||
RPGEngine.Parse
|
RPGEngine.Parse
|
||||||
RPGEngine.Parse.Core
|
RPGEngine.Parse.Core
|
||||||
RPGEngine.Parse.Game
|
RPGEngine.Parse.TextToStructure
|
||||||
RPGEngine.Parse.StructElement
|
RPGEngine.Parse.StructureToGame
|
||||||
|
|
||||||
RPGEngine.Render
|
RPGEngine.Render
|
||||||
RPGEngine.Render.Core
|
RPGEngine.Render.Core
|
||||||
RPGEngine.Render.GUI
|
RPGEngine.Render.LevelSelection
|
||||||
RPGEngine.Render.Level
|
RPGEngine.Render.Lose
|
||||||
RPGEngine.Render.LvlSelect
|
RPGEngine.Render.Menu
|
||||||
RPGEngine.Render.Player
|
RPGEngine.Render.Paused
|
||||||
|
RPGEngine.Render.Playing
|
||||||
|
RPGEngine.Render.Win
|
||||||
|
|
||||||
executable rpg-engine
|
executable rpg-engine
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
|
@ -44,10 +52,10 @@ executable rpg-engine
|
||||||
|
|
||||||
test-suite rpg-engine-test
|
test-suite rpg-engine-test
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
main-is: RPGEngineSpec.hs
|
main-is: Spec.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
build-depends: base >=4.7 && <5, hspec <= 2.10.6, hspec-discover, rpg-engine
|
build-depends: base >=4.7 && <5, hspec <= 2.10.6, hspec-discover, rpg-engine
|
||||||
other-modules:
|
other-modules:
|
||||||
-- Parsing
|
Parser.GameSpec
|
||||||
ParseGameSpec, ParseStructElementSpec
|
Parser.StructureSpec
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
module ParseGameSpec where
|
module Parser.GameSpec where
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import RPGEngine.Parse.StructElement
|
|
||||||
import RPGEngine.Data
|
import RPGEngine.Data
|
||||||
import RPGEngine.Parse.Core
|
import RPGEngine.Parse.Core
|
||||||
import RPGEngine.Parse.Game
|
import RPGEngine.Parse.TextToStructure
|
||||||
|
import RPGEngine.Parse.StructureToGame
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
|
@ -21,19 +22,21 @@ spec = do
|
||||||
let input = "player: { hp: infinite, inventory: [] }"
|
let input = "player: { hp: infinite, inventory: [] }"
|
||||||
correct = Player {
|
correct = Player {
|
||||||
playerHp = Prelude.Nothing,
|
playerHp = Prelude.Nothing,
|
||||||
inventory = []
|
inventory = [],
|
||||||
|
position = (0, 0)
|
||||||
}
|
}
|
||||||
Right (Entry (Tag "player") struct) = parseWith structElement input
|
Right (Entry (Tag "player") struct) = parseWith structure input
|
||||||
structToPlayer struct `shouldBe` correct
|
structureToPlayer struct `shouldBe` correct
|
||||||
|
|
||||||
it "without inventory" $ do
|
it "without inventory" $ do
|
||||||
let input = "player: { hp: 50, inventory: [] }"
|
let input = "player: { hp: 50, inventory: [] }"
|
||||||
correct = Player {
|
correct = Player {
|
||||||
playerHp = Just 50,
|
playerHp = Just 50,
|
||||||
inventory = []
|
inventory = [],
|
||||||
|
position = (0, 0)
|
||||||
}
|
}
|
||||||
Right (Entry (Tag "player") struct) = parseWith structElement input
|
Right (Entry (Tag "player") struct) = parseWith structure input
|
||||||
structToPlayer struct `shouldBe` correct
|
structureToPlayer struct `shouldBe` correct
|
||||||
|
|
||||||
it "with inventory" $ do
|
it "with inventory" $ do
|
||||||
let input = "player: { hp: 50, inventory: [ { id: \"dagger\", x: 0, y: 0, name: \"Dolk\", description: \"Basis schade tegen monsters\", useTimes: infinite, value: 10, actions: {} } ] }"
|
let input = "player: { hp: 50, inventory: [ { id: \"dagger\", x: 0, y: 0, name: \"Dolk\", description: \"Basis schade tegen monsters\", useTimes: infinite, value: 10, actions: {} } ] }"
|
||||||
|
@ -50,10 +53,11 @@ spec = do
|
||||||
itemValue = Just 10,
|
itemValue = Just 10,
|
||||||
useTimes = Prelude.Nothing
|
useTimes = Prelude.Nothing
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
position = (0, 0)
|
||||||
}
|
}
|
||||||
Right (Entry (Tag "player") struct) = parseWith structElement input
|
Right (Entry (Tag "player") struct) = parseWith structure input
|
||||||
structToPlayer struct `shouldBe` correct
|
structureToPlayer struct `shouldBe` correct
|
||||||
|
|
||||||
describe "Layout" $ do
|
describe "Layout" $ do
|
||||||
it "simple" $ do
|
it "simple" $ do
|
||||||
|
@ -72,8 +76,8 @@ spec = do
|
||||||
itemActions = [],
|
itemActions = [],
|
||||||
useTimes = Prelude.Nothing
|
useTimes = Prelude.Nothing
|
||||||
}
|
}
|
||||||
Right struct = parseWith structElement input
|
Right struct = parseWith structure input
|
||||||
structToItem struct `shouldBe` correct
|
structureToItem struct `shouldBe` correct
|
||||||
|
|
||||||
it "with actions" $ do
|
it "with actions" $ do
|
||||||
let input = "{ id: \"key\", x: 3, y: 1, name: \"Doorkey\", description: \"Unlocks a secret door\", useTimes: 1, value: 0, actions: { [not(inventoryFull())] retrieveItem(key), [] leave() } }"
|
let input = "{ id: \"key\", x: 3, y: 1, name: \"Doorkey\", description: \"Unlocks a secret door\", useTimes: 1, value: 0, actions: { [not(inventoryFull())] retrieveItem(key), [] leave() } }"
|
||||||
|
@ -90,27 +94,27 @@ spec = do
|
||||||
itemValue = Just 0,
|
itemValue = Just 0,
|
||||||
useTimes = Just 1
|
useTimes = Just 1
|
||||||
}
|
}
|
||||||
Right struct = parseWith structElement input
|
Right struct = parseWith structure input
|
||||||
structToItem struct `shouldBe` correct
|
structureToItem struct `shouldBe` correct
|
||||||
|
|
||||||
describe "Actions" $ do
|
describe "Actions" $ do
|
||||||
it "no conditions" $ do
|
it "no conditions" $ do
|
||||||
let input = "{[] leave()}"
|
let input = "{[] leave()}"
|
||||||
correct = [([], Leave)]
|
correct = [([], Leave)]
|
||||||
Right struct = parseWith structElement input
|
Right struct = parseWith structure input
|
||||||
structToActions struct `shouldBe` correct
|
structureToActions struct `shouldBe` correct
|
||||||
|
|
||||||
it "single condition" $ do
|
it "single condition" $ do
|
||||||
let input = "{ [inventoryFull()] useItem(itemId)}"
|
let input = "{ [inventoryFull()] useItem(itemId)}"
|
||||||
correct = [([InventoryFull], UseItem "itemId")]
|
correct = [([InventoryFull], UseItem "itemId")]
|
||||||
Right struct = parseWith structElement input
|
Right struct = parseWith structure input
|
||||||
structToActions struct `shouldBe` correct
|
structureToActions struct `shouldBe` correct
|
||||||
|
|
||||||
it "multiple conditions" $ do
|
it "multiple conditions" $ do
|
||||||
let input = "{ [not(inventoryFull()), inventoryContains(itemId)] increasePlayerHp(itemId)}"
|
let input = "{ [not(inventoryFull()), inventoryContains(itemId)] increasePlayerHp(itemId)}"
|
||||||
correct = [([Not InventoryFull, InventoryContains "itemId"], IncreasePlayerHp "itemId")]
|
correct = [([Not InventoryFull, InventoryContains "itemId"], IncreasePlayerHp "itemId")]
|
||||||
Right struct = parseWith structElement input
|
Right struct = parseWith structure input
|
||||||
structToActions struct `shouldBe` correct
|
structureToActions struct `shouldBe` correct
|
||||||
|
|
||||||
describe "Entities" $ do
|
describe "Entities" $ do
|
||||||
it "TODO: Simple entity" $ do
|
it "TODO: Simple entity" $ do
|
||||||
|
@ -118,7 +122,7 @@ spec = do
|
||||||
|
|
||||||
describe "Level" $ do
|
describe "Level" $ do
|
||||||
it "Simple layout" $ do
|
it "Simple layout" $ do
|
||||||
let input = "{ layout: { | * * * * * * \n| * s . . e *\n| * * * * * *\n}, items: [], entities: [] }"
|
let input = "{ layout: { | * * * * * * \n| * s . . e *\n| * * * * * * }, items: [], entities: [] }"
|
||||||
correct = Level {
|
correct = Level {
|
||||||
RPGEngine.Data.layout = [
|
RPGEngine.Data.layout = [
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
|
@ -128,7 +132,8 @@ spec = do
|
||||||
items = [],
|
items = [],
|
||||||
entities = []
|
entities = []
|
||||||
}
|
}
|
||||||
Right struct = parseWith structElement input
|
Right struct = parseWith structure input
|
||||||
structToLevel struct `shouldBe` correct
|
structureToLevel struct `shouldBe` correct
|
||||||
|
|
||||||
it "TODO: Complex layout" $ do
|
it "TODO: Complex layout" $ do
|
||||||
pending
|
pending
|
|
@ -1,10 +1,10 @@
|
||||||
module ParseStructElementSpec where
|
module Parser.StructureSpec where
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import RPGEngine.Data
|
import RPGEngine.Data
|
||||||
import RPGEngine.Parse.Core
|
import RPGEngine.Parse.Core
|
||||||
import RPGEngine.Parse.StructElement
|
import RPGEngine.Parse.TextToStructure
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
|
@ -12,21 +12,21 @@ spec = do
|
||||||
it "can parse blocks" $ do
|
it "can parse blocks" $ do
|
||||||
let input = "{}"
|
let input = "{}"
|
||||||
correct = Right $ Block []
|
correct = Right $ Block []
|
||||||
parseWith structElement input `shouldBe` correct
|
parseWith structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "{{}}"
|
let input = "{{}}"
|
||||||
correct = Right $ Block [Block []]
|
correct = Right $ Block [Block []]
|
||||||
parseWith structElement input `shouldBe` correct
|
parseWith structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "{{}, {}}"
|
let input = "{{}, {}}"
|
||||||
correct = Right $ Block [Block [], Block []]
|
correct = Right $ Block [Block [], Block []]
|
||||||
parseWith structElement input `shouldBe` correct
|
parseWith structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "{ id: 1 }"
|
let input = "{ id: 1 }"
|
||||||
correct = Right (Block [
|
correct = Right (Block [
|
||||||
Entry (Tag "id") $ Regular $ Integer 1
|
Entry (Tag "id") $ Regular $ Integer 1
|
||||||
], "")
|
], "")
|
||||||
parseWithRest structElement input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "{ id: \"key\", x: 3, y: 1}"
|
let input = "{ id: \"key\", x: 3, y: 1}"
|
||||||
correct = Right $ Block [
|
correct = Right $ Block [
|
||||||
|
@ -34,14 +34,14 @@ spec = do
|
||||||
Entry (Tag "x") $ Regular $ Integer 3,
|
Entry (Tag "x") $ Regular $ Integer 3,
|
||||||
Entry (Tag "y") $ Regular $ Integer 1
|
Entry (Tag "y") $ Regular $ Integer 1
|
||||||
]
|
]
|
||||||
parseWith structElement input `shouldBe` correct
|
parseWith structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "actions: { [not(inventoryFull())] retrieveItem(key), [] leave()}"
|
let input = "actions: { [not(inventoryFull())] retrieveItem(key), [] leave()}"
|
||||||
correct = Right (Entry (Tag "actions") $ Block [
|
correct = Right (Entry (Tag "actions") $ Block [
|
||||||
Entry (ConditionList [Not InventoryFull]) $ Regular $ Action $ RetrieveItem "key",
|
Entry (ConditionList [Not InventoryFull]) $ Regular $ Action $ RetrieveItem "key",
|
||||||
Entry (ConditionList []) $ Regular $ Action Leave
|
Entry (ConditionList []) $ Regular $ Action Leave
|
||||||
], "")
|
], "")
|
||||||
parseWithRest structElement input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "entities: [ { id: \"door\", x: 4, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left, y: 1}]"
|
let input = "entities: [ { id: \"door\", x: 4, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left, y: 1}]"
|
||||||
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
||||||
|
@ -52,7 +52,7 @@ spec = do
|
||||||
Entry (Tag "direction") $ Regular $ Direction West,
|
Entry (Tag "direction") $ Regular $ Direction West,
|
||||||
Entry (Tag "y") $ Regular $ Integer 1
|
Entry (Tag "y") $ Regular $ Integer 1
|
||||||
]], "")
|
]], "")
|
||||||
parseWithRest structElement input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "entities: [ { id: \"door\", x: 4, y: 1, name:\"Secret door\", description: \"This secret door can only be opened with a key\", actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]"
|
let input = "entities: [ { id: \"door\", x: 4, y: 1, name:\"Secret door\", description: \"This secret door can only be opened with a key\", actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]"
|
||||||
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
||||||
|
@ -66,7 +66,7 @@ spec = do
|
||||||
Entry (ConditionList []) $ Regular $ Action Leave
|
Entry (ConditionList []) $ Regular $ Action Leave
|
||||||
]
|
]
|
||||||
]], "")
|
]], "")
|
||||||
parseWithRest structElement input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
let input = "entities: [ { id: \"door\", x: 4, y: 1, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left , actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]"
|
let input = "entities: [ { id: \"door\", x: 4, y: 1, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left , actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]"
|
||||||
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
||||||
|
@ -81,7 +81,7 @@ spec = do
|
||||||
Entry (ConditionList []) $ Regular $ Action Leave
|
Entry (ConditionList []) $ Regular $ Action Leave
|
||||||
]
|
]
|
||||||
]], "")
|
]], "")
|
||||||
parseWithRest structElement input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
it "can parse entries" $ do
|
it "can parse entries" $ do
|
||||||
let input = "id: \"dagger\""
|
let input = "id: \"dagger\""
|
||||||
|
@ -105,7 +105,7 @@ spec = do
|
||||||
Entry (ConditionList [Not InventoryFull]) $ Regular $ Action $ RetrieveItem "key",
|
Entry (ConditionList [Not InventoryFull]) $ Regular $ Action $ RetrieveItem "key",
|
||||||
Entry (ConditionList []) $ Regular $ Action Leave
|
Entry (ConditionList []) $ Regular $ Action Leave
|
||||||
], "")
|
], "")
|
||||||
parseWithRest structElement input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
it "can parse regulars" $ do
|
it "can parse regulars" $ do
|
||||||
let input = "this is a string"
|
let input = "this is a string"
|
||||||
|
@ -237,19 +237,19 @@ spec = do
|
||||||
it "can parse directions" $ do
|
it "can parse directions" $ do
|
||||||
let input = "up"
|
let input = "up"
|
||||||
correct = Right $ Direction North
|
correct = Right $ Direction North
|
||||||
parseWith RPGEngine.Parse.StructElement.direction input `shouldBe` correct
|
parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct
|
||||||
|
|
||||||
let input = "right"
|
let input = "right"
|
||||||
correct = Right $ Direction East
|
correct = Right $ Direction East
|
||||||
parseWith RPGEngine.Parse.StructElement.direction input `shouldBe` correct
|
parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct
|
||||||
|
|
||||||
let input = "down"
|
let input = "down"
|
||||||
correct = Right $ Direction South
|
correct = Right $ Direction South
|
||||||
parseWith RPGEngine.Parse.StructElement.direction input `shouldBe` correct
|
parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct
|
||||||
|
|
||||||
let input = "left"
|
let input = "left"
|
||||||
correct = Right $ Direction West
|
correct = Right $ Direction West
|
||||||
parseWith RPGEngine.Parse.StructElement.direction input `shouldBe` correct
|
parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct
|
||||||
|
|
||||||
it "can parse layouts" $ do
|
it "can parse layouts" $ do
|
||||||
let input = "| * * * * * * * *\n| * s . . . . e *\n| * * * * * * * *"
|
let input = "| * * * * * * * *\n| * s . . . . e *\n| * * * * * * * *"
|
||||||
|
@ -258,7 +258,16 @@ spec = do
|
||||||
[Blocked, Entrance, Walkable, Walkable, Walkable, Walkable, Exit, Blocked],
|
[Blocked, Entrance, Walkable, Walkable, Walkable, Walkable, Exit, Blocked],
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
||||||
]
|
]
|
||||||
parseWith RPGEngine.Parse.StructElement.layout input `shouldBe` correct
|
parseWith RPGEngine.Parse.TextToStructure.layout input `shouldBe` correct
|
||||||
|
|
||||||
|
let input = "{ |* * * * * * * *|* s . . . . e *|* * * * * * * * }"
|
||||||
|
-- correct = Right $ Entry (Tag "layout") $ Regular $ Layout [
|
||||||
|
correct = Right $ Layout [
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
|
[Blocked, Entrance, Walkable, Walkable, Walkable, Walkable, Exit, Blocked],
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
||||||
|
]
|
||||||
|
parseWith RPGEngine.Parse.TextToStructure.value input `shouldBe` correct
|
||||||
|
|
||||||
describe "Brackets" $ do
|
describe "Brackets" $ do
|
||||||
it "matches closing <" $ do
|
it "matches closing <" $ do
|
Reference in a new issue