Allow for concurrent effects
This commit is contained in:
+6
-2
@@ -6,6 +6,7 @@ import Dodge.TestString
|
|||||||
import Dodge.Data
|
import Dodge.Data
|
||||||
import Dodge.StartNewGame
|
import Dodge.StartNewGame
|
||||||
import Dodge.Initialisation
|
import Dodge.Initialisation
|
||||||
|
import Dodge.Concurrent
|
||||||
--import Dodge.LevelGen
|
--import Dodge.LevelGen
|
||||||
import Dodge.Update
|
import Dodge.Update
|
||||||
import Dodge.Event
|
import Dodge.Event
|
||||||
@@ -40,12 +41,13 @@ main = do
|
|||||||
sizey = floor $ _windowY con
|
sizey = floor $ _windowY con
|
||||||
posx = _windowPosX con
|
posx = _windowPosX con
|
||||||
posy = _windowPosY con
|
posy = _windowPosY con
|
||||||
setupLoop
|
setupConLoop
|
||||||
20
|
20
|
||||||
(T.pack "Simple Game Loop")
|
(T.pack "Simple Game Loop")
|
||||||
(winConfig sizex sizey (Just (posx,posy)))
|
(winConfig sizex sizey (Just (posx,posy)))
|
||||||
theCleanup
|
theCleanup
|
||||||
(firstWorldLoad con)
|
(firstWorldLoad con)
|
||||||
|
conTest
|
||||||
theUpdateStep
|
theUpdateStep
|
||||||
(flip handleEvent)
|
(flip handleEvent)
|
||||||
|
|
||||||
@@ -77,9 +79,11 @@ firstWorldLoad theConfig = do
|
|||||||
,_uvConfig = theConfig
|
,_uvConfig = theConfig
|
||||||
,_preloadData = pdata
|
,_preloadData = pdata
|
||||||
,_menuLayers = []
|
,_menuLayers = []
|
||||||
,_savedWorlds = M.empty
|
|
||||||
, _uvIOEffects = return
|
, _uvIOEffects = return
|
||||||
, _uvTestString = testStringInit
|
, _uvTestString = testStringInit
|
||||||
|
, _uvConcMessage = ""
|
||||||
|
, _quickSave = Nothing
|
||||||
|
, _uvConcEffects = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
theUpdateStep :: Universe -> IO Universe
|
theUpdateStep :: Universe -> IO Universe
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
module Dodge.Concurrent where
|
||||||
|
|
||||||
|
import Dodge.Data.Universe
|
||||||
|
--import qualified Data.Set as S
|
||||||
|
--import Control.Lens
|
||||||
|
--import Control.Concurrent
|
||||||
|
--import SDL.Input.Keyboard.Codes
|
||||||
|
|
||||||
|
conTest :: Universe -> Maybe (IO (Universe -> Universe))
|
||||||
|
conTest = _uvConcEffects
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{-# LANGUAGE StrictData #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
||||||
|
module Dodge.Data.SaveSlot where
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
data SaveSlot
|
||||||
|
= QuicksaveSlot
|
||||||
|
| LevelStartSlot Int
|
||||||
|
| SaveSlotNum Int
|
||||||
|
deriving (Eq, Ord, Show, Read)
|
||||||
|
|
||||||
|
makeLenses ''SaveSlot
|
||||||
@@ -10,7 +10,7 @@ module Dodge.Data.Universe (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import qualified Data.Map.Strict as M
|
--import qualified Data.Map.Strict as M
|
||||||
import Data.Preload
|
import Data.Preload
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Dodge.Data.Config
|
import Dodge.Data.Config
|
||||||
@@ -22,17 +22,14 @@ data Universe = Universe
|
|||||||
{ _uvWorld :: World
|
{ _uvWorld :: World
|
||||||
, _preloadData :: PreloadData
|
, _preloadData :: PreloadData
|
||||||
, _menuLayers :: [ScreenLayer]
|
, _menuLayers :: [ScreenLayer]
|
||||||
, _savedWorlds :: M.Map SaveSlot World
|
, _quickSave :: Maybe World
|
||||||
, _uvIOEffects :: Universe -> IO Universe
|
, _uvIOEffects :: Universe -> IO Universe
|
||||||
|
, _uvConcEffects :: Maybe (IO (Universe -> Universe))
|
||||||
, _uvConfig :: Configuration
|
, _uvConfig :: Configuration
|
||||||
, _uvTestString :: Universe -> [String]
|
, _uvTestString :: Universe -> [String]
|
||||||
|
, _uvConcMessage :: String
|
||||||
}
|
}
|
||||||
|
|
||||||
data SaveSlot
|
|
||||||
= QuicksaveSlot
|
|
||||||
| LevelStartSlot
|
|
||||||
| SaveSlotNum Int
|
|
||||||
deriving (Eq, Ord, Show, Read)
|
|
||||||
|
|
||||||
data OptionScreenFlag = NormalOptions | GameOverOptions
|
data OptionScreenFlag = NormalOptions | GameOverOptions
|
||||||
deriving (Eq, Ord, Show, Read)
|
deriving (Eq, Ord, Show, Read)
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
module Dodge.Data.World (
|
module Dodge.Data.World (
|
||||||
module Dodge.Data.World,
|
module Dodge.Data.World,
|
||||||
|
module Dodge.Data.SaveSlot,
|
||||||
module Dodge.Data.CWorld,
|
module Dodge.Data.CWorld,
|
||||||
module Dodge.Data.RightButtonOptions,
|
module Dodge.Data.RightButtonOptions,
|
||||||
module Dodge.Data.SoundOrigin,
|
module Dodge.Data.SoundOrigin,
|
||||||
module Dodge.Data.Hammer,
|
module Dodge.Data.Hammer,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Dodge.Data.SaveSlot
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
@@ -40,8 +42,13 @@ data World = World
|
|||||||
, _backspaceTimer :: Int
|
, _backspaceTimer :: Int
|
||||||
, _timeFlow :: TimeFlowStatus
|
, _timeFlow :: TimeFlowStatus
|
||||||
, _rbOptions :: RightButtonOptions
|
, _rbOptions :: RightButtonOptions
|
||||||
|
, _gameSlot :: GameSlot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data GameSlot = GameNum Int
|
||||||
|
| GameLoading SaveSlot
|
||||||
|
| GameStartScreen
|
||||||
|
|
||||||
data TimeFlowStatus
|
data TimeFlowStatus
|
||||||
= RewindingNow
|
= RewindingNow
|
||||||
| RewindingLastFrame
|
| RewindingLastFrame
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ defaultWorld =
|
|||||||
, _backspaceTimer = 0
|
, _backspaceTimer = 0
|
||||||
, _timeFlow = NormalTimeFlow
|
, _timeFlow = NormalTimeFlow
|
||||||
, _rbOptions = NoRightButtonOptions
|
, _rbOptions = NoRightButtonOptions
|
||||||
|
, _gameSlot = GameStartScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultCWorld :: CWorld
|
defaultCWorld :: CWorld
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ handlePressedKey True ScancodeBackspace u
|
|||||||
| otherwise = return $ Just $ u & uvWorld . backspaceTimer -~ 1
|
| otherwise = return $ Just $ u & uvWorld . backspaceTimer -~ 1
|
||||||
handlePressedKey True _ u = return $ Just u
|
handlePressedKey True _ u = return $ Just u
|
||||||
handlePressedKey _ scode u = case scode of
|
handlePressedKey _ scode u = case scode of
|
||||||
ScancodeF1 -> Just <$> (writeSaveSlot (SaveSlotNum 1) u >> return u)
|
-- ScancodeF1 -> Just <$> (writeSaveSlot (SaveSlotNum 1) u >> return u)
|
||||||
ScancodeF2 -> Just <$> readSaveSlot (SaveSlotNum 1) u
|
-- ScancodeF2 -> Just <$> readSaveSlot (SaveSlotNum 1) u
|
||||||
ScancodeF5 -> return . Just $ doQuicksave u
|
ScancodeF5 -> return . Just $ doQuicksave u
|
||||||
ScancodeF9 -> return . Just $ loadSaveSlot QuicksaveSlot u
|
ScancodeF9 -> return . Just $ loadSaveSlot QuicksaveSlot u
|
||||||
ScancodeSemicolon -> return . Just $ gotoTerminal u
|
ScancodeSemicolon -> return . Just $ gotoTerminal u
|
||||||
|
|||||||
+2
-2
@@ -36,7 +36,7 @@ pauseMenuOptions :: [MenuOption]
|
|||||||
pauseMenuOptions =
|
pauseMenuOptions =
|
||||||
basicKeyOptions
|
basicKeyOptions
|
||||||
[ Toggle (return . Just . startNewGame) (opText "NEW LEVEL")
|
[ Toggle (return . Just . startNewGame) (opText "NEW LEVEL")
|
||||||
, Toggle (return . Just . loadSaveSlot LevelStartSlot) (opText "RESTART")
|
, Toggle (return . Just . reloadLevelStart) (opText "RESTART")
|
||||||
, Toggle (pushScreen $ seedStartMenu "START FROM SEED") (opText "START FROM SEED")
|
, Toggle (pushScreen $ seedStartMenu "START FROM SEED") (opText "START FROM SEED")
|
||||||
, Toggle (pushScreen optionMenu) (opText "OPTIONS")
|
, Toggle (pushScreen optionMenu) (opText "OPTIONS")
|
||||||
, Toggle (pushScreen displayControls) (opText "CONTROLS")
|
, Toggle (pushScreen displayControls) (opText "CONTROLS")
|
||||||
@@ -63,7 +63,7 @@ trySeedFromClipboard u = do
|
|||||||
pushScreen
|
pushScreen
|
||||||
(seedStartMenu "CLIPBOARD UNUSABLE, NEED INTEGER")
|
(seedStartMenu "CLIPBOARD UNUSABLE, NEED INTEGER")
|
||||||
(u & menuLayers %~ tail)
|
(u & menuLayers %~ tail)
|
||||||
Just i -> return . Just $ startSeedGame i u
|
Just i -> return . Just $ startSeedGame 0 i u
|
||||||
|
|
||||||
slTitleOptions :: String -> [MenuOption] -> ScreenLayer
|
slTitleOptions :: String -> [MenuOption] -> ScreenLayer
|
||||||
slTitleOptions title ops = slTitleOptionsEff title ops (popScreen . writeConfig)
|
slTitleOptions title ops = slTitleOptionsEff title ops (popScreen . writeConfig)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ module Dodge.Render.Picture (
|
|||||||
fixedCoordPictures,
|
fixedCoordPictures,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Dodge.Base.Window
|
||||||
|
import Dodge.Render.List
|
||||||
import Dodge.Base.WinScale
|
import Dodge.Base.WinScale
|
||||||
import Dodge.Data.Universe
|
import Dodge.Data.Universe
|
||||||
import Dodge.Render.HUD
|
import Dodge.Render.HUD
|
||||||
@@ -10,17 +12,23 @@ import Geometry
|
|||||||
import Picture
|
import Picture
|
||||||
|
|
||||||
fixedCoordPictures :: Universe -> Picture
|
fixedCoordPictures :: Universe -> Picture
|
||||||
fixedCoordPictures u = case _menuLayers u of
|
fixedCoordPictures u =
|
||||||
[] ->
|
drawConcurrentMessage u <> case _menuLayers u of
|
||||||
pictures
|
[] ->
|
||||||
[ hudDrawings u
|
pictures
|
||||||
, customMouseCursor cfig w
|
[ hudDrawings u
|
||||||
]
|
, customMouseCursor cfig w
|
||||||
(lay : _) -> setDepth (-1) . winScale cfig $ menuScreen u lay
|
]
|
||||||
|
(lay : _) -> setDepth (-1) . winScale cfig $ menuScreen u lay
|
||||||
where
|
where
|
||||||
w = _uvWorld u
|
w = _uvWorld u
|
||||||
cfig = _uvConfig u
|
cfig = _uvConfig u
|
||||||
|
|
||||||
|
drawConcurrentMessage :: Universe -> Picture
|
||||||
|
drawConcurrentMessage uv = listPicturesAt (halfWidth cfig) (_windowY cfig - 50) cfig [text (_uvConcMessage uv)]
|
||||||
|
where
|
||||||
|
cfig = _uvConfig uv
|
||||||
|
|
||||||
customMouseCursor :: Configuration -> World -> Picture
|
customMouseCursor :: Configuration -> World -> Picture
|
||||||
customMouseCursor cfig w =
|
customMouseCursor cfig w =
|
||||||
winScale cfig
|
winScale cfig
|
||||||
|
|||||||
+30
-28
@@ -1,71 +1,73 @@
|
|||||||
module Dodge.Save (
|
module Dodge.Save (
|
||||||
saveWorldInEmptySlot,
|
saveWorldInSlot,
|
||||||
loadSaveSlot,
|
loadSaveSlot,
|
||||||
doQuicksave,
|
doQuicksave,
|
||||||
saveLevelStartSlot,
|
-- saveLevelStartSlot,
|
||||||
writeSaveSlot,
|
writeSaveSlot,
|
||||||
readSaveSlot,
|
readSaveSlot,
|
||||||
|
reloadLevelStart,
|
||||||
) where
|
) where
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
|
import Dodge.Data.SaveSlot
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import qualified Data.Aeson.Encode.Pretty as AEP
|
import qualified Data.Aeson.Encode.Pretty as AEP
|
||||||
import qualified Data.ByteString.Lazy as BS
|
import qualified Data.ByteString.Lazy as BS
|
||||||
import qualified Data.Map.Strict as M
|
|
||||||
import Dodge.Data.Universe
|
import Dodge.Data.Universe
|
||||||
--import qualified Data.Set as S
|
--import qualified Data.Set as S
|
||||||
import System.Directory
|
import System.Directory
|
||||||
|
|
||||||
writeSaveSlot :: SaveSlot -> Universe -> IO ()
|
writeSaveSlot :: SaveSlot -> Universe -> IO (Universe -> Universe)
|
||||||
writeSaveSlot ss u = do
|
writeSaveSlot ss u = do
|
||||||
createDirectoryIfMissing True "saveSlot"
|
createDirectoryIfMissing True "saveSlot"
|
||||||
BS.writeFile (saveSlotPath ss) $
|
BS.writeFile (saveSlotPath ss) $
|
||||||
AEP.encodePretty'
|
AEP.encodePretty'
|
||||||
(AEP.Config (AEP.Spaces 2) compare AEP.Generic False)
|
(AEP.Config (AEP.Spaces 2) compare AEP.Generic False)
|
||||||
(u ^. uvWorld . cWorld)
|
(u ^. uvWorld . cWorld)
|
||||||
|
return id
|
||||||
|
|
||||||
--writeFile (saveSlotPath ss) (show $ u ^. uvWorld . cWorld)
|
--writeFile (saveSlotPath ss) (show $ u ^. uvWorld . cWorld)
|
||||||
|
|
||||||
readSaveSlot :: SaveSlot -> Universe -> IO Universe
|
readSaveSlot :: SaveSlot -> IO (Universe -> Universe)
|
||||||
readSaveSlot ss uv = do
|
readSaveSlot ss = do
|
||||||
fExists <- doesFileExist $ saveSlotPath ss
|
fExists <- doesFileExist $ saveSlotPath ss
|
||||||
if fExists
|
if fExists
|
||||||
then do
|
then do
|
||||||
--cwstr <- readFile $ saveSlotPath ss
|
|
||||||
--case readMaybe cwstr of
|
|
||||||
cwstr <- decodeFileStrict $ saveSlotPath ss
|
cwstr <- decodeFileStrict $ saveSlotPath ss
|
||||||
case cwstr of
|
case cwstr of
|
||||||
Nothing -> putStrLn "loadSaveSlot failed to read saved file" >> return uv
|
Nothing -> putStrLn "loadSaveSlot failed to read saved file" >> return id
|
||||||
Just cw -> return $ uv & uvWorld . cWorld .~ cw
|
Just cw -> return $ \uv -> uv & uvWorld . cWorld .~ cw
|
||||||
else putStrLn "loadSaveSlot failed to find saved file" >> return uv
|
else putStrLn "loadSaveSlot failed to find saved file" >> return id
|
||||||
|
|
||||||
saveSlotPath :: SaveSlot -> String
|
saveSlotPath :: SaveSlot -> String
|
||||||
saveSlotPath (SaveSlotNum i) = "saveSlot/" ++ show i
|
saveSlotPath (SaveSlotNum i) = "saveSlot/" ++ show i
|
||||||
saveSlotPath QuicksaveSlot = "saveSlot/QuickSave"
|
saveSlotPath QuicksaveSlot = "saveSlot/QuickSave"
|
||||||
saveSlotPath LevelStartSlot = "saveSlot/LevelStartSave"
|
saveSlotPath (LevelStartSlot i) = "saveSlot/LevelStart" ++ show i
|
||||||
|
|
||||||
saveWorldInEmptySlot :: SaveSlot -> Universe -> Universe
|
|
||||||
saveWorldInEmptySlot slot w = case M.lookup slot $ _savedWorlds w of
|
|
||||||
Nothing -> w & savedWorlds %~ M.insert slot (clearKeys (_uvWorld w))
|
|
||||||
_ -> w
|
|
||||||
|
|
||||||
saveWorldInSlot :: SaveSlot -> Universe -> Universe
|
saveWorldInSlot :: SaveSlot -> Universe -> Universe
|
||||||
saveWorldInSlot slot u = u & savedWorlds %~ M.insert slot (clearKeys (_uvWorld u))
|
saveWorldInSlot slot u = u & uvConcEffects ?~
|
||||||
|
writeSaveSlot slot u
|
||||||
|
|
||||||
|
reloadLevelStart :: Universe -> Universe
|
||||||
|
reloadLevelStart u = case u ^. uvWorld . gameSlot of
|
||||||
|
GameNum i -> loadSaveSlot (LevelStartSlot i) u
|
||||||
|
_ -> u
|
||||||
|
|
||||||
loadSaveSlot :: SaveSlot -> Universe -> Universe
|
loadSaveSlot :: SaveSlot -> Universe -> Universe
|
||||||
loadSaveSlot slot u =
|
loadSaveSlot slot = (uvConcEffects ?~ readSaveSlot slot)
|
||||||
maybe
|
. (uvWorld . gameSlot .~ GameLoading slot)
|
||||||
u
|
-- maybe
|
||||||
(\w -> u & menuLayers .~ [] & uvWorld .~ w)
|
-- u
|
||||||
$ M.lookup slot (_savedWorlds u)
|
-- (\w -> u & menuLayers .~ [] & uvWorld .~ w)
|
||||||
|
-- $ M.lookup slot (_savedWorlds u)
|
||||||
|
|
||||||
doQuicksave :: Universe -> Universe
|
doQuicksave :: Universe -> Universe
|
||||||
doQuicksave = saveWorldInSlot QuicksaveSlot
|
doQuicksave = saveWorldInSlot QuicksaveSlot
|
||||||
|
|
||||||
clearKeys :: World -> World
|
--clearKeys :: World -> World
|
||||||
clearKeys = (keys .~ mempty) . (mouseButtons .~ mempty)
|
--clearKeys = (keys .~ mempty) . (mouseButtons .~ mempty)
|
||||||
|
|
||||||
saveLevelStartSlot :: Universe -> Universe
|
--saveLevelStartSlot :: Universe -> Universe
|
||||||
--saveLevelStartSlot = id
|
----saveLevelStartSlot = id
|
||||||
saveLevelStartSlot = saveWorldInSlot LevelStartSlot
|
--saveLevelStartSlot = saveWorldInSlot LevelStartSlot
|
||||||
|
|
||||||
--saveLevelStartSlot = sideEffects %~ (fmap (undefined) . )
|
--saveLevelStartSlot = sideEffects %~ (fmap (undefined) . )
|
||||||
|
|||||||
@@ -1,21 +1,30 @@
|
|||||||
module Dodge.StartNewGame where
|
module Dodge.StartNewGame where
|
||||||
|
|
||||||
|
import Dodge.Save
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Dodge.Data.Universe
|
import Dodge.Data.Universe
|
||||||
import Dodge.LevelGen
|
import Dodge.LevelGen
|
||||||
import System.Random
|
import System.Random
|
||||||
|
|
||||||
startNewGame :: Universe -> Universe
|
startNewGame :: Universe -> Universe
|
||||||
startNewGame u = startSeedGame i u
|
startNewGame u = case u ^. uvWorld . gameSlot of
|
||||||
|
GameNum i -> startNewGameInSlot i u
|
||||||
|
GameLoading{} -> u
|
||||||
|
GameStartScreen -> startNewGameInSlot 0 u -- this needs to be changed
|
||||||
|
|
||||||
|
startNewGameInSlot :: Int -> Universe -> Universe
|
||||||
|
startNewGameInSlot slot u = startSeedGame slot i u
|
||||||
where
|
where
|
||||||
i = fst $ randomR (0, maxBound) (_randGen (_uvWorld u))
|
i = fst $ randomR (0, maxBound) (_randGen (_uvWorld u))
|
||||||
|
|
||||||
startSeedGame :: Int -> Universe -> Universe
|
startSeedGame :: Int -> Int -> Universe -> Universe
|
||||||
startSeedGame i u =
|
startSeedGame slot i u =
|
||||||
u
|
u
|
||||||
& menuLayers .~ [WaitScreen (const "GENERATING...") 1]
|
& menuLayers .~ [WaitScreen (const "GENERATING...") 1]
|
||||||
& uvIOEffects .~ \_ -> do
|
& uvIOEffects .~ \_ -> do
|
||||||
w <- generateWorldFromSeed i
|
w <- generateWorldFromSeed i
|
||||||
return $
|
return $
|
||||||
u & menuLayers .~ [] & uvWorld .~ w
|
u & menuLayers .~ [] & uvWorld .~ w
|
||||||
& savedWorlds . at LevelStartSlot ?~ w
|
& uvWorld . gameSlot .~ GameNum slot
|
||||||
|
& saveWorldInSlot (LevelStartSlot slot)
|
||||||
|
-- & savedWorlds . at LevelStartSlot ?~ w
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ import Dodge.Data.Universe
|
|||||||
|
|
||||||
testStringInit :: Universe -> [String]
|
testStringInit :: Universe -> [String]
|
||||||
testStringInit _ = []
|
testStringInit _ = []
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ import Sound.Data
|
|||||||
|
|
||||||
{- For most menus the only way to change the world is using event handling. -}
|
{- For most menus the only way to change the world is using event handling. -}
|
||||||
updateUniverse :: Universe -> Universe
|
updateUniverse :: Universe -> Universe
|
||||||
updateUniverse u = case _menuLayers u of
|
updateUniverse u = (uvConcEffects .~ Nothing) $ case _menuLayers u of
|
||||||
(WaitScreen s i : _)
|
(WaitScreen s i : _)
|
||||||
| i < 1 -> u & over uvWorld doWorldEvents
|
| i < 1 -> u & over uvWorld doWorldEvents
|
||||||
| otherwise -> u & menuLayers %~ ((WaitScreen s (i -1) :) . tail)
|
| otherwise -> u & menuLayers %~ ((WaitScreen s (i -1) :) . tail)
|
||||||
|
|||||||
+76
@@ -8,6 +8,7 @@ This module sets up an SDL window which may be updated using a simple game loop.
|
|||||||
-}
|
-}
|
||||||
module Loop (
|
module Loop (
|
||||||
setupLoop,
|
setupLoop,
|
||||||
|
setupConLoop,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
@@ -89,3 +90,78 @@ applyEventIO fn mw e = case eventPayload e of
|
|||||||
mupdate = case mw of
|
mupdate = case mw of
|
||||||
Nothing -> return Nothing
|
Nothing -> return Nothing
|
||||||
Just w -> fn w e
|
Just w -> fn w e
|
||||||
|
|
||||||
|
-- | Create a game loop with an SDL window.
|
||||||
|
setupConLoop ::
|
||||||
|
-- | Target seconds per frame
|
||||||
|
Int ->
|
||||||
|
-- | Window title
|
||||||
|
T.Text ->
|
||||||
|
WindowConfig ->
|
||||||
|
-- | Function for cleaning up parameters, applied when exiting loop.
|
||||||
|
(world -> IO ()) ->
|
||||||
|
-- | Initial simulation state.
|
||||||
|
IO world ->
|
||||||
|
-- | Concurrent effects
|
||||||
|
(world -> Maybe (IO (world -> world))) ->
|
||||||
|
-- | update, called once per frame. Allows for side effects such as rendering.
|
||||||
|
(world -> IO world) ->
|
||||||
|
-- | SDL Event handling, once per frame. Evaluating 'Nothing' exits the loop.
|
||||||
|
(world -> Event -> IO (Maybe world)) ->
|
||||||
|
IO ()
|
||||||
|
setupConLoop spf title winconfig paramCleanup ioStartWorld coneffs sideEffects eventFn = do
|
||||||
|
initializeAll
|
||||||
|
themvar <- newEmptyMVar
|
||||||
|
bracket
|
||||||
|
(createWindow title winconfig)
|
||||||
|
destroyWindow
|
||||||
|
$ \window -> bracket
|
||||||
|
(glCreateContext window)
|
||||||
|
(\con -> GL.finish >> glDeleteContext con)
|
||||||
|
$ \_ ->
|
||||||
|
bracket
|
||||||
|
ioStartWorld
|
||||||
|
paramCleanup
|
||||||
|
(doConLoop themvar spf window coneffs sideEffects eventFn)
|
||||||
|
|
||||||
|
-- | The internal loop.
|
||||||
|
doConLoop ::
|
||||||
|
-- | The mvar for concurrency
|
||||||
|
MVar (world -> world) ->
|
||||||
|
-- | target msec per frame
|
||||||
|
Int ->
|
||||||
|
-- | The SDL window.
|
||||||
|
Window ->
|
||||||
|
-- | Concurrent effects
|
||||||
|
(world -> Maybe (IO (world -> world))) ->
|
||||||
|
-- | simulation update.
|
||||||
|
(world -> IO world) ->
|
||||||
|
-- | SDL Event handling.
|
||||||
|
(world -> Event -> IO (Maybe world)) ->
|
||||||
|
-- | Current simulation state.
|
||||||
|
world ->
|
||||||
|
IO ()
|
||||||
|
doConLoop themvar spf window coneffs worldSideEffects eventFn startWorld = do
|
||||||
|
startTicks <- ticks
|
||||||
|
mconupdate <- tryTakeMVar themvar
|
||||||
|
case coneffs startWorld of
|
||||||
|
Nothing -> return ()
|
||||||
|
Just acc -> do
|
||||||
|
_ <- forkIO $ do
|
||||||
|
up <- acc
|
||||||
|
putMVar themvar up
|
||||||
|
return ()
|
||||||
|
let startWorld' = case mconupdate of
|
||||||
|
Just conupdate -> conupdate startWorld
|
||||||
|
Nothing -> startWorld
|
||||||
|
worldAfterSimStep <- worldSideEffects startWorld'
|
||||||
|
glSwapWindow window
|
||||||
|
maybeUpdatedWorld <- pollEvents >>= foldM (applyEventIO eventFn) (Just worldAfterSimStep)
|
||||||
|
case maybeUpdatedWorld of
|
||||||
|
Just updatedWorld -> do
|
||||||
|
performGC
|
||||||
|
endTicks <- ticks -- it might be better to use System.Clock (monotonic)
|
||||||
|
let theDelay = max 0 (spf + fromIntegral startTicks - fromIntegral endTicks)
|
||||||
|
threadDelay (theDelay * 1000)
|
||||||
|
doConLoop themvar spf window coneffs worldSideEffects eventFn updatedWorld
|
||||||
|
Nothing -> return ()
|
||||||
|
|||||||
Reference in New Issue
Block a user