edge-0.8: 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
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 :: Double -- 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 :: Double
, 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 :: Double -> 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 :: Double -> Lance -> Lance
updateIdealTargetCenter t lance =
lance { idealTargetCenter = Just (M.idealNewLocation (wrapMap lance)
(center lance)
(velocity lance)
t)
}
updateVelocity :: Double -> 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