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)