parent
5cc96cbdba
commit
f3bce99120
18 changed files with 289 additions and 103 deletions
|
@ -58,6 +58,8 @@ These are the keybinds *in* the game. All other keybinds in the menus should be
|
||||||
| Move down | `Arrow Down` | `s` |
|
| Move down | `Arrow Down` | `s` |
|
||||||
| Move right | `Arrow Right` | `d` |
|
| Move right | `Arrow Right` | `d` |
|
||||||
| Show inventory | `i` | |
|
| Show inventory | `i` | |
|
||||||
|
| Restart level | `r` | |
|
||||||
|
| Quit game | `Esc` | |
|
||||||
|
|
||||||
### Example playthrough
|
### Example playthrough
|
||||||
|
|
||||||
|
|
|
@ -15,4 +15,4 @@ levels: [
|
||||||
|
|
||||||
entities: []
|
entities: []
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -19,8 +19,8 @@ levels: [
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: "key",
|
id: "key",
|
||||||
x: 0,
|
x: 1,
|
||||||
y: 1,
|
y: 2,
|
||||||
name: "Sleutel",
|
name: "Sleutel",
|
||||||
description: "Deze sleutel kan een deur openen",
|
description: "Deze sleutel kan een deur openen",
|
||||||
useTimes: 1,
|
useTimes: 1,
|
||||||
|
@ -35,8 +35,8 @@ levels: [
|
||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
id: "door",
|
id: "door",
|
||||||
x: 0,
|
x: 1,
|
||||||
y: 3,
|
y: 4,
|
||||||
name: "Deur",
|
name: "Deur",
|
||||||
description: "Deze deur kan geopend worden met een sleutel",
|
description: "Deze deur kan geopend worden met een sleutel",
|
||||||
direction: up,
|
direction: up,
|
||||||
|
@ -48,4 +48,4 @@ levels: [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -76,4 +76,4 @@ levels: [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -131,4 +131,4 @@ levels: [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -1,9 +1,6 @@
|
||||||
# Dit gehele bestand is een Block
|
player: {
|
||||||
|
|
||||||
# Dit is een entry: key + value
|
|
||||||
player: { # Value is hier een block
|
|
||||||
hp: 50,
|
hp: 50,
|
||||||
inventory: [ # BlockList
|
inventory: [
|
||||||
{
|
{
|
||||||
id: "dagger",
|
id: "dagger",
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -18,7 +15,6 @@ player: { # Value is hier een block
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Dit is een entry
|
|
||||||
levels: [
|
levels: [
|
||||||
{
|
{
|
||||||
layout: {
|
layout: {
|
||||||
|
@ -135,4 +131,4 @@ levels: [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -9,7 +9,7 @@ import RPGEngine.Config ( bgColor, winDimensions, winOffsets )
|
||||||
import RPGEngine.Render ( initWindow, render )
|
import RPGEngine.Render ( initWindow, render )
|
||||||
import RPGEngine.Input ( handleAllInput )
|
import RPGEngine.Input ( handleAllInput )
|
||||||
import RPGEngine.Input.Playing ( checkPlaying, spawnPlayer )
|
import RPGEngine.Input.Playing ( checkPlaying, spawnPlayer )
|
||||||
import RPGEngine.Data (Game (..), State (..), Layout, Level (..), Physical (..))
|
import RPGEngine.Data (Game (..), State (..), Layout, Level (..), Physical (..), Entity(..), Direction(..), Player(..))
|
||||||
import RPGEngine.Data.Default (defaultLevel, defaultPlayer)
|
import RPGEngine.Data.Default (defaultLevel, defaultPlayer)
|
||||||
|
|
||||||
import Graphics.Gloss ( play )
|
import Graphics.Gloss ( play )
|
||||||
|
@ -27,22 +27,15 @@ playRPGEngine title fps = do
|
||||||
-- TODO revert this
|
-- TODO revert this
|
||||||
-- Initialize the game
|
-- Initialize the game
|
||||||
initGame :: Game
|
initGame :: Game
|
||||||
-- initGame = Game {
|
initGame = Game { state = Menu }
|
||||||
-- state = Menu{ base = StateBase{
|
-- initGame = Game{ state = initState }
|
||||||
-- renderer = renderMenu,
|
-- where initState = Playing{
|
||||||
-- inputHandler = handleInputMenu
|
-- levels = [defaultLevel, otherLevel],
|
||||||
-- }}
|
-- count = 0,
|
||||||
-- }
|
-- level = defaultLevel,
|
||||||
initGame = Game{
|
-- player = spawnPlayer defaultLevel defaultPlayer,
|
||||||
state = initState
|
-- restart = initState
|
||||||
}
|
-- }
|
||||||
where initState = Playing{
|
|
||||||
levels = [defaultLevel, otherLevel],
|
|
||||||
count = 0,
|
|
||||||
level = defaultLevel,
|
|
||||||
player = spawnPlayer defaultLevel defaultPlayer,
|
|
||||||
restart = initState
|
|
||||||
}
|
|
||||||
|
|
||||||
-- TODO remove this
|
-- TODO remove this
|
||||||
otherLayout :: Layout
|
otherLayout :: Layout
|
||||||
|
@ -50,6 +43,8 @@ otherLayout = [
|
||||||
[Blocked, Blocked, Blocked],
|
[Blocked, Blocked, Blocked],
|
||||||
[Blocked, Entrance, Blocked],
|
[Blocked, Entrance, Blocked],
|
||||||
[Blocked, Walkable, Blocked],
|
[Blocked, Walkable, Blocked],
|
||||||
|
[Blocked, Walkable, Blocked],
|
||||||
|
[Blocked, Walkable, Blocked],
|
||||||
[Blocked, Exit, Blocked],
|
[Blocked, Exit, Blocked],
|
||||||
[Blocked, Blocked, Blocked]
|
[Blocked, Blocked, Blocked]
|
||||||
]
|
]
|
||||||
|
@ -69,12 +64,30 @@ otherLevel = Level {
|
||||||
(1, 2, Walkable),
|
(1, 2, Walkable),
|
||||||
(2, 2, Blocked),
|
(2, 2, Blocked),
|
||||||
(0, 3, Blocked),
|
(0, 3, Blocked),
|
||||||
(1, 3, Exit),
|
(1, 3, Walkable),
|
||||||
(2, 3, Blocked),
|
(2, 3, Blocked),
|
||||||
(0, 4, Blocked),
|
(0, 4, Blocked),
|
||||||
(1, 4, Blocked),
|
(1, 4, Walkable),
|
||||||
(2, 4, Blocked)
|
(2, 4, Blocked),
|
||||||
|
(0, 5, Blocked),
|
||||||
|
(1, 5, Exit),
|
||||||
|
(2, 5, Blocked),
|
||||||
|
(0, 6, Blocked),
|
||||||
|
(1, 6, Blocked),
|
||||||
|
(2, 6, Blocked)
|
||||||
],
|
],
|
||||||
items = [],
|
items = [],
|
||||||
entities = []
|
entities = [
|
||||||
|
Entity{
|
||||||
|
entityId = "door",
|
||||||
|
entityX = 1,
|
||||||
|
entityY = 3,
|
||||||
|
entityName = "Epic door",
|
||||||
|
entityDescription = "epic description",
|
||||||
|
entityActions = [],
|
||||||
|
entityValue = Nothing,
|
||||||
|
entityHp = Nothing,
|
||||||
|
direction = North
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ import RPGEngine.Render.Core ( Renderer )
|
||||||
-- A game is the base data container.
|
-- A game is the base data container.
|
||||||
data Game = Game {
|
data Game = Game {
|
||||||
state :: State
|
state :: State
|
||||||
}
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------- State --------------------------------
|
------------------------------- State --------------------------------
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ data State = Menu
|
||||||
| Win
|
| Win
|
||||||
-- Lost a level
|
-- Lost a level
|
||||||
| Lose { restart :: State }
|
| Lose { restart :: State }
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------- Level --------------------------------
|
------------------------------- Level --------------------------------
|
||||||
|
|
||||||
|
|
|
@ -64,9 +64,30 @@ defaultLevel = Level {
|
||||||
|
|
||||||
defaultPlayer :: Player
|
defaultPlayer :: Player
|
||||||
defaultPlayer = Player {
|
defaultPlayer = Player {
|
||||||
playerHp = Prelude.Nothing, -- Compares to infinity
|
-- playerHp = Prelude.Nothing, -- Compares to infinity
|
||||||
inventory = [],
|
playerHp = Just 50,
|
||||||
position = (0, 0)
|
inventory = [ Item{
|
||||||
|
itemId = "key",
|
||||||
|
itemX = 0,
|
||||||
|
itemY = 0,
|
||||||
|
itemName = "Epic key",
|
||||||
|
itemDescription = "MyKey",
|
||||||
|
itemActions = [],
|
||||||
|
itemValue = Nothing,
|
||||||
|
useTimes = Nothing
|
||||||
|
}, Item{
|
||||||
|
itemId = "dagger",
|
||||||
|
itemX = 0,
|
||||||
|
itemY = 0,
|
||||||
|
itemName = "My dagger",
|
||||||
|
itemDescription = "dagger",
|
||||||
|
itemActions = [],
|
||||||
|
itemValue = Nothing,
|
||||||
|
useTimes = Nothing
|
||||||
|
}],
|
||||||
|
position = (0, 0),
|
||||||
|
showInventory = False,
|
||||||
|
showHp = True
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultSelector :: ListSelector
|
defaultSelector :: ListSelector
|
||||||
|
|
|
@ -20,7 +20,7 @@ type InputHandler a = Event -> (a -> a)
|
||||||
data ListSelector = ListSelector {
|
data ListSelector = ListSelector {
|
||||||
selection :: Int,
|
selection :: Int,
|
||||||
selected :: Bool
|
selected :: Bool
|
||||||
}
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
------------------------------ Exported ------------------------------
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ module RPGEngine.Input.Playing
|
||||||
( handleInputPlaying
|
( handleInputPlaying
|
||||||
, checkPlaying
|
, checkPlaying
|
||||||
, spawnPlayer
|
, spawnPlayer
|
||||||
|
, putCoords
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import RPGEngine.Input.Core (InputHandler, handle, handleKey, composeInputHandlers)
|
import RPGEngine.Input.Core (InputHandler, handle, handleKey, composeInputHandlers)
|
||||||
|
@ -32,6 +33,8 @@ handleInputPlaying = composeInputHandlers [
|
||||||
handleKey (Char 's') Down $ movePlayer South,
|
handleKey (Char 's') Down $ movePlayer South,
|
||||||
handleKey (Char 'a') Down $ movePlayer West,
|
handleKey (Char 'a') Down $ movePlayer West,
|
||||||
|
|
||||||
|
handleKey (Char 'r') Down restartGame,
|
||||||
|
|
||||||
handleKey (Char 'i') Down $ toggleInventoryShown True,
|
handleKey (Char 'i') Down $ toggleInventoryShown True,
|
||||||
handleKey (Char 'i') Up $ toggleInventoryShown False
|
handleKey (Char 'i') Up $ toggleInventoryShown False
|
||||||
]
|
]
|
||||||
|
@ -58,13 +61,15 @@ pauseGame g@Game{ state = playing@Playing{} } = pausedGame
|
||||||
where pausedGame = g{ state = Paused playing }
|
where pausedGame = g{ state = Paused playing }
|
||||||
pauseGame g = g
|
pauseGame g = g
|
||||||
|
|
||||||
|
restartGame :: Game -> Game
|
||||||
|
restartGame g@Game{ state = playing@Playing{ restart = restarted } } = g{ state = restarted }
|
||||||
|
|
||||||
-- Go to next level if there is a next level, otherwise, initialize win state.
|
-- Go to next level if there is a next level, otherwise, initialize win state.
|
||||||
goToNextLevel :: State -> State
|
goToNextLevel :: State -> State
|
||||||
goToNextLevel s@Playing{ levels = levels, level = current, count = count, player = player } = nextState
|
goToNextLevel s@Playing{ levels = levels, level = current, count = count, player = player } = nextState
|
||||||
where -- Either the next level or winState
|
where nextState | (count + 1) < length levels = nextLevelState
|
||||||
nextState | (count + 1) < length levels = nextLevelState
|
|
||||||
| otherwise = Win
|
| otherwise = Win
|
||||||
nextLevelState = s{ level = nextLevel, count = count + 1, player = movedPlayer }
|
nextLevelState = s{ level = nextLevel, count = count + 1, player = movedPlayer, restart = nextLevelState }
|
||||||
nextLevel = levels !! (count + 1)
|
nextLevel = levels !! (count + 1)
|
||||||
movedPlayer = spawnPlayer nextLevel player
|
movedPlayer = spawnPlayer nextLevel player
|
||||||
goToNextLevel s = s
|
goToNextLevel s = s
|
||||||
|
|
|
@ -6,13 +6,11 @@ import RPGEngine.Data ( Game )
|
||||||
import RPGEngine.Parse.StructureToGame ( structureToGame )
|
import RPGEngine.Parse.StructureToGame ( structureToGame )
|
||||||
import GHC.IO (unsafePerformIO)
|
import GHC.IO (unsafePerformIO)
|
||||||
import Text.Parsec.String (parseFromFile)
|
import Text.Parsec.String (parseFromFile)
|
||||||
import RPGEngine.Parse.TextToStructure (structure)
|
import RPGEngine.Parse.TextToStructure ( gameFile )
|
||||||
|
|
||||||
------------------------------ Exported ------------------------------
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
parse :: FilePath -> Game
|
parse :: FilePath -> Game
|
||||||
parse filename = structureToGame struct
|
parse filename = structureToGame struct
|
||||||
where (Right struct) = unsafePerformIO io
|
where (Right struct) = unsafePerformIO io
|
||||||
io = parseFromFile structure filename
|
io = parseFromFile gameFile filename
|
||||||
|
|
||||||
tempParse = parseFromFile
|
|
|
@ -10,26 +10,25 @@ import RPGEngine.Data
|
||||||
entityActions, entityValue, entityHp, direction),
|
entityActions, entityValue, entityHp, direction),
|
||||||
Item(itemId, itemX, itemY, itemName, itemDescription, itemValue,
|
Item(itemId, itemX, itemY, itemName, itemDescription, itemValue,
|
||||||
itemActions, useTimes),
|
itemActions, useTimes),
|
||||||
Level(layout, items, entities),
|
Level(layout, items, entities, index),
|
||||||
Game (..), State (..) )
|
Game (..), State (..) )
|
||||||
import RPGEngine.Parse.TextToStructure
|
import RPGEngine.Parse.TextToStructure
|
||||||
( Value(Infinite, Action, Layout, String, Direction, Integer),
|
( Value(Infinite, Action, Layout, String, Direction, Integer),
|
||||||
Key(Tag, ConditionList),
|
Key(Tag, ConditionList),
|
||||||
Structure(..) )
|
Structure(..) )
|
||||||
import RPGEngine.Data.Default (defaultPlayer, defaultLevel, defaultItem, defaultEntity)
|
import RPGEngine.Data.Default (defaultPlayer, defaultLevel, defaultItem, defaultEntity)
|
||||||
|
import RPGEngine.Input.Playing (putCoords, spawnPlayer)
|
||||||
|
|
||||||
------------------------------ Exported ------------------------------
|
------------------------------ Exported ------------------------------
|
||||||
|
|
||||||
structureToGame :: Structure -> Game
|
structureToGame :: [Structure] -> Game
|
||||||
-- structureToGame [Entry(Tag "player") playerBlock, Entry(Tag "levels") levelsBlock] = game
|
structureToGame [Entry (Tag "player") playerBlock, Entry (Tag "levels") levelsBlock] = game
|
||||||
structureToGame (Entry (Tag "player") playerBlock) = game
|
where game = Game newState
|
||||||
where game = Game{ state = newState }
|
newState = Playing newLevels 0 currentLevel newPlayer newState
|
||||||
newState = Playing{ levels = newLevels, level = currentLevel, player = newPlayer, restart = newState }
|
newLevels = structureToLevels levelsBlock
|
||||||
-- newLevels = structureToLevels levelsBlock
|
currentLevel = head newLevels
|
||||||
-- currentLevel = head newLevels
|
newPlayer = spawnPlayer currentLevel $ structureToPlayer playerBlock
|
||||||
newLevels = [defaultLevel]
|
structureToGame _ = Game Menu
|
||||||
currentLevel = defaultLevel
|
|
||||||
newPlayer = structureToPlayer playerBlock
|
|
||||||
|
|
||||||
------------------------------- Player -------------------------------
|
------------------------------- Player -------------------------------
|
||||||
|
|
||||||
|
@ -60,7 +59,9 @@ structureToLevels (Block struct) = structureToLevel <$> struct
|
||||||
structureToLevels _ = [defaultLevel]
|
structureToLevels _ = [defaultLevel]
|
||||||
|
|
||||||
structureToLevel :: Structure -> Level
|
structureToLevel :: Structure -> Level
|
||||||
structureToLevel (Block entries) = structureToLevel' entries defaultLevel
|
structureToLevel (Block entries) = indexIsSet
|
||||||
|
where indexIsSet = level{ index = putCoords level }
|
||||||
|
level = structureToLevel' entries defaultLevel
|
||||||
structureToLevel _ = defaultLevel
|
structureToLevel _ = defaultLevel
|
||||||
|
|
||||||
structureToLevel' :: [Structure] -> Level -> Level
|
structureToLevel' :: [Structure] -> Level -> Level
|
||||||
|
|
|
@ -18,9 +18,13 @@ import Text.Parsec
|
||||||
notFollowedBy,
|
notFollowedBy,
|
||||||
sepBy,
|
sepBy,
|
||||||
many,
|
many,
|
||||||
try )
|
try, spaces, endOfLine )
|
||||||
import qualified Text.Parsec as P ( string )
|
import qualified Text.Parsec as P ( string )
|
||||||
import Text.Parsec.String ( Parser )
|
import Text.Parsec.String ( Parser )
|
||||||
|
import Text.Parsec.Combinator (lookAhead)
|
||||||
|
|
||||||
|
gameFile :: Parser [Structure]
|
||||||
|
gameFile = try $ do many1 $ ignoreWS structure
|
||||||
|
|
||||||
-------------------------- StructureElement --------------------------
|
-------------------------- StructureElement --------------------------
|
||||||
|
|
||||||
|
@ -111,7 +115,7 @@ data Value = String String
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
value :: Parser Value
|
value :: Parser Value
|
||||||
value = choice [layout, string, integer, infinite, action, direction]
|
value = choice [layout, string, integer, infinite, direction, action]
|
||||||
|
|
||||||
string :: Parser Value
|
string :: Parser Value
|
||||||
string = try $ String <$> between (char '\"') (char '\"') reading
|
string = try $ String <$> between (char '\"') (char '\"') reading
|
||||||
|
@ -149,7 +153,7 @@ direction = try $ do
|
||||||
ignoreWS $ P.string "left",
|
ignoreWS $ P.string "left",
|
||||||
ignoreWS $ P.string "right"
|
ignoreWS $ P.string "right"
|
||||||
]
|
]
|
||||||
notFollowedBy alphaNum
|
-- lookAhead $ char ','
|
||||||
return $ Direction $ make value
|
return $ Direction $ make value
|
||||||
where make "up" = North
|
where make "up" = North
|
||||||
make "right" = East
|
make "right" = East
|
||||||
|
@ -160,15 +164,12 @@ direction = try $ do
|
||||||
layout :: Parser Value
|
layout :: Parser Value
|
||||||
layout = try $ do
|
layout = try $ do
|
||||||
open <- ignoreWS $ oneOf openingBrackets
|
open <- ignoreWS $ oneOf openingBrackets
|
||||||
ignoreWS $ char '|'
|
|
||||||
list <- ignoreWS $ ignoreWS strip `sepBy` ignoreWS (char '|')
|
|
||||||
let closing = getMatchingClosingBracket open
|
let closing = getMatchingClosingBracket open
|
||||||
ignoreWS $ char closing
|
value <- many1 strip <* ignoreWS (char closing)
|
||||||
return $ Layout list
|
return $ Layout value
|
||||||
|
|
||||||
strip :: Parser Strip
|
strip :: Parser Strip
|
||||||
strip = try $ do
|
strip = try $ do ignoreWS (char '|') *> ignoreWS (physical `sepBy` char ' ')
|
||||||
physical `sepBy` char ' '
|
|
||||||
|
|
||||||
physical :: Parser Physical
|
physical :: Parser Physical
|
||||||
physical = try $ do
|
physical = try $ do
|
||||||
|
|
|
@ -55,7 +55,11 @@ test-suite rpg-engine-test
|
||||||
main-is: Spec.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,
|
||||||
|
rpg-engine,
|
||||||
|
hspec <= 2.10.6, hspec-discover,
|
||||||
|
parsec >= 3.1.15.1
|
||||||
other-modules:
|
other-modules:
|
||||||
Parser.GameSpec
|
Parser.GameSpec
|
||||||
Parser.StructureSpec
|
Parser.StructureSpec
|
|
@ -6,24 +6,50 @@ import RPGEngine.Data
|
||||||
import RPGEngine.Parse.Core
|
import RPGEngine.Parse.Core
|
||||||
import RPGEngine.Parse.TextToStructure
|
import RPGEngine.Parse.TextToStructure
|
||||||
import RPGEngine.Parse.StructureToGame
|
import RPGEngine.Parse.StructureToGame
|
||||||
|
import RPGEngine.Parse.TextToStructure (gameFile)
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
describe "Game" $ do
|
describe "Game" $ do
|
||||||
it "TODO: Simple game" $ do
|
-- TODO There is a weird bug that caused this to go in an infinite loop. Fix later.
|
||||||
pending
|
xit "Simple game" $ do
|
||||||
it "TODO: More complex game" $ do
|
let input = "player: {\n hp: 50,\n inventory: []\n}\n\nlevels: [\n {\n layout: {\n | * * * * * *\n | * s . . e *\n | * * * * * *\n },\n \n items: [],\n\n entities: []\n\n\n }\n]"
|
||||||
pending
|
correct = Game {
|
||||||
it "TODO: Game with multiple levels" $ do
|
state = Playing {
|
||||||
pending
|
levels = [],
|
||||||
|
count = 0,
|
||||||
|
level = Level {
|
||||||
|
RPGEngine.Data.layout = [],
|
||||||
|
index = [],
|
||||||
|
items = [],
|
||||||
|
entities = []
|
||||||
|
},
|
||||||
|
player = Player {
|
||||||
|
playerHp = Just 50,
|
||||||
|
inventory = [],
|
||||||
|
position = (0, 0),
|
||||||
|
showHp = True,
|
||||||
|
showInventory = False
|
||||||
|
},
|
||||||
|
restart = Menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Right struct) = parseWith gameFile input
|
||||||
|
structureToGame struct `shouldBe` correct
|
||||||
|
it "More complex game" $ do
|
||||||
|
pendingWith "fix parsing first"
|
||||||
|
it "Game with multiple levels" $ do
|
||||||
|
pendingWith "fix parsing first"
|
||||||
|
|
||||||
describe "Player" $ do
|
describe "Player" $ do
|
||||||
it "cannot die" $ do
|
it "cannot die" $ 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)
|
position = (0, 0),
|
||||||
|
showHp = True,
|
||||||
|
showInventory = False
|
||||||
}
|
}
|
||||||
Right (Entry (Tag "player") struct) = parseWith structure input
|
Right (Entry (Tag "player") struct) = parseWith structure input
|
||||||
structureToPlayer struct `shouldBe` correct
|
structureToPlayer struct `shouldBe` correct
|
||||||
|
@ -31,9 +57,11 @@ spec = do
|
||||||
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)
|
position = (0, 0),
|
||||||
|
showHp = True,
|
||||||
|
showInventory = False
|
||||||
}
|
}
|
||||||
Right (Entry (Tag "player") struct) = parseWith structure input
|
Right (Entry (Tag "player") struct) = parseWith structure input
|
||||||
structureToPlayer struct `shouldBe` correct
|
structureToPlayer struct `shouldBe` correct
|
||||||
|
@ -54,14 +82,12 @@ spec = do
|
||||||
useTimes = Prelude.Nothing
|
useTimes = Prelude.Nothing
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
position = (0, 0)
|
position = (0, 0),
|
||||||
|
showHp = True,
|
||||||
|
showInventory = False
|
||||||
}
|
}
|
||||||
Right (Entry (Tag "player") struct) = parseWith structure input
|
Right (Entry (Tag "player") struct) = parseWith structure input
|
||||||
structureToPlayer struct `shouldBe` correct
|
structureToPlayer struct `shouldBe` correct
|
||||||
|
|
||||||
describe "Layout" $ do
|
|
||||||
it "simple" $ do
|
|
||||||
pending
|
|
||||||
|
|
||||||
describe "Items" $ do
|
describe "Items" $ do
|
||||||
it "simple" $ do
|
it "simple" $ do
|
||||||
|
@ -117,23 +143,40 @@ spec = do
|
||||||
structureToActions struct `shouldBe` correct
|
structureToActions struct `shouldBe` correct
|
||||||
|
|
||||||
describe "Entities" $ do
|
describe "Entities" $ do
|
||||||
it "TODO: Simple entity" $ do
|
it "Simple entity" $ do
|
||||||
pending
|
pendingWith "fix parsing first"
|
||||||
|
|
||||||
describe "Level" $ do
|
describe "Level" $ do
|
||||||
it "Simple layout" $ do
|
it "Simple layout" $ do
|
||||||
let input = "{ layout: { | * * * * * * \n| * s . . e *\n| * * * * * * }, items: [], entities: [] }"
|
let input = "{ layout: { | * * * * * *\n| * s . . e *\n| * * * * * *\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],
|
||||||
[Blocked, Entrance, Walkable, Walkable, Exit, Blocked],
|
[Blocked, Entrance, Walkable, Walkable, Exit, Blocked],
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
||||||
],
|
],
|
||||||
|
index = [
|
||||||
|
(0, 0, Blocked),
|
||||||
|
(1, 0, Blocked),
|
||||||
|
(2, 0, Blocked),
|
||||||
|
(3, 0, Blocked),
|
||||||
|
(4, 0, Blocked),
|
||||||
|
(5, 0, Blocked),
|
||||||
|
(0, 1, Blocked),
|
||||||
|
(1, 1, Entrance),
|
||||||
|
(2, 1, Walkable),
|
||||||
|
(3, 1, Walkable),
|
||||||
|
(4, 1, Exit),
|
||||||
|
(5, 1, Blocked),
|
||||||
|
(0, 2, Blocked),
|
||||||
|
(1, 2, Blocked),
|
||||||
|
(2, 2, Blocked),
|
||||||
|
(3, 2, Blocked),
|
||||||
|
(4, 2, Blocked),
|
||||||
|
(5, 2, Blocked)
|
||||||
|
],
|
||||||
items = [],
|
items = [],
|
||||||
entities = []
|
entities = []
|
||||||
}
|
}
|
||||||
Right struct = parseWith structure input
|
Right struct = parseWith structure input
|
||||||
structureToLevel struct `shouldBe` correct
|
structureToLevel struct `shouldBe` correct
|
||||||
|
|
||||||
it "TODO: Complex layout" $ do
|
|
||||||
pending
|
|
|
@ -5,6 +5,8 @@ import Test.Hspec
|
||||||
import RPGEngine.Data
|
import RPGEngine.Data
|
||||||
import RPGEngine.Parse.Core
|
import RPGEngine.Parse.Core
|
||||||
import RPGEngine.Parse.TextToStructure
|
import RPGEngine.Parse.TextToStructure
|
||||||
|
import Text.Parsec.String (parseFromFile)
|
||||||
|
import GHC.IO (unsafePerformIO)
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
|
@ -68,7 +70,7 @@ spec = do
|
||||||
]], "")
|
]], "")
|
||||||
parseWithRest structure 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 [
|
||||||
Entry (Tag "id") $ Regular $ String "door",
|
Entry (Tag "id") $ Regular $ String "door",
|
||||||
Entry (Tag "x") $ Regular $ Integer 4,
|
Entry (Tag "x") $ Regular $ Integer 4,
|
||||||
|
@ -83,6 +85,17 @@ spec = do
|
||||||
]], "")
|
]], "")
|
||||||
parseWithRest structure input `shouldBe` correct
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
|
it "combines actions and direction" $ do
|
||||||
|
let input = "entities: [ { direction: left, actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]"
|
||||||
|
correct = Right (Entry (Tag "entities") $ Block [ Block [
|
||||||
|
Entry (Tag "direction") $ Regular $ Direction West,
|
||||||
|
Entry (Tag "actions") $ Block [
|
||||||
|
Entry (ConditionList [InventoryContains "key"]) $ Regular $ Action $ UseItem "key",
|
||||||
|
Entry (ConditionList []) $ Regular $ Action Leave
|
||||||
|
]
|
||||||
|
]], "")
|
||||||
|
parseWithRest structure input `shouldBe` correct
|
||||||
|
|
||||||
it "can parse entries" $ do
|
it "can parse entries" $ do
|
||||||
let input = "id: \"dagger\""
|
let input = "id: \"dagger\""
|
||||||
correct = Right $ Entry (Tag "id") $ Regular $ String "dagger"
|
correct = Right $ Entry (Tag "id") $ Regular $ String "dagger"
|
||||||
|
@ -252,22 +265,21 @@ spec = do
|
||||||
parseWith RPGEngine.Parse.TextToStructure.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 | * * * * * * * *\n }"
|
||||||
correct = Right $ Layout [
|
correct = Right $ Layout [
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
[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.TextToStructure.layout input `shouldBe` correct
|
parseWith value input `shouldBe` correct
|
||||||
|
|
||||||
let input = "{ |* * * * * * * *|* s . . . . e *|* * * * * * * * }"
|
let input = "layout: { | * * * * * * * *\n | * s . . . . e *\n | * * * * * * * *\n }"
|
||||||
-- correct = Right $ Entry (Tag "layout") $ Regular $ Layout [
|
correct = Right $ Entry (Tag "layout") $ Regular $ Layout [
|
||||||
correct = Right $ Layout [
|
|
||||||
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
[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.TextToStructure.value input `shouldBe` correct
|
parseWith structure input `shouldBe` correct
|
||||||
|
|
||||||
describe "Brackets" $ do
|
describe "Brackets" $ do
|
||||||
it "matches closing <" $ do
|
it "matches closing <" $ do
|
||||||
|
@ -289,3 +301,75 @@ spec = do
|
||||||
let input = '['
|
let input = '['
|
||||||
correct = ']'
|
correct = ']'
|
||||||
getMatchingClosingBracket input `shouldBe` correct
|
getMatchingClosingBracket input `shouldBe` correct
|
||||||
|
|
||||||
|
describe "Full game file" $ do
|
||||||
|
it "single level" $ do
|
||||||
|
let input = "player: {\n hp: 50,\n inventory: []\n}\n\nlevels: [\n {\n layout: {\n | * * * * * *\n | * s . . e *\n | * * * * * *\n },\n \n items: [],\n\n entities: []\n\n\n }\n]"
|
||||||
|
correct = Right [
|
||||||
|
Entry (Tag "player") $ Block [
|
||||||
|
Entry (Tag "hp") $ Regular $ Integer 50,
|
||||||
|
Entry (Tag "inventory") $ Block []
|
||||||
|
],
|
||||||
|
Entry (Tag "levels") $ Block [ Block [
|
||||||
|
Entry (Tag "layout") $ Regular $ Layout [
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
|
[Blocked, Entrance, Walkable, Walkable, Exit, Blocked],
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
||||||
|
],
|
||||||
|
Entry (Tag "items") $ Block [],
|
||||||
|
Entry (Tag "entities") $ Block []
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
parseWith gameFile input `shouldBe` correct
|
||||||
|
|
||||||
|
it "two levels" $ do
|
||||||
|
let input = "player: {\n hp: 50,\n inventory: []\n}\n\nlevels: [\n {\n layout: {\n | * * * * * *\n | * s . . e *\n | * * * * * *\n },\n \n items: [],\n\n entities: []\n },\n {\n layout: {\n | * * *\n | * e *\n | * . *\n | * . *\n | * . *\n | * . *\n | * s *\n | * * *\n },\n\n items: [],\n\n entities: []\n }\n]"
|
||||||
|
correct = Right [
|
||||||
|
Entry (Tag "player") $ Block [
|
||||||
|
Entry (Tag "hp") $ Regular $ Integer 50,
|
||||||
|
Entry (Tag "inventory") $ Block []
|
||||||
|
],
|
||||||
|
Entry (Tag "levels") $ Block [
|
||||||
|
Block [
|
||||||
|
Entry (Tag "layout") $ Regular $ Layout [
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
|
[Blocked, Entrance, Walkable, Walkable, Exit, Blocked],
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
||||||
|
],
|
||||||
|
Entry (Tag "items") $ Block [],
|
||||||
|
Entry (Tag "entities") $ Block []
|
||||||
|
], Block [
|
||||||
|
Entry (Tag "layout") $ Regular $ Layout [
|
||||||
|
[Blocked,Blocked,Blocked],
|
||||||
|
[Blocked,Exit,Blocked],
|
||||||
|
[Blocked,Walkable,Blocked],
|
||||||
|
[Blocked,Walkable,Blocked],
|
||||||
|
[Blocked,Walkable,Blocked],
|
||||||
|
[Blocked,Walkable,Blocked],
|
||||||
|
[Blocked,Entrance,Blocked],
|
||||||
|
[Blocked,Blocked,Blocked]
|
||||||
|
],
|
||||||
|
Entry (Tag "items") $ Block [],
|
||||||
|
Entry (Tag "entities") $ Block []
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
parseWith gameFile input `shouldBe` correct
|
||||||
|
|
||||||
|
it "from file" $ do
|
||||||
|
let correct = Right [
|
||||||
|
Entry (Tag "player") $ Block [
|
||||||
|
Entry (Tag "hp") $ Regular $ Integer 50,
|
||||||
|
Entry (Tag "inventory") $ Block []
|
||||||
|
],
|
||||||
|
Entry (Tag "levels") $ Block [ Block [
|
||||||
|
Entry (Tag "layout") $ Regular $ Layout [
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked],
|
||||||
|
[Blocked, Entrance, Walkable, Walkable, Exit, Blocked],
|
||||||
|
[Blocked, Blocked, Blocked, Blocked, Blocked, Blocked]
|
||||||
|
],
|
||||||
|
Entry (Tag "items") $ Block [],
|
||||||
|
Entry (Tag "entities") $ Block []
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
unsafePerformIO (parseFromFile gameFile "levels/level1.txt") `shouldBe` correct
|
19
test/Spec.hs
19
test/Spec.hs
|
@ -1 +1,18 @@
|
||||||
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
||||||
|
|
||||||
|
-------------------------- How to use Hspec --------------------------
|
||||||
|
|
||||||
|
-- If a test has not yet been written:
|
||||||
|
-- Use `pending` or `pendingWith`.
|
||||||
|
-- it "Description" $ do
|
||||||
|
-- pendingWith "Reason"
|
||||||
|
|
||||||
|
-- Temporarily disable running a test:
|
||||||
|
-- Replace `it` with `xit`
|
||||||
|
-- xit "Description" $ do ...
|
||||||
|
|
||||||
|
-- Temporarily only run a specific test:
|
||||||
|
-- Put `focus` in front.
|
||||||
|
-- it "Description" $ do ...
|
||||||
|
-- becomes
|
||||||
|
-- focus $ it "Description" $ do ...
|
Reference in a new issue