packages feed

edge-0.9.1.0: Unit/Smart/Death.hs

{-|
Module      : Unit.Smart.Death
Description : Death boss unit
Copyright   : (c) Christopher Howard, 2016
License     : GPL-3
Maintainer  : ch.howard@zoho.com
-}

module Unit.Smart.Death ( Death(..)
                       , new
                       ) where

import Prelude (Maybe(..), max, (-), (*), pi, (/), (+), (++), map, Bool(..), (.), fst, (>=), (>), (<), otherwise, abs, Double, (<=))
import Sound.ALUT ( Source )

import Data.WrapAround ( WP, WM, vectorRelation )
import Animation ( Audible(..), Animation(..), termSndSrc, handSndSrc )
import Math
import ResourceTracker
import Updating
import qualified Moving as M ( Moving(..), Locatable(..), Colliding(..), newVelocity, newLocation )
import Combat
import qualified Projectile.BulletSII as P.BulletSII ( speed, new )
import qualified Projectile.BulletMII as P.BulletMII ( new )
import AfterEffect ( AfterEffect(AfterEffect) )
import qualified AfterEffect.SimpleExplosion as SimpleExplosion
    ( new )
import Universe ( Arena(lance) )
import qualified Universe as U ( Arena(wrapMap) )
import Common

radialVelocity = pi
maxVelocityMag = 200
kamikazeDamage = 100
maxIntegrity = 12
accelerationRate = 200
adjAngleC = pi / 8
shotDelay_sniper = 3
shotDelay_spread = 2
collisionR = 60

data Death = Death { angle :: Angle -- radians
                   , velocity :: Velocity
                   , center :: WP
                   , idealTargetLocation :: Maybe WP
                   , wmap :: WM
                   , launchTube :: [Projectile]
                   , sinceLastShot_sniper :: Time
                   , sinceLastShot_spread :: Time
                   , integrity :: Double
                   , vision :: Maybe Arena
                   , rt :: RT
                   , queueShotSnd :: Bool
                   , shotSndSrc :: Maybe Source
                   }

instance Audible Death where

  processAudio s a = handSndSrc s
                       queueShotSnd
                       shotSndSrc
                       (\a -> a { queueShotSnd = False })
                       rt
                       "energy-shot-02.wav"
                       (\a b -> a { shotSndSrc = Just b })
                       a (wmap s) (M.center)

  terminateAudio s = termSndSrc s shotSndSrc

new a b c d = Death { center = c
                    , angle = d
                    , idealTargetLocation = Nothing
                    , velocity = (0, 0)
                    , wmap = b
                    , launchTube = []
                    , sinceLastShot_sniper = 0
                    , sinceLastShot_spread = 0
                    , integrity = maxIntegrity
                    , vision = Nothing
                    , rt = a
                    , queueShotSnd = False
                    , shotSndSrc = Nothing
                    }

instance Observant Death where

  updateVision s a = s { vision = Just a }

updateAngle t s
 = case vision s of
     Nothing -> s
     Just a -> case lance a of
                Nothing -> s
                Just l -> let b = angle s in
                          let e = if isNan b then 0.1 else b in
                          let m = vectorDirection
                                       (vectorRelation
                                         (wmap s)
                                         (center s)
                                         (M.center l)) in
                          let adj
                               | m - e > adjAngleC = radialVelocity * t
                               | m - e < (-1) * adjAngleC = (-radialVelocity) * t
                               | otherwise = 0 in
                          s { angle = angle s + adj }

updateVelocity t s =
  s { velocity = b }
  where c = M.newVelocity
              (velocity s) accelerationRate (angle s) maxVelocityMag t
        b = case vision s of
              Nothing -> velocity s
              Just a ->
                case lance a of
                  Nothing -> velocity s
                  Just l -> let d = angle s in
                            let e = if isNan d then 0.1 else d in
                            let m = vectorDirection
                                         (vectorRelation
                                           (wmap s)
                                           (center s)
                                           (M.center l)) in
                            if abs (m - e) <= adjAngleC then c else velocity s

instance Animation Death where
  image s _ = protectedGetImage (rt s) "death.bmp"

instance M.Locatable Death where
  center = center

instance M.Moving Death where
  velocity = velocity

instance M.Colliding Death where
  collisionRadius _ = collisionR

instance InternallyUpdating Death where

  preUpdate s t = (updateFiringInformation t
                   . updateVelocity t
                   . updateAngle t) s

  postUpdate s t =
    s { center = M.newLocation (wmap s) (center s) (velocity s) t }

updateFiringInformation t s =
  fst ((handleSpreadFiring . handleSniperFiring) (s, t))

handleSniperFiring (self, t) =
  let sinceLastShot_sniper' = sinceLastShot_sniper self + t in
    if sinceLastShot_sniper' >= shotDelay_sniper
      then (self { sinceLastShot_sniper = 0.0
                , launchTube = projectile : launchTube self
                , queueShotSnd = True
                }, t)
      else (self { sinceLastShot_sniper = sinceLastShot_sniper' }, t)
  where projectile = Projectile
                       (P.BulletSII.new
                         (wmap self)
                         pAngle
                         (center self)
                         (velocity self))
        pSpeed = P.BulletSII.speed
        pAngle = case vision self of
                   Nothing -> angle self
                   Just arena ->
                     case lance arena of
                       Nothing -> angle self
                       Just l -> targetingA
                                   pSpeed
                                   (vectorRelation
                                     (U.wrapMap arena)
                                     (center self)
                                     (M.center l))
                                   (subV
                                     (M.velocity l)
                                     (M.velocity self))

handleSpreadFiring (self, t) =
  let sinceLastShot_spread' = sinceLastShot_spread self + t in
    if sinceLastShot_spread' >= shotDelay_spread
      then (self { sinceLastShot_spread = 0.0
                , launchTube = projectiles ++ launchTube self
                , queueShotSnd = True
                }, t)
      else (self { sinceLastShot_spread = sinceLastShot_spread' }, t)
  where projectiles = map projectile [ x * pi / 8.0 - pi / 4.0 | x <- [0..7] ]
        projectile x = Projectile
                        (P.BulletMII.new
                          (wmap self)
                          (pAngle + x)
                          (center self)
                          (velocity self))
        pAngle = case vision self of
                   Nothing -> angle self
                   Just arena ->
                     case lance arena of
                       Nothing -> angle self
                       Just l -> vectorDirection
                                  (vectorRelation
                                    (U.wrapMap arena)
                                    (center self)
                                    (M.center l))


instance Launcher Death where

  deployProjectiles s = (launchTube s, s { launchTube = [] })

instance Transient Death where

  expired' s = if moreThanZero (integrity s) then Nothing else Just [a]

    where a = AfterEffect
                (SimpleExplosion.new (rt s) (wmap s) (center s) (velocity s))

instance Damageable Death where

  inflictDamage s d = s { integrity = max 0 (integrity s - d) }

instance Damaging Death where

  damageEnergy _ = kamikazeDamage