edge-0.8.2: Lance.hs
module Lance ( Lance ( rotationalThrusters
, linearThrusters
, deflector
, fireTrigger
, deflectorCharge
, center
, angle
, velocity
, godMode
, integrity
, inventory
, currentWeapon
)
, new
, RotationDirection (..)
, shielded
, processItem
, changeCurrentWeapon
) where
import Data.WrapAround
import Animation
import Graphics.Gloss.Data.Picture
import Graphics.Gloss.Data.Color
import GHC.Float
import Trigonometry
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 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
data RotationDirection = Stable | CW | CCW
deriving (Eq)
type LanceInventory = [Bool]
data Lance = Lance { angle :: Angle -- radians
, center :: WrapPoint
, rotationalThrusters :: RotationDirection
, idealTargetCenter :: Maybe WrapPoint
, velocity :: (Double, Double)
, linearThrusters :: Bool
, wrapMap :: WrapMap
, resourceTracker :: ResourceTracker
-- deflector
, deflectorCharge :: Double
, deflector :: Bool
-- firing
, launchTube :: [Projectile]
, sinceLastShot :: Time
, fireTrigger :: Bool
, currentWeapon :: Int
, inventory :: LanceInventory
-- death
, integrity :: Double
-- Sound
, queueShotSound :: Bool
, shotSoundSource :: Maybe Source
, godMode :: Bool
}
new :: ResourceTracker -> WrapMap -> WrapPoint -> Lance
new rt wmap center =
Lance { center = center
, angle = 0.0
, rotationalThrusters = Stable
, idealTargetCenter = Nothing
, velocity = (0.0, 0.0)
, linearThrusters = False
, wrapMap = wmap
, resourceTracker = rt
, 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
}
changeCurrentWeapon self =
let i = inventory self in
let c = currentWeapon self in
let c' = if c + 1 > 5
then if i !! 0
then 1
else 0
else c + 1 in
if c' /= 0 && not (i !! (c' - 1))
then changeCurrentWeapon self { currentWeapon = c' }
else self { currentWeapon = c' }
replaceAt :: Int -> a -> [a] -> [a]
replaceAt i a as
| i < 0 = as
| i >= length as = as
| otherwise = let x = take i as in
let y = drop (i + 1) as in
x ++ [a] ++ y
processItem :: Lance -> Item -> Lance
processItem self (Item typ _ _) =
case typ of
Health -> self { integrity = 3.0 }
FourWay -> self { inventory = replaceAt 0 True (inventory self)
, currentWeapon = 1
}
Cannon -> self { inventory = replaceAt 1 True (inventory self)
, currentWeapon = 2
}
Spread -> self { inventory = replaceAt 2 True (inventory self)
, currentWeapon = 3
}
RapidFire -> self { inventory = replaceAt 3 True (inventory self)
, currentWeapon = 4
}
Nuke -> self { inventory = replaceAt 4 True (inventory self)
, currentWeapon = 5
}
instance Audible Lance 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 play [fromJust $ shotSoundSource self]
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) "simple-energy-shot.wav"
-- ...
return self { shotSoundSource = Just source }
shielded :: Lance -> Bool
shielded lance = deflectorCharge lance >= 1.0 && deflector lance
updateAngle :: Time -> Lance -> Lance
updateAngle t lance
| rotationalThrusters lance == CW
= lance { angle = angle lance - radialVelocity * t}
| rotationalThrusters lance == CCW
= lance { angle = angle lance + radialVelocity * t}
| otherwise = lance
instance Animation Lance where
image lance _ = let rt = resourceTracker lance in
let lancePic = fromMaybe
(Scale 0.20 0.20
(Color white
(Text "Error! Missing image!")))
(getImage rt "lance.bmp") in
let lanceThrustingPic =
fromMaybe
(Scale 0.20 0.20
(Color white
(Text "Error! Missing image!")))
(getImage rt "lance-thrusting.bmp") in
let pic = if linearThrusters lance then lanceThrustingPic
else lancePic in
let deflector' = if deflector lance
&& deflectorCharge lance >= 1.0
then Color white (Circle 40.0)
else Blank in
Pictures [ deflector'
, Rotate (radToDeg
(double2Float
(angle lance)) * (-1) - 90) pic
]
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 lance t = (updateFiringInformation t .
updateIdealTargetCenter t .
updateVelocity t .
updateAngle t) lance
postUpdate lance t =
let center' = fromMaybe (center lance) (idealTargetCenter lance) in
updateDeflectorCharge t
lance { center = center'
, idealTargetCenter = Nothing
}
updateFiringInformation t lance =
let sinceLastShot' = sinceLastShot lance + t in
case currentWeapon lance of
1 -> handleFourWayWeapon lance sinceLastShot'
2 -> handleCannonWeapon lance sinceLastShot'
3 -> handleSpreadWeapon lance sinceLastShot'
4 -> handleRapidFireWeapon lance sinceLastShot'
5 -> handleNukeWeapon lance sinceLastShot'
otherwise -> handleDefaultWeapon lance sinceLastShot'
handleDefaultWeapon self ls =
if ls >= 0.4 && fireTrigger self
then self { sinceLastShot = 0.0
, launchTube =
Projectile ( P.BulletMkI.new
(wrapMap 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
(wrapMap self)
(angle self)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap self)
(angle self + pi / 2)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap self)
(angle self + pi)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap 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
(wrapMap 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
(wrapMap self)
(angle self)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap self)
(angle self + spreadAngle)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap self)
(angle self + spreadAngle * 2)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap self)
(angle self - spreadAngle)
(center self)
(velocity self) ) ]
++ [ Projectile ( P.BulletMkI.new
(wrapMap 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
(wrapMap 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
(wrapMap self)
(resourceTracker 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' }
updateIdealTargetCenter :: Time -> Lance -> Lance
updateIdealTargetCenter t lance =
lance { idealTargetCenter = Just (M.idealNewLocation (wrapMap lance)
(center lance)
(velocity lance)
t)
}
updateVelocity :: Time -> Lance -> Lance
updateVelocity t lance
| linearThrusters lance =
lance { velocity = M.calcNewVelocity
(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 (resourceTracker self)
(wrapMap self)
(Lance.center self)
(Lance.velocity self))
-- instance Audible Lance where
-- processAudio self = queueShotFX