nyx-game-0.1.0.0: app/DecObj/Particle.hs
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
module DecObj.Particle where
import qualified SDL
import SDL.Vect (Point(P))
import Play.Engine
import Control.Lens
import GHC.Generics
import Control.DeepSeq
import qualified Control.Monad.State as SM
import qualified DecorationObject as DecObj
import qualified Play.Engine.Movement as MV
data Particle
= Particle
{ _pos :: {-# UNPACK #-} !IPoint
, _size :: {-# UNPACK #-} !Size
, _movement :: {-# UNPACK #-} !MV.Movement
, _direction :: {-# UNPACK #-} !FPoint
, _texture :: SDL.Texture
, _texturePos :: {-# UNPACK #-} !IPoint
, _textureSize :: {-# UNPACK #-} !IPoint
, _deathTime :: {-# UNPACK #-} !Int
, _timers :: {-# UNPACK #-} !Timers
}
data Timers
= Timers
{ _deathAnimationTimer :: {-# UNPACK #-} !Int
}
deriving (Eq, Ord, Show, Generic, NFData)
instance NFData Particle where
rnf (Particle {..}) =
rnf _pos
`seq` rnf _size
`seq` rnf _movement
`seq` rnf _direction
`seq` rnf _texturePos
`seq` rnf _textureSize
`seq` rnf _deathTime
`seq` rnf _timers
makeFieldsNoPrefix ''Particle
makeFieldsNoPrefix ''Timers
data MakeParticle
= MakeParticle
{ mkPos :: {-# UNPACK #-} !IPoint
, mkSize :: {-# UNPACK #-} !Size
, mkDir :: {-# UNPACK #-} !FPoint
, mkMov :: {-# UNPACK #-} !MV.Movement
, mkTexture :: !SDL.Texture
, mkTexturePos :: {-# UNPACK #-} !IPoint
, mkTextureSize :: {-# UNPACK #-} !IPoint
, mkDeathTime :: {-# UNPACK #-} !Int
}
make :: MakeParticle -> Particle
make MakeParticle{..} =
Particle
{ _pos = mkPos
, _size = mkSize
, _direction = mkDir
, _movement = mkMov
, _texture = mkTexture
, _texturePos = mkTexturePos
, _textureSize = mkTextureSize
, _deathTime = mkDeathTime
, _timers = initEnemyTimers mkDeathTime
}
toDecObj :: Particle -> DecObj.DecorationObject
toDecObj part = DecObj.make
part
(\i p -> (,[]) <$> update i p)
renderFading
initEnemyTimers :: Int -> Timers
initEnemyTimers death = Timers
{ _deathAnimationTimer = death
}
update :: Input -> Particle -> Result (Maybe Particle)
update _ part = do
wsize <- _windowSize <$> SM.get
let
(mv, move) =
MV.update (part ^. direction)
$ (part ^. movement)
part' =
part
& over pos (`addPoint` move)
& set movement mv
& set timers (updateTimers part)
pure
( if part' ^. timers . deathAnimationTimer <= 0
|| not (isInWindow wsize (part ^. pos) (part ^. size))
then Nothing
else pure part'
)
updateTimers :: Particle -> Timers
updateTimers part =
(part ^. timers)
& over deathAnimationTimer
(\t ->
if
| t >= 0 -> t - 1
| otherwise -> -1
)
renderFading :: SDL.Renderer -> Camera -> Particle -> IO ()
renderFading renderer cam part = do
SDL.textureBlendMode (part ^. texture) SDL.$= SDL.BlendAlphaBlend
SDL.textureAlphaMod (part ^. texture) SDL.$=
( floor @Float
$ (255 *)
$ fromIntegral (part ^. timers . deathAnimationTimer)
/ fromIntegral (max (part ^. deathTime) 1)
)
let
rect = toRect (cam $ part ^. pos) (part ^. size)
clip = SDL.Rectangle (P $ part ^. texturePos) (part ^. size)
SDL.copy renderer (part ^. texture) (Just $ fmap fromIntegral clip) (Just rect)
SDL.textureAlphaMod (part ^. texture) SDL.$= 255