packages feed

astrds-0.1: src/Game.hs

-- the main game loop
module Game where

import Data.Maybe

import StaticInterface 
import Static 

import States  

import RenderInterface 
import Render 

import UInputInterface 
import UInputUtils

import Utilities
import Options
import Debug

startGame :: Options -> IO () 
startGame opts
   = do 
      -- initialise static data
      debug_msg opts "loading graphical data ..."
      static <- initStatic opts
      -- enter the main game loop
      debug_msg opts "entering main game loop ..."
      runGameWrapper opts static
      return ()

-- | Wrapper around the 'runGame' function
-- (using the concrete engines for rendering and input handling)
runGameWrapper :: Options -> Static -> IO ()
runGameWrapper opts static = runGame opts 
                                     (standardRenderEngine static) 
                                     (standardUInputEngine (options static)) 
                                     getTicks

-- | The length of a "tick" in ms (i.e., the state is updated
-- approximately every @1000/tick_length@ times per second.
tick_length :: Int
tick_length = 30

-----------------------------------------------------------
-- * Game loop
-----------------------------------------------------------

runGame :: Options -> RenderEngine -> UInputEngine -> IO Int -> IO ()
runGame opts rengine iengine getTime 
   = do
      -- init State
      let s = initState
      -- wait/quit
      q <- inputWaitQ 
      if q then return ()
           else do
                  -- call the runGameLoop with the right arguments
                  t <- getTime
                  s' <- runGameLoop t s 
                  -- restart the game
                  runGame opts rengine iengine getTime
 where
  -- unpack the rendering engine
  renderR = render_running   rengine
  renderE = render_end       rengine

  -- unpack the input handling engine
  inputHandle = input_handleRunning iengine
  inputWait   = input_waitForAnyKey iengine
  inputWaitQ  = waitOrQuit inputWait

  -- the game loop 
  runGameLoop :: Int -> State -> IO State
  runGameLoop tu s
    = do
       -- player still alive?
       let st = status s
       case st of
         Alive -> runGameLoopAlive tu s
         Dead  -> do
                   renderE s
                   return s

  runGameLoopAlive :: Int -> State -> IO State
  runGameLoopAlive tu s
    = do 
       -- has enough time passed since the last update of the state to update again?
       t <- getTime   
       let dt = t - tu
       debug_msg opts (show t ++ " - " ++ show tu ++ " = " ++ show dt)
       if dt >= tick_length
        then  -- update
         do
           -- get user input 
           ui <- inputHandle (input s)
           -- did the player quit?
           if hasQuit ui 
             then return s
             else
               do
                 -- update the state according to input
                 -- debug_msg "update state"
                 s' <-  updateState s ui dt
                 -- render the state
                 renderR 0 s'
                 -- re-enter the loop
                 runGameLoop t s' 
        else  -- render interpolated state
         do
           -- render the state
           renderR dt s
           -- re-enter the loop
           runGameLoopAlive tu s