packages feed

edge-0.8.16: Unit/Smart/STank.hs

module Unit.Smart.STank ( STank(..)
                       , 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.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/4 -- radians per second

maxVelocityMag = 100

kamikazeDamage = 20.0

maxIntegrity = 6

accelerationRate = 60

collisionRadiusC = 45

shotDelay = 2.0

data STank = STank { angle :: Angle
                   , velocity :: Velocity
                   , center :: WrapPoint
                   , idealTargetLocation :: Maybe WrapPoint
                   , wrapMap :: WrapMap
                   , launchTube :: [Projectile]
                   , sinceLastShot :: Time
                   , integrity :: Double
                   , vision :: Maybe Arena
                   , resourceTracker :: ResourceTracker

                   -- Sound
                   , queueShotSound :: Bool
                   , shotSoundSource :: Maybe Source
                   }

instance Audible STank 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
        -> STank
new rt wmap center' angle' =
  STank { 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 STank where

  updateVision self arena = self { vision = Just arena }

updateAngle t self
 = case vision self of
     Nothing -> self
     Just arena -> if isNothing (lance arena)
                     then self
                     else let sDir = vectorDirection (velocity self) in
                          let tDir = vectorDirection
                                       (vectorRelation
                                         (U.wrapMap arena)
                                         (center self)
                                         (M.center (fromJust (lance arena)))) in
                          let adj
                                | tDir - sDir > pi / 6 = radialVelocity * t
                                | tDir -sDir < (-pi) / 6 = (-radialVelocity) * t
                                | otherwise = 0.0 in
                          self { angle = angle self + adj }

updateVelocity :: Time -> STank -> STank
updateVelocity t self =
  self { velocity = M.newVelocity
                      (velocity self)
                      accelerationRate
                      (angle self)
                      maxVelocityMag
                      t                               
       }

instance Animation STank where
  image self _ = Rotate (double2Float
                          (radToDeg
                            (angle self)) * (-1) - 90)
                              pic
    where pic = fromMaybe
                 (Scale 0.20 0.20
                    (Color white
                    (Text "Error! Missing image!")))
                  (getImage rt "stank.bmp")
          rt = resourceTracker self

instance M.Locatable STank where
  center = center

instance M.Moving STank where
  velocity = velocity

instance M.Colliding STank where
  collisionRadius _ = collisionRadiusC

instance InternallyUpdating STank 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 = projectile1 
                               : (projectile2
                               : (projectile3
                               : launchTube self))
                , queueShotSound = True
                }
      else self { sinceLastShot = sinceLastShot' }
  where projectile1 = Projectile
                        (P.BulletMII.new
                          (wrapMap self)
                          pAngle
                          (center self)
                          (velocity self))
        projectile2 = Projectile
                        (P.BulletMII.new
                          (wrapMap self)
                          (pAngle + pi / 8)
                          (center self)
                          (velocity self))
        projectile3 = Projectile
                        (P.BulletMII.new
                          (wrapMap self)
                          (pAngle - pi / 8)
                          (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 -> STank -> STank
updateIdealTargetLocation t self =
  self { idealTargetLocation = Just (M.newLocation (wrapMap self)
                                                        (center self)
                                                        (velocity self)
                                                        t)
        }

instance Launcher STank where

  deployProjectiles self = (launchTube self, self { launchTube = [] })

instance Transient STank 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 STank where

  inflictDamage self d = self { integrity = max 0.0 (integrity self - d) }

instance Damaging STank where

  damageEnergy self = kamikazeDamage