edge-0.8: Unit/Smart/Ninja.hs
module Unit.Smart.Ninja ( Ninja(..)
, new
) where
import Data.WrapAround
import Animation
import Graphics.Gloss.Data.Picture
import Graphics.Gloss.Data.Color
import GHC.Float
import Trigonometry
import ResourceTracker
import Updating
import qualified Moving as M
import Combat
import qualified Projectile.Blade as P.Blade
import AfterEffect
import qualified AfterEffect.SimpleExplosion as SimpleExplosion
import Data.Maybe
import Universe hiding (wrapMap, resourceTracker)
import qualified Universe as U
import Sound.ALUT
radialVelocity = pi/2 -- radians per second
maxVelocityMag = 200.0
kamikazeDamage = 8.0
maxIntegrity = 2
accelerationRate = 200.0
adjAngle = pi / 8
shotDelay = 5.0
data Ninja = Ninja { angle :: Double -- radians
, velocity :: (Double, Double)
, center :: WrapPoint
, idealTargetLocation :: Maybe WrapPoint
, wrapMap :: WrapMap
, launchTube :: [Projectile]
, sinceLastShot :: Double
, integrity :: Double
, vision :: Maybe Arena
, resourceTracker :: ResourceTracker
-- Sound
, queueShotSound :: Bool
, shotSoundSource :: Maybe Source
}
instance Audible Ninja 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
-> Double
-> Ninja
new rt wmap center' angle' =
Ninja { center = center'
, angle = angle'
, idealTargetLocation = Nothing
, velocity = (0.0, 0.0)
, wrapMap = wmap
, launchTube = []
, sinceLastShot = 0.0
, integrity = maxIntegrity
, vision = Nothing
, resourceTracker = rt
, queueShotSound = False
, shotSoundSource = Nothing
}
instance Observant Ninja 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 :: Double -> Ninja -> Ninja
updateVelocity t self =
let thrustingVelocity = M.calcNewVelocity
(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 Ninja where
image self _ = Rotate (radToDeg
(double2Float
(angle self)) * (-1) - 90)
pic
where pic = fromMaybe
(Scale 0.20 0.20
(Color white
(Text "Error! Missing image!")))
(getImage rt "ninja.bmp")
rt = resourceTracker self
instance M.Locatable Ninja where
center = center
instance M.Moving Ninja where
velocity = velocity
instance M.Colliding Ninja where
collisionRadius _ = 40.0
instance InternallyUpdating Ninja 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 =
let sinceLastShot' = sinceLastShot self + t in
if sinceLastShot' >= shotDelay
then self { sinceLastShot = 0.0
, launchTube = projectile : launchTube self
, queueShotSound = True
}
else self { sinceLastShot = sinceLastShot' }
where projectile = Projectile
(P.Blade.new
(wrapMap self)
(resourceTracker self)
pAngle
(center self)
(velocity self))
pSpeed = P.Blade.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))
updateIdealTargetLocation :: Double -> Ninja -> Ninja
updateIdealTargetLocation t self =
self { idealTargetLocation = Just (M.idealNewLocation (wrapMap self)
(center self)
(velocity self)
t)
}
instance Launcher Ninja where
deployProjectiles self = (launchTube self, self { launchTube = [] })
instance Transient Ninja 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 Ninja where
inflictDamage self d = self { integrity = max 0.0 (integrity self - d) }
instance Damaging Ninja where
damageEnergy self = kamikazeDamage