Compare commits
57 commits
multiwindo
...
master
Author | SHA1 | Date | |
---|---|---|---|
d0465fa3e1 | |||
|
523265b2dc | ||
|
f92949b7f0 | ||
1976a6c4c1 | |||
086eb3e1f2 | |||
818a1e424c | |||
445b5807e0 | |||
f7e7c9dcc8 | |||
5bef189c30 | |||
efbb9b4c5e | |||
76225dff4b | |||
eda79eb626 | |||
04d6905de1 | |||
b56ed86e45 | |||
af0dcc16da | |||
81a76d4eb3 | |||
1b8d754cd6 | |||
60d38217c8 | |||
17978b0bd7 | |||
9b7b1c6167 | |||
2696dedbb4 | |||
eb3357cfac | |||
4a5cc6d0e0 | |||
e81408e54f | |||
ccd8b7eeaf | |||
cd662da0b7 | |||
bca6fd69e1 | |||
2ea955d37d | |||
ef1ba1f988 | |||
3332370012 | |||
fc6bfa1ca5 | |||
3b9a366830 | |||
0aa971a343 | |||
bc08ca55a4 | |||
8b26781217 | |||
f52e94e82a | |||
8785405420 | |||
d6804f95fb | |||
a7eac3e392 | |||
9ac25d5fca | |||
7df3bb8cf6 | |||
c8d3ad6c56 | |||
8af8193a0a | |||
44ec3cc8e0 | |||
786bfe1b21 | |||
13f67c6cd1 | |||
65d47f7477 | |||
e4103e11da | |||
18775515d8 | |||
9ece773de8 | |||
2dda8f9db0 | |||
6d4f7d3227 | |||
d73609297c | |||
25fd9acffd | |||
8ff4b216c3 | |||
c1ed74bb66 | |||
2ac3b007a9 |
26 changed files with 804 additions and 738 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,3 +16,5 @@ cabal.project.local*
|
||||||
dist*/
|
dist*/
|
||||||
report.html
|
report.html
|
||||||
*.bak
|
*.bak
|
||||||
|
.direnv/
|
||||||
|
result
|
||||||
|
|
|
@ -6,7 +6,7 @@ name: affection
|
||||||
-- PVP summary: +-+------- breaking API changes
|
-- PVP summary: +-+------- breaking API changes
|
||||||
-- | | +----- non-breaking API additions
|
-- | | +----- non-breaking API additions
|
||||||
-- | | | +--- code changes with no API change
|
-- | | | +--- code changes with no API change
|
||||||
version: 0.0.0.9
|
version: 0.0.0.10
|
||||||
synopsis: A simple Game Engine using SDL
|
synopsis: A simple Game Engine using SDL
|
||||||
description: This package contains Affection, a simple game engine
|
description: This package contains Affection, a simple game engine
|
||||||
written in Haskell using SDL.
|
written in Haskell using SDL.
|
||||||
|
@ -16,7 +16,7 @@ homepage: https://github.com/nek0/affection#readme
|
||||||
license: LGPL-3
|
license: LGPL-3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: nek0
|
author: nek0
|
||||||
maintainer: nek0@chelnok.de
|
maintainer: nek0@nek0.eu
|
||||||
category: Game
|
category: Game
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
extra-source-files: ChangeLog.md
|
extra-source-files: ChangeLog.md
|
||||||
|
@ -25,11 +25,11 @@ extra-source-files: ChangeLog.md
|
||||||
-- README.
|
-- README.
|
||||||
-- extra-source-files:
|
-- extra-source-files:
|
||||||
|
|
||||||
cabal-version: >=1.10
|
cabal-version: >=2.0
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/nek0/affection
|
location: https://gitea.nek0.eu/nek0/affection.git
|
||||||
|
|
||||||
flag verbose
|
flag verbose
|
||||||
description: Enable verbose debug messages
|
description: Enable verbose debug messages
|
||||||
|
@ -67,6 +67,7 @@ library
|
||||||
exposed-modules: Affection
|
exposed-modules: Affection
|
||||||
, Affection.Logging
|
, Affection.Logging
|
||||||
, Affection.Types
|
, Affection.Types
|
||||||
|
, Affection.Class
|
||||||
, Affection.StateMachine
|
, Affection.StateMachine
|
||||||
, Affection.Util
|
, Affection.Util
|
||||||
, Affection.MessageBus
|
, Affection.MessageBus
|
||||||
|
@ -84,35 +85,39 @@ library
|
||||||
, Affection.Subsystems.AffectionMouse
|
, Affection.Subsystems.AffectionMouse
|
||||||
, Affection.Subsystems.AffectionJoystick
|
, Affection.Subsystems.AffectionJoystick
|
||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
|
, TypeFamilies
|
||||||
|
, AllowAmbiguousTypes
|
||||||
|
|
||||||
-- Modules included in this library but not exported.
|
-- Modules included in this library but not exported.
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
|
|
||||||
-- LANGUAGE extensions used by modules in this package.
|
-- LANGUAGE extensions used by modules in this package.
|
||||||
other-extensions: GADTs
|
-- other-extensions: GADTs
|
||||||
, KindSignatures
|
-- , KindSignatures
|
||||||
, FlexibleInstances
|
-- , FlexibleInstances
|
||||||
, MultiParamTypeClasses
|
-- , MultiParamTypeClasses
|
||||||
, UndecidableInstances
|
-- , UndecidableInstances
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall
|
ghc-options: -threaded -Wall
|
||||||
-- Other library packages from which modules are imported.
|
-- Other library packages from which modules are imported.
|
||||||
build-depends: base >=4.9 && < 5
|
build-depends: base >=4.9 && < 5
|
||||||
, sdl2 >= 2.4 && < 2.5
|
, sdl2 >= 2.5
|
||||||
, linear
|
, linear
|
||||||
, text
|
, text
|
||||||
, mtl
|
, mtl
|
||||||
, monad-loops
|
, monad-loops
|
||||||
, monad-parallel
|
, monad-parallel
|
||||||
, containers
|
, containers
|
||||||
, clock
|
, clock >= 0.8
|
||||||
, glib
|
, glib
|
||||||
, bytestring
|
, bytestring
|
||||||
, OpenGL
|
, OpenGL
|
||||||
|
, OpenGLRaw
|
||||||
, stm
|
, stm
|
||||||
, uuid
|
, uuid
|
||||||
, vector
|
, vector
|
||||||
|
, resourcet
|
||||||
|
|
||||||
-- This example shows the message system. only makes sense when compiling with
|
-- This example shows the message system. only makes sense when compiling with
|
||||||
-- verbose flag.
|
-- verbose flag.
|
||||||
|
@ -122,12 +127,11 @@ executable example00
|
||||||
ghc-options: -threaded -Wall
|
ghc-options: -threaded -Wall
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
if flag(examples)
|
build-depends: base >=4.9 && < 5
|
||||||
build-depends: base >=4.9 && < 5
|
, affection
|
||||||
, affection
|
, sdl2 >= 2.5
|
||||||
, sdl2
|
, stm
|
||||||
, stm
|
if !flag(examples)
|
||||||
else
|
|
||||||
buildable: False
|
buildable: False
|
||||||
|
|
||||||
-- A small game of life implementation
|
-- A small game of life implementation
|
||||||
|
@ -138,19 +142,18 @@ executable example01
|
||||||
ghc-options: -threaded -Wall
|
ghc-options: -threaded -Wall
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
if flag(examples)
|
build-depends: base >=4.9 && < 5
|
||||||
build-depends: base >=4.9 && < 5
|
, affection
|
||||||
, affection
|
, sdl2 >= 2.5
|
||||||
, sdl2
|
, stm
|
||||||
, stm
|
, OpenGL
|
||||||
, OpenGL
|
, random
|
||||||
, random
|
, containers
|
||||||
, containers
|
, linear
|
||||||
, linear
|
, matrix
|
||||||
, matrix
|
, nanovg >= 0.6.0.0
|
||||||
, nanovg >= 0.6.0.0
|
, deepseq
|
||||||
, deepseq
|
if !flag(examples)
|
||||||
else
|
|
||||||
buildable: False
|
buildable: False
|
||||||
|
|
||||||
-- Another small game of life implementation
|
-- Another small game of life implementation
|
||||||
|
@ -161,17 +164,16 @@ executable example02
|
||||||
ghc-options: -threaded -Wall
|
ghc-options: -threaded -Wall
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
default-extensions: OverloadedStrings
|
default-extensions: OverloadedStrings
|
||||||
if flag(examples)
|
build-depends: base >=4.9 && < 5
|
||||||
build-depends: base >=4.9 && < 5
|
, affection
|
||||||
, affection
|
, sdl2 >= 2.5
|
||||||
, sdl2
|
, stm
|
||||||
, stm
|
, OpenGL
|
||||||
, OpenGL
|
, random
|
||||||
, random
|
, containers
|
||||||
, containers
|
, linear
|
||||||
, linear
|
, matrix
|
||||||
, matrix
|
, nanovg >= 0.6.0.0
|
||||||
, nanovg >= 0.6.0.0
|
, deepseq
|
||||||
, deepseq
|
if !flag(examples)
|
||||||
else
|
|
||||||
buildable: False
|
buildable: False
|
||||||
|
|
4
cabal.project
Normal file
4
cabal.project
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
packages: ./*.cabal
|
||||||
|
|
||||||
|
package affection
|
||||||
|
flags: +examples
|
|
@ -1,21 +1,34 @@
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
import Affection as A
|
import Affection as A
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Concurrent.MVar
|
||||||
import Control.Monad (when)
|
import Control.Monad
|
||||||
|
|
||||||
import qualified SDL hiding (Window(..))
|
import qualified SDL
|
||||||
|
|
||||||
import Data.Maybe (isJust, fromJust)
|
import Data.Maybe (isJust, fromJust)
|
||||||
|
import Data.String
|
||||||
|
|
||||||
data StateData = StateData
|
data StateData = StateData
|
||||||
{ sdSubs :: Subsystems
|
{ sdSubs :: Subsystems
|
||||||
, sdJoys :: [SDL.Joystick]
|
, sdJoys :: MVar [SDL.Joystick]
|
||||||
|
, doNextStep :: MVar Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance Affectionate StateData where
|
||||||
|
loadState = load
|
||||||
|
preLoop = pre
|
||||||
|
handleEvents = handle
|
||||||
|
update = Main.update
|
||||||
|
draw = Main.draw
|
||||||
|
cleanUp = clean
|
||||||
|
hasNextStep = liftIO . readMVar . doNextStep
|
||||||
|
|
||||||
data Subsystems = Subsystems
|
data Subsystems = Subsystems
|
||||||
{ subWindow :: Main.Window
|
{ subWindow :: Main.Window
|
||||||
, subMouse :: Main.Mouse
|
, subMouse :: Main.Mouse
|
||||||
|
@ -23,36 +36,13 @@ data Subsystems = Subsystems
|
||||||
, subJoystick :: Main.Joystick
|
, subJoystick :: Main.Joystick
|
||||||
}
|
}
|
||||||
|
|
||||||
newtype Window = Window (TVar [(UUID, WindowMessage -> Affection StateData ())])
|
newtype Window = Window (TVar [(UUID, WindowMessage -> Affection ())])
|
||||||
newtype Mouse = Mouse (TVar [(UUID, MouseMessage -> Affection StateData ())])
|
newtype Mouse = Mouse (TVar [(UUID, MouseMessage -> Affection ())])
|
||||||
newtype Keyboard = Keyboard (TVar [(UUID, KeyboardMessage -> Affection StateData ())])
|
newtype Keyboard = Keyboard (TVar [(UUID, KeyboardMessage -> Affection ())])
|
||||||
newtype Joystick = Joystick (TVar [(UUID, JoystickMessage -> Affection StateData ())])
|
newtype Joystick = Joystick (TVar [(UUID, JoystickMessage -> Affection ())])
|
||||||
|
|
||||||
generalSubscribers
|
instance Participant Main.Window where
|
||||||
:: TVar [(UUID, msg -> Affection StateData ())]
|
type Mesg Main.Window = WindowMessage
|
||||||
-> Affection StateData [msg -> Affection StateData ()]
|
|
||||||
generalSubscribers t = do
|
|
||||||
subTups <- liftIO $ readTVarIO t
|
|
||||||
return $ map snd subTups
|
|
||||||
|
|
||||||
generalSubscribe
|
|
||||||
:: TVar [(UUID, msg -> Affection StateData ())]
|
|
||||||
-> (msg -> Affection StateData())
|
|
||||||
-> Affection StateData UUID
|
|
||||||
generalSubscribe t funct = do
|
|
||||||
uuid <- genUUID
|
|
||||||
liftIO $ atomically $ modifyTVar' t ((uuid, funct) :)
|
|
||||||
return uuid
|
|
||||||
|
|
||||||
generalUnSubscribe
|
|
||||||
:: TVar [(UUID, msg -> Affection StateData ())]
|
|
||||||
-> UUID
|
|
||||||
-> Affection StateData ()
|
|
||||||
generalUnSubscribe t uuid =
|
|
||||||
liftIO $ atomically $ modifyTVar' t (filter (\(u, _) -> u /= uuid))
|
|
||||||
|
|
||||||
instance Participant Main.Window StateData where
|
|
||||||
type Mesg Main.Window StateData = WindowMessage
|
|
||||||
|
|
||||||
partSubscribers (Window t) = generalSubscribers t
|
partSubscribers (Window t) = generalSubscribers t
|
||||||
|
|
||||||
|
@ -60,11 +50,11 @@ instance Participant Main.Window StateData where
|
||||||
|
|
||||||
partUnSubscribe (Window t) = generalUnSubscribe t
|
partUnSubscribe (Window t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Main.Window StateData where
|
instance SDLSubsystem Main.Window where
|
||||||
consumeSDLEvents = consumeSDLWindowEvents
|
consumeSDLEvents = consumeSDLWindowEvents
|
||||||
|
|
||||||
instance Participant Mouse StateData where
|
instance Participant Mouse where
|
||||||
type Mesg Mouse StateData = MouseMessage
|
type Mesg Mouse = MouseMessage
|
||||||
|
|
||||||
partSubscribers (Mouse t) = generalSubscribers t
|
partSubscribers (Mouse t) = generalSubscribers t
|
||||||
|
|
||||||
|
@ -72,11 +62,11 @@ instance Participant Mouse StateData where
|
||||||
|
|
||||||
partUnSubscribe (Mouse t) = generalUnSubscribe t
|
partUnSubscribe (Mouse t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Mouse StateData where
|
instance SDLSubsystem Mouse where
|
||||||
consumeSDLEvents = consumeSDLMouseEvents
|
consumeSDLEvents = consumeSDLMouseEvents
|
||||||
|
|
||||||
instance Participant Keyboard StateData where
|
instance Participant Keyboard where
|
||||||
type Mesg Keyboard StateData = KeyboardMessage
|
type Mesg Keyboard = KeyboardMessage
|
||||||
|
|
||||||
partSubscribers (Keyboard t) = generalSubscribers t
|
partSubscribers (Keyboard t) = generalSubscribers t
|
||||||
|
|
||||||
|
@ -84,11 +74,11 @@ instance Participant Keyboard StateData where
|
||||||
|
|
||||||
partUnSubscribe (Keyboard t) = generalUnSubscribe t
|
partUnSubscribe (Keyboard t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Keyboard StateData where
|
instance SDLSubsystem Keyboard where
|
||||||
consumeSDLEvents = consumeSDLKeyboardEvents
|
consumeSDLEvents = consumeSDLKeyboardEvents
|
||||||
|
|
||||||
instance Participant Joystick StateData where
|
instance Participant Joystick where
|
||||||
type Mesg Joystick StateData = JoystickMessage
|
type Mesg Joystick = JoystickMessage
|
||||||
|
|
||||||
partSubscribers (Joystick t) = generalSubscribers t
|
partSubscribers (Joystick t) = generalSubscribers t
|
||||||
|
|
||||||
|
@ -96,7 +86,7 @@ instance Participant Joystick StateData where
|
||||||
|
|
||||||
partUnSubscribe (Joystick t) = generalUnSubscribe t
|
partUnSubscribe (Joystick t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Joystick StateData where
|
instance SDLSubsystem Joystick where
|
||||||
consumeSDLEvents = consumeSDLJoystickEvents
|
consumeSDLEvents = consumeSDLJoystickEvents
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
|
@ -109,21 +99,14 @@ main = do
|
||||||
[
|
[
|
||||||
( 0
|
( 0
|
||||||
, SDL.defaultWindow
|
, SDL.defaultWindow
|
||||||
{ SDL.windowOpenGL = Just SDL.defaultOpenGL
|
{ SDL.windowGraphicsContext = SDL.OpenGLContext SDL.defaultOpenGL
|
||||||
{ SDL.glProfile = SDL.Core SDL.Normal 3 3
|
{ SDL.glProfile = SDL.Core SDL.Normal 3 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
, SDL.Windowed
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
, initScreenMode = SDL.Windowed
|
} :: AffectionConfig StateData
|
||||||
, canvasSize = Nothing
|
|
||||||
, loadState = load
|
|
||||||
, preLoop = pre
|
|
||||||
, eventLoop = handle
|
|
||||||
, updateLoop = update
|
|
||||||
, drawLoop = draw
|
|
||||||
, cleanUp = clean
|
|
||||||
}
|
|
||||||
withAffection conf
|
withAffection conf
|
||||||
|
|
||||||
load :: IO StateData
|
load :: IO StateData
|
||||||
|
@ -134,57 +117,80 @@ load =
|
||||||
<*> (Keyboard <$> newTVarIO [])
|
<*> (Keyboard <$> newTVarIO [])
|
||||||
<*> (Joystick <$> newTVarIO [])
|
<*> (Joystick <$> newTVarIO [])
|
||||||
)
|
)
|
||||||
<*> return []
|
<*> newMVar []
|
||||||
|
<*> newMVar True
|
||||||
|
|
||||||
pre :: Affection StateData ()
|
pre :: StateData -> Affection ()
|
||||||
pre = do
|
pre sd = do
|
||||||
sd <- getAffection
|
_ <- partSubscribe (subKeyboard $ sdSubs sd) (exitOnQ (doNextStep sd))
|
||||||
_ <- partSubscribe (subKeyboard $ sdSubs sd) exitOnQ
|
_ <- partSubscribe (subWindow $ sdSubs sd) (exitOnWindowClose (doNextStep sd))
|
||||||
_ <- partSubscribe (subWindow $ sdSubs sd) exitOnWindowClose
|
_ <- partSubscribe (subJoystick $ sdSubs sd) (joyConnectDisconnect (sdJoys sd))
|
||||||
_ <- partSubscribe (subJoystick $ sdSubs sd) joyConnectDisconnect
|
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
exitOnQ :: KeyboardMessage -> Affection StateData ()
|
exitOnQ :: MVar Bool -> KeyboardMessage -> Affection ()
|
||||||
exitOnQ (MsgKeyboardEvent _ _ _ _ sym) =
|
exitOnQ nextStep (MsgKeyboardEvent _ _ _ _ sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeQ -> do
|
SDL.KeycodeQ -> do
|
||||||
liftIO $ logIO Debug "Yo dog I heard..."
|
liftIO $ logIO Debug "Yo dog I heard..."
|
||||||
quit
|
void $ liftIO $ swapMVar nextStep False
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
exitOnWindowClose :: WindowMessage -> Affection StateData ()
|
exitOnWindowClose :: MVar Bool -> WindowMessage -> Affection ()
|
||||||
exitOnWindowClose wm =
|
exitOnWindowClose nextStep wm =
|
||||||
case wm of
|
case wm of
|
||||||
MsgWindowClose _ _ -> do
|
MsgWindowClose _ _ -> do
|
||||||
liftIO $ logIO Debug "I heard another one..."
|
liftIO $ logIO Debug "I heard another one..."
|
||||||
quit
|
void $ liftIO $ swapMVar nextStep False
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
joyConnectDisconnect :: JoystickMessage -> Affection StateData ()
|
joyConnectDisconnect :: MVar [SDL.Joystick] -> JoystickMessage -> Affection ()
|
||||||
joyConnectDisconnect msg = do
|
joyConnectDisconnect mvjs msg = do
|
||||||
mj <- joystickAutoConnect msg
|
mj <- joystickAutoConnect msg
|
||||||
when (isJust mj) $ do
|
when (isJust mj) $ do
|
||||||
sd <- getAffection
|
js <- liftIO $ readMVar mvjs
|
||||||
putAffection sd
|
void $ liftIO $ swapMVar mvjs (fromJust mj : js)
|
||||||
{ sdJoys = fromJust mj : sdJoys sd
|
js <- liftIO $ readMVar mvjs
|
||||||
}
|
njs <- joystickAutoDisconnect js msg
|
||||||
sd <- getAffection
|
liftIO $ putMVar mvjs njs
|
||||||
njs <- joystickAutoDisconnect (sdJoys sd) msg
|
|
||||||
putAffection sd
|
|
||||||
{ sdJoys = njs
|
|
||||||
}
|
|
||||||
|
|
||||||
handle :: [SDL.EventPayload] -> Affection StateData ()
|
handle :: StateData -> [SDL.EventPayload] -> Affection ()
|
||||||
handle es = do
|
handle sd es = do
|
||||||
(Subsystems a b c d) <- sdSubs <$> getAffection
|
let (Subsystems a b c d) = sdSubs sd
|
||||||
leftovers <- consumeSDLEvents a
|
leftovers <- consumeSDLEvents a
|
||||||
=<< consumeSDLEvents b
|
=<< consumeSDLEvents b
|
||||||
=<< consumeSDLEvents c
|
=<< consumeSDLEvents c
|
||||||
=<< consumeSDLEvents d es
|
=<< consumeSDLEvents d es
|
||||||
mapM_ (\e -> liftIO $ logIO Verbose $ "LEFTOVER: " ++ show e) leftovers
|
mapM_ (\e -> liftIO $ logIO Verbose $ "LEFTOVER: " <> fromString (show e))
|
||||||
|
leftovers
|
||||||
|
|
||||||
update _ = return ()
|
update :: StateData -> Double -> Affection ()
|
||||||
|
update _ _ = return ()
|
||||||
|
|
||||||
draw = return ()
|
draw :: StateData -> Affection ()
|
||||||
|
draw _ = return ()
|
||||||
|
|
||||||
|
clean :: StateData -> IO ()
|
||||||
clean _ = return ()
|
clean _ = return ()
|
||||||
|
|
||||||
|
generalSubscribers
|
||||||
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
|
-> Affection [msg -> Affection ()]
|
||||||
|
generalSubscribers t = do
|
||||||
|
subTups <- liftIO $ readTVarIO t
|
||||||
|
return $ map snd subTups
|
||||||
|
|
||||||
|
generalSubscribe
|
||||||
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
|
-> (msg -> Affection())
|
||||||
|
-> Affection UUID
|
||||||
|
generalSubscribe t funct = do
|
||||||
|
uuid <- genUUID
|
||||||
|
liftIO $ atomically $ modifyTVar' t ((uuid, funct) :)
|
||||||
|
return uuid
|
||||||
|
|
||||||
|
generalUnSubscribe
|
||||||
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
|
-> UUID
|
||||||
|
-> Affection ()
|
||||||
|
generalUnSubscribe t uuid =
|
||||||
|
liftIO $ atomically $ modifyTVar' t (filter (\(u, _) -> u /= uuid))
|
||||||
|
|
|
@ -3,30 +3,36 @@
|
||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||||
|
|
||||||
import Affection as A
|
import Affection as A
|
||||||
import SDL (($=))
|
|
||||||
import qualified SDL
|
import qualified SDL
|
||||||
import qualified Graphics.Rendering.OpenGL as GL
|
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Concurrent.MVar
|
||||||
import Control.Monad (when)
|
import Control.Monad
|
||||||
import Control.DeepSeq (deepseq)
|
import Control.DeepSeq (deepseq)
|
||||||
|
|
||||||
import Data.Matrix as M
|
import Data.Matrix as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
import Data.String
|
||||||
|
|
||||||
import System.Random (randomRIO)
|
import System.Random (randomRIO)
|
||||||
|
|
||||||
import NanoVG hiding (V2(..))
|
import NanoVG hiding (V2(..))
|
||||||
|
|
||||||
import Linear
|
|
||||||
|
|
||||||
import Foreign.C.Types (CInt(..))
|
import Foreign.C.Types (CInt(..))
|
||||||
|
|
||||||
-- internal imports
|
-- internal imports
|
||||||
|
|
||||||
import Types
|
import Types
|
||||||
|
|
||||||
|
instance Affectionate UserData where
|
||||||
|
loadState = load
|
||||||
|
preLoop = pre
|
||||||
|
handleEvents = handle
|
||||||
|
update = Main.update
|
||||||
|
draw = Main.draw
|
||||||
|
cleanUp = clean
|
||||||
|
hasNextStep = liftIO . readMVar . doNextStep
|
||||||
|
|
||||||
foreign import ccall unsafe "glewInit"
|
foreign import ccall unsafe "glewInit"
|
||||||
glewInit :: IO CInt
|
glewInit :: IO CInt
|
||||||
|
|
||||||
|
@ -40,23 +46,16 @@ main = do
|
||||||
[
|
[
|
||||||
( 0
|
( 0
|
||||||
, SDL.defaultWindow
|
, SDL.defaultWindow
|
||||||
{ SDL.windowOpenGL = Just SDL.defaultOpenGL
|
{ SDL.windowGraphicsContext = SDL.OpenGLContext SDL.defaultOpenGL
|
||||||
{ SDL.glProfile = SDL.Core SDL.Normal 3 3
|
{ SDL.glProfile = SDL.Core SDL.Normal 3 3
|
||||||
}
|
}
|
||||||
, SDL.windowInitialSize = SDL.V2 600 600
|
, SDL.windowInitialSize = SDL.V2 600 600
|
||||||
, SDL.windowResizable = True
|
, SDL.windowResizable = True
|
||||||
}
|
}
|
||||||
|
, SDL.Windowed
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
, initScreenMode = SDL.Windowed
|
} :: AffectionConfig UserData
|
||||||
, canvasSize = Nothing
|
|
||||||
, loadState = load
|
|
||||||
, preLoop = pre
|
|
||||||
, eventLoop = handle
|
|
||||||
, updateLoop = update
|
|
||||||
, drawLoop = draw
|
|
||||||
, cleanUp = clean
|
|
||||||
}
|
|
||||||
withAffection conf
|
withAffection conf
|
||||||
|
|
||||||
load :: IO UserData
|
load :: IO UserData
|
||||||
|
@ -65,156 +64,146 @@ load = do
|
||||||
liftIO $ logIO A.Debug "init GLEW"
|
liftIO $ logIO A.Debug "init GLEW"
|
||||||
_ <- glewInit
|
_ <- glewInit
|
||||||
liftIO $ logIO A.Debug "making random"
|
liftIO $ logIO A.Debug "making random"
|
||||||
randList <- mapM (\_ -> randomRIO (0,1)) [0..3599]
|
randList <- mapM (\_ -> randomRIO (0,1)) [0 .. (3599 :: Int)]
|
||||||
liftIO $ logIO A.Debug "creating context"
|
liftIO $ logIO A.Debug "creating context"
|
||||||
nanoCtx <- createGL3 (S.fromList [Antialias, StencilStrokes, NanoVG.Debug])
|
nanoCtx <- createGL3 (S.fromList [Antialias, StencilStrokes, NanoVG.Debug])
|
||||||
let fullMatrix = fromList 60 60 randList
|
let fullMatrix = fromList 60 60 randList
|
||||||
-- logIO A.Debug $ prettyMatrix fullMatrix
|
-- logIO A.Debug $ prettyMatrix fullMatrix
|
||||||
empty1 <- newTVarIO [] -- ([] :: [(UUID, WindowMessage -> Affection UserData ())])
|
empty1 <- newTVarIO [] -- ([] :: [(UUID, WindowMessage -> Affection ())])
|
||||||
empty3 <- newTVarIO [] -- ([] :: [(UUID, KeyboardMessage -> Affection UserData ())])
|
empty3 <- newTVarIO [] -- ([] :: [(UUID, KeyboardMessage -> Affection ())])
|
||||||
return $ UserData
|
step <- newMVar True
|
||||||
|
(\life food time -> UserData
|
||||||
{ subsystems = Subsystems
|
{ subsystems = Subsystems
|
||||||
(Window empty1)
|
(Window empty1)
|
||||||
(Keyboard empty3)
|
(Keyboard empty3)
|
||||||
, lifeMat = fullMatrix
|
, lifeMat = life
|
||||||
, foodMat = fromList 60 60 (repeat 10)
|
, foodMat = food
|
||||||
, timeMat = M.zero 60 60
|
, timeMat = time
|
||||||
, nano = nanoCtx
|
, nano = nanoCtx
|
||||||
|
, doNextStep = step
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
<$> newMVar fullMatrix
|
||||||
|
<*> (newMVar (fromList 60 60 (repeat 10)))
|
||||||
|
<*> (newMVar (M.zero 60 60))
|
||||||
|
|
||||||
pre :: Affection UserData ()
|
pre :: UserData -> Affection ()
|
||||||
pre = do
|
pre ud = do
|
||||||
sd <- getAffection
|
void $ partSubscribe (subKeyboard $ subsystems ud) (exitOnEsc (doNextStep ud))
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) exitOnEsc
|
void $ partSubscribe (subKeyboard $ subsystems ud) (reloadOnR ud)
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) reloadOnR
|
void $ partSubscribe (subKeyboard $ subsystems ud) showFPS
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) showFPS
|
void $ partSubscribe (subKeyboard $ subsystems ud) toggleFullScreen
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) toggleFullScreen
|
void $ partSubscribe (subWindow $ subsystems ud) (exitOnWindowClose (doNextStep ud))
|
||||||
_ <- partSubscribe (subWindow $ subsystems sd) exitOnWindowClose
|
void $ partSubscribe (subWindow $ subsystems ud) (fitViewport (600/600))
|
||||||
_ <- partSubscribe (subWindow $ subsystems sd) (fitViewport (600/600))
|
|
||||||
now <- getElapsedTime
|
|
||||||
putAffection sd
|
|
||||||
|
|
||||||
toggleFullScreen :: KeyboardMessage -> Affection UserData ()
|
toggleFullScreen :: KeyboardMessage -> Affection ()
|
||||||
toggleFullScreen (MsgKeyboardEvent _ _ SDL.Pressed False sym)
|
toggleFullScreen (MsgKeyboardEvent _ _ SDL.Pressed False sym)
|
||||||
| SDL.keysymKeycode sym == SDL.KeycodeF11 = toggleScreen
|
| SDL.keysymKeycode sym == SDL.KeycodeF11 = toggleScreen 0
|
||||||
| otherwise = return ()
|
| otherwise = return ()
|
||||||
toggleFullScreen _ = return ()
|
toggleFullScreen _ = return ()
|
||||||
|
|
||||||
exitOnEsc :: KeyboardMessage -> Affection UserData ()
|
exitOnEsc :: MVar Bool -> KeyboardMessage -> Affection ()
|
||||||
exitOnEsc (MsgKeyboardEvent _ _ SDL.Pressed False sym) =
|
exitOnEsc step (MsgKeyboardEvent _ _ SDL.Pressed False sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeEscape -> do
|
SDL.KeycodeEscape -> do
|
||||||
liftIO $ logIO A.Debug "Yo dog I heard..."
|
liftIO $ logIO A.Debug "Yo dog I heard..."
|
||||||
quit
|
void $ liftIO $ swapMVar step False
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
exitOnEsc _ = return ()
|
exitOnEsc _ _ = return ()
|
||||||
|
|
||||||
reloadOnR :: KeyboardMessage -> Affection UserData ()
|
reloadOnR :: UserData -> KeyboardMessage -> Affection ()
|
||||||
reloadOnR (MsgKeyboardEvent _ _ _ _ sym) =
|
reloadOnR ud (MsgKeyboardEvent _ _ _ _ sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeR -> reload
|
SDL.KeycodeR -> reload ud
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
reloadOnR _ = return ()
|
|
||||||
|
|
||||||
reload :: Affection UserData ()
|
reload :: UserData -> Affection ()
|
||||||
reload = do
|
reload ud = do
|
||||||
ud <- getAffection
|
randList <- liftIO $ mapM (\_ -> randomRIO (0,1)) [0 .. (3599 :: Int)]
|
||||||
now <- getElapsedTime
|
|
||||||
randList <- liftIO $ mapM (\_ -> randomRIO (0,1)) [0..3599]
|
|
||||||
let fullMatrix = fromList 60 60 randList
|
let fullMatrix = fromList 60 60 randList
|
||||||
putAffection ud
|
void $ liftIO $ swapMVar (lifeMat ud) fullMatrix
|
||||||
{ lifeMat = fullMatrix
|
void $ liftIO $ swapMVar (foodMat ud) (fromList 60 60 (repeat 10))
|
||||||
, foodMat = fromList 60 60 (repeat 10)
|
void $ liftIO $ swapMVar (timeMat ud) (M.zero 60 60)
|
||||||
, timeMat = M.zero 60 60
|
|
||||||
}
|
|
||||||
|
|
||||||
showFPS :: KeyboardMessage -> Affection UserData ()
|
showFPS :: KeyboardMessage -> Affection ()
|
||||||
showFPS (MsgKeyboardEvent _ _ _ _ sym) =
|
showFPS (MsgKeyboardEvent _ _ _ _ sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeF -> do
|
SDL.KeycodeF -> do
|
||||||
dt <- getDelta
|
dt <- getDelta
|
||||||
liftIO $ logIO A.Debug $ "FPS: " ++ show (1 / dt)
|
liftIO $ logIO A.Debug $ "FPS: " <> fromString (show (1 / dt))
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
showFPS _ = return ()
|
|
||||||
|
|
||||||
exitOnWindowClose :: WindowMessage -> Affection UserData ()
|
exitOnWindowClose :: MVar Bool -> WindowMessage -> Affection ()
|
||||||
exitOnWindowClose wm =
|
exitOnWindowClose step wm =
|
||||||
case wm of
|
case wm of
|
||||||
MsgWindowClose _ _ -> do
|
MsgWindowClose _ _ -> do
|
||||||
liftIO $ logIO A.Debug "I heard another one..."
|
liftIO $ logIO A.Debug "I heard another one..."
|
||||||
quit
|
void $ liftIO $ swapMVar step False
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
exitOnWindowClose _ = return ()
|
|
||||||
|
|
||||||
handle :: [SDL.EventPayload] -> Affection UserData ()
|
handle :: UserData -> [SDL.EventPayload] -> Affection ()
|
||||||
handle es = do
|
handle ud es = do
|
||||||
(Subsystems a b) <- subsystems <$> getAffection
|
let (Subsystems a b) = subsystems ud
|
||||||
_ <- consumeSDLEvents a =<< consumeSDLEvents b es
|
_ <- consumeSDLEvents a =<< consumeSDLEvents b es
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
update :: Double -> Affection UserData ()
|
update :: UserData -> Double -> Affection ()
|
||||||
update _ = do
|
update ud _ = do
|
||||||
ud <- getAffection
|
-- liftIO $ logIO A.Debug ("FPS: " <> fromString (show (1/dt)))
|
||||||
|
pastLife <- liftIO $ readMVar (lifeMat ud)
|
||||||
|
pastFood <- liftIO $ readMVar (foodMat ud)
|
||||||
|
pastTime <- liftIO $ readMVar (timeMat ud)
|
||||||
newList <- mapM (\coord -> do
|
newList <- mapM (\coord -> do
|
||||||
let x = (coord `mod` 60) + 1
|
let x = (coord `mod` 60) + 1
|
||||||
y = (coord `div` 60) + 1
|
y = (coord `div` 60) + 1
|
||||||
subm
|
subm
|
||||||
| x == 1 && y == 1 =
|
| x == 1 && y == 1 =
|
||||||
(submatrix 60 60 60 60 (lifeMat ud) <|> submatrix 60 60 1 2 (lifeMat ud))
|
(submatrix 60 60 60 60 pastLife <|> submatrix 60 60 1 2 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 2 60 60 (lifeMat ud) <|> submatrix 1 2 1 2 (lifeMat ud))
|
(submatrix 1 2 60 60 pastLife <|> submatrix 1 2 1 2 pastLife)
|
||||||
| x == 1 && y == 60 =
|
| x == 1 && y == 60 =
|
||||||
(submatrix 59 60 60 60 (lifeMat ud) <|> submatrix 59 60 1 2 (lifeMat ud))
|
(submatrix 59 60 60 60 pastLife <|> submatrix 59 60 1 2 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 1 60 60 (lifeMat ud) <|> submatrix 1 1 1 2 (lifeMat ud))
|
(submatrix 1 1 60 60 pastLife <|> submatrix 1 1 1 2 pastLife)
|
||||||
| x == 60 && y == 1 =
|
| x == 60 && y == 1 =
|
||||||
(submatrix 60 60 59 60 (lifeMat ud) <|> submatrix 60 60 1 1 (lifeMat ud))
|
(submatrix 60 60 59 60 pastLife <|> submatrix 60 60 1 1 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 2 59 60 (lifeMat ud) <|> submatrix 1 2 1 1 (lifeMat ud))
|
(submatrix 1 2 59 60 pastLife <|> submatrix 1 2 1 1 pastLife)
|
||||||
| x == 60 && y == 60 =
|
| x == 60 && y == 60 =
|
||||||
(submatrix 59 60 59 60 (lifeMat ud) <|> submatrix 59 60 1 1 (lifeMat ud))
|
(submatrix 59 60 59 60 pastLife <|> submatrix 59 60 1 1 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 1 59 60 (lifeMat ud) <|> submatrix 1 1 1 1 (lifeMat ud))
|
(submatrix 1 1 59 60 pastLife <|> submatrix 1 1 1 1 pastLife)
|
||||||
| x == 1 =
|
| x == 1 =
|
||||||
(submatrix (y - 1) (y + 1) 60 60 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 60 60 pastLife)
|
||||||
<|>
|
<|>
|
||||||
(submatrix (y - 1) (y + 1) 1 2 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 1 2 pastLife)
|
||||||
| y == 1 =
|
| y == 1 =
|
||||||
(submatrix 60 60 (x - 1) (x + 1) (lifeMat ud))
|
(submatrix 60 60 (x - 1) (x + 1) pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 2 (x - 1) (x + 1) (lifeMat ud))
|
(submatrix 1 2 (x - 1) (x + 1) pastLife)
|
||||||
| x == 60 =
|
| x == 60 =
|
||||||
(submatrix (y - 1) (y + 1) 59 60 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 59 60 pastLife)
|
||||||
<|>
|
<|>
|
||||||
(submatrix (y - 1) (y + 1) 1 1 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 1 1 pastLife)
|
||||||
| y == 60 =
|
| y == 60 =
|
||||||
(submatrix 59 60 (x -1 ) (x + 1) (lifeMat ud))
|
(submatrix 59 60 (x -1 ) (x + 1) pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 1 (x - 1) (x + 1) (lifeMat ud))
|
(submatrix 1 1 (x - 1) (x + 1) pastLife)
|
||||||
| otherwise =
|
| otherwise =
|
||||||
(submatrix (y - 1) (y + 1) (x - 1) (x + 1) (lifeMat ud))
|
(submatrix (y - 1) (y + 1) (x - 1) (x + 1) pastLife)
|
||||||
life = countLife subm
|
life = countLife subm
|
||||||
ret
|
if pastLife M.! (y, x) == 1
|
||||||
| life == 0 && lifeMat ud M.! (y, x) == 0 =
|
then if (life == 2 || life == 3) && pastFood M.! (y, x) > 0
|
||||||
( 0
|
then return (1, (pastFood M.! (y, x)) - 1, 0)
|
||||||
, if timeMat ud M.! (y, x) >= 10
|
else return (0, pastFood M.! (y, x), 0)
|
||||||
then min 10 (foodMat ud M.! (y, x) + 1)
|
else if life == 3 && pastFood M.! (y, x) > 0
|
||||||
else min 10 (foodMat ud M.! (y, x))
|
then return (1, (pastFood M.! (y, x)) - 1, 0)
|
||||||
, timeMat ud M.! (y, x) + 1
|
|
||||||
)
|
|
||||||
| otherwise = (1, 1, 1)
|
|
||||||
if lifeMat ud M.! (y, x) == 1
|
|
||||||
then if (life == 2 || life == 3) && foodMat ud M.! (y, x) > 0
|
|
||||||
then return (1, (foodMat ud M.! (y, x)) - 1, 0)
|
|
||||||
else return (0, foodMat ud M.! (y, x), 0)
|
|
||||||
else if life == 3 && foodMat ud M.! (y, x) > 0
|
|
||||||
then return (1, (foodMat ud M.! (y, x)) - 1, 0)
|
|
||||||
else return
|
else return
|
||||||
( 0
|
( (0 :: Word)
|
||||||
, if timeMat ud M.! (y, x) > 10
|
, if pastTime M.! (y, x) > 10
|
||||||
then min 10 ((foodMat ud M.! (y, x)) + 1)
|
then min 10 ((pastFood M.! (y, x)) + 1)
|
||||||
else min 10 (foodMat ud M.! (y, x))
|
else min 10 (pastFood M.! (y, x))
|
||||||
, timeMat ud M.! (y, x) + 1
|
, pastTime M.! (y, x) + 1
|
||||||
)
|
)
|
||||||
) [0..3599]
|
) [0..3599]
|
||||||
let newLifeMat = fromList 60 60 (map (\(x, _, _) -> x) newList)
|
let newLifeMat = fromList 60 60 (map (\(x, _, _) -> x) newList)
|
||||||
|
@ -222,22 +211,21 @@ update _ = do
|
||||||
newTimeMat = fromList 60 60 (map (\(_, _, x) -> x) newList)
|
newTimeMat = fromList 60 60 (map (\(_, _, x) -> x) newList)
|
||||||
if newLifeMat == M.zero 60 60
|
if newLifeMat == M.zero 60 60
|
||||||
then
|
then
|
||||||
reload
|
reload ud
|
||||||
else
|
else do
|
||||||
putAffection ((newLifeMat, newFoodMat, newTimeMat) `deepseq` ud)
|
((newLifeMat, newFoodMat, newTimeMat) `deepseq` return ())
|
||||||
{ lifeMat = newLifeMat
|
void $ liftIO $ swapMVar (lifeMat ud) newLifeMat
|
||||||
--, foodMat = newFoodMat
|
void $ liftIO $ swapMVar (timeMat ud) newTimeMat
|
||||||
, timeMat = newTimeMat
|
|
||||||
}
|
|
||||||
|
|
||||||
countLife :: Matrix Word -> Word
|
countLife :: Matrix Word -> Word
|
||||||
countLife mat = res - (mat M.! (2, 2))
|
countLife mat = res - (mat M.! (2, 2))
|
||||||
where
|
where
|
||||||
res = foldr (flip (+)) 0 mat
|
res = foldr (flip (+)) 0 mat
|
||||||
|
|
||||||
draw :: Affection UserData ()
|
draw :: UserData -> Affection ()
|
||||||
draw = do
|
draw ud = do
|
||||||
ud <- getAffection
|
life <- liftIO $ readMVar (lifeMat ud)
|
||||||
|
food <- liftIO $ readMVar (foodMat ud)
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
beginFrame (nano ud) 600 600 1
|
beginFrame (nano ud) 600 600 1
|
||||||
save (nano ud)
|
save (nano ud)
|
||||||
|
@ -245,7 +233,7 @@ draw = do
|
||||||
let x = coord `mod` 60
|
let x = coord `mod` 60
|
||||||
y = coord `div` 60
|
y = coord `div` 60
|
||||||
ctx = nano ud
|
ctx = nano ud
|
||||||
mult = lifeMat ud M.! (x + 1, y + 1)
|
mult = life M.! (x + 1, y + 1)
|
||||||
-- logIO A.Debug $ show mult
|
-- logIO A.Debug $ show mult
|
||||||
beginPath ctx
|
beginPath ctx
|
||||||
rect ctx (fromIntegral $ x * 10) (fromIntegral $ y * 10) 10 10
|
rect ctx (fromIntegral $ x * 10) (fromIntegral $ y * 10) 10 10
|
||||||
|
@ -253,10 +241,11 @@ draw = do
|
||||||
then
|
then
|
||||||
fillColor ctx (rgba 255 255 255 255)
|
fillColor ctx (rgba 255 255 255 255)
|
||||||
else
|
else
|
||||||
fillColor ctx (rgba 0 (fromIntegral $ 25 * (foodMat ud M.! (x+1, y+1))) 0 255)
|
fillColor ctx (rgba 0 (fromIntegral $ 25 * (food M.! (x+1, y+1))) 0 255)
|
||||||
fill ctx
|
fill ctx
|
||||||
) [0..3599]
|
) [0..3599]
|
||||||
restore (nano ud)
|
restore (nano ud)
|
||||||
endFrame (nano ud)
|
endFrame (nano ud)
|
||||||
|
|
||||||
|
clean :: UserData -> IO ()
|
||||||
clean _ = return ()
|
clean _ = return ()
|
||||||
|
|
|
@ -10,14 +10,15 @@ import Data.Matrix as M
|
||||||
import NanoVG
|
import NanoVG
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Concurrent.MVar
|
||||||
|
|
||||||
data UserData = UserData
|
data UserData = UserData
|
||||||
{ lifeMat :: Matrix Word
|
{ lifeMat :: MVar (Matrix Word)
|
||||||
, foodMat :: Matrix Word
|
, foodMat :: MVar (Matrix Word)
|
||||||
, timeMat :: Matrix Word
|
, timeMat :: MVar (Matrix Word)
|
||||||
, subsystems :: Subsystems
|
, subsystems :: Subsystems
|
||||||
, nano :: Context
|
, nano :: Context
|
||||||
|
, doNextStep :: MVar Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data Subsystems = Subsystems
|
data Subsystems = Subsystems
|
||||||
|
@ -25,12 +26,12 @@ data Subsystems = Subsystems
|
||||||
, subKeyboard :: Types.Keyboard
|
, subKeyboard :: Types.Keyboard
|
||||||
}
|
}
|
||||||
|
|
||||||
newtype Window = Window (TVar [(UUID, WindowMessage -> Affection UserData ())])
|
newtype Window = Window (TVar [(UUID, WindowMessage -> Affection ())])
|
||||||
|
|
||||||
newtype Keyboard = Keyboard (TVar [(UUID, KeyboardMessage -> Affection UserData ())])
|
newtype Keyboard = Keyboard (TVar [(UUID, KeyboardMessage -> Affection ())])
|
||||||
|
|
||||||
instance Participant Types.Window UserData where
|
instance Participant Types.Window where
|
||||||
type Mesg Types.Window UserData = WindowMessage
|
type Mesg Types.Window = WindowMessage
|
||||||
|
|
||||||
partSubscribers (Window t) = do
|
partSubscribers (Window t) = do
|
||||||
subTups <- liftIO $ readTVarIO t
|
subTups <- liftIO $ readTVarIO t
|
||||||
|
@ -40,11 +41,11 @@ instance Participant Types.Window UserData where
|
||||||
|
|
||||||
partUnSubscribe (Window t) = generalUnSubscribe t
|
partUnSubscribe (Window t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Types.Window UserData where
|
instance SDLSubsystem Types.Window where
|
||||||
consumeSDLEvents = consumeSDLWindowEvents
|
consumeSDLEvents = consumeSDLWindowEvents
|
||||||
|
|
||||||
instance Participant Keyboard UserData where
|
instance Participant Keyboard where
|
||||||
type Mesg Keyboard UserData = KeyboardMessage
|
type Mesg Keyboard = KeyboardMessage
|
||||||
|
|
||||||
partSubscribers (Keyboard t) = do
|
partSubscribers (Keyboard t) = do
|
||||||
subTups <- liftIO $ readTVarIO t
|
subTups <- liftIO $ readTVarIO t
|
||||||
|
@ -54,24 +55,24 @@ instance Participant Keyboard UserData where
|
||||||
|
|
||||||
partUnSubscribe (Keyboard t) = generalUnSubscribe t
|
partUnSubscribe (Keyboard t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Keyboard UserData where
|
instance SDLSubsystem Keyboard where
|
||||||
consumeSDLEvents = consumeSDLKeyboardEvents
|
consumeSDLEvents = consumeSDLKeyboardEvents
|
||||||
|
|
||||||
generalSubscribe
|
generalSubscribe
|
||||||
:: TVar [(UUID, msg -> Affection UserData ())]
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
-> (msg -> Affection UserData ())
|
-> (msg -> Affection ())
|
||||||
-> Affection UserData UUID
|
-> Affection UUID
|
||||||
generalSubscribe t funct = do
|
generalSubscribe t funct = do
|
||||||
uuid <- genUUID
|
uuid <- genUUID
|
||||||
liftIO $ atomically $ modifyTVar' t ((uuid, funct) :)
|
liftIO $ atomically $ modifyTVar' t ((uuid, funct) :)
|
||||||
return uuid
|
return uuid
|
||||||
|
|
||||||
generalUnSubscribe
|
generalUnSubscribe
|
||||||
:: TVar [(UUID, msg -> Affection UserData ())]
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
-> UUID
|
-> UUID
|
||||||
-> Affection UserData ()
|
-> Affection ()
|
||||||
generalUnSubscribe t uuid =
|
generalUnSubscribe t uuid =
|
||||||
liftIO $ atomically $ modifyTVar' t (filter (`filterMsg` uuid))
|
liftIO $ atomically $ modifyTVar' t (filter (`filterMsg` uuid))
|
||||||
where
|
where
|
||||||
filterMsg :: (UUID, msg -> Affection UserData ()) -> UUID -> Bool
|
filterMsg :: (UUID, msg -> Affection ()) -> UUID -> Bool
|
||||||
filterMsg (u, _) p = u /= p
|
filterMsg (u, _) p = u /= p
|
||||||
|
|
|
@ -3,30 +3,36 @@
|
||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||||
|
|
||||||
import Affection as A
|
import Affection as A
|
||||||
import SDL (($=))
|
|
||||||
import qualified SDL
|
import qualified SDL
|
||||||
import qualified Graphics.Rendering.OpenGL as GL
|
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Concurrent.MVar
|
||||||
import Control.Monad (when)
|
import Control.Monad
|
||||||
import Control.DeepSeq (deepseq)
|
import Control.DeepSeq (deepseq)
|
||||||
|
|
||||||
import Data.Matrix as M
|
import Data.Matrix as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
import Data.String
|
||||||
|
|
||||||
import System.Random (randomRIO)
|
import System.Random (randomRIO)
|
||||||
|
|
||||||
import NanoVG hiding (V2(..))
|
import NanoVG hiding (V2(..))
|
||||||
|
|
||||||
import Linear
|
|
||||||
|
|
||||||
import Foreign.C.Types (CInt(..))
|
import Foreign.C.Types (CInt(..))
|
||||||
|
|
||||||
-- internal imports
|
-- internal imports
|
||||||
|
|
||||||
import Types
|
import Types
|
||||||
|
|
||||||
|
instance Affectionate UserData where
|
||||||
|
loadState = load
|
||||||
|
preLoop = pre
|
||||||
|
handleEvents = handle
|
||||||
|
update = Main.update
|
||||||
|
draw = Main.draw
|
||||||
|
cleanUp = clean
|
||||||
|
hasNextStep = liftIO . readMVar . doNextStep
|
||||||
|
|
||||||
foreign import ccall unsafe "glewInit"
|
foreign import ccall unsafe "glewInit"
|
||||||
glewInit :: IO CInt
|
glewInit :: IO CInt
|
||||||
|
|
||||||
|
@ -43,23 +49,16 @@ main = do
|
||||||
[
|
[
|
||||||
( 0
|
( 0
|
||||||
, SDL.defaultWindow
|
, SDL.defaultWindow
|
||||||
{ SDL.windowOpenGL = Just SDL.defaultOpenGL
|
{ SDL.windowGraphicsContext = SDL.OpenGLContext SDL.defaultOpenGL
|
||||||
{ SDL.glProfile = SDL.Core SDL.Normal 3 3
|
{ SDL.glProfile = SDL.Core SDL.Normal 3 3
|
||||||
}
|
}
|
||||||
, SDL.windowInitialSize = SDL.V2 600 600
|
, SDL.windowInitialSize = SDL.V2 600 600
|
||||||
, SDL.windowResizable = True
|
, SDL.windowResizable = True
|
||||||
}
|
}
|
||||||
|
, SDL.Windowed
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
, initScreenMode = SDL.Windowed
|
} :: AffectionConfig UserData
|
||||||
, canvasSize = Nothing
|
|
||||||
, loadState = load
|
|
||||||
, preLoop = pre
|
|
||||||
, eventLoop = handle
|
|
||||||
, updateLoop = update
|
|
||||||
, drawLoop = draw
|
|
||||||
, cleanUp = clean
|
|
||||||
}
|
|
||||||
withAffection conf
|
withAffection conf
|
||||||
|
|
||||||
load :: IO UserData
|
load :: IO UserData
|
||||||
|
@ -68,156 +67,145 @@ load = do
|
||||||
liftIO $ logIO A.Debug "init GLEW"
|
liftIO $ logIO A.Debug "init GLEW"
|
||||||
_ <- glewInit
|
_ <- glewInit
|
||||||
liftIO $ logIO A.Debug "making random"
|
liftIO $ logIO A.Debug "making random"
|
||||||
randList <- mapM (\_ -> randomRIO (0,1)) [0..3599]
|
randList <- mapM (\_ -> randomRIO (0,1)) [0 .. (3599 :: Int)]
|
||||||
liftIO $ logIO A.Debug "creating context"
|
liftIO $ logIO A.Debug "creating context"
|
||||||
nanoCtx <- createGL3 (S.fromList [Antialias, StencilStrokes, NanoVG.Debug])
|
nanoCtx <- createGL3 (S.fromList [Antialias, StencilStrokes, NanoVG.Debug])
|
||||||
let fullMatrix = fromList 60 60 randList
|
let fullMatrix = fromList 60 60 randList
|
||||||
-- logIO A.Debug $ prettyMatrix fullMatrix
|
-- logIO A.Debug $ prettyMatrix fullMatrix
|
||||||
empty1 <- newTVarIO [] -- ([] :: [(UUID, WindowMessage -> Affection UserData ())])
|
empty1 <- newTVarIO [] -- ([] :: [(UUID, WindowMessage -> Affection ())])
|
||||||
empty3 <- newTVarIO [] -- ([] :: [(UUID, KeyboardMessage -> Affection UserData ())])
|
empty3 <- newTVarIO [] -- ([] :: [(UUID, KeyboardMessage -> Affection ())])
|
||||||
return $ UserData
|
(\life food time nextStep -> UserData
|
||||||
{ subsystems = Subsystems
|
{ subsystems = Subsystems
|
||||||
(Window empty1)
|
(Window empty1)
|
||||||
(Keyboard empty3)
|
(Keyboard empty3)
|
||||||
, lifeMat = fullMatrix
|
, lifeMat = life
|
||||||
, foodMat = fromList 60 60 (repeat maxFood)
|
, foodMat = food
|
||||||
, timeMat = M.zero 60 60
|
, timeMat = time
|
||||||
, nano = nanoCtx
|
, nano = nanoCtx
|
||||||
|
, doNextStep = nextStep
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
<$> newMVar fullMatrix
|
||||||
|
<*> newMVar (fromList 60 60 (repeat maxFood))
|
||||||
|
<*> newMVar (M.zero 60 60)
|
||||||
|
<*> newMVar True
|
||||||
|
|
||||||
pre :: Affection UserData ()
|
pre :: UserData -> Affection ()
|
||||||
pre = do
|
pre ud = do
|
||||||
sd <- getAffection
|
void $ partSubscribe (subKeyboard $ subsystems ud) (exitOnEsc (doNextStep ud))
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) exitOnEsc
|
void $ partSubscribe (subKeyboard $ subsystems ud) (reloadOnR ud)
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) reloadOnR
|
void $ partSubscribe (subKeyboard $ subsystems ud) showFPS
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) showFPS
|
void $ partSubscribe (subKeyboard $ subsystems ud) toggleFullScreen
|
||||||
_ <- partSubscribe (subKeyboard $ subsystems sd) toggleFullScreen
|
void $ partSubscribe (subWindow $ subsystems ud) (exitOnWindowClose (doNextStep ud))
|
||||||
_ <- partSubscribe (subWindow $ subsystems sd) exitOnWindowClose
|
void $ partSubscribe (subWindow $ subsystems ud) (fitViewport (600/600))
|
||||||
_ <- partSubscribe (subWindow $ subsystems sd) (fitViewport (600/600))
|
|
||||||
now <- getElapsedTime
|
|
||||||
putAffection sd
|
|
||||||
|
|
||||||
toggleFullScreen :: KeyboardMessage -> Affection UserData ()
|
toggleFullScreen :: KeyboardMessage -> Affection ()
|
||||||
toggleFullScreen (MsgKeyboardEvent _ _ SDL.Pressed False sym)
|
toggleFullScreen (MsgKeyboardEvent _ _ SDL.Pressed False sym)
|
||||||
| SDL.keysymKeycode sym == SDL.KeycodeF11 = toggleScreen
|
| SDL.keysymKeycode sym == SDL.KeycodeF11 = toggleScreen 0
|
||||||
| otherwise = return ()
|
| otherwise = return ()
|
||||||
toggleFullScreen _ = return ()
|
toggleFullScreen _ = return ()
|
||||||
|
|
||||||
exitOnEsc :: KeyboardMessage -> Affection UserData ()
|
exitOnEsc :: MVar Bool -> KeyboardMessage -> Affection ()
|
||||||
exitOnEsc (MsgKeyboardEvent _ _ SDL.Pressed False sym) =
|
exitOnEsc step (MsgKeyboardEvent _ _ SDL.Pressed False sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeEscape -> do
|
SDL.KeycodeEscape -> do
|
||||||
liftIO $ logIO A.Debug "Yo dog I heard..."
|
liftIO $ logIO A.Debug "Yo dog I heard..."
|
||||||
quit
|
void $ liftIO $ swapMVar step False
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
exitOnEsc _ = return ()
|
exitOnEsc _ _ = return ()
|
||||||
|
|
||||||
reloadOnR :: KeyboardMessage -> Affection UserData ()
|
reloadOnR :: UserData -> KeyboardMessage -> Affection ()
|
||||||
reloadOnR (MsgKeyboardEvent _ _ _ _ sym) =
|
reloadOnR ud (MsgKeyboardEvent _ _ _ _ sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeR -> reload
|
SDL.KeycodeR -> reload ud
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
reloadOnR _ = return ()
|
|
||||||
|
|
||||||
reload :: Affection UserData ()
|
reload :: UserData -> Affection ()
|
||||||
reload = do
|
reload ud = do
|
||||||
ud <- getAffection
|
randList <- liftIO $ mapM (\_ -> randomRIO (0,1)) [0 .. (3599 :: Int)]
|
||||||
now <- getElapsedTime
|
|
||||||
randList <- liftIO $ mapM (\_ -> randomRIO (0,1)) [0..3599]
|
|
||||||
let fullMatrix = fromList 60 60 randList
|
let fullMatrix = fromList 60 60 randList
|
||||||
putAffection ud
|
void $ liftIO $ swapMVar (lifeMat ud) fullMatrix
|
||||||
{ lifeMat = fullMatrix
|
void $ liftIO $ swapMVar (foodMat ud) (fromList 60 60 (repeat maxFood))
|
||||||
, foodMat = fromList 60 60 (repeat maxFood)
|
void $ liftIO $ swapMVar (timeMat ud) (M.zero 60 60)
|
||||||
, timeMat = M.zero 60 60
|
|
||||||
}
|
|
||||||
|
|
||||||
showFPS :: KeyboardMessage -> Affection UserData ()
|
showFPS :: KeyboardMessage -> Affection ()
|
||||||
showFPS (MsgKeyboardEvent _ _ _ _ sym) =
|
showFPS (MsgKeyboardEvent _ _ _ _ sym) =
|
||||||
case SDL.keysymKeycode sym of
|
case SDL.keysymKeycode sym of
|
||||||
SDL.KeycodeF -> do
|
SDL.KeycodeF -> do
|
||||||
dt <- getDelta
|
dt <- getDelta
|
||||||
liftIO $ logIO A.Debug $ "FPS: " ++ show (1 / dt)
|
liftIO $ logIO A.Debug $ "FPS: " <> fromString (show (1 / dt))
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
showFPS _ = return ()
|
|
||||||
|
|
||||||
exitOnWindowClose :: WindowMessage -> Affection UserData ()
|
exitOnWindowClose :: MVar Bool -> WindowMessage -> Affection ()
|
||||||
exitOnWindowClose wm =
|
exitOnWindowClose step wm =
|
||||||
case wm of
|
case wm of
|
||||||
MsgWindowClose _ _ -> do
|
MsgWindowClose _ _ -> do
|
||||||
liftIO $ logIO A.Debug "I heard another one..."
|
liftIO $ logIO A.Debug "I heard another one..."
|
||||||
quit
|
void $ liftIO $ swapMVar step False
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
exitOnWindowClose _ = return ()
|
|
||||||
|
|
||||||
handle :: [SDL.EventPayload] -> Affection UserData ()
|
handle :: UserData -> [SDL.EventPayload] -> Affection ()
|
||||||
handle es = do
|
handle ud es = do
|
||||||
(Subsystems a b) <- subsystems <$> getAffection
|
let (Subsystems a b) = subsystems ud
|
||||||
_ <- consumeSDLEvents a =<< consumeSDLEvents b es
|
_ <- consumeSDLEvents a =<< consumeSDLEvents b es
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
update :: Double -> Affection UserData ()
|
update :: UserData -> Double -> Affection ()
|
||||||
update _ = do
|
update ud _ = do
|
||||||
ud <- getAffection
|
pastLife <- liftIO $ readMVar (lifeMat ud)
|
||||||
|
pastFood <- liftIO $ readMVar (foodMat ud)
|
||||||
|
pastTime <- liftIO $ readMVar (timeMat ud)
|
||||||
newList <- mapM (\coord -> do
|
newList <- mapM (\coord -> do
|
||||||
let x = (coord `mod` 60) + 1
|
let x = (coord `mod` 60) + 1
|
||||||
y = (coord `div` 60) + 1
|
y = (coord `div` 60) + 1
|
||||||
subm
|
subm
|
||||||
| x == 1 && y == 1 =
|
| x == 1 && y == 1 =
|
||||||
(submatrix 60 60 60 60 (lifeMat ud) <|> submatrix 60 60 1 2 (lifeMat ud))
|
(submatrix 60 60 60 60 pastLife <|> submatrix 60 60 1 2 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 2 60 60 (lifeMat ud) <|> submatrix 1 2 1 2 (lifeMat ud))
|
(submatrix 1 2 60 60 pastLife <|> submatrix 1 2 1 2 pastLife)
|
||||||
| x == 1 && y == 60 =
|
| x == 1 && y == 60 =
|
||||||
(submatrix 59 60 60 60 (lifeMat ud) <|> submatrix 59 60 1 2 (lifeMat ud))
|
(submatrix 59 60 60 60 pastLife <|> submatrix 59 60 1 2 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 1 60 60 (lifeMat ud) <|> submatrix 1 1 1 2 (lifeMat ud))
|
(submatrix 1 1 60 60 pastLife <|> submatrix 1 1 1 2 pastLife)
|
||||||
| x == 60 && y == 1 =
|
| x == 60 && y == 1 =
|
||||||
(submatrix 60 60 59 60 (lifeMat ud) <|> submatrix 60 60 1 1 (lifeMat ud))
|
(submatrix 60 60 59 60 pastLife <|> submatrix 60 60 1 1 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 2 59 60 (lifeMat ud) <|> submatrix 1 2 1 1 (lifeMat ud))
|
(submatrix 1 2 59 60 pastLife <|> submatrix 1 2 1 1 pastLife)
|
||||||
| x == 60 && y == 60 =
|
| x == 60 && y == 60 =
|
||||||
(submatrix 59 60 59 60 (lifeMat ud) <|> submatrix 59 60 1 1 (lifeMat ud))
|
(submatrix 59 60 59 60 pastLife <|> submatrix 59 60 1 1 pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 1 59 60 (lifeMat ud) <|> submatrix 1 1 1 1 (lifeMat ud))
|
(submatrix 1 1 59 60 pastLife <|> submatrix 1 1 1 1 pastLife)
|
||||||
| x == 1 =
|
| x == 1 =
|
||||||
(submatrix (y - 1) (y + 1) 60 60 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 60 60 pastLife)
|
||||||
<|>
|
<|>
|
||||||
(submatrix (y - 1) (y + 1) 1 2 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 1 2 pastLife)
|
||||||
| y == 1 =
|
| y == 1 =
|
||||||
(submatrix 60 60 (x - 1) (x + 1) (lifeMat ud))
|
(submatrix 60 60 (x - 1) (x + 1) pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 2 (x - 1) (x + 1) (lifeMat ud))
|
(submatrix 1 2 (x - 1) (x + 1) pastLife)
|
||||||
| x == 60 =
|
| x == 60 =
|
||||||
(submatrix (y - 1) (y + 1) 59 60 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 59 60 pastLife)
|
||||||
<|>
|
<|>
|
||||||
(submatrix (y - 1) (y + 1) 1 1 (lifeMat ud))
|
(submatrix (y - 1) (y + 1) 1 1 pastLife)
|
||||||
| y == 60 =
|
| y == 60 =
|
||||||
(submatrix 59 60 (x -1 ) (x + 1) (lifeMat ud))
|
(submatrix 59 60 (x -1 ) (x + 1) pastLife)
|
||||||
<->
|
<->
|
||||||
(submatrix 1 1 (x - 1) (x + 1) (lifeMat ud))
|
(submatrix 1 1 (x - 1) (x + 1) pastLife)
|
||||||
| otherwise =
|
| otherwise =
|
||||||
(submatrix (y - 1) (y + 1) (x - 1) (x + 1) (lifeMat ud))
|
(submatrix (y - 1) (y + 1) (x - 1) (x + 1) pastLife)
|
||||||
life = countLife subm
|
life = countLife subm
|
||||||
ret
|
if pastLife M.! (y, x) == 1
|
||||||
| life == 0 && lifeMat ud M.! (y, x) == 0 =
|
then if (life == 2 || life == 3) && pastFood M.! (y, x) > 0
|
||||||
( 0
|
then return (1, (pastFood M.! (y, x)) - 1, 0)
|
||||||
, if timeMat ud M.! (y, x) >= 10
|
else return (0, pastFood M.! (y, x), 0)
|
||||||
then min 10 (foodMat ud M.! (y, x) + 1)
|
else if life == 3 && pastFood M.! (y, x) > 0
|
||||||
else min 10 (foodMat ud M.! (y, x))
|
then return (1, (pastFood M.! (y, x)) - 1, 0)
|
||||||
, timeMat ud M.! (y, x) + 1
|
|
||||||
)
|
|
||||||
| otherwise = (1, 1, 1)
|
|
||||||
if lifeMat ud M.! (y, x) == 1
|
|
||||||
then if (life == 2 || life == 3) && foodMat ud M.! (y, x) > 0
|
|
||||||
then return (1, (foodMat ud M.! (y, x)) - 1, 0)
|
|
||||||
else return (0, foodMat ud M.! (y, x), 0)
|
|
||||||
else if life == 3 && foodMat ud M.! (y, x) > 0
|
|
||||||
then return (1, (foodMat ud M.! (y, x)) - 1, 0)
|
|
||||||
else return
|
else return
|
||||||
( 0
|
( 0 :: Word
|
||||||
, if timeMat ud M.! (y, x) > 10
|
, if pastTime M.! (y, x) > 10
|
||||||
then min maxFood ((foodMat ud M.! (y, x)) + 1)
|
then min maxFood ((pastFood M.! (y, x)) + 1)
|
||||||
else min maxFood (foodMat ud M.! (y, x))
|
else min maxFood (pastFood M.! (y, x))
|
||||||
, timeMat ud M.! (y, x) + 1
|
, pastTime M.! (y, x) + 1
|
||||||
)
|
)
|
||||||
) [0..3599]
|
) [0..3599]
|
||||||
let newLifeMat = fromList 60 60 (map (\(x, _, _) -> x) newList)
|
let newLifeMat = fromList 60 60 (map (\(x, _, _) -> x) newList)
|
||||||
|
@ -225,41 +213,41 @@ update _ = do
|
||||||
newTimeMat = fromList 60 60 (map (\(_, _, x) -> x) newList)
|
newTimeMat = fromList 60 60 (map (\(_, _, x) -> x) newList)
|
||||||
if newLifeMat == M.zero 60 60
|
if newLifeMat == M.zero 60 60
|
||||||
then
|
then
|
||||||
reload
|
reload ud
|
||||||
else
|
else do
|
||||||
putAffection ((newLifeMat, newFoodMat, newTimeMat) `deepseq` ud)
|
((newLifeMat, newFoodMat, newTimeMat) `deepseq` return ())
|
||||||
{ lifeMat = newLifeMat
|
void $ liftIO $ swapMVar (lifeMat ud) newLifeMat
|
||||||
, foodMat = newFoodMat
|
void $ liftIO $ swapMVar (timeMat ud) newTimeMat
|
||||||
, timeMat = newTimeMat
|
void $ liftIO $ swapMVar (foodMat ud) newFoodMat
|
||||||
}
|
|
||||||
|
|
||||||
countLife :: Matrix Word -> Word
|
countLife :: Matrix Word -> Word
|
||||||
countLife mat = res - (mat M.! (2, 2))
|
countLife mat = res - (mat M.! (2, 2))
|
||||||
where
|
where
|
||||||
res = foldr (flip (+)) 0 mat
|
res = foldr (flip (+)) 0 mat
|
||||||
|
|
||||||
draw :: Affection UserData ()
|
draw :: UserData -> Affection ()
|
||||||
draw = do
|
draw ud = liftIO $ do
|
||||||
ud <- getAffection
|
pastLife <- readMVar (lifeMat ud)
|
||||||
liftIO $ do
|
pastFood <- readMVar (foodMat ud)
|
||||||
beginFrame (nano ud) 600 600 1
|
beginFrame (nano ud) 600 600 1
|
||||||
save (nano ud)
|
save (nano ud)
|
||||||
mapM_ (\coord -> do
|
mapM_ (\coord -> do
|
||||||
let x = coord `mod` 60
|
let x = coord `mod` 60
|
||||||
y = coord `div` 60
|
y = coord `div` 60
|
||||||
ctx = nano ud
|
ctx = nano ud
|
||||||
mult = lifeMat ud M.! (x + 1, y + 1)
|
multiplicator = pastLife M.! (x + 1, y + 1)
|
||||||
-- logIO A.Debug $ show mult
|
-- logIO A.Debug $ show mult
|
||||||
beginPath ctx
|
beginPath ctx
|
||||||
rect ctx (fromIntegral $ x * 10) (fromIntegral $ y * 10) 10 10
|
rect ctx (fromIntegral $ x * 10) (fromIntegral $ y * 10) 10 10
|
||||||
if mult == 1
|
if multiplicator == 1
|
||||||
then
|
then
|
||||||
fillColor ctx (rgba 255 255 255 255)
|
fillColor ctx (rgba 255 255 255 255)
|
||||||
else
|
else
|
||||||
fillColor ctx (rgba 0 (fromIntegral $ (255 `div` maxFood) * (foodMat ud M.! (x+1, y+1))) 0 255)
|
fillColor ctx (rgba 0 (fromIntegral $ (255 `div` maxFood) * (pastFood M.! (x+1, y+1))) 0 255)
|
||||||
fill ctx
|
fill ctx
|
||||||
) [0..3599]
|
) [0..3599]
|
||||||
restore (nano ud)
|
restore (nano ud)
|
||||||
endFrame (nano ud)
|
endFrame (nano ud)
|
||||||
|
|
||||||
|
clean :: UserData -> IO ()
|
||||||
clean _ = return ()
|
clean _ = return ()
|
||||||
|
|
|
@ -10,14 +10,15 @@ import Data.Matrix as M
|
||||||
import NanoVG
|
import NanoVG
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Concurrent.MVar
|
||||||
|
|
||||||
data UserData = UserData
|
data UserData = UserData
|
||||||
{ lifeMat :: Matrix Word
|
{ lifeMat :: MVar (Matrix Word)
|
||||||
, foodMat :: Matrix Word
|
, foodMat :: MVar (Matrix Word)
|
||||||
, timeMat :: Matrix Word
|
, timeMat :: MVar (Matrix Word)
|
||||||
, subsystems :: Subsystems
|
, subsystems :: Subsystems
|
||||||
, nano :: Context
|
, nano :: Context
|
||||||
|
, doNextStep :: MVar Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data Subsystems = Subsystems
|
data Subsystems = Subsystems
|
||||||
|
@ -25,12 +26,12 @@ data Subsystems = Subsystems
|
||||||
, subKeyboard :: Types.Keyboard
|
, subKeyboard :: Types.Keyboard
|
||||||
}
|
}
|
||||||
|
|
||||||
newtype Window = Window (TVar [(UUID, WindowMessage -> Affection UserData ())])
|
newtype Window = Window (TVar [(UUID, WindowMessage -> Affection ())])
|
||||||
|
|
||||||
newtype Keyboard = Keyboard (TVar [(UUID, KeyboardMessage -> Affection UserData ())])
|
newtype Keyboard = Keyboard (TVar [(UUID, KeyboardMessage -> Affection ())])
|
||||||
|
|
||||||
instance Participant Types.Window UserData where
|
instance Participant Types.Window where
|
||||||
type Mesg Types.Window UserData = WindowMessage
|
type Mesg Types.Window = WindowMessage
|
||||||
|
|
||||||
partSubscribers (Window t) = do
|
partSubscribers (Window t) = do
|
||||||
subTups <- liftIO $ readTVarIO t
|
subTups <- liftIO $ readTVarIO t
|
||||||
|
@ -40,11 +41,11 @@ instance Participant Types.Window UserData where
|
||||||
|
|
||||||
partUnSubscribe (Window t) = generalUnSubscribe t
|
partUnSubscribe (Window t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Types.Window UserData where
|
instance SDLSubsystem Types.Window where
|
||||||
consumeSDLEvents = consumeSDLWindowEvents
|
consumeSDLEvents = consumeSDLWindowEvents
|
||||||
|
|
||||||
instance Participant Keyboard UserData where
|
instance Participant Keyboard where
|
||||||
type Mesg Keyboard UserData = KeyboardMessage
|
type Mesg Keyboard = KeyboardMessage
|
||||||
|
|
||||||
partSubscribers (Keyboard t) = do
|
partSubscribers (Keyboard t) = do
|
||||||
subTups <- liftIO $ readTVarIO t
|
subTups <- liftIO $ readTVarIO t
|
||||||
|
@ -54,24 +55,24 @@ instance Participant Keyboard UserData where
|
||||||
|
|
||||||
partUnSubscribe (Keyboard t) = generalUnSubscribe t
|
partUnSubscribe (Keyboard t) = generalUnSubscribe t
|
||||||
|
|
||||||
instance SDLSubsystem Keyboard UserData where
|
instance SDLSubsystem Keyboard where
|
||||||
consumeSDLEvents = consumeSDLKeyboardEvents
|
consumeSDLEvents = consumeSDLKeyboardEvents
|
||||||
|
|
||||||
generalSubscribe
|
generalSubscribe
|
||||||
:: TVar [(UUID, msg -> Affection UserData ())]
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
-> (msg -> Affection UserData ())
|
-> (msg -> Affection ())
|
||||||
-> Affection UserData UUID
|
-> Affection UUID
|
||||||
generalSubscribe t funct = do
|
generalSubscribe t funct = do
|
||||||
uuid <- genUUID
|
uuid <- genUUID
|
||||||
liftIO $ atomically $ modifyTVar' t ((uuid, funct) :)
|
liftIO $ atomically $ modifyTVar' t ((uuid, funct) :)
|
||||||
return uuid
|
return uuid
|
||||||
|
|
||||||
generalUnSubscribe
|
generalUnSubscribe
|
||||||
:: TVar [(UUID, msg -> Affection UserData ())]
|
:: TVar [(UUID, msg -> Affection ())]
|
||||||
-> UUID
|
-> UUID
|
||||||
-> Affection UserData ()
|
-> Affection ()
|
||||||
generalUnSubscribe t uuid =
|
generalUnSubscribe t uuid =
|
||||||
liftIO $ atomically $ modifyTVar' t (filter (`filterMsg` uuid))
|
liftIO $ atomically $ modifyTVar' t (filter (`filterMsg` uuid))
|
||||||
where
|
where
|
||||||
filterMsg :: (UUID, msg -> Affection UserData ()) -> UUID -> Bool
|
filterMsg :: (UUID, msg -> Affection ()) -> UUID -> Bool
|
||||||
filterMsg (u, _) p = u /= p
|
filterMsg (u, _) p = u /= p
|
||||||
|
|
60
flake.lock
Normal file
60
flake.lock
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694529238,
|
||||||
|
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1700844339,
|
||||||
|
"narHash": "sha256-VRlPYk0eRn1ao75hoA99LGehYhxZH5RlkP1YXpVROHM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "33576fdfce2d11204067d6cfa99a2f990ef2169a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
54
flake.nix
Normal file
54
flake.nix
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
description = "A game stub written in Haskell";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
haskellPackages = pkgs.haskellPackages;
|
||||||
|
|
||||||
|
jailbreakUnbreak = pkg:
|
||||||
|
pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; }));
|
||||||
|
|
||||||
|
packageName = "affection";
|
||||||
|
in rec {
|
||||||
|
packages.${packageName} = # (ref:haskell-package-def)
|
||||||
|
haskellPackages.callCabal2nixWithOptions packageName self "--flag examples" {
|
||||||
|
# Dependency overrides go here
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultPackage = self.packages.${system}.${packageName};
|
||||||
|
|
||||||
|
apps = {
|
||||||
|
example00 = {
|
||||||
|
type = "app";
|
||||||
|
program = "${self.packages.${system}.${packageName}}/bin/example00";
|
||||||
|
};
|
||||||
|
example01 = {
|
||||||
|
type = "app";
|
||||||
|
program = "${self.packages.${system}.${packageName}}/bin/example01";
|
||||||
|
};
|
||||||
|
example02 = {
|
||||||
|
type = "app";
|
||||||
|
program = "${self.packages.${system}.${packageName}}/bin/example02";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
devShell = haskellPackages.shellFor {
|
||||||
|
packages = p: [ defaultPackage ];
|
||||||
|
withHoogle = true;
|
||||||
|
buildInputs = with haskellPackages; [
|
||||||
|
haskell-language-server
|
||||||
|
ghcid
|
||||||
|
cabal-install
|
||||||
|
] ++ (builtins.attrValues self.packages.${system});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# TODO
|
|
||||||
|
|
||||||
Implement following things in approximately this Order:
|
|
||||||
|
|
||||||
* Message bus
|
|
||||||
* Console
|
|
||||||
* GUI
|
|
||||||
* Framework
|
|
||||||
* Everything else
|
|
124
shell.nix
124
shell.nix
|
@ -1,123 +1,7 @@
|
||||||
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default", doBenchmark ? false }:
|
{ pkgs ? import <nixpkgs> {}}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
affection = pkgs.haskellPackages.callCabal2nix "affection" (gitignore ./.) {};
|
||||||
inherit (nixpkgs) pkgs;
|
gitignore = dir: pkgs.nix-gitignore.gitignoreSource [] dir;
|
||||||
|
|
||||||
nanovgNeko = with haskellPackages; callPackage (
|
|
||||||
{ mkDerivation, base, bytestring, c2hs, containers, glew
|
|
||||||
, hspec, inline-c, libGL, libGLU, QuickCheck, stdenv, text, vector
|
|
||||||
}:
|
|
||||||
mkDerivation {
|
|
||||||
pname = "nanovg";
|
|
||||||
version = "0.6.0.0";
|
|
||||||
src = ../nanovg-hs;
|
|
||||||
isLibrary = true;
|
|
||||||
isExecutable = true;
|
|
||||||
doCheck = false;
|
|
||||||
libraryHaskellDepends = [ base bytestring containers text vector ];
|
|
||||||
librarySystemDepends = [ glew libGL libGLU ];
|
|
||||||
libraryPkgconfigDepends = [ glew ];
|
|
||||||
libraryToolDepends = [ c2hs ];
|
|
||||||
testHaskellDepends = [ base containers hspec inline-c QuickCheck ];
|
|
||||||
homepage = "https://github.com/cocreature/nanovg-hs";
|
|
||||||
description = "Haskell bindings for nanovg";
|
|
||||||
license = stdenv.lib.licenses.isc;
|
|
||||||
}) {};
|
|
||||||
|
|
||||||
glib = with haskellPackages; callPackage(
|
|
||||||
{ mkDerivation, base, bytestring, Cabal, containers, glib
|
|
||||||
, stdenv, text, utf8-string
|
|
||||||
}:
|
|
||||||
mkDerivation {
|
|
||||||
pname = "glib";
|
|
||||||
version = "0.13.6.0";
|
|
||||||
sha256 = "4e71062c6a458440294d820e21449aa4666deed2ea233ef5915da7c1d4aee8eb";
|
|
||||||
setupHaskellDepends = [ base Cabal gtk2hs-buildtools ];
|
|
||||||
libraryHaskellDepends = [
|
|
||||||
base bytestring containers text utf8-string
|
|
||||||
];
|
|
||||||
libraryPkgconfigDepends = [ pkgs.glib ];
|
|
||||||
homepage = "http://projects.haskell.org/gtk2hs/";
|
|
||||||
description = "Binding to the GLIB library for Gtk2Hs";
|
|
||||||
license = stdenv.lib.licenses.lgpl21;
|
|
||||||
}) {};
|
|
||||||
|
|
||||||
gtk2hs-buildtools = with haskellPackages; callPackage(
|
|
||||||
{ mkDerivation, alex, array, base, Cabal, containers, directory
|
|
||||||
, filepath, happy, hashtables, pretty, process, random, stdenv
|
|
||||||
}:
|
|
||||||
mkDerivation {
|
|
||||||
pname = "gtk2hs-buildtools";
|
|
||||||
version = "0.13.4.0";
|
|
||||||
src = ../gtk2hs/tools;
|
|
||||||
isLibrary = true;
|
|
||||||
isExecutable = true;
|
|
||||||
enableSeparateDataOutput = true;
|
|
||||||
libraryHaskellDepends = [
|
|
||||||
array base Cabal containers directory filepath hashtables pretty
|
|
||||||
process random
|
|
||||||
];
|
|
||||||
libraryToolDepends = [ alex happy ];
|
|
||||||
executableHaskellDepends = [ base ];
|
|
||||||
homepage = "http://projects.haskell.org/gtk2hs/";
|
|
||||||
description = "Tools to build the Gtk2Hs suite of User Interface libraries";
|
|
||||||
license = stdenv.lib.licenses.gpl2;
|
|
||||||
}) {};
|
|
||||||
|
|
||||||
sdl2Nek0 = with haskellPackages; callPackage(
|
|
||||||
{ mkDerivation, base, bytestring, deepseq, exceptions, linear, SDL2
|
|
||||||
, StateVar, stdenv, text, transformers, vector, weigh
|
|
||||||
}:
|
|
||||||
mkDerivation {
|
|
||||||
pname = "sdl2";
|
|
||||||
version = "2.4.1.0";
|
|
||||||
sha256 = "21a569c0c19f8ff2bbe1cf1d3eb32f65e8143806de353cedd240df5e9d088b5c";
|
|
||||||
isLibrary = true;
|
|
||||||
isExecutable = true;
|
|
||||||
enableSeparateDataOutput = true;
|
|
||||||
libraryHaskellDepends = [
|
|
||||||
base bytestring exceptions linear StateVar text transformers vector
|
|
||||||
];
|
|
||||||
librarySystemDepends = [ SDL2 ];
|
|
||||||
libraryPkgconfigDepends = [ SDL2 ];
|
|
||||||
testHaskellDepends = [ base deepseq linear vector weigh ];
|
|
||||||
doCheck = false;
|
|
||||||
description = "Both high- and low-level bindings to the SDL library (version 2.0.4+).";
|
|
||||||
license = stdenv.lib.licenses.bsd3;
|
|
||||||
}) {};
|
|
||||||
|
|
||||||
f = { mkDerivation, base, bytestring, clock, containers, deepseq
|
|
||||||
, linear, matrix, monad-loops, monad-parallel, mtl
|
|
||||||
, OpenGL, random, stdenv, stm, text, uuid, vector
|
|
||||||
}:
|
|
||||||
mkDerivation {
|
|
||||||
pname = "affection";
|
|
||||||
version = "0.0.0.9";
|
|
||||||
src = ./.;
|
|
||||||
configureFlags = [ "-fexamples" ];
|
|
||||||
isLibrary = true;
|
|
||||||
isExecutable = true;
|
|
||||||
libraryHaskellDepends = [
|
|
||||||
base bytestring clock containers glib linear monad-loops
|
|
||||||
monad-parallel mtl OpenGL sdl2Nek0 stm text uuid vector
|
|
||||||
];
|
|
||||||
executableHaskellDepends = [
|
|
||||||
base containers deepseq linear matrix nanovgNeko OpenGL random sdl2Nek0 stm
|
|
||||||
];
|
|
||||||
homepage = "https://github.com/nek0/affection#readme";
|
|
||||||
description = "A simple Game Engine using SDL";
|
|
||||||
license = stdenv.lib.licenses.lgpl3;
|
|
||||||
};
|
|
||||||
|
|
||||||
haskellPackages = if compiler == "default"
|
|
||||||
then pkgs.haskellPackages
|
|
||||||
else pkgs.haskell.packages.${compiler};
|
|
||||||
|
|
||||||
variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;
|
|
||||||
|
|
||||||
drv = variant (haskellPackages.callPackage f {});
|
|
||||||
|
|
||||||
in
|
in
|
||||||
|
affection.env
|
||||||
if pkgs.lib.inNixShell then drv.env else drv
|
|
||||||
|
|
230
src/Affection.hs
230
src/Affection.hs
|
@ -1,5 +1,8 @@
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
{-# LANGUAGE BangPatterns #-}
|
{-# LANGUAGE Strict #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE RankNTypes #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
module Affection
|
module Affection
|
||||||
( withAffection
|
( withAffection
|
||||||
, get
|
, get
|
||||||
|
@ -13,15 +16,25 @@ import qualified SDL
|
||||||
import qualified SDL.Raw.Video as SDL (glSetAttribute)
|
import qualified SDL.Raw.Video as SDL (glSetAttribute)
|
||||||
import qualified SDL.Raw.Enum as SDL
|
import qualified SDL.Raw.Enum as SDL
|
||||||
|
|
||||||
|
import qualified Graphics.Rendering.OpenGL as GL (clear, flush, ClearBuffer(..))
|
||||||
|
import qualified Graphics.GL as GLRaw
|
||||||
|
|
||||||
|
import Foreign.Marshal.Array
|
||||||
|
|
||||||
|
import qualified Data.ByteString as B
|
||||||
|
|
||||||
|
import Data.String (fromString)
|
||||||
|
|
||||||
import System.Clock
|
import System.Clock
|
||||||
|
|
||||||
import Control.Monad.Loops
|
import Control.Monad.Loops
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.Trans.Resource
|
||||||
|
|
||||||
import Foreign.C.Types (CInt(..))
|
-- internal imports
|
||||||
|
|
||||||
import Affection.Types as A
|
import Affection.Types as A
|
||||||
|
import Affection.Class as A
|
||||||
import Affection.StateMachine as A
|
import Affection.StateMachine as A
|
||||||
import Affection.Util as A
|
import Affection.Util as A
|
||||||
import Affection.MessageBus as A
|
import Affection.MessageBus as A
|
||||||
|
@ -29,102 +42,163 @@ import Affection.Subsystems as A
|
||||||
|
|
||||||
import Affection.Logging as A
|
import Affection.Logging as A
|
||||||
|
|
||||||
import qualified Graphics.Rendering.OpenGL as GL (clear, flush, ClearBuffer(..))
|
|
||||||
|
|
||||||
|
|
||||||
-- | Main function which bootstraps everything else.
|
-- | Main function which bootstraps everything else.
|
||||||
withAffection
|
withAffection
|
||||||
:: AffectionConfig us -- ^ Configuration of the Game and its engine.
|
:: forall us. (Affectionate us)
|
||||||
|
=> AffectionConfig us -- ^ Configuration of the Game and its engine.
|
||||||
-> IO ()
|
-> IO ()
|
||||||
withAffection AffectionConfig{..} = do
|
withAffection AffectionConfig{..} = runResourceT $ do
|
||||||
liftIO $ logIO Debug "Affection starting"
|
liftIO $ logIO Debug "Affection starting"
|
||||||
liftIO $ logIO Debug "Initializing SDL"
|
liftIO $ logIO Debug "Initializing SDL"
|
||||||
|
|
||||||
-- intialiaze SDL
|
-- intialiaze SDL
|
||||||
case initComponents of
|
case initComponents of
|
||||||
All ->
|
All ->
|
||||||
SDL.initializeAll
|
SDL.initializeAll
|
||||||
Only is ->
|
Only is ->
|
||||||
SDL.initialize is
|
SDL.initialize is
|
||||||
|
|
||||||
-- give SDL render quality
|
-- give SDL render quality
|
||||||
SDL.HintRenderScaleQuality SDL.$= SDL.ScaleLinear
|
SDL.HintRenderScaleQuality SDL.$= SDL.ScaleLinear
|
||||||
|
|
||||||
-- just checking…
|
-- just checking…
|
||||||
do
|
renderQuality <- SDL.get SDL.HintRenderScaleQuality
|
||||||
renderQuality <- SDL.get SDL.HintRenderScaleQuality
|
when (renderQuality /= SDL.ScaleLinear) $
|
||||||
when (renderQuality /= SDL.ScaleLinear) $
|
liftIO $ logIO Warn "Linear texture filtering not enabled!"
|
||||||
logIO Warn "Linear texture filtering not enabled!"
|
void $ liftIO (logIO Debug . fromString . show <$> (SDL.version :: IO (Integer, Integer, Integer)))
|
||||||
|
|
||||||
-- construct window
|
-- construct window
|
||||||
liftIO $ logIO Debug "Creating Window(s)"
|
liftIO $ logIO Debug "Creating Window(s)"
|
||||||
windows <- zip (map fst windowConfigs) <$>
|
windows <-
|
||||||
mapM
|
mapM
|
||||||
(\wc -> SDL.createWindow windowTitle (snd wc))
|
(\(_, sdlWindowConfig, mode) -> do
|
||||||
windowConfigs
|
(windowKey, window) <-
|
||||||
mapM_ (SDL.showWindow . snd) windows
|
allocate
|
||||||
_ <- SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
|
(SDL.createWindow windowTitle sdlWindowConfig)
|
||||||
contexts <- zip (map fst windows) <$> mapM (SDL.glCreateContext . snd) windows
|
(\window -> do
|
||||||
-- let SDL.V2 (CInt rw) (CInt rh) = SDL.windowInitialSize windowConfigs
|
logIO Debug "Destroying Window"
|
||||||
-- (w, h) = case canvasSize of
|
SDL.destroyWindow window
|
||||||
-- Just (cw, ch) -> (cw, ch)
|
)
|
||||||
-- Nothing -> (fromIntegral rw, fromIntegral rh)
|
return $ AffectionWindow window windowKey mode
|
||||||
mapM_ (flip SDL.setWindowMode initScreenMode . snd) windows
|
)
|
||||||
|
windowConfigs
|
||||||
|
|
||||||
|
-- Show windows
|
||||||
|
mapM_ (SDL.showWindow . awWindow) windows
|
||||||
|
|
||||||
|
-- set modes of windows
|
||||||
|
mapM_ (\(AffectionWindow window _ mode) -> SDL.setWindowMode window mode) windows
|
||||||
|
|
||||||
|
-- Make GL context shareable
|
||||||
|
void $ SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
|
||||||
|
|
||||||
|
-- Create OpenGL contexts
|
||||||
|
contexts <-
|
||||||
|
mapM
|
||||||
|
(\(AffectionWindow window _ _) -> do
|
||||||
|
(contextKey, context) <-
|
||||||
|
allocate
|
||||||
|
(SDL.glCreateContext window)
|
||||||
|
(\context -> do
|
||||||
|
logIO Debug "Destroying context"
|
||||||
|
SDL.glDeleteContext context
|
||||||
|
)
|
||||||
|
return $ AffectionContext context contextKey
|
||||||
|
)
|
||||||
|
windows
|
||||||
|
|
||||||
|
-- sync updates with monitor
|
||||||
-- SDL.swapInterval $= SDL.SynchronizedUpdates -- <- causes Problems with windows
|
-- SDL.swapInterval $= SDL.SynchronizedUpdates -- <- causes Problems with windows
|
||||||
liftIO $ logIO Debug "Getting Time"
|
|
||||||
|
-- print current used GL Version
|
||||||
|
version <- liftIO $ peekArray0 (0 :: Word8) =<< GLRaw.glGetString GLRaw.GL_VERSION
|
||||||
|
liftIO $ print (B.pack version)
|
||||||
|
|
||||||
-- get current time
|
-- get current time
|
||||||
execTime <- getTime Monotonic
|
liftIO $ logIO Debug "Getting Time"
|
||||||
|
execTime <- liftIO $ getTime Monotonic
|
||||||
liftIO $ logIO Debug "Loading initial data container"
|
liftIO $ logIO Debug "Loading initial data container"
|
||||||
initContainer <- (\x -> AffectionData
|
|
||||||
{ quitEvent = False
|
-- construct game data object from provided Affectionate instance
|
||||||
, userState = x
|
(gameDataKey, gameData) <-
|
||||||
, drawWindows = windows
|
allocate
|
||||||
, glContext = contexts
|
(liftIO $ loadState @us)
|
||||||
, screenMode = initScreenMode
|
(liftIO . cleanUp)
|
||||||
, elapsedTime = 0
|
-- build state container
|
||||||
, deltaTime = 0
|
let initContainer = AffectionData
|
||||||
, sysTime = execTime
|
{ drawWindows = windows
|
||||||
, pausedTime = False
|
, glContext = contexts
|
||||||
}) <$> loadState
|
, elapsedTime = 0
|
||||||
(_, nState) <- runStateT ( A.runState $ do
|
, deltaTime = 0
|
||||||
liftIO $ logIO Debug "Starting Loop"
|
, sysTime = execTime
|
||||||
preLoop
|
, pausedTime = False
|
||||||
whileM_ (not . A.quitEvent <$> get)
|
}
|
||||||
|
|
||||||
|
-- initialize and run state
|
||||||
|
void $ liftIO $ runAffection initContainer
|
||||||
(do
|
(do
|
||||||
-- get state
|
liftIO $ logIO Debug "Running Pre-Loop stage"
|
||||||
ad <- get
|
|
||||||
-- Measure time difference form last run
|
-- run preLoop function from Affectionate
|
||||||
now <- liftIO $ getTime Monotonic
|
preLoop gameData
|
||||||
let lastTime = sysTime ad
|
liftIO $ logIO Debug "Starting Loop"
|
||||||
-- compute dt and update elapsedTime
|
whileM_ (hasNextStep gameData)
|
||||||
let !dt = fromIntegral
|
(do
|
||||||
(toNanoSecs $ diffTimeSpec lastTime now) / (10 ^ (9 :: Int))
|
-- get state
|
||||||
!ne = elapsedTime ad + dt
|
ad <- get
|
||||||
put $ ad
|
|
||||||
{ elapsedTime = ne
|
-- Measure time difference form last run
|
||||||
, deltaTime = dt
|
now <- liftIO $ getTime Monotonic
|
||||||
}
|
let lastTime = sysTime ad
|
||||||
-- poll events
|
|
||||||
evs <- preHandleEvents =<< liftIO SDL.pollEvents
|
-- compute dt and update elapsedTime
|
||||||
-- handle events
|
let dt = fromIntegral
|
||||||
eventLoop evs
|
(toNanoSecs $ diffTimeSpec lastTime now) / (10 ^ (9 :: Int))
|
||||||
-- execute user defined update loop
|
ne = elapsedTime ad + dt
|
||||||
unless (pausedTime ad) (updateLoop dt)
|
|
||||||
-- execute user defined draw loop
|
-- update state data object with new time values
|
||||||
liftIO $ GL.clear [GL.ColorBuffer, GL.DepthBuffer, GL.StencilBuffer]
|
put $ ad
|
||||||
drawLoop
|
{ elapsedTime = ne
|
||||||
liftIO GL.flush
|
, deltaTime = dt
|
||||||
-- actual displaying of newly drawn frame
|
}
|
||||||
mapM_ (SDL.glSwapWindow . snd) windows
|
|
||||||
-- save new time
|
-- poll events
|
||||||
ad3 <- get
|
liftIO SDL.pumpEvents
|
||||||
when (sysTime ad == sysTime ad3) (
|
evs <- preHandleEvents =<< liftIO SDL.pollEvents
|
||||||
put ad3
|
|
||||||
{ sysTime = now
|
-- handle events
|
||||||
}
|
handleEvents gameData evs
|
||||||
)
|
|
||||||
)
|
-- execute user defined update loop
|
||||||
) initContainer
|
unless (pausedTime ad) (update gameData dt)
|
||||||
|
|
||||||
|
-- clear GL buffer >> execute user defined draw loop >> flush GL buffer
|
||||||
|
liftIO $ GL.clear [GL.ColorBuffer, GL.DepthBuffer, GL.StencilBuffer]
|
||||||
|
draw gameData
|
||||||
|
liftIO GL.flush
|
||||||
|
|
||||||
|
-- actual displaying of newly drawn frame
|
||||||
|
mapM_ (SDL.glSwapWindow . awWindow) windows
|
||||||
|
|
||||||
|
-- save new time
|
||||||
|
ad3 <- get
|
||||||
|
when (sysTime ad == sysTime ad3) (
|
||||||
|
put ad3
|
||||||
|
{ sysTime = now
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-- Cleanup works
|
||||||
liftIO $ logIO Debug "Loop ended. Cleaning"
|
liftIO $ logIO Debug "Loop ended. Cleaning"
|
||||||
cleanUp $ userState nState
|
release gameDataKey
|
||||||
liftIO $ logIO Debug "Destroying Window"
|
-- mapM_ (SDL.glDeleteContext . snd) contexts
|
||||||
mapM_ (SDL.glDeleteContext . snd) contexts
|
-- mapM_ (SDL.destroyWindow . (\(_,y,_) -> y)) windows
|
||||||
mapM_ (SDL.destroyWindow . snd) windows
|
|
||||||
-- SDL.quit -- <- This causes segfaults depending on hardware
|
-- SDL.quit -- <- This causes segfaults depending on hardware
|
||||||
liftIO $ logIO Debug "This is the end"
|
liftIO $ logIO Debug "This is the end"
|
||||||
|
|
||||||
|
runAffection
|
||||||
|
:: AffectionData
|
||||||
|
-> AffectionState AffectionData ResIO a
|
||||||
|
-> IO (a, AffectionData)
|
||||||
|
runAffection initialState a = runResourceT $ runStateT (A.runState a) initialState
|
||||||
|
|
21
src/Affection/Class.hs
Normal file
21
src/Affection/Class.hs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
module Affection.Class where
|
||||||
|
|
||||||
|
import qualified SDL
|
||||||
|
|
||||||
|
import Affection.Types as A
|
||||||
|
|
||||||
|
class Affectionate a where
|
||||||
|
|
||||||
|
loadState :: IO a
|
||||||
|
|
||||||
|
preLoop :: a -> Affection ()
|
||||||
|
|
||||||
|
handleEvents :: a -> [SDL.EventPayload] -> Affection ()
|
||||||
|
|
||||||
|
update :: a -> Double -> Affection ()
|
||||||
|
|
||||||
|
draw :: a -> Affection ()
|
||||||
|
|
||||||
|
cleanUp :: a -> IO ()
|
||||||
|
|
||||||
|
hasNextStep :: a -> Affection Bool
|
|
@ -3,6 +3,8 @@
|
||||||
-- from "Debug.Trace".
|
-- from "Debug.Trace".
|
||||||
module Affection.Logging where
|
module Affection.Logging where
|
||||||
|
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
import Debug.Trace
|
import Debug.Trace
|
||||||
|
|
||||||
-- | The log level definition
|
-- | The log level definition
|
||||||
|
@ -15,38 +17,38 @@ data LogLevel
|
||||||
-- | Pure logging function
|
-- | Pure logging function
|
||||||
log
|
log
|
||||||
:: LogLevel -- ^ Log level to log to
|
:: LogLevel -- ^ Log level to log to
|
||||||
-> String -- ^ The message string
|
-> T.Text -- ^ The message string
|
||||||
-> a -- ^ Arbitrary datatype to return
|
-> a -- ^ Arbitrary datatype to return
|
||||||
-> a -- ^ Returned data
|
-> a -- ^ Returned data
|
||||||
#if defined(VERBOSE)
|
#if defined(VERBOSE)
|
||||||
log Verbose s = trace ("VERBOSE: " ++ s)
|
log Verbose s = trace ("VERBOSE: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(DEBUG) || defined(VERBOSE)
|
#if defined(DEBUG) || defined(VERBOSE)
|
||||||
log Debug s = trace ("DEBUG: " ++ s)
|
log Debug s = trace ("DEBUG: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
#if defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
||||||
log Warn s = trace ("WARN: " ++ s)
|
log Warn s = trace ("WARN: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(ERROR) || defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
#if defined(ERROR) || defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
||||||
log Error s = trace ("ERROR: " ++ s)
|
log Error s = trace ("ERROR: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
log _ _ = id
|
log _ _ = id
|
||||||
|
|
||||||
-- | Manadic logging function residing in the 'IO' Monad
|
-- | Manadic logging function residing in the 'IO' Monad
|
||||||
logIO
|
logIO
|
||||||
:: LogLevel -- ^ Log level to log to
|
:: LogLevel -- ^ Log level to log to
|
||||||
-> String -- ^ The message string
|
-> T.Text -- ^ The message string
|
||||||
-> IO ()
|
-> IO ()
|
||||||
#if defined(VERBOSE)
|
#if defined(VERBOSE)
|
||||||
logIO Verbose s = traceIO ("VERBOSE: " ++ s)
|
logIO Verbose s = traceIO ("VERBOSE: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(DEBUG) || defined(VERBOSE)
|
#if defined(DEBUG) || defined(VERBOSE)
|
||||||
logIO Debug s = traceIO ("DEBUG: " ++ s)
|
logIO Debug s = traceIO ("DEBUG: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
#if defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
||||||
logIO Warn s = traceIO ("WARN: " ++ s)
|
logIO Warn s = traceIO ("WARN: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
#if defined(ERROR) || defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
#if defined(ERROR) || defined(WARN) || defined(DEBUG) || defined(VERBOSE)
|
||||||
logIO Error s = traceIO ("ERROR: " ++ s)
|
logIO Error s = traceIO ("ERROR: " ++ T.unpack s)
|
||||||
#endif
|
#endif
|
||||||
logIO _ _ = return ()
|
logIO _ _ = return ()
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
module Affection.MessageBus
|
module Affection.MessageBus
|
||||||
( module M
|
( module M
|
||||||
, module Msg
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Affection.MessageBus.Class as M
|
import Affection.MessageBus.Class as M
|
||||||
import Affection.MessageBus.Message as M
|
import Affection.MessageBus.Message as M
|
||||||
|
|
||||||
import Affection.MessageBus.Message as Msg
|
|
||||||
|
|
|
@ -17,28 +17,30 @@ import Control.Monad.IO.Class (liftIO)
|
||||||
import Data.UUID
|
import Data.UUID
|
||||||
import Data.UUID.V4
|
import Data.UUID.V4
|
||||||
|
|
||||||
|
import Data.String as S (fromString)
|
||||||
|
|
||||||
import Affection.Logging
|
import Affection.Logging
|
||||||
|
|
||||||
-- | This typeclass defines the behaviour of a participant in the message system
|
-- | This typeclass defines the behaviour of a participant in the message system
|
||||||
class (Message (Mesg prt us), Show (Mesg prt us)) => Participant prt us where
|
class (Message (Mesg prt), Show (Mesg prt)) => Participant prt where
|
||||||
-- | Message datatype
|
-- | Message datatype
|
||||||
type Mesg prt us :: *
|
type Mesg prt :: *
|
||||||
|
|
||||||
-- | Function to get the list of subscribers from the participant
|
-- | Function to get the list of subscribers from the participant
|
||||||
partSubscribers
|
partSubscribers
|
||||||
:: prt
|
:: prt
|
||||||
-- ^ the 'Participant''s subscriber storage
|
-- ^ the 'Participant''s subscriber storage
|
||||||
-> Affection us [Mesg prt us -> Affection us ()]
|
-> Affection [Mesg prt -> Affection ()]
|
||||||
-- ^ List of Subscriber functions
|
-- ^ List of Subscriber functions
|
||||||
|
|
||||||
-- | Subscribe to the 'Participant''s events
|
-- | Subscribe to the 'Participant''s events
|
||||||
partSubscribe
|
partSubscribe
|
||||||
:: prt
|
:: prt
|
||||||
-- ^ The 'Participant''s subscriber storage
|
-- ^ The 'Participant''s subscriber storage
|
||||||
-> (Mesg prt us -> Affection us ())
|
-> (Mesg prt -> Affection ())
|
||||||
-- ^ What to do in case of a 'Message'
|
-- ^ What to do in case of a 'Message'
|
||||||
-- (Subscriber function)
|
-- (Subscriber function)
|
||||||
-> Affection us UUID
|
-> Affection UUID
|
||||||
-- ^ 'UUID' of the registered subscriber Function
|
-- ^ 'UUID' of the registered subscriber Function
|
||||||
|
|
||||||
-- | Unsubscribe a Subscriber function from Participant
|
-- | Unsubscribe a Subscriber function from Participant
|
||||||
|
@ -47,20 +49,20 @@ class (Message (Mesg prt us), Show (Mesg prt us)) => Participant prt us where
|
||||||
-- ^ The 'Participant''s subscriber storage to unsubscribe from
|
-- ^ The 'Participant''s subscriber storage to unsubscribe from
|
||||||
-> UUID
|
-> UUID
|
||||||
-- ^ The subscriber function's 'UUID'
|
-- ^ The subscriber function's 'UUID'
|
||||||
-> Affection us ()
|
-> Affection ()
|
||||||
|
|
||||||
-- | Get the 'Participant' to emit a 'Message' on all of its subscribers
|
-- | Get the 'Participant' to emit a 'Message' on all of its subscribers
|
||||||
partEmit
|
partEmit
|
||||||
:: prt
|
:: prt
|
||||||
-- ^ The 'Participant''s subscriber storage
|
-- ^ The 'Participant''s subscriber storage
|
||||||
-> Mesg prt us
|
-> Mesg prt
|
||||||
-- ^ The 'Message' to emit
|
-- ^ The 'Message' to emit
|
||||||
-> Affection us ()
|
-> Affection ()
|
||||||
partEmit p m = do
|
partEmit p m = do
|
||||||
liftIO $ logIO Verbose $ "Emitting message: " ++ show m
|
liftIO $ logIO Verbose $ "Emitting message: " <> S.fromString (show m)
|
||||||
l <- partSubscribers p
|
l <- partSubscribers p
|
||||||
mapM_ ($ m) l
|
mapM_ ($ m) l
|
||||||
|
|
||||||
-- | Helper function to generate new 'UUID's
|
-- | Helper function to generate new 'UUID's
|
||||||
genUUID :: Affection us UUID
|
genUUID :: Affection UUID
|
||||||
genUUID = liftIO nextRandom
|
genUUID = liftIO nextRandom
|
||||||
|
|
|
@ -7,14 +7,14 @@ import Affection.Types
|
||||||
import qualified SDL
|
import qualified SDL
|
||||||
|
|
||||||
-- | Typeclass for simple scaffolding of a state machine
|
-- | Typeclass for simple scaffolding of a state machine
|
||||||
class StateMachine a us where
|
class StateMachine us a where
|
||||||
-- | State load routine
|
-- | State load routine
|
||||||
smLoad :: a -> Affection us ()
|
smLoad :: a -> us -> Affection ()
|
||||||
-- | state update routine
|
-- | state update routine
|
||||||
smUpdate :: a -> Double -> Affection us ()
|
smUpdate :: a -> us -> Double -> Affection ()
|
||||||
-- | State event handler routine
|
-- | State event handler routine
|
||||||
smEvent :: a -> [SDL.EventPayload] -> Affection us ()
|
smEvent :: a -> us -> [SDL.EventPayload] -> Affection ()
|
||||||
-- | State draw routine
|
-- | State draw routine
|
||||||
smDraw :: a -> Affection us ()
|
smDraw :: a -> us -> Affection ()
|
||||||
-- | State clean routine
|
-- | State clean routine
|
||||||
smClean :: a -> Affection us ()
|
smClean :: a -> us -> Affection ()
|
||||||
|
|
|
@ -13,6 +13,7 @@ import Control.Monad (filterM)
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
|
import Data.String as S (fromString)
|
||||||
|
|
||||||
import Foreign.C.Types (CInt(..))
|
import Foreign.C.Types (CInt(..))
|
||||||
|
|
||||||
|
@ -21,15 +22,15 @@ import qualified SDL
|
||||||
-- | Helper function that consumes all Joystick-related 'SDL.EventPayload's
|
-- | Helper function that consumes all Joystick-related 'SDL.EventPayload's
|
||||||
-- and emits appropriate 'JoystickMessage's
|
-- and emits appropriate 'JoystickMessage's
|
||||||
consumeSDLJoystickEvents
|
consumeSDLJoystickEvents
|
||||||
:: forall am us. (Participant am us, Mesg am us ~ JoystickMessage)
|
:: forall am. (Participant am, Mesg am ~ JoystickMessage)
|
||||||
=> am -- ^ The message system participant
|
=> am -- ^ The message system participant
|
||||||
-> [SDL.EventPayload] -- ^ Incoming events
|
-> [SDL.EventPayload] -- ^ Incoming events
|
||||||
-> Affection us [SDL.EventPayload] -- ^ Leftover SDL events
|
-> Affection [SDL.EventPayload] -- ^ Leftover SDL events
|
||||||
consumeSDLJoystickEvents am = doConsume
|
consumeSDLJoystickEvents am = doConsume
|
||||||
where
|
where
|
||||||
doConsume
|
doConsume
|
||||||
:: [SDL.EventPayload]
|
:: [SDL.EventPayload]
|
||||||
-> Affection us [SDL.EventPayload]
|
-> Affection [SDL.EventPayload]
|
||||||
doConsume [] = return []
|
doConsume [] = return []
|
||||||
doConsume (e:es) = do
|
doConsume (e:es) = do
|
||||||
ts <- getElapsedTime
|
ts <- getElapsedTime
|
||||||
|
@ -80,13 +81,14 @@ consumeSDLJoystickEvents am = doConsume
|
||||||
joystickAutoConnect
|
joystickAutoConnect
|
||||||
:: JoystickMessage -- ^ Any 'JoystickMessage' will do,
|
:: JoystickMessage -- ^ Any 'JoystickMessage' will do,
|
||||||
-- but listens only on 'MsgJoystickDevice' messages
|
-- but listens only on 'MsgJoystickDevice' messages
|
||||||
-> Affection us (Maybe SDL.Joystick)
|
-> Affection (Maybe SDL.Joystick)
|
||||||
-- ^ Returns a joystick descriptor, if successful
|
-- ^ Returns a joystick descriptor, if successful
|
||||||
joystickAutoConnect (MsgJoystickDevice _ which SDL.JoyDeviceAdded) = liftIO $ do
|
joystickAutoConnect (MsgJoystickDevice _ which SDL.JoyDeviceAdded) = liftIO $ do
|
||||||
[descr] <- V.toList <$>
|
[descr] <- V.toList <$>
|
||||||
(V.filter (\(SDL.JoystickDevice _ i) -> i == CInt which) <$>
|
(V.filter (\(SDL.JoystickDevice _ i) -> i == CInt which) <$>
|
||||||
SDL.availableJoysticks)
|
SDL.availableJoysticks)
|
||||||
logIO Verbose $ "Connecting Joystick " ++ show which ++ " " ++ show descr
|
logIO Verbose $ "Connecting Joystick " <> fromString (show which) <> " " <>
|
||||||
|
fromString (show descr)
|
||||||
Just <$> SDL.openJoystick descr
|
Just <$> SDL.openJoystick descr
|
||||||
joystickAutoConnect _ = return Nothing
|
joystickAutoConnect _ = return Nothing
|
||||||
|
|
||||||
|
@ -96,19 +98,20 @@ joystickAutoDisconnect
|
||||||
:: [SDL.Joystick] -- ^ List of Joystick descriptors
|
:: [SDL.Joystick] -- ^ List of Joystick descriptors
|
||||||
-> JoystickMessage -- ^ Any 'JoystickMessage' will do, but listens
|
-> JoystickMessage -- ^ Any 'JoystickMessage' will do, but listens
|
||||||
-- specifically to 'MsgJoystickDevice' messages
|
-- specifically to 'MsgJoystickDevice' messages
|
||||||
-> Affection us [SDL.Joystick] -- ^ Returns altered list of Joystick descriptors
|
-> Affection [SDL.Joystick] -- ^ Returns altered list of Joystick descriptors
|
||||||
joystickAutoDisconnect js (MsgJoystickDevice _ which SDL.JoyDeviceRemoved) =
|
joystickAutoDisconnect js (MsgJoystickDevice _ which SDL.JoyDeviceRemoved) =
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
joyIds <- mapM SDL.getJoystickID js
|
joyIds <- mapM SDL.getJoystickID js
|
||||||
logIO Verbose $ "These are the Joysticks connected: " ++ show joyIds
|
logIO Verbose $ "These are the Joysticks connected: " <>
|
||||||
|
fromString (show joyIds)
|
||||||
d <- filterM (\j -> fmap (== which) (SDL.getJoystickID j)) js
|
d <- filterM (\j -> fmap (== which) (SDL.getJoystickID j)) js
|
||||||
if not (null d)
|
if not (null d)
|
||||||
then do
|
then do
|
||||||
logIO Verbose $ "disconnected joysticks: " ++ show (head d)
|
logIO Verbose $ "disconnected joysticks: " <> fromString (show $ head d)
|
||||||
logIO Verbose $ "Disconnecting Joystick " ++ show which
|
logIO Verbose $ "Disconnecting Joystick " <> fromString (show which)
|
||||||
SDL.closeJoystick (head d)
|
SDL.closeJoystick (head d)
|
||||||
njoys <- filterM (\j -> return $ head d /= j) js
|
njoys <- filterM (\j -> return $ head d /= j) js
|
||||||
logIO Verbose $ "returning joysticks: " ++ show njoys
|
logIO Verbose $ "returning joysticks: " <> fromString (show njoys)
|
||||||
return njoys
|
return njoys
|
||||||
else do
|
else do
|
||||||
logIO Error $ "Error while disconnecting Joystick"
|
logIO Error $ "Error while disconnecting Joystick"
|
||||||
|
|
|
@ -13,10 +13,10 @@ import qualified SDL
|
||||||
-- | Helper function that consumes all Keyboard-related 'SDL.EventPayload's
|
-- | Helper function that consumes all Keyboard-related 'SDL.EventPayload's
|
||||||
-- and emits appropriate 'KeyboardMessage's
|
-- and emits appropriate 'KeyboardMessage's
|
||||||
consumeSDLKeyboardEvents
|
consumeSDLKeyboardEvents
|
||||||
:: forall ak us. (Participant ak us, Mesg ak us ~ KeyboardMessage)
|
:: forall ak. (Participant ak, Mesg ak ~ KeyboardMessage)
|
||||||
=> ak -- ^ The message system participant
|
=> ak -- ^ The message system participant
|
||||||
-> [SDL.EventPayload] -- ^ Incoming events
|
-> [SDL.EventPayload] -- ^ Incoming events
|
||||||
-> Affection us [SDL.EventPayload] -- ^ Leftover SDL Events
|
-> Affection [SDL.EventPayload] -- ^ Leftover SDL Events
|
||||||
consumeSDLKeyboardEvents ak = doConsume
|
consumeSDLKeyboardEvents ak = doConsume
|
||||||
where
|
where
|
||||||
doConsume [] = return []
|
doConsume [] = return []
|
||||||
|
|
|
@ -15,15 +15,15 @@ import qualified SDL
|
||||||
-- | Helper funtion that consumes all Mouse-related 'SDL.Eventpayload's
|
-- | Helper funtion that consumes all Mouse-related 'SDL.Eventpayload's
|
||||||
-- and emits appropriate 'MouseMessage's
|
-- and emits appropriate 'MouseMessage's
|
||||||
consumeSDLMouseEvents
|
consumeSDLMouseEvents
|
||||||
:: forall am us. (Participant am us, Mesg am us ~ MouseMessage)
|
:: forall am. (Participant am, Mesg am ~ MouseMessage)
|
||||||
=> am -- ^ The message system participant
|
=> am -- ^ The message system participant
|
||||||
-> [SDL.EventPayload] -- ^ Incoming events
|
-> [SDL.EventPayload] -- ^ Incoming events
|
||||||
-> Affection us [SDL.EventPayload] -- ^ Leftover SDL events
|
-> Affection [SDL.EventPayload] -- ^ Leftover SDL events
|
||||||
consumeSDLMouseEvents am = doConsume
|
consumeSDLMouseEvents am = doConsume
|
||||||
where
|
where
|
||||||
doConsume
|
doConsume
|
||||||
:: [SDL.EventPayload]
|
:: [SDL.EventPayload]
|
||||||
-> Affection us [SDL.EventPayload]
|
-> Affection [SDL.EventPayload]
|
||||||
doConsume [] = return []
|
doConsume [] = return []
|
||||||
doConsume (e:es) = do
|
doConsume (e:es) = do
|
||||||
ts <- getElapsedTime
|
ts <- getElapsedTime
|
||||||
|
|
|
@ -15,16 +15,16 @@ import qualified SDL
|
||||||
-- | Helper function that consumes all Window-related 'SDL.EventPayload's
|
-- | Helper function that consumes all Window-related 'SDL.EventPayload's
|
||||||
-- and emits appropriate 'WindowMessage's.
|
-- and emits appropriate 'WindowMessage's.
|
||||||
consumeSDLWindowEvents
|
consumeSDLWindowEvents
|
||||||
:: forall aw us. (Participant aw us, Mesg aw us ~ WindowMessage)
|
:: forall aw. (Participant aw, Mesg aw ~ WindowMessage)
|
||||||
=> aw -- ^ The message system participant
|
=> aw -- ^ The message system participant
|
||||||
-> [SDL.EventPayload] -- ^ Incoming events
|
-> [SDL.EventPayload] -- ^ Incoming events
|
||||||
-> Affection us [SDL.EventPayload] -- ^ Leftover SDL events
|
-> Affection [SDL.EventPayload] -- ^ Leftover SDL events
|
||||||
consumeSDLWindowEvents aw = doConsume
|
consumeSDLWindowEvents aw = doConsume
|
||||||
where
|
where
|
||||||
doConsume
|
doConsume
|
||||||
:: (Mesg aw us ~ WindowMessage)
|
:: (Mesg aw ~ WindowMessage)
|
||||||
=> [SDL.EventPayload]
|
=> [SDL.EventPayload]
|
||||||
-> Affection us [SDL.EventPayload]
|
-> Affection [SDL.EventPayload]
|
||||||
doConsume [] = return []
|
doConsume [] = return []
|
||||||
doConsume (e:es) = do
|
doConsume (e:es) = do
|
||||||
ts <- getElapsedTime
|
ts <- getElapsedTime
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Affection.Types
|
||||||
import qualified SDL
|
import qualified SDL
|
||||||
|
|
||||||
-- | This class denotes a Subsystem to be part of SDL
|
-- | This class denotes a Subsystem to be part of SDL
|
||||||
class SDLSubsystem s us where
|
class SDLSubsystem s where
|
||||||
-- | Consume the given 'SDL.EventPayload's and return only those not
|
-- | Consume the given 'SDL.EventPayload's and return only those not
|
||||||
-- recognised
|
-- recognised
|
||||||
consumeSDLEvents :: s -> [SDL.EventPayload] -> Affection us [SDL.EventPayload]
|
consumeSDLEvents :: s -> [SDL.EventPayload] -> Affection [SDL.EventPayload]
|
||||||
|
|
|
@ -5,6 +5,8 @@ module Affection.Types
|
||||||
, AffectionData(..)
|
, AffectionData(..)
|
||||||
, AffectionStateInner
|
, AffectionStateInner
|
||||||
, AffectionState(..)
|
, AffectionState(..)
|
||||||
|
, AffectionWindow(..)
|
||||||
|
, AffectionContext(..)
|
||||||
, InitComponents(..)
|
, InitComponents(..)
|
||||||
, Angle
|
, Angle
|
||||||
-- | SDL reexports
|
-- | SDL reexports
|
||||||
|
@ -23,6 +25,7 @@ import qualified Data.Text as T
|
||||||
|
|
||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
|
import Control.Monad.Trans.Resource
|
||||||
import qualified Control.Monad.Parallel as MP
|
import qualified Control.Monad.Parallel as MP
|
||||||
|
|
||||||
import System.Clock (TimeSpec)
|
import System.Clock (TimeSpec)
|
||||||
|
@ -35,27 +38,12 @@ data AffectionConfig us = AffectionConfig
|
||||||
-- ^ Window title
|
-- ^ Window title
|
||||||
, windowConfigs ::
|
, windowConfigs ::
|
||||||
[
|
[
|
||||||
( Word -- ^ Window identifier
|
( Word -- --^ Window identifier
|
||||||
, SDL.WindowConfig -- ^ Window config for given window
|
, SDL.WindowConfig -- --^ Window config for given window
|
||||||
|
, SDL.WindowMode -- -- ^ Window mode to start in
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
-- ^ Window configurations
|
-- ^ Window configurations
|
||||||
, canvasSize :: Maybe (Int, Int)
|
|
||||||
-- ^ size of the texture canvas
|
|
||||||
, initScreenMode :: SDL.WindowMode
|
|
||||||
-- ^ Window mode to start in
|
|
||||||
, loadState :: IO us
|
|
||||||
-- ^ Provide your own load function to create this data.
|
|
||||||
, preLoop :: Affection us ()
|
|
||||||
-- ^ Actions to be performed, before loop starts
|
|
||||||
, eventLoop :: [SDL.EventPayload] -> Affection us ()
|
|
||||||
-- ^ Main update function. Takes fractions of a second as input.
|
|
||||||
, updateLoop :: Double -> Affection us ()
|
|
||||||
-- ^ Main update function. Takes fractions of a second as input.
|
|
||||||
, drawLoop :: Affection us ()
|
|
||||||
-- ^ Function for updating graphics.
|
|
||||||
, cleanUp :: us -> IO ()
|
|
||||||
-- ^ Provide your own finisher function to clean your data.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | Components to initialize in SDL.
|
-- | Components to initialize in SDL.
|
||||||
|
@ -64,38 +52,36 @@ data InitComponents
|
||||||
| Only [SDL.InitFlag]
|
| Only [SDL.InitFlag]
|
||||||
|
|
||||||
-- | Main type for defining the look, feel and action of the whole application.
|
-- | Main type for defining the look, feel and action of the whole application.
|
||||||
data AffectionData us = AffectionData
|
data AffectionData = AffectionData
|
||||||
{ quitEvent :: Bool -- ^ Loop breaker.
|
{ drawWindows :: [ AffectionWindow ] -- ^ SDL windows
|
||||||
, userState :: us -- ^ State data provided by user
|
, glContext :: [ AffectionContext ] -- ^ OpenGL rendering contexts
|
||||||
, drawWindows ::
|
, elapsedTime :: Double -- ^ Elapsed time in seconds
|
||||||
[
|
, deltaTime :: Double -- ^ Elapsed time in seconds since last tick
|
||||||
( Word -- ^ Window identifier
|
, sysTime :: TimeSpec -- ^ System time (NOT the time on the clock)
|
||||||
, SDL.Window -- ^ Window linked with identifier
|
, pausedTime :: Bool -- ^ Should the update loop be executed?
|
||||||
)
|
|
||||||
] -- ^ SDL windows
|
|
||||||
, glContext ::
|
|
||||||
[
|
|
||||||
( Word -- ^ Window identifier
|
|
||||||
, SDL.GLContext -- ^ Associated OpenGL context
|
|
||||||
)
|
|
||||||
] -- ^ OpenGL rendering contexts
|
|
||||||
, screenMode :: SDL.WindowMode -- ^ current screen mode
|
|
||||||
, elapsedTime :: Double -- ^ Elapsed time in seconds
|
|
||||||
, deltaTime :: Double -- ^ Elapsed time in seconds since last tick
|
|
||||||
, sysTime :: TimeSpec -- ^ System time (NOT the time on the clock)
|
|
||||||
, pausedTime :: Bool -- ^ Should the update loop be executed?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | Inner 'StateT' monad for the update state
|
-- | Inner 'StateT' monad for the update state
|
||||||
type AffectionStateInner us a = StateT us a
|
type AffectionStateInner sd m = StateT sd m
|
||||||
|
|
||||||
-- | Affection's state monad
|
-- | Affection's state monad
|
||||||
newtype AffectionState us m a = AffectionState
|
newtype AffectionState sd m a = AffectionState
|
||||||
{ runState :: AffectionStateInner us m a }
|
{ runState :: AffectionStateInner sd m a }
|
||||||
deriving (Functor, Applicative, Monad, MonadIO, MonadState us)
|
deriving (Functor, Applicative, Monad, MonadIO, MonadState sd, MonadResource)
|
||||||
|
|
||||||
instance MP.MonadParallel m => MP.MonadParallel (AffectionState us m)
|
instance MP.MonadParallel m => MP.MonadParallel (AffectionState sd m)
|
||||||
|
|
||||||
type Affection us a = AffectionState (AffectionData us) IO a
|
type Affection a = AffectionState AffectionData ResIO a
|
||||||
|
|
||||||
type Angle = Double
|
type Angle = Double
|
||||||
|
|
||||||
|
data AffectionWindow = AffectionWindow
|
||||||
|
{ awWindow :: SDL.Window
|
||||||
|
, awReleaseKey :: ReleaseKey
|
||||||
|
, awMode :: SDL.WindowMode
|
||||||
|
}
|
||||||
|
|
||||||
|
data AffectionContext = AffectionContext
|
||||||
|
{ acContext :: SDL.GLContext
|
||||||
|
, acReleaseKey :: ReleaseKey
|
||||||
|
}
|
||||||
|
|
|
@ -10,26 +10,16 @@ import qualified Graphics.Rendering.OpenGL as GL
|
||||||
|
|
||||||
import System.Clock
|
import System.Clock
|
||||||
|
|
||||||
|
import Data.String (fromString)
|
||||||
|
import Data.List (find)
|
||||||
|
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
|
|
||||||
-- | Prehandle SDL events
|
-- | Prehandle SDL events
|
||||||
preHandleEvents :: [SDL.Event] -> Affection us [SDL.EventPayload]
|
preHandleEvents :: [SDL.Event] -> Affection [SDL.EventPayload]
|
||||||
preHandleEvents evs =
|
preHandleEvents evs =
|
||||||
return $ map SDL.eventPayload evs
|
return $ map SDL.eventPayload evs
|
||||||
|
|
||||||
-- | Return the userstate to the user
|
|
||||||
getAffection :: Affection us us
|
|
||||||
getAffection = gets userState
|
|
||||||
|
|
||||||
-- | Put altered user state back
|
|
||||||
putAffection
|
|
||||||
:: us -- User state
|
|
||||||
-> Affection us ()
|
|
||||||
putAffection us = do
|
|
||||||
ad <- get
|
|
||||||
put $ ad
|
|
||||||
{ userState = us }
|
|
||||||
|
|
||||||
-- | block a thread for a specified amount of time
|
-- | block a thread for a specified amount of time
|
||||||
delaySec
|
delaySec
|
||||||
:: Int -- ^ Number of seconds
|
:: Int -- ^ Number of seconds
|
||||||
|
@ -37,46 +27,54 @@ delaySec
|
||||||
delaySec dur = SDL.delay (fromIntegral $ dur * 1000)
|
delaySec dur = SDL.delay (fromIntegral $ dur * 1000)
|
||||||
|
|
||||||
-- | Get time since start but always the same in the current tick.
|
-- | Get time since start but always the same in the current tick.
|
||||||
getElapsedTime :: Affection us Double
|
getElapsedTime :: Affection Double
|
||||||
getElapsedTime = gets elapsedTime
|
getElapsedTime = gets elapsedTime
|
||||||
|
|
||||||
-- | Get delta time (time elapsed from last frame)
|
-- | Get delta time (time elapsed from last frame)
|
||||||
getDelta :: Affection us Double
|
getDelta :: Affection Double
|
||||||
getDelta = gets deltaTime
|
getDelta = gets deltaTime
|
||||||
|
|
||||||
-- | Quit the engine loop
|
|
||||||
quit :: Affection us ()
|
|
||||||
quit = do
|
|
||||||
ad <- get
|
|
||||||
put $ ad { quitEvent = True }
|
|
||||||
|
|
||||||
-- | Toggle the Screen mode between 'SDL.Windowed' and 'SDL.FullscreenDesktop'.
|
-- | Toggle the Screen mode between 'SDL.Windowed' and 'SDL.FullscreenDesktop'.
|
||||||
-- Pauses the Engine in the process.
|
-- Pauses the Engine in the process.
|
||||||
toggleScreen :: Affection us ()
|
toggleScreen :: Word -> Affection ()
|
||||||
toggleScreen = do
|
toggleScreen windowIdent = do
|
||||||
ad <- get
|
ad <- get
|
||||||
newMode <- case screenMode ad of
|
(stop, alteredWindowList) <- foldM
|
||||||
SDL.Windowed -> do
|
(\(stop, resWindows) (num, aw@(AffectionWindow window _ mode)) -> do
|
||||||
mapM_ (flip SDL.setWindowMode SDL.FullscreenDesktop . snd) (drawWindows ad)
|
if stop || num == windowIdent
|
||||||
return SDL.FullscreenDesktop
|
then do
|
||||||
SDL.FullscreenDesktop -> do
|
newMode <- case mode of
|
||||||
mapM_ (flip SDL.setWindowMode SDL.Windowed . snd) (drawWindows ad)
|
SDL.FullscreenDesktop -> do
|
||||||
return SDL.Windowed
|
liftIO $ SDL.setWindowMode window SDL.Windowed
|
||||||
x -> do
|
return SDL.Windowed
|
||||||
liftIO $ logIO Warn ("Unexpected Screen mode: " ++ show x)
|
SDL.Windowed -> do
|
||||||
return x
|
liftIO $ SDL.setWindowMode window SDL.FullscreenDesktop
|
||||||
now <- liftIO $ getTime Monotonic
|
return SDL.FullscreenDesktop
|
||||||
put ad
|
x -> do
|
||||||
{ sysTime = now
|
liftIO $ logIO Warn ("Unexpected window mode: " <> fromString (show x))
|
||||||
, screenMode = newMode
|
return x
|
||||||
}
|
return (True, resWindows ++ [aw { awMode = newMode }])
|
||||||
|
else
|
||||||
|
return (stop, resWindows ++ [aw])
|
||||||
|
)
|
||||||
|
(False, [])
|
||||||
|
(zip [0..] (drawWindows ad))
|
||||||
|
if stop
|
||||||
|
then do
|
||||||
|
now <- liftIO $ getTime Monotonic
|
||||||
|
put ad
|
||||||
|
{ sysTime = now
|
||||||
|
, drawWindows = alteredWindowList
|
||||||
|
}
|
||||||
|
else
|
||||||
|
liftIO $ logIO Warn ("No window found with ident " <> fromString (show windowIdent))
|
||||||
|
|
||||||
-- | Fit the GL Viewport to Window size
|
-- | Fit the GL Viewport to Window size
|
||||||
fitViewport
|
fitViewport
|
||||||
:: Double -- ^ Image Ratio (width / height)
|
:: Double -- ^ Image Ratio (width / height)
|
||||||
-> WindowMessage -- ^ Incoming Message. Listens only on
|
-> WindowMessage -- ^ Incoming Message. Listens only on
|
||||||
-- 'MsgWindowResize' and ignores all others.
|
-- 'MsgWindowResize' and ignores all others.
|
||||||
-> Affection us ()
|
-> Affection ()
|
||||||
fitViewport ratio (MsgWindowResize _ _ (SDL.V2 w h)) = do
|
fitViewport ratio (MsgWindowResize _ _ (SDL.V2 w h)) = do
|
||||||
liftIO $ logIO Verbose "Fitting Viewport to size"
|
liftIO $ logIO Verbose "Fitting Viewport to size"
|
||||||
if (fromIntegral w / fromIntegral h) > ratio
|
if (fromIntegral w / fromIntegral h) > ratio
|
||||||
|
|
Loading…
Reference in a new issue