apecs-0.2.0.0: example/RTS.hs
{-# LANGUAGE MultiParamTypeClasses, TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
module Main where
import Control.Monad as M
import qualified SDL
import SDL (($=))
import System.Random
import Data.Proxy
import SDL.Vect
import Apecs as A
import Apecs.Stores
import Apecs.Util
hres, vres :: Num a => a
hres = 1024
vres = 768
newtype Position = Position {getPos :: V2 Double} deriving (Show, Num)
instance Component Position where
type Storage Position = Map Position
newtype Target = Target (V2 Double)
instance Component Target where
type Storage Target = Map Target
data Selected = Selected
instance Flag Selected where flag = Selected
instance Component Selected where
type Storage Selected = Set Selected
data MouseState = Dragging !(V2 Double) !(V2 Double) | Rest
instance Component MouseState where
type Storage MouseState = Global MouseState
data World = World
{ positions :: Storage Position
, targets :: Storage Target
, selected :: Storage Selected
, mouseState :: Storage MouseState
, entityCounter :: Storage EntityCounter
}
instance World `Has` Position where getStore = System $ asks positions
instance World `Has` Target where getStore = System $ asks targets
instance World `Has` Selected where getStore = System $ asks selected
instance World `Has` MouseState where getStore = System $ asks mouseState
instance World `Has` EntityCounter where getStore = System $ asks entityCounter
type System' a = System World a
game :: System' ()
game = do
(window, renderer) <- initRenderer
-- Add units
replicateM_ 5000 $ do
x <- liftIO$ randomRIO (100,hres/2)
y <- liftIO$ randomRIO (100,vres-100)
newEntity (Position (V2 x y))
let loop = do
shouldQuit <- handleEvents
step
render renderer
unless shouldQuit loop
loop
cleanup (window, renderer)
render :: SDL.Renderer -> System' ()
render renderer = do
liftIO$ SDL.rendererDrawColor renderer $= V4 0 0 0 255
liftIO$ SDL.clear renderer
cimapM_ $ \(e, Position p) -> do
e <- exists (cast e :: Entity Selected)
liftIO$ SDL.rendererDrawColor renderer $= if e then V4 255 255 255 255 else V4 255 0 0 255
SDL.drawPoint renderer (P (round <$> p))
liftIO$ SDL.rendererDrawColor renderer $= V4 255 255 255 255
r <- readGlobal
case r of
Dragging a b -> SDL.drawRect renderer (Just $ SDL.Rectangle (P (round <$> a)) (round <$> b-a))
_ -> return ()
SDL.present renderer
step = do
let speed :: Num a => a
speed = 5
stepPosition :: (Target, Position) -> Safe (Target, Position)
stepPosition (Target t, Position p)
| norm (p-t) < speed = Safe (Nothing, Just (Position t))
| otherwise = Safe (Just (Target t), Just (Position (p + speed * normalize (t-p))))
cmap' stepPosition
m <- readGlobal
case m of
Rest -> return ()
Dragging (V2 ax ay) (V2 bx by) -> do
resetStore (Proxy :: Proxy Selected)
let f :: Position -> Safe Selected
f (Position (V2 x y)) = Safe (x >= min ax bx && x <= max ax bx && y >= min ay by && y <= max ay by)
rmap' f
handleEvents = do
events <- fmap SDL.eventPayload <$> SDL.pollEvents
mapM_ handleEvent events
return (SDL.QuitEvent `elem` events)
where
handleEvent :: SDL.EventPayload -> System' ()
handleEvent (SDL.MouseButtonEvent (SDL.MouseButtonEventData _ SDL.Pressed _ SDL.ButtonLeft _ (P p))) =
let p' = fromIntegral <$> p in writeGlobal (Dragging p' p')
handleEvent (SDL.MouseButtonEvent (SDL.MouseButtonEventData _ SDL.Released _ SDL.ButtonLeft _ _)) =
writeGlobal Rest
handleEvent (SDL.MouseMotionEvent (SDL.MouseMotionEventData _ _ _ (P p) _)) = do
md <- readGlobal
case md of
Rest -> return ()
Dragging a _ -> writeGlobal (Dragging a (fromIntegral <$> p))
handleEvent (SDL.MouseButtonEvent (SDL.MouseButtonEventData _ SDL.Pressed _ SDL.ButtonRight _ (P (V2 px py)))) = do
sl :: Slice Selected <- slice All
let r = (*3) . subtract 1 . sqrt . fromIntegral$ sliceSize sl
sliceForM_ sl $ \e -> do
dx <- liftIO$ randomRIO (-r,r)
dy <- liftIO$ randomRIO (-r,r)
set e (Target (V2 (fromIntegral px+dx) (fromIntegral py+dy)))
handleEvent _ = return ()
initRenderer = liftIO$ do
SDL.initialize [SDL.InitVideo]
SDL.HintRenderScaleQuality $= SDL.ScaleLinear
window <- SDL.createWindow "Apecs tutorial" SDL.defaultWindow {SDL.windowInitialSize = V2 hres vres}
SDL.showWindow window
renderer <- SDL.createRenderer window (-1) (SDL.RendererConfig SDL.AcceleratedRenderer False)
return (window, renderer)
cleanup (window, renderer) = liftIO$ do
SDL.destroyRenderer renderer
SDL.destroyWindow window
SDL.quit
main :: IO ()
main = do
w <- World <$> initStore <*> initStore <*> initStore <*> initStoreWith Rest <*> initCounter
runSystem game w