edge-0.8.16: Unit/Smart/Death.hs
module Unit.Smart.Death ( Death(..)
, new
) where
import Data.WrapAround
import Animation
import Graphics.Gloss.Data.Picture
import Graphics.Gloss.Data.Color
import GHC.Float
import Math
import ResourceTracker
import Updating
import qualified Moving as M
import Combat
import qualified Projectile.BulletSII as P.BulletSII
import qualified Projectile.BulletMII as P.BulletMII
import AfterEffect
import qualified AfterEffect.SimpleExplosion as SimpleExplosion
import Data.Maybe
import Universe hiding (wrapMap, resourceTracker)
import qualified Universe as U
import Sound.ALUT
import Common
radialVelocity = pi -- radians per second
maxVelocityMag = 200.0
kamikazeDamage = 100.0
maxIntegrity = 12
accelerationRate = 200.0
adjAngle = pi / 8
shotDelay_sniper = 3.0
shotDelay_spread = 2.0
data Death = Death { angle :: Angle -- radians
, velocity :: Velocity
, center :: WrapPoint
, idealTargetLocation :: Maybe WrapPoint
, wrapMap :: WrapMap
, launchTube :: [Projectile]
, sinceLastShot_sniper :: Time
, sinceLastShot_spread :: Time
, integrity :: Double
, vision :: Maybe Arena
, resourceTracker :: ResourceTracker
-- Sound
, queueShotSound :: Bool
, shotSoundSource :: Maybe Source
}
instance Audible Death where
processAudio self lcenter =
do self' <- if isNothing (shotSoundSource self)
then initializeShotSoundSource self
else return self
if not (queueShotSound self)
then return self'
else do let (x, y) = vectorRelation
(wrapMap self)
(lcenter)
(center self)
let s = fromJust $ shotSoundSource self
sourcePosition s $= (Vertex3 (double2Float x)
(double2Float (-y))
0)
play [s]
return self' { queueShotSound = False }
terminateAudio self =
if isNothing (shotSoundSource self)
then return self
else do stop [fromJust (shotSoundSource self)]
return self
initializeShotSoundSource self =
do [source] <- genObjectNames 1
buffer source $= getSound (resourceTracker self) "energy-shot-02.wav"
sourceRelative source $= Listener
referenceDistance source $= audioReferenceDistance
rolloffFactor source $= audioRolloffFactor
return self { shotSoundSource = Just source }
new :: ResourceTracker
-> WrapMap
-> WrapPoint
-> Angle
-> Death
new rt wmap center' angle' =
Death { center = center'
, angle = angle'
, idealTargetLocation = Nothing
, velocity = (0.0, 0.0)
, wrapMap = wmap
, launchTube = []
, sinceLastShot_sniper = 0.0
, sinceLastShot_spread = 0.0
, integrity = maxIntegrity
, vision = Nothing
, resourceTracker = rt
, queueShotSound = False
, shotSoundSource = Nothing
}
instance Observant Death where
updateVision self arena = self { vision = Just arena }
updateAngle t self
= case vision self of
Nothing -> self
Just a -> case lance a of
Nothing -> self
Just l -> let sDir = angle self in
let sDir' = if sDir == 0.0 / 0.0
then 0.1
else sDir in
let tDir = vectorDirection
(vectorRelation
(wrapMap self)
(center self)
(M.center l)) in
let adj
| tDir - sDir' > adjAngle = radialVelocity * t
| tDir - sDir' < (-1) * adjAngle = (-radialVelocity) * t
| otherwise = 0.0 in
self { angle = angle self + adj }
updateVelocity t self =
let thrustingVelocity = M.newVelocity
(velocity self)
accelerationRate
(angle self)
maxVelocityMag
t in
let velocity' = case vision self of
Nothing -> velocity self
Just a ->
case lance a of
Nothing -> velocity self
Just l -> let sDir = angle self in
let sDir' = if sDir == 0.0 / 0.0
then 0.1
else sDir in
let tDir = vectorDirection
(vectorRelation
(wrapMap self)
(center self)
(M.center l)) in
if abs (tDir - sDir') <= adjAngle
then thrustingVelocity
else velocity self in
self { velocity = velocity' }
instance Animation Death where
image self _ = fromMaybe
(Scale 0.20 0.20
(Color white
(Text "Error! Missing image!")))
(getImage rt "death.bmp")
where rt = resourceTracker self
instance M.Locatable Death where
center = center
instance M.Moving Death where
velocity = velocity
instance M.Colliding Death where
collisionRadius _ = 60.0
instance InternallyUpdating Death where
preUpdate self t = (updateFiringInformation t
. updateIdealTargetLocation t
. updateVelocity t
. updateAngle t) self
postUpdate self t =
let center' = fromMaybe (center self) (idealTargetLocation self) in
self { center = center'
, idealTargetLocation = Nothing
}
updateFiringInformation t self =
fst $ (handleSpreadFiring . handleSniperFiring) (self, 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
, queueShotSound = True
}, t)
else (self { sinceLastShot_sniper = sinceLastShot_sniper' }, t)
where projectile = Projectile
(P.BulletSII.new
(wrapMap 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
, queueShotSound = 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
(wrapMap 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))
updateIdealTargetLocation :: Time -> Death -> Death
updateIdealTargetLocation t self =
self { idealTargetLocation = Just (M.newLocation (wrapMap self)
(center self)
(velocity self)
t)
}
instance Launcher Death where
deployProjectiles self = (launchTube self, self { launchTube = [] })
instance Transient Death where
expired' self = if integrity self > 0.0
then Nothing
else Just [aeffect]
where aeffect = AfterEffect (SimpleExplosion.new (resourceTracker self)
(wrapMap self)
(center self)
(velocity self))
instance Damageable Death where
inflictDamage self d = self { integrity = max 0.0 (integrity self - d) }
instance Damaging Death where
damageEnergy self = kamikazeDamage