packages feed

nyx-game-0.1.0.0: app/Credits.hs

{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE DuplicateRecordFields  #-}
{-# LANGUAGE FlexibleInstances  #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}

module Credits where

import SDL.Vect (V4(..))
import qualified SDL
import qualified SDL.Mixer as Mix
import qualified Play.Engine.MySDL.MySDL as MySDL

import Play.Engine
import Control.Monad.Except
import Control.Lens
import System.Random
import qualified Play.Engine.Load as Load
import qualified Control.Monad.State as SM

import qualified Script
import qualified Play.Engine.Sprite as Spr


data State
  = State
  { _bg :: Maybe Spr.Sprite
  , _script :: Script.Script
  , _camera :: !Int
  , _exit :: !Bool
  }

makeFieldsNoPrefix ''State

wantedAssets :: [(String, MySDL.ResourceType FilePath)]
wantedAssets =
  [ ("bg", MySDL.Texture "VNBG.png")
  , ("unispace", MySDL.Font "unispace/unispace.ttf")
  ]

make :: Int -> Script.ScriptData -> Scene
make t sd = Load.mkState t (wantedAssets ++ Script.assets sd) (mkState $ Script.script sd)

mkState
  :: (MySDL.Resources -> Script.Script)
  -> MySDL.Resources -> Result Scene
mkState scrpt rs = do
  state <- initState (scrpt rs)
  pure $ mkScene
    state
    update
    render

initState :: Script.Script -> Result State
initState scrpt = do
  pure $ State
    { _bg = Nothing
    , _script = scrpt
    , _camera = 0
    , _exit = False
    }

update :: Input -> State -> Result (StackCommand, State)
update input state = do
  _wSize <- _windowSize <$> SM.get

  (acts, script') <- Script.update input Nothing mempty (state ^. script)

  let
    newState =
      state'
        & set script script'
        & over camera
          (\c ->
             if
               | c <= 0 && Script.shake acts -> 60
               | c <= 0 -> 0
               | otherwise -> c - 1
          )
      where
        state' =
          if Script.stopTheWorld acts
            then
              state
            else
              state
                & over (bg . _Just)
                ( case Script.changeSprite acts of
                    Nothing -> Spr.update Nothing False
                    Just sp -> const sp
                )

  if
    | keyReleased KeyQuit input -> do
      pure (None, set exit True state)
    | state ^. exit -> do
      pure (Done, state)
    | otherwise ->
      pure (Script.command acts, newState)

render :: SDL.Renderer -> State -> IO ()
render renderer state = do
  cam' <- Point <$> randomRIO (-1, 1) <*> randomRIO (-1, 1) :: IO FPoint
  let cam = addPoint $ fmap (floor . (*) (fromIntegral $ state ^. camera `div` 3)) cam'
  void $ MySDL.setBGColor (V4 0 0 0 255) renderer
  case state ^. bg of
    Nothing -> pure ()
    Just bgSpr ->
      Spr.render renderer cam (Point 0 0) (bgSpr ^. size) 255 bgSpr

  Script.render renderer cam (state ^. script)

  when (state ^. exit) $
    Mix.haltMusic