{-| 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