packages feed

spanout-0.1: src/Spanout/Main.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}

module Spanout.Main (main) where

import Spanout.Common
import Spanout.Gameplay
import Spanout.Wire

import Control.Lens
import Control.Monad.Random
import Control.Monad.Reader

import qualified Data.Set as Set

import qualified Graphics.Gloss as Gloss
import qualified Graphics.Gloss.Data.ViewPort as Gloss
import qualified Graphics.Gloss.Interface.IO.Game as Gloss

import Linear

import qualified System.Exit as System



type MainWire = () ->> Gloss.Picture

data World = World
  { _worldWire     :: MainWire
  , _worldEnv      :: Env
  , _worldLastPic  :: Gloss.Picture
  , _worldViewPort :: Gloss.ViewPort
  }

makeLenses ''World

main :: IO ()
main = Gloss.playIO disp Gloss.black fps world
    obtainPicture registerEvent performIteration
  where
    disp = Gloss.InWindow "spanout" winSize (0, 0)
    fps = 60
    winSize = (960, 540)
    world = World
      { _worldWire     = game
      , _worldEnv      = env
      , _worldLastPic  = Gloss.blank
      , _worldViewPort = viewPort winSize
      }
    env = Env
      { _envMouse = zero
      , _envKeys  = Set.empty
      }



-- Updated the world with a gloss event
registerEvent :: Gloss.Event -> World -> IO World
registerEvent (Gloss.EventResize wh) world =
  return $ set worldViewPort (viewPort wh) world
registerEvent (Gloss.EventMotion p) world =
  return $ set (worldEnv . envMouse) (V2 x y) world
  where
    (x, y) = Gloss.invertViewPort vp p
    vp = view worldViewPort world
registerEvent (Gloss.EventKey key Gloss.Down _ _) world =
  return $ over (worldEnv . envKeys) (Set.insert key) world
registerEvent (Gloss.EventKey key Gloss.Up _ _) world =
  return $ over (worldEnv . envKeys) (Set.delete key) world

-- Steps the wire stored in the world and stores the resulting picture and wire
performIteration :: Float -> World -> IO World
performIteration dTime world = do
  let
    timed = Timed dTime ()
    input = Right ()
    mb = stepWire (view worldWire world) timed input
  (epic, wire') <- evalRandIO . runReaderT mb $ view worldEnv world
  case epic of
    Right pic -> return . set worldWire wire' . set worldLastPic pic $ world
    Left ()   -> System.exitSuccess

-- The rendered picture from the world
obtainPicture :: World -> IO Gloss.Picture
obtainPicture world = return $ Gloss.applyViewPortToPicture vp pic
  where
    vp = view worldViewPort world
    pic = view worldLastPic world

-- The viewport based on the new dimensions of the window
viewPort :: (Int, Int) -> Gloss.ViewPort
viewPort (w, h) = Gloss.viewPortInit { Gloss.viewPortScale = scale }
  where
    scale = min scaleX scaleY
    scaleX = fromIntegral w / 2 / screenBoundX
    scaleY = fromIntegral h / 2 / screenBoundY