Files
loop/src/Dodge/Item/Weapon/Launcher.hs
T

229 lines
6.3 KiB
Haskell

module Dodge.Item.Weapon.Launcher
where
import Dodge.Data
import Dodge.Data.SoundOrigin
import Dodge.Default.Weapon
import Dodge.Default.Shell
import Dodge.SoundLogic.LoadSound
import Dodge.WorldEvent.Explosion
import Dodge.Picture.Layer
import Dodge.Item.Data
import Dodge.Item.Draw
import Dodge.Item.Weapon.Shell
import Dodge.Item.Weapon.TriggerType
import Dodge.Base
import Dodge.Zone
import Dodge.SoundLogic
import Dodge.WorldEvent.SpawnParticle
import Dodge.WorldEvent.Cloud
import Dodge.RandomHelp
import Geometry
import Geometry.Vector3D
import Picture
import qualified IntMapHelp as IM
import Padding
import ShapePicture
import Shape
import Data.Maybe
import Control.Lens
import Control.Applicative
import Control.Monad.State
import System.Random
launcher :: Item
launcher = defaultGun
{ _itName = "ROCKO"
, _itIdentity = Launcher
, _wpMaxAmmo = 30
, _wpLoadedAmmo = 30
, _wpReloadTime = 80
, _wpReloadState = 0
, _itUseRate = 20
, _itUseTime = 0
, _itUse = aRocketWithItemParams
, _itUseModifiers =
[ ammoCheckI
, useTimeCheckI
, withSoundStart tap4S
, useAmmo 1
]
, _wpSpread = 0.02
, _wpRange = 20
, _itFloorPict = SPic emptySH $ onLayer FlItLayer launcherPic
, _itAimingSpeed = 0.2
, _itAimingRange = 0.5
, _itEquipPict = pictureWeaponOnAim $ SPic emptySH launcherPic
, _itHammer = NoHammer
, _itEffect = NoItEffect
, _wpAmmo = defaultShellAmmo
{ _amPayload = makeExplosionAt
, _amString = ""
, _amPjParams = basicAmPjMoves
, _amPjDraw = shellPic
}
, _itAimStance = TwoHandTwist
}
launcherPic :: Picture
launcherPic = color cyan $ polygon $ rectNESW 5 10 (-5) (-10)
basicAmPjMoves :: [PjParam]
basicAmPjMoves =
[spinDrag
,spinStart
,thrustParam
]
spinDrag :: PjParam
spinDrag = PjParam
{ _pjMoveParam = \i _ _ -> reduceSpinBy (1 - fromIntegral i*2 / 200)
, _pjIntParam = 1
, _pjDisplayParam = pjPadText "SPIN SLOWDOWN" . show
, _pjMaxParam = 5
}
pjPadText :: String -> String -> String
pjPadText s = (rightPad 12 ' ' s ++ ) . (' ':)
spinStart :: PjParam
spinStart = PjParam
{ _pjMoveParam = \i _ cr -> pjEffAtTime 35 $ trySpinByCID (_crID cr) i
, _pjIntParam = 2
, _pjDisplayParam = pjPadText "SPIN AMOUNT" . show
, _pjMaxParam = 5
}
thrustParam :: PjParam
thrustParam = PjParam
{ _pjMoveParam = \i _ _ -> pjThrust i
, _pjIntParam = 1
, _pjDisplayParam = pjPadText "THRUST DELAY" . show
, _pjMaxParam = 5
}
pjThrust :: Int -> Projectile -> World -> World
pjThrust i = pjEffTimeRange (st,et) doThrust
where
et | i == 0 = 36
| otherwise = 40 - (i * 20)
st = et - 100
doThrust :: Projectile -> World -> World
doThrust pj w = w
& randGen .~ g
& projectiles . ix i . pjVel %~ (\v -> accel +.+ frict *.* v)
& soundContinue (ShellSound i) newPos missileLaunchS (Just 1)
& makeFlameletTimed (oldPos -.- vel) 0 (vel +.+ rotateV (pi+sparkD) accel) Nothing 3 10
& smokeGen
where
accel = _pjAcc pj
i = _pjID pj
oldPos = _pjPos pj
vel = _pjVel pj
newPos = oldPos +.+ vel
(frict,g) = randomR (0.6,0.9) $ _randGen w
(sparkD,_) = randomR (-0.2,0.2) $ _randGen w
r1 = randInCirc 10 & evalState $ _randGen w
smokeGen = shellTrailCloud $ addZ 20 (oldPos +.+ r1 +.+ 30 *.* normalizeV (oldPos -.- newPos))
flameLauncher :: Item
flameLauncher = launcher & wpAmmo . amPayload .~ makeFlameExplosionAt
poisonLauncher :: Item
poisonLauncher = launcher & wpAmmo . amPayload .~ makePoisonExplosionAt
teslaLauncher :: Item
teslaLauncher = launcher & wpAmmo . amPayload .~ makeTeslaExplosionAt
aRocketWithItemParams
:: Item -- ^ Firing item
-> Creature
-> World
-> World
aRocketWithItemParams it cr w = over projectiles (IM.insert i theShell) w
where
am = _wpAmmo it
i = IM.newKey $ _projectiles w
pos = _crPos cr +.+ ((_crRad cr +1) *.* unitVectorAtAngle dir)
dir = _crDir cr
ammoMvs pj w' = foldr (\pjP -> _pjMoveParam pjP (_pjIntParam pjP) it cr pj) w' (_amPjParams am)
theShell = defaultShell
{ _pjPos = pos
, _pjStartPos = pos
, _pjVel = rotateV dir (V2 1 0)
, _pjAcc = rotateV dir (V2 3 0)
, _pjDraw = _amPjDraw am
, _pjID = i
, _pjUpdate = \pj -> ammoMvs pj . decTimMvVel pj . moveShell pj
, _pjPayload = _amPayload am
, _pjTimer = 50
}
moveShell
:: Projectile -- ^ Projectile id
-> World
-> World
moveShell pj w
| time > 40 = if circOnSomeWall oldPos 4 w
then doExplode
else w
| isJust thingHit = doExplode
| time > -300 = w
| otherwise = doExplode
where
time = _pjTimer pj
doExplode = w
& projectileExplosion oldPos
& stopSoundFrom (ShellSound i)
& projectiles %~ IM.delete i
i = _pjID pj
oldPos = _pjPos pj
vel = _pjVel pj
projectileExplosion = _pjPayload pj
newPos = oldPos +.+ vel
hitCr = fst <$> collideCircCrsPoint oldPos newPos 4 w
hitWl = fst <$> collideCircWalls oldPos newPos 2 (wallsNearPoint newPos w)
thingHit = hitCr <|> hitWl
reduceSpinBy :: Float -> Projectile -> World -> World
reduceSpinBy x pj = projectiles . ix (_pjID pj) . pjSpin *~ x
shellPic :: Projectile -> Picture
shellPic pj = setDepth 20 . uncurryV translate pos $ rotate (argV accel) basePic
where
basePic = color black $ polygon $ map toV2 [(-6,4),(-6,-4),(6,-4),(8,0),(6,4)]
accel = _pjAcc pj
pos = _pjPos pj
pjEffTimeRange
:: (Int,Int)
-> (Projectile -> World -> World)
-> Projectile
-> World
-> World
pjEffTimeRange (st,et) f pj
| t <= et && t >= st = f pj
| otherwise = id
where
t = _pjTimer pj
pjEffAtTime
:: Int
-> (Projectile -> World -> World)
-> Projectile
-> World
-> World
pjEffAtTime t f pj
| _pjTimer pj == t = f pj
| otherwise = id
trySpinByCID
:: Int -- ^ creature id
-> Int -- ^ Spin amount
-> Projectile
-> World
-> World
trySpinByCID cid i pj w = w & projectiles . ix pjid . pjSpin .~ newSpin
where
pjid = _pjID pj
dir = argV $ _pjVel pj
newSpin = case w ^? creatures . ix cid of
Just cr -> negate $ min 0.2 $ max (-0.2) $ normalizeAnglePi (dir - _crDir cr) / spinFactor
_ -> 0
spinFactor = 5 * (6 - fromIntegral i)