edge-0.8.16: Lance.hs
module Lance ( Lance ( rotationalThrusters
, linearThrusters
, deflector
, fireTrigger
, deflectorCharge
, center
, angle
, velocity
, godMode
, integrity
, inventory
, currentWeapon
, swClock
)
, new
, RotationDirection (..)
, shielded
, processItem
, changeCurrentWeapon
, swTimeLimit
) where
import Data.WrapAround
import Animation
import Graphics.Gloss.Data.Picture
import Graphics.Gloss.Data.Color
import GHC.Float
import Math
import ResourceTracker
import Data.Maybe
import Updating
import qualified Moving as M
import Combat
import qualified Projectile.BulletMkI as P.BulletMkI
import qualified Projectile.Cannon as P.Cannon
import qualified Projectile.Nuke as P.Nuke
import qualified Projectile.SWSide as P.SWSide
import qualified Projectile.SWForward as P.SWForward
import AfterEffect
import qualified AfterEffect.SimpleExplosion as SimpleExplosion
import Sound.ALUT
import Item
import Common
radialVelocity = pi -- radians per second
accelerationRate = 200 -- points per second
maxVelocity = 500 -- points per second
kamikazeDamage = 8.0
deflectorChargeLossFactor = 0.8
swTimeLimit = 10.0
data RotationDirection = Stable | CW | CCW
deriving (Eq)
type LanceInventory = [Bool]
data Lance = Lance { angle :: Angle
, center :: WP
, wmap :: WM
, rotationalThrusters :: RotationDirection
, velocity :: Velocity
, linearThrusters :: Bool
, queueShotSound :: Bool
, godMode :: Bool
, deflector :: Bool
, fireTrigger :: Bool
, rt :: RT
, launchTube :: [Projectile]
, currentWeapon :: Int
, inventory :: LanceInventory
, swClock :: Time
, sinceLastShot :: Time
, integrity :: Double
, deflectorCharge :: Double
, shotSoundSource :: Maybe Source
}
new r w c =
Lance { center = c
, angle = 0.0
, rotationalThrusters = Stable
, velocity = (0, 0)
, linearThrusters = False
, wmap = w
, rt = r
, deflector = False
, deflectorCharge = 2.0
, launchTube = []
, sinceLastShot = 0.0
, fireTrigger = False
, currentWeapon = 0
, inventory = [False, False, False, False, False]
, queueShotSound = False
, shotSoundSource = Nothing
, godMode = False
, integrity = 3.0
, swClock = 0.0
}
changeCurrentWeapon s =
if neither (isZero c) (a !! dec c) then changeCurrentWeapon d else d
where a = inventory s
b = currentWeapon s
c = if inc b > 5
then if a !! 0 then 1 else 0
else inc b
d = s { currentWeapon = c }
processItem s (Item a _ _) =
if a == Health then b else b { swClock = 0 }
where b = case a of
Health -> s { integrity = 3.0 }
FourWay -> f 0
Cannon -> f 1
Spread -> f 2
RapidFire -> f 3
Nuke -> f 4
f v = s { inventory = replaceAt v True (inventory s)
, currentWeapon = inc v
}
instance Audible Lance where
processAudio s _ = handSndSrc s
queueShotSound
shotSoundSource
(\a -> a { queueShotSound = False })
rt
"simple-energy-shot.wav"
(\a b -> a { shotSoundSource = Just b })
terminateAudio s = termSndSrc s shotSoundSource
shielded s = deflectorCharge s >= 1.0 && deflector s
updateAngle :: Time -> Lance -> Lance
updateAngle t s =
case rotationalThrusters s of CW -> f (-); CCW -> f (+); Stable -> s
where f a = s { angle = a (angle s) (radialVelocity * t) }
instance Animation Lance where
image s _ = Pictures [ b, reorient (angle s) a ]
where a = if linearThrusters s
then protectedGetImage (rt s) "lance-thrusting.bmp"
else protectedGetImage (rt s) "lance.bmp"
b = if deflector s && deflectorCharge s >= 1.0
then Color white (Circle 40.0)
else Blank
instance M.Locatable Lance where
center = Lance.center
instance M.Moving Lance where
velocity = velocity
instance M.Colliding Lance where
collisionRadius _ = 20.0
instance InternallyUpdating Lance where
preUpdate s t = ( updateFiringInformation t
. updateVelocity t
. updateAngle t ) s
postUpdate s t =
updateDeflectorCharge t
s { center = M.newLocation' s (wmap s) t }
updateFiringInformation t s =
b { swClock = (swClock b) + t }
where a = sinceLastShot s + t
b = if allTrue (inventory s) && swClock s < swTimeLimit
then handleSuperWeapon s a
else case currentWeapon s of
1 -> handleFourWayWeapon s a
2 -> handleCannonWeapon s a
3 -> handleSpreadWeapon s a
4 -> handleRapidFireWeapon s a
5 -> handleNukeWeapon s a
otherwise -> handleDefaultWeapon s a
handleSuperWeapon s a = if a >= 0.2 && fireTrigger s
then s { sinceLastShot = 0
, launchTube = b ++ c ++ [d] ++ launchTube s
, queueShotSound = True
}
else s { sinceLastShot = a }
where f u v = Projectile ( u (wmap s) (angle s + v) (center s) (velocity s) )
g = f P.BulletMkI.new
h = f P.SWSide.new
b = map g [ pi / 2, 3 * pi / 4, pi, 5 * pi / 4, 3 * pi / 2 ]
c = map h [ pi / 10, pi / 5, (-pi) / 10, (-pi) / 5 ]
d = f P.SWForward.new 0
handleDefaultWeapon self ls =
if ls >= 0.4 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
Projectile ( P.BulletMkI.new
(wmap self)
(angle self)
(center self)
(velocity self) ) : launchTube self
, queueShotSound = True
}
else self { sinceLastShot = ls }
handleFourWayWeapon self ls =
if ls >= 0.4 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
[ Projectile ( P.BulletMkI.new
(wmap self)
(angle self)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self + pi / 2)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self + pi)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self + 3 * pi / 2)
(center self)
(velocity self) ) ]
++ launchTube self
, queueShotSound = True
}
else self { sinceLastShot = ls }
handleCannonWeapon self ls =
if ls >= 0.7 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
Projectile ( P.Cannon.new
(wmap self)
(angle self)
(center self)
(velocity self) ) : launchTube self
, queueShotSound = True
}
else self { sinceLastShot = ls }
handleSpreadWeapon self ls =
let spreadAngle = pi / 10 in
if ls >= 0.4 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
[ Projectile ( P.BulletMkI.new
(wmap self)
(angle self)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self + spreadAngle)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self + spreadAngle * 2)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self - spreadAngle)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wmap self)
(angle self - spreadAngle * 2)
(center self)
(velocity self) ) ]
++ launchTube self
, queueShotSound = True
}
else self { sinceLastShot = ls }
handleRapidFireWeapon self ls =
if ls >= 0.2 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
Projectile ( P.BulletMkI.new
(wmap self)
(angle self)
(center self)
(velocity self) ) : launchTube self
, queueShotSound = True
}
else self { sinceLastShot = ls }
handleNukeWeapon self ls =
if ls >= 3.0 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
Projectile ( P.Nuke.new
(wmap self)
(rt self)
(angle self)
(center self)
(velocity self) ) : launchTube self
, queueShotSound = True
}
else self { sinceLastShot = ls }
updateDeflectorCharge t lance =
let charge = deflectorCharge lance in
let charge' = if deflector lance then max 0.8 (charge - t
* deflectorChargeLossFactor)
else min 2.0 (charge + t * 0.05) in
lance { deflectorCharge = charge' }
updateVelocity :: Time -> Lance -> Lance
updateVelocity t lance
| linearThrusters lance =
lance { velocity = M.newVelocity
(velocity lance)
accelerationRate
(angle lance)
maxVelocity
t
}
| otherwise = lance
instance Launcher Lance where
deployProjectiles self = (launchTube self, self { launchTube = []
-- , sinceLastShot = 0.0
})
instance Damaging Lance where
damageEnergy self =
if not (deflector self) || deflectorCharge self < 1.0
then kamikazeDamage
else 0.0
instance Damageable Lance where
inflictDamage self d =
let d' = if godMode self then 0 else d in
if d' > 0 && (not (deflector self) || deflectorCharge self < 1.0)
then self { integrity = integrity self - d }
else self
instance Transient Lance where
expired' self = if not (integrity self <= 0.0)
then Nothing
else Just [ef]
where ef = AfterEffect (SimpleExplosion.new (rt self)
(wmap self)
(Lance.center self)
(Lance.velocity self))
-- instance Audible Lance where
-- processAudio self = queueShotFX