packages feed

immutaball-core-0.1.0.4.1: Immutaball/Ball/State/Play.hs

{-# OPTIONS_GHC -fno-warn-tabs #-}  -- Support tab indentation better, for a better default of no warning if tabs are used: https://dmitryfrank.com/articles/indent_with_tabs_align_with_spaces .
-- Enable warnings:
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

-- Play.hs.

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE Arrows, TemplateHaskell, ScopedTypeVariables #-}

module Immutaball.Ball.State.Play
	(
		mkPlayState,
		PlayWidget(..), AsPlayWidget(..),
		playGui
	) where

import Prelude ()
import Immutaball.Prelude

import Control.Arrow
import Control.Lens
import Control.Monad
--import Data.Functor.Identity

--import Control.Lens
--import Data.Array.IArray
--import qualified Data.Map as M
import qualified SDL.Raw.Enum as Raw

import Immutaball.Ball.Game
import Immutaball.Ball.LevelSets
import Immutaball.Ball.State.Game
import Immutaball.Share.Context
import Immutaball.Share.GUI
import Immutaball.Share.ImmutaballIO.SDLIO
import Immutaball.Share.Level
import Immutaball.Share.Math
import Immutaball.Share.SDLManager
import Immutaball.Share.State
import Immutaball.Share.State.Context
import Immutaball.Share.Utils
import Immutaball.Share.Wire

-- TODO:
mkPlayState :: Maybe LevelSet -> String -> LevelIB -> (Either IBContext IBStateContext -> Immutaball) -> Either IBContext IBStateContext -> Immutaball
mkPlayState mlevelSet levelPath level mkBack baseCxt0 = closeSecondI . switch . fromImmutaballSingleWith Nothing . openSecondI $ proc (Identity request) -> do
	rec
		cxtLast <- delay cxt0 -< cxt
		cxtn <- requireBasics -< (cxtLast, request)

		let sdlh = cxtn^.ibContext.ibSDLManagerHandle

		-- Capture mouse on entry (TODO: only capture when actively playing, not paused or in intermession).
		isFirst <- delay True -< False
		() <- monadic -< if' (not $ isFirst) (return ()) . void . liftIBIO $ sdl sdlh (SDLSetRelativeMouseMode True id)

		-- GUI: don't process here quite yet, only because our overall rendering
		-- plan requires the scene to render first, before GUI.

		-- Set up and step the game.
		-- TODO: implement hasLevelBeenCompleted bool for initialGameState call.  Just setting to False in the meantime.
		let theInitialGameState = initialGameState (cxtn^.ibContext) (cxtn^.ibNeverballrc) False mlevelSet levelPath level
		--lastGameState <- delay theInitialGameState -< gameState
		lastGameState <- delayWith -< (gameState, theInitialGameState)
		(GameResponse _gameEvents gameState cxtnp1) <- stepGame -< GameRequest request lastGameState cxtn
		let gameStateAnalysis = mkGameStateAnalysis cxtnp1 gameState

		-- If playing, prepare the clock to display.  TODO: more than seconds.
		let (timeElapsed :: Double) = gameState^.gsTimeElapsed
		let (secondsElapsed :: Integer) = floor $ timeElapsed
		lastSecondsElapsed <- delay 0 -< secondsElapsed
		let (newClockSeconds :: Maybe Integer) = if' (secondsElapsed /= lastSecondsElapsed) (Just secondsElapsed) Nothing
		let (guiClockInputs :: [WidgetRequest PlayWidget]) = case newClockSeconds of
			Nothing -> []
			Just s  -> [GUISetText ClockLabel (show s) defaultSetTextOptions]

		-- Render the scene.
		let (mview :: MView) = gameStateAnalysis^.gsaView
		isPaint <- returnA -< ((const False) ||| (const True)) . matching (_Paint) $ request
		cxtnp2 <- returnA ||| renderLevel -< if' (not isPaint) (Left cxtnp1) (Right $ ((mview, (gameState^.gsSwa), gameState), cxtnp1))
		cxtnp3 <- returnA ||| renderBall -< if' (not isPaint) (Left cxtnp1) (Right $ (gameState, cxtnp2))

		-- GUI.  Positioned after scene rendering.
		--(_guiResponse, cxtnp4) <- mkGUI playGui -< (GUIDrive request, cxtnp3)
		let guiInputs = concat $
			[ guiClockInputs
			]
		(_guiResponses, cxtnp4) <- multistepFeedbackList $ mkGUI playGui -< (GUIDrive request : guiInputs, cxtnp3)
		let response = ContinueResponse

		let isEsc  = (const False ||| (== (fromIntegral Raw.SDLK_ESCAPE, True))) . matching _Keybd $ request
		let isBack = isEsc

		-- Release mouse on isEsc or isBack.
		() <- monadic -< if' (not (isEsc || isBack)) (return ()) . void . liftIBIO $ sdl sdlh (SDLSetRelativeMouseMode False id)

		() <- finishFrame -< (request, cxtnp4)
		cxt <- returnA -< cxtnp4

	-- Switch on Back button.
	let switchTo = if' (not isBack) Nothing . Just . openSecondI $ mkBack (Right cxt)
	returnA -< (Identity response, switchTo)

	where cxt0 = either initialStateCxt id baseCxt0

data PlayWidget =
	  PlayRoot
	| StatusLabel
	| ClockLabel
	| Anonymous Integer
	deriving (Eq, Ord, Show)
--makeClassyPrisms ''PlayWidget

-- TODO:
playGui :: [Widget PlayWidget]
playGui =
	[
		RootWidget $ Root { _rootWid = PlayRoot },

		LabelWidget $ Label { _labelWid = StatusLabel, _labelWparent = PlayRoot,
			_labelText = "", _labelRect = Just $ Rect (Vec2 (-0.100) (-0.040)) (Vec2 (0.100) ( 0.040)) },
		LabelWidget $ Label { _labelWid = ClockLabel, _labelWparent = PlayRoot,
			_labelText = "", _labelRect = Just $ Rect (Vec2 ( 0.750) (-0.870)) (Vec2 (0.950) (-0.950)) }
	]

makeClassyPrisms ''PlayWidget