{-# LANGUAGE PatternGuards, GeneralizedNewtypeDeriving #-} import Game import Level import Render import Tank import GLDriver import GLX import Control.Concurrent (threadDelay) import Control.Monad.State import Data.Maybe import qualified Data.Map as M import Data.Ratio import qualified Data.Set as S import Data.Time.Clock data MainState = MainState { run :: !Bool , driver :: !SomeDriver , time :: !UTCTime , keyset :: !(S.Set Key) } newtype MainT m a = MainT (StateT MainState m a) deriving (Monad, MonadState MainState, MonadIO, MonadTrans) type Main = MainT Game runMain :: MainState -> Main a -> Game (a, MainState) runMain st (MainT a) = runStateT a st main :: IO () main = do gl <- initGL glxDriver when (initialized gl) $ do currentTime <- getCurrentTime let mainState = MainState {run = True, driver = SomeDriver gl, time = currentTime, keyset = S.empty} gameState = GameState {level = testLevel, tanks = [Tank 0.0 0.0 0 0 2], textures = M.empty} runGame gameState $ do setup 800 600 runMain mainState mainLoop deinitGL gl minFrameTime :: NominalDiffTime minFrameTime = 0.01 mainLoop :: Main () mainLoop = do gl <- gets driver t <- gets time handleEvents lift render liftIO $ swapBuffers gl rtime <- liftIO getCurrentTime let drender = diffUTCTime rtime t when (drender < minFrameTime) $ liftIO $ threadDelay $ truncate $ 1e6*(minFrameTime - drender) currenttime <- liftIO getCurrentTime let d = round $ 1e3*(diffUTCTime currenttime t) replicateM_ d simulationStep --liftIO $ print $ d let newtime = addUTCTime ((1e-3)*(fromIntegral d)) t modify $ \state -> state {time = newtime} runnext <- gets run when runnext mainLoop simulationStep :: Main () simulationStep = do keys <- gets keyset let x = (if (S.member KeyLeft keys) then (-1) else 0) + (if (S.member KeyRight keys) then 1 else 0) y = (if (S.member KeyDown keys) then (-1) else 0) + (if (S.member KeyUp keys) then 1 else 0) let lengthsq = ((x*x)+(y*y)) when (lengthsq /= 0) $ do let length = sqrt lengthsq oldtank <- lift $ gets (head . tanks) let dx = (speed oldtank) * fromRational (round (x*1000/length)%1000000) dy = (speed oldtank) * fromRational (round (y*1000/length)%1000000) let tank = oldtank {posx = dx + posx oldtank, posy = dy + posy oldtank, dir = fromRational $ round ((atan2 y x)*1000000*180/pi)%1000000} lift $ modify $ \state -> state {tanks = tank:(tail . tanks $ state)} handleEvents :: Main () handleEvents = do event <- gets driver >>= liftIO . nextEvent when (isJust event) $ handleEvent $ fromJust event handleEvent :: SomeEvent -> Main () handleEvent ev | Just (ResizeEvent w h) <- fromEvent ev = lift $ resize w h | Just (KeyPressEvent key) <- fromEvent ev = modify $ \state -> state {keyset = S.insert key (keyset state)} | Just (KeyReleaseEvent key) <- fromEvent ev = modify $ \state -> state {keyset = S.delete key (keyset state)} | Just QuitEvent <- fromEvent ev = modify $ \state -> state {run = False} | otherwise = return ()