2010-02-23 23:31:11 +01:00
|
|
|
{-# LANGUAGE PatternGuards, GeneralizedNewtypeDeriving #-}
|
2010-02-22 22:25:06 +01:00
|
|
|
|
2010-02-22 16:50:42 +01:00
|
|
|
import Game
|
|
|
|
import Level
|
2010-02-23 15:05:31 +01:00
|
|
|
import Render
|
2010-03-02 21:36:37 +01:00
|
|
|
import Player
|
2010-03-02 23:22:44 +01:00
|
|
|
import CPUPlayer
|
2010-03-02 21:36:37 +01:00
|
|
|
import DefaultPlayer
|
2010-02-22 16:50:42 +01:00
|
|
|
|
|
|
|
import GLDriver
|
|
|
|
import GLX
|
|
|
|
|
2010-02-22 18:27:18 +01:00
|
|
|
import Control.Concurrent (threadDelay)
|
|
|
|
import Control.Monad.State
|
2010-03-02 06:10:34 +01:00
|
|
|
import Data.Fixed
|
2010-02-22 18:27:18 +01:00
|
|
|
import Data.Maybe
|
2010-02-25 03:16:44 +01:00
|
|
|
import qualified Data.Map as M
|
2010-02-24 03:40:06 +01:00
|
|
|
import Data.Ratio
|
|
|
|
import qualified Data.Set as S
|
2010-02-24 02:42:10 +01:00
|
|
|
import Data.Time.Clock
|
2010-02-22 18:27:18 +01:00
|
|
|
|
|
|
|
|
2010-02-23 23:31:11 +01:00
|
|
|
data MainState = MainState
|
2010-02-24 03:40:06 +01:00
|
|
|
{ run :: !Bool
|
|
|
|
, driver :: !SomeDriver
|
2010-02-24 02:42:10 +01:00
|
|
|
, time :: !UTCTime
|
2010-03-02 21:36:37 +01:00
|
|
|
, players :: ![SomePlayer]
|
2010-02-23 23:31:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
2010-02-22 16:50:42 +01:00
|
|
|
main :: IO ()
|
|
|
|
main = do
|
2010-03-08 20:16:51 +01:00
|
|
|
let theLevel = testLevel
|
|
|
|
gl <- initGL $ glxDriver (levelWidth theLevel) (levelHeight theLevel)
|
2010-02-22 16:50:42 +01:00
|
|
|
|
2010-02-23 15:05:31 +01:00
|
|
|
when (initialized gl) $ do
|
2010-02-24 02:42:10 +01:00
|
|
|
currentTime <- getCurrentTime
|
2010-03-02 23:22:44 +01:00
|
|
|
let mainState = MainState {run = True, driver = SomeDriver gl, time = currentTime, players =
|
2010-03-08 22:13:35 +01:00
|
|
|
[ SomePlayer $ DefaultPlayer S.empty 0 0 False
|
2010-03-02 23:22:44 +01:00
|
|
|
, SomePlayer $ CPUPlayer 0
|
|
|
|
]}
|
2010-03-08 23:12:14 +01:00
|
|
|
gameState = GameState {level = theLevel, tanks = [ Tank 5.0 5.0 0 0 2 270 False 3 2 5
|
|
|
|
, Tank 5.0 3.5 0 0 2 270 False 3 2 5
|
2010-03-08 22:13:35 +01:00
|
|
|
], shoots = [], textures = M.empty}
|
2010-02-23 20:51:30 +01:00
|
|
|
|
2010-02-25 02:15:26 +01:00
|
|
|
runGame gameState $ do
|
2010-03-05 03:32:02 +01:00
|
|
|
setup
|
2010-02-25 02:15:26 +01:00
|
|
|
runMain mainState mainLoop
|
2010-02-23 15:05:31 +01:00
|
|
|
|
|
|
|
deinitGL gl
|
|
|
|
|
2010-02-24 02:42:10 +01:00
|
|
|
minFrameTime :: NominalDiffTime
|
|
|
|
minFrameTime = 0.01
|
2010-02-22 16:50:42 +01:00
|
|
|
|
2010-02-23 23:31:11 +01:00
|
|
|
mainLoop :: Main ()
|
|
|
|
mainLoop = do
|
|
|
|
gl <- gets driver
|
|
|
|
t <- gets time
|
2010-02-24 03:40:06 +01:00
|
|
|
handleEvents
|
2010-02-22 18:27:18 +01:00
|
|
|
|
2010-02-23 23:31:11 +01:00
|
|
|
lift render
|
2010-02-23 15:05:31 +01:00
|
|
|
|
|
|
|
liftIO $ swapBuffers gl
|
|
|
|
|
2010-02-24 02:42:10 +01:00
|
|
|
rtime <- liftIO getCurrentTime
|
|
|
|
let drender = diffUTCTime rtime t
|
|
|
|
when (drender < minFrameTime) $
|
|
|
|
liftIO $ threadDelay $ truncate $ 1e6*(minFrameTime - drender)
|
2010-02-23 15:05:31 +01:00
|
|
|
|
2010-02-24 02:42:10 +01:00
|
|
|
currenttime <- liftIO getCurrentTime
|
|
|
|
let d = round $ 1e3*(diffUTCTime currenttime t)
|
2010-02-23 23:31:11 +01:00
|
|
|
|
2010-02-24 03:40:06 +01:00
|
|
|
replicateM_ d simulationStep
|
2010-02-23 15:05:31 +01:00
|
|
|
|
2010-02-24 02:42:10 +01:00
|
|
|
let newtime = addUTCTime ((1e-3)*(fromIntegral d)) t
|
|
|
|
|
|
|
|
modify $ \state -> state {time = newtime}
|
2010-02-23 15:05:31 +01:00
|
|
|
|
2010-02-24 03:40:06 +01:00
|
|
|
runnext <- gets run
|
|
|
|
when runnext mainLoop
|
|
|
|
|
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
updateAngle :: Micro -> State Tank ()
|
|
|
|
updateAngle angle = do
|
|
|
|
oldangle <- gets dir
|
|
|
|
tspeed <- gets turnspeed >>= return . (/1000)
|
2010-02-24 03:40:06 +01:00
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
let diff = angle - oldangle
|
|
|
|
let diff360 = if (diff > 180)
|
|
|
|
then (diff-360)
|
|
|
|
else if (diff <= -180)
|
|
|
|
then (diff+360)
|
|
|
|
else diff
|
2010-02-24 03:40:06 +01:00
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
let (diff180, angle180) = if (diff360 > 90)
|
|
|
|
then (diff360-180, oldangle+180)
|
|
|
|
else if (diff360 <= -90)
|
|
|
|
then (diff360+180, oldangle-180)
|
|
|
|
else (diff360, oldangle)
|
2010-02-24 03:40:06 +01:00
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
let turn = if (diff180 > tspeed)
|
|
|
|
then tspeed
|
|
|
|
else if (diff180 < -tspeed)
|
|
|
|
then (-tspeed)
|
|
|
|
else diff180
|
2010-03-02 06:10:34 +01:00
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
let newangle = angle180 + turn
|
|
|
|
|
|
|
|
let newangle180 = if (newangle > 180)
|
|
|
|
then (newangle-360)
|
|
|
|
else if (newangle <= -180)
|
|
|
|
then (newangle+360)
|
|
|
|
else newangle
|
|
|
|
|
|
|
|
modify $ \tank -> tank {dir = newangle180}
|
|
|
|
|
2010-03-02 23:22:44 +01:00
|
|
|
|
2010-03-05 03:32:02 +01:00
|
|
|
updateTank :: Maybe Micro -> Bool -> Maybe Micro -> State Tank ()
|
|
|
|
updateTank angle move aangle = do
|
2010-03-02 21:36:37 +01:00
|
|
|
when (isJust angle) $
|
|
|
|
updateAngle $ fromJust angle
|
2010-03-05 03:32:02 +01:00
|
|
|
|
|
|
|
when (isJust aangle) $
|
|
|
|
modify $ \tank -> tank {aim = fromJust aangle}
|
|
|
|
|
2010-03-02 06:10:34 +01:00
|
|
|
when move $ do
|
2010-03-02 21:36:37 +01:00
|
|
|
tdir <- gets dir
|
|
|
|
tspeed <- gets speed
|
|
|
|
moved <- gets moving
|
2010-03-02 06:10:34 +01:00
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
when (isNothing angle || (isJust angle && (tdir == fromJust angle)) || moved) $ do
|
|
|
|
let anglej = (fromRational . toRational $ tdir)*pi/180
|
|
|
|
x = tspeed * fromRational (round ((cos anglej)*1000)%1000000)
|
|
|
|
y = tspeed * fromRational (round ((sin anglej)*1000)%1000000)
|
2010-03-02 06:10:34 +01:00
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
modify $ \tank -> tank {posx = x + posx tank, posy = y + posy tank, moving = True}
|
2010-03-02 06:10:34 +01:00
|
|
|
|
|
|
|
when (not move) $ do
|
2010-03-02 21:36:37 +01:00
|
|
|
modify $ \tank -> tank {moving = False}
|
|
|
|
|
|
|
|
|
2010-03-08 22:13:35 +01:00
|
|
|
updateShoot :: State Shoot ()
|
|
|
|
updateShoot = modify $ \shoot ->
|
|
|
|
let angle = (fromRational . toRational . shootDir $ shoot)*pi/180
|
|
|
|
dx = (shootSpeed shoot) * fromRational (round ((cos angle)*1000)%1000000)
|
|
|
|
dy = (shootSpeed shoot) * fromRational (round ((sin angle)*1000)%1000000)
|
|
|
|
in shoot {shootX = dx + shootX shoot, shootY = dy + shootY shoot}
|
|
|
|
|
|
|
|
|
2010-03-02 21:36:37 +01:00
|
|
|
simulationStep :: Main ()
|
|
|
|
simulationStep = do
|
|
|
|
oldplayers <- gets players
|
|
|
|
oldtanks <- lift $ gets tanks
|
|
|
|
|
2010-03-08 22:32:52 +01:00
|
|
|
let (p, t, s) = unzip3 $ map updateTank' $ zip oldplayers oldtanks
|
2010-03-08 23:12:14 +01:00
|
|
|
ts = zip3 t s [0..]
|
|
|
|
shootingtanks = map (\(tank, _, n) -> (tank, n)) $ filter (\(tank, shoot, _) -> shoot && (shootsLeft tank) > 0) $ ts
|
|
|
|
newtanks = map (\(tank, shoot, _) -> if shoot then tank {shootsLeft = (shootsLeft tank) - 1} else tank) $ ts
|
|
|
|
newshoots = map (\(tank, n) -> Shoot
|
|
|
|
{ shootX = posx tank
|
2010-03-08 22:32:52 +01:00
|
|
|
, shootY = posy tank
|
|
|
|
, shootDir = aim tank
|
|
|
|
, shootSpeed = tankShootSpeed tank
|
|
|
|
, bouncesLeft = tankShootBounces tank
|
2010-03-08 23:12:14 +01:00
|
|
|
, shootTank = n
|
2010-03-08 22:32:52 +01:00
|
|
|
}) shootingtanks
|
|
|
|
|
|
|
|
|
|
|
|
modify $ \state -> state {players = p}
|
2010-03-08 23:12:14 +01:00
|
|
|
lift $ modify $ \state -> state {tanks = newtanks, shoots = map (execState updateShoot) (shoots state ++ newshoots)}
|
2010-03-02 21:36:37 +01:00
|
|
|
where
|
2010-03-08 22:13:35 +01:00
|
|
|
updateTank' (player, tank) = let (p, angle, move, aangle, shoot) = playerUpdate player tank
|
2010-03-05 03:32:02 +01:00
|
|
|
t = execState (updateTank angle move aangle) tank
|
2010-03-08 22:32:52 +01:00
|
|
|
in (p, t, shoot)
|
2010-03-02 21:36:37 +01:00
|
|
|
|
2010-02-23 15:05:31 +01:00
|
|
|
|
2010-02-24 03:40:06 +01:00
|
|
|
handleEvents :: Main ()
|
|
|
|
handleEvents = do
|
2010-03-05 04:38:31 +01:00
|
|
|
(newgl, event) <- gets driver >>= liftIO . nextEvent
|
|
|
|
modify $ \state -> state {driver = newgl}
|
2010-02-25 05:08:10 +01:00
|
|
|
when (isJust event) $ do
|
2010-03-02 21:36:37 +01:00
|
|
|
Main.handleEvent $ fromJust event
|
|
|
|
modify $ \state -> state {players = map (\p -> Player.handleEvent p $ fromJust event) $ players state}
|
2010-02-25 05:08:10 +01:00
|
|
|
handleEvents
|
2010-02-22 18:27:18 +01:00
|
|
|
|
2010-02-24 03:40:06 +01:00
|
|
|
handleEvent :: SomeEvent -> Main ()
|
2010-02-22 22:25:06 +01:00
|
|
|
handleEvent ev
|
2010-03-05 03:32:02 +01:00
|
|
|
| Just QuitEvent <- fromEvent ev = modify $ \state -> state {run = False}
|
2010-02-24 03:40:06 +01:00
|
|
|
| otherwise = return ()
|