{-# 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