{- | Definitions of weapons.
In progress: move out effects into other modules.  -}
module Dodge.Item.Weapon 
    where
import Dodge.Data
import Dodge.Data.SoundOrigin
import Dodge.Base
import Dodge.Zone
import Dodge.Picture.Layer
import Dodge.SoundLogic
import Dodge.SoundLogic.LoadSound
import Dodge.Creature.Action
import Dodge.RandomHelp
import Dodge.WorldEvent
import Dodge.Default
--import Dodge.Default.Shell
import Dodge.Item.Draw
--import Dodge.Particle.Bullet.HitEffect
import Dodge.Item.Data
import Dodge.Item.Weapon.InventoryDisplay
import Dodge.Item.Weapon.TriggerType
import Dodge.Item.Weapon.ExtraEffect
import Dodge.Item.Weapon.UseEffect
import Dodge.Item.Weapon.Laser
import Dodge.Item.Weapon.Shell
import Dodge.Item.Weapon.Bullet
import Dodge.Item.Weapon.AmmoParams
import Dodge.Item.Attachment.Data
import Dodge.Default.Weapon
import Dodge.Item.Weapon.Booster
import Dodge.Item.Weapon.Grenade
--import Dodge.Item.Attachment.Data
import Dodge.Item.Attachment
import Geometry
import Geometry.Vector3D
import Picture
import qualified IntMapHelp as IM
import Shape
import ShapePicture

--import Data.List
--import Data.Char
import Data.Maybe
import Data.Function
--import qualified Data.ByteString as B
import qualified Data.Sequence as Seq
import Control.Lens
import Control.Applicative
import Control.Monad.State
--import Control.Monad
import qualified SDL
import System.Random
import qualified Data.Set as S
--import qualified Data.Map as M

pistol :: Item
pistol = defaultGun 
    { _itName = "PISTOL"
    , _itIdentity = Pistol
    , _wpMaxAmmo = 15
    , _wpLoadedAmmo = 15
    , _wpReloadTime = 40
    , _wpReloadState = 0
    , _itUseRate = 8
    , _itUseTime = 0
    , _itUse = useAmmoParams 
    , _itUseModifiers = 
        [ ammoCheckI
        , hammerCheckI
        , useTimeCheckI
        , withSoundStart tap3S 
        , useAmmo 1
        , randSpreadDir
        , withMuzFlareI
        ]
    , _itLeftClickUse = Nothing
    , _wpSpread = 0.02
    , _wpRange = 20
    , _itHammer = HammerUp
    , _itFloorPict = pistolPic
    , _itAmount = 1
    , _itMaxStack = 1
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itZoom = defaultItZoom
    , _itEquipPict = pictureWeaponOnAim pistolPic
    , _itAttachment = NoItAttachment
    , _itID = Nothing
    , _itEffect = wpRecock
    , _itInvDisplay = basicWeaponDisplay
    , _itInvColor = white
    , _itTargeting = Nothing
    , _itWorldTrigger = Nothing
    , _wpAmmo = basicBullet
    }
pistolPic :: SPic
pistolPic = 
    ( colorSH green $ upperPrismPoly 3 $ rectNESW 5 5 (-5) (-5)
    , mempty
    )


effectGun :: String -> (Creature -> World -> World) -> Item
effectGun name eff = defaultGun
    { _itName = name ++ "Gun"
    , _itUse = const eff
    }
autoEffectGun :: String -> (Creature -> World -> World) -> Item
autoEffectGun name eff = defaultAutoGun
    { _itName = name ++ "Gun"
    , _itUse = const eff
    }
rezGun :: Item
rezGun = defaultGun
    { _itName = "REANIMATOR"
    , _wpMaxAmmo = 50
    , _wpLoadedAmmo = 50
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = const aTeslaArc
    , _itUseModifiers =
        []
    , _wpSpread = 0.001
    , _wpRange = 20
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color chartreuse $ pictures 
        [polygon $ rectNESW 7    3 1    (-3)
        ,polygon $ rectNESW (-1) 3 (-7) (-3)
        ]
    , _itAimingSpeed = 0.4
    , _itAimingRange = 0
    , _itEquipPict = pictureWeaponOnAim $ (,) emptySH $ color chartreuse $ pictures 
        [polygon $ rectNESW 7    3 1    (-3)
        ,polygon $ rectNESW (-1) 3 (-7) (-3)
        ]
    }
teslaGun :: Item
teslaGun = defaultGun 
    { _itName = "TESLA"
    , _itIdentity = TeslaGun
    , _wpMaxAmmo = 200
    , _wpLoadedAmmo = 200
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = const aTeslaArc
    , _itUseModifiers =
        [ ammoCheckI
        , useTimeCheckI
        , withTempLight 1 100 (V3 0 0 1)
        , withSoundForI elecCrackleS 1
        , useAmmo 1
        ]
    , _wpSpread = 0.001
    , _wpRange = 20
    , _itFloorPict = teslaGunPic
    , _itAimingSpeed = 0.4
    , _itZoom = defaultItZoom
    , _itAimingRange = 0
    , _itEquipPict = pictureWeaponOnAim teslaGunPic
    }
teslaGunPic :: SPic
teslaGunPic = 
    ( colorSH blue $
        upperPrismPoly 5 (rectNESW xb    y xa    (-y))
        ++ (upperPrismPoly 5 $ rectNESW (-xa) y (-xb) (-y))
    , mempty
    )
  where
    xa = 1
    xb = 9
    y = 4
lasGun :: Item
lasGun = defaultAutoGun 
    { _itName = "LASGUN ////"
    , _itIdentity = LasGun
    , _wpMaxAmmo = 200
    , _wpLoadedAmmo = 200
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = const aLaser
    , _itUseModifiers =
        [ ammoCheckI
        , useTimeCheckI
        , withTempLight 1 100 (V3 1 1 0)
        , withSoundForI tone440sawtoothS 1
        , useAmmo 1
        ]
    , _wpSpread = 0.001
    , _wpRange = 20
    , _itFloorPict = lasGunPic
    , _itAimingSpeed = 0.4
    , _itAimingRange = 1
    , _itEquipPict = pictureWeaponOnAim lasGunPic
    , _itAttachment = ItCharMode $ Seq.fromList "/VZ"
    , _itScroll   = scrollCharMode
    , _itInvDisplay = basicWeaponDisplay
    }
lasGunPic :: SPic
lasGunPic = 
    ( colorSH blue . upperPrismPoly 5 $ rectNESW 3 15 (-3) (-15)
    , mempty
    )
forceFieldGun :: Item
forceFieldGun = defaultGun 
    { _itName = "FORCEFIELD"
    , _itIdentity = ForceFieldGun
    , _wpMaxAmmo = 100
    , _wpLoadedAmmo = 100
    , _wpReloadTime = 40
    , _wpReloadState = 0
    , _itUseRate = 10
    , _itUseTime = 0
    , _itUse = undefined
    , _wpSpread = 0.02
    , _wpRange = 20
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-4,-4),(-4,4),(4,4),(4,0),(0,0),(0,-4)]
    , _itAimingSpeed = 0.4
    , _itAimingRange = 0.5
    }
-- grapGun = defaultGun 
--     { _itName = "grapGun"
--     , _itIdentity = GrapGun
--     , _wpMaxAmmo = 1
--     , _wpLoadedAmmo = 1
--     , _wpReloadTime = 40
--     , _wpReloadState = 0
--     , _itUseRate = 10
--     , _itUseTime = 0
--     , _itUse = grapFire
--     , _wpSpread = 0.002
--     , _wpRange = 20
--     , _wpIsAuto = False
--     , _itFloorPict = onLayer FlItLayer $ polygon [(-4,-4),(-4,4),(4,4),(4,0),(0,0),(0,-4)]
--     , _itAmount = 1
--     , _itMaxStack = 1
--     , _itAimingSpeed = 1
--     , _itAimingRange = 0.5
--     }
tractorGun :: Item
tractorGun = defaultAutoGun 
    { _itName = "TRACTORGUN"
    , _itIdentity = TractorGun
    , _wpMaxAmmo = 10000
    , _wpLoadedAmmo = 10000
    , _wpReloadTime = 40
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = \_ -> aTractorBeam 0
    , _itUseModifiers =
        [ ammoUseCheckI
        ]
    , _wpSpread = 0.00001
    , _wpRange = 20
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color blue $ pictures [polygon $ rectNESW 1.5 6 (-1.5) 0 ]
    , _itAimingSpeed = 0.4
    , _itAimingRange = 0.5
    , _itEquipPict = pictureWeaponOnAim $ (,) emptySH $ color blue $ pictures [polygon $ rectNESW 1.5 6 (-1.5) 0 ]
    }


removeItAttachment :: Int -> Int -> World -> World
removeItAttachment i _ w = w & creatures . ix i . crInv . ix itRef . itAttachment .~ NoItAttachment
  where 
    cr = _creatures w IM.! i
    itRef = _crInvSel cr
remoteLauncher :: Item
remoteLauncher = defaultGun 
    { _itName = "ROCKO-REM"
    , _itIdentity = RemoteLauncher
    , _wpMaxAmmo = 1
    , _wpLoadedAmmo = 1
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate = 10
    , _itUseTime = 0
    , _itUse =  const fireRemoteLauncher
    , _itUseModifiers =
        [ ammoCheckI
        , hammerCheckI
        ]
    , _wpSpread = 0.02
    , _wpRange = 20
    , _itFloorPict = remoteLauncherSPic
    , _itAimingSpeed = 0.2
    , _itAimingRange = 0.5
    , _itEquipPict = pictureWeaponOnAim remoteLauncherSPic
    , _itAttachment = ItScope (V2 0 0) 0 1 True
    }
remoteLauncherSPic :: SPic
remoteLauncherSPic =
    ( colorSH cyan $ upperPrismPoly 7 $ rectNESW 5 5 (-5) (-5)
    , mempty
    )

hvAutoGun :: Item
hvAutoGun = defaultAutoGun 
    { _itName = "AUTO-HV"
    , _itIdentity = HvAutoGun
    , _wpMaxAmmo = 100
    , _wpLoadedAmmo = 100
    , _wpReloadTime = 200
    , _wpReloadState = 0
    , _itUseRate = 25
    , _itUseTime = 0
    , _itUse = useAmmoParams
    , _itUseModifiers =
        [ ammoCheckI
        , rateIncABI 24 7 (torqueBeforeAtLeast 0.1 0.1) (torqueAfterI 0.2)
        , withSoundStart bangEchoS
        , withThinSmokeI
        , withMuzFlareI
        ]
    , _wpRange = 20
    , _itFloorPict = hvAutoGunPic
    , _itAimingSpeed = 0.2
    , _itAimingRange = 1
    , _itEquipPict = pictureWeaponOnAim hvAutoGunPic
    , _wpAmmo = hvBullet
    }
hvAutoGunPic :: SPic
hvAutoGunPic = 
    ( colorSH orange $ upperPrismPoly 5 $ rectNESW 5 12 (-5) (-12)
    , mempty
    )
ltAutoGun :: Item                           
ltAutoGun = defaultAutoGun 
    { _itName = "AUTO-LT"
    , _itIdentity = LtAutoGun
    , _wpMaxAmmo = 25
    , _wpLoadedAmmo = 25
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate = 3
    , _itUseTime = 0
    , _itUse = useAmmoParams
    , _itUseModifiers = 
        [ ammoCheckI
        , useTimeCheckI
        , withSoundStart tap1S
        , useAmmo 1
        , withRandomDirI 0.1
        , torqueAfterI 0.2
        , withSidePushI 50
        , modClock 2 withMuzFlareI
        ]
    , _wpSpread = 0.5
    , _wpRange = 20
    , _itFloorPict = ltAutoGunPic
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itZoom = defaultItZoom
    , _itEquipPict = pictureWeaponOnAim ltAutoGunPic    
    , _itAimStance = OneHand
    , _wpAmmo = ltBullet
    }
ltAutoGunPic :: SPic
ltAutoGunPic = 
    ( colorSH green $ upperPrismPoly 5 $ mirrorXAxis [V2 (-5) 5, V2 5 5, V2 10 0]
    , mempty
    )
miniGun :: Item
miniGun = defaultAutoGun 
    { _itName = "MINI-G"
    , _itIdentity = MiniGun
    , _wpMaxAmmo = 1500
    , _wpLoadedAmmo = 1500
    , _wpReloadTime = 200
    , _wpReloadState = 0
    , _wpMaxWarmUp = 100
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = useAmmoParamsVelMod vm4
    , _itUseModifiers =
        [ ammoCheckI
        , withWarmUpI crankSlowS
        , useTimeCheckI
        --, afterRecoil recoilAmount
        , withSoundForI mini1S 2
        --, withThinSmokeI
        , torqueAfterI 0.05
        , withSidePushI 53
        , afterRecoil recoilAmount
        , withRandomOffsetI 12
        , trigDoAlso (useAmmoParamsVelMod vm1)
        --, torqueBeforeForcedI 0.001
        , withSidePushI 52
        , afterRecoil recoilAmount
        , withRandomOffsetI 11
        , withOldDir od1
        , trigDoAlso (useAmmoParamsVelMod vm2)
        --, torqueBeforeForcedI 0.001
        , withSidePushI 51
        , afterRecoil recoilAmount
        , withRandomOffsetI 10
        , withOldDir od2
        , trigDoAlso (useAmmoParamsVelMod vm3)
        , useAmmo 4
        --, torqueBeforeForcedI 0.001
        , withSidePushI 50
        , afterRecoil recoilAmount
        , withRandomOffsetI 9
        , withMuzFlareI
        , withOldDir od3
        , withSmoke 1 black 20 200 5
        ]
    , _wpRange = 20
    , _itFloorPict = miniGunPict
    , _itAimingSpeed = 0.4
    , _itAimingRange = 1
    , _itEquipPict = pictureWeaponOnAim miniGunPict
    , _wpAmmo = basicBullet
    }
  where
    recoilAmount = 5
    [vm1,vm2,vm3,vm4] = 
        [ 0.25
        , 0.5
        , 0.75
        , 1
        ]
    [od1,od2,od3] = 
        [ 0.25, 0.5, 0.9]
        --[ 0.75, 0.5, 0.25]
        --[ 0, 0, 0]
miniGunPict :: SPic
miniGunPict = 
    (   (translateSHf 5 0 . colorSH red $ upperPrismPoly 10 $ rectNESW 9 7 (-9) (-5))
        ++ (colorSH red . upperPrismPoly 5 $ rectNESW 4 12 (-4) (-12))
        
    , mempty
    )
spreadGun :: Item
spreadGun = defaultGun 
    { _itName = "SPREAD"
    , _itIdentity = SpreadGun
    , _wpMaxAmmo = 5
    , _wpLoadedAmmo = 5
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate = 20
    , _itUseTime = 0
    --, _itUse = \_ -> spreadNumVelWthHiteff spreadGunSpread 9 (V2 30 0) 2 basicBulletEffect
    , _itUse = useAmmoParams
    , _itUseModifiers =
        [ ammoCheckI
        , hammerCheckI
        , useTimeCheckI
        , withSoundStart shotgunS
        , useAmmo 1
        , withRecoilI 100
        , withMuzFlareI
        , spreadNumI
        ]
    , _wpSpread = spreadGunSpread
    , _wpRange = 20
    , _itFloorPict = spreadGunPic
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itEquipPict = pictureWeaponOnAim spreadGunPic
    , _wpAmmo = basicBullet
    , _wpNumBarrels = 50
    }
spreadGunPic :: SPic
spreadGunPic = 
    ( colorSH green $ upperPrismPoly 5 $ map toV2[(-3,0),(3,6),(3,-6)]
    , mempty
    )
multGun :: Item
multGun = defaultGun 
    { _itName = "MULTGUN"
    , _itIdentity = MultGun
    , _wpMaxAmmo = 2
    , _wpLoadedAmmo = 2
    , _wpReloadTime = 40
    , _wpReloadState = 0
    , _itUseRate = 20
    , _itUseTime = 0
    , _itUse = useAmmoParams
    , _itUseModifiers =
        [ ammoCheckI
        , hammerCheckI
        , useTimeCheckI
        , withSoundStart shotgunS
        , useAmmo 1
        , withRecoilI 200
        , withMuzFlareI
        , numI
        ]
    , _wpSpread = spreadGunSpread
    , _wpRange = 20
    , _itFloorPict = multGunSPic
    , _itAimingSpeed = 0.4
    , _itAimingRange = 1
    , _itZoom       = defaultItZoom
    , _itAimZoom    = defaultItZoom {_itZoomFac = 1.5}
    , _itEquipPict = pictureWeaponOnAim multGunSPic
    , _itAimStance = TwoHandTwist
    , _wpAmmo = basicBullet
    , _wpNumBarrels = 5
    }
multGunSPic :: SPic
multGunSPic = 
    ( colorSH red $
        upperPrismPoly 2 (rectNESW 4 8 (-4) (-12)) ++
        translateSHf 8 0 (mconcat 
            [ upperPrismPoly 5 $ rectNSEW 12   8     2 (-2)
            , upperPrismPoly 5 $ rectNSEW 7    3     2 (-2)
            , upperPrismPoly 5 $ rectNSEW 2    (-2)  2 (-2)
            , upperPrismPoly 5 $ rectNSEW (-3) (-7)  2 (-2)
            , upperPrismPoly 5 $ rectNSEW (-8) (-12) 2 (-2)
            , upperPrismPoly 2 $ map toV2 [(-1.5,12),(-2,12),(-2,-12),(-1.5,-12)]
            ]
            )
    , mempty
    )
longGun :: Item
longGun = defaultGun 
    { _itName = "LONGGUN"
    , _itIdentity = LongGun
    , _wpMaxAmmo = 1
    , _wpLoadedAmmo = 1
    , _wpReloadTime = 100
    , _wpReloadState = 0
    , _wpReloadType = PassiveReload skwareFadeTwoSecS
    , _itUseRate = 100
    , _itUseTime = 0
    , _itUse = useAmmoParams
    , _itUseModifiers =
        [ ammoCheckI
        , hammerCheckI
        , useTimeCheckI
        , withSoundStart bangEchoS
        , useAmmo 1
        , withThickSmokeI
        , torqueAfterI 0.05
        , withMuzFlareI
        ]
    , _wpRange = 200
    , _itFloorPict = longGunSPic
    , _itAimingSpeed = 0.2
    , _itAimingRange = 1
    , _itZoom       = defaultItZoom 
    , _itAimZoom    = defaultItZoom {_itZoomMax = 0.5, _itZoomMin = 0.5}
    , _itEquipPict = pictureWeaponOnAim longGunSPic
    , _itScroll = zoomLongGun
    , _itAttachment = ItScope (V2 0 0) 0 1 False
    , _itEffect = itemLaserScopeEffect
    , _wpAmmo = hvBullet
    , _itAimStance = TwoHandTwist
    }
longGunSPic :: SPic
longGunSPic =
    ( colorSH orange $ upperPrismPoly 5 $ rectNESW 2 12 (-2) (-12)
    , mempty
    )

zoomLongGun :: Float -> Creature -> Item -> Item
zoomLongGun x _
    | x > 0 = startZoomInLongGun 
    | x < 0 = startZoomOutLongGun
    | otherwise = id

startZoomInLongGun :: Item -> Item
startZoomInLongGun   = itAttachment . scopeZoomChange %~ \x -> max 5 (x + 5)
startZoomOutLongGun :: Item -> Item
startZoomOutLongGun  = itAttachment . scopeZoomChange %~ \x -> min (-5) (x-5)
poisonSprayer :: Item
poisonSprayer = defaultAutoGun 
    { _itName = "POISON"
    , _itIdentity = PoisonSprayer
    , _wpMaxAmmo = 500
    , _wpLoadedAmmo = 500
    , _wpReloadTime = 100
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = const aGasCloud 
    , _itUseModifiers =
        [ ammoCheckI
        , useTimeCheckI
        , withSoundForI foamSprayLoopS 5
        , useAmmo 1
        , spreadNumI
        ]
    , _wpRange = 8
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color yellow $ polygon $ rectNESW 4 4 (-4) (-4)
    , _itAimingSpeed = 0.2
    , _itAimingRange = 0
    , _itZoom = defaultItZoom
    , _itEquipPict = pictureWeaponOnAim $ (,) emptySH $ color yellow $ polygon $ rectNESW 4 4 (-4) (-4)
    , _wpSpread = 0.1
    , _wpNumBarrels = 3
    }
flamer :: Item
flamer = defaultAutoGun 
    { _itName = "FLAMER"
    , _itIdentity = Flamethrower
    , _wpMaxAmmo = 250
    , _wpLoadedAmmo = 250
    , _wpReloadTime = 100
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = \_ -> randWalkAngle 0.2 0.01 aFlame
    , _itUseModifiers =
        [ ammoUseCheckI
        , withSidePushI 5
        --, withTempLight 1 100 (V3 1 0 0)
        , withSidePushAfterI 20
        ]
    , _wpSpread = 0
    , _wpRange = 8
    , _itFloorPict = flamerPic
    , _itAimingSpeed = 0.5
    , _itAimingRange = 0
    , _itZoom       = defaultItZoom
    , _itAimZoom    = defaultItZoom {_itZoomMax = 5, _itZoomMin = 1.5}
    , _itEquipPict = pictureWeaponOnAim flamerPic
    , _itAttachment = NoItAttachment
    }
flamerPic :: SPic
flamerPic = 
    ( colorSH yellow $ 
        translateSHf (negate 5) (negate 7) (upperPrismPoly 3 $ polyCirc 3 5)
        ++ upperPrismPoly 5 (rectNESW 2 9 (-2) (-9))
    , mempty
    )
blinkGun :: Item
blinkGun = defaultGun 
    { _itName = "BLINKER"
    , _itIdentity = Blinker
    , _wpMaxAmmo = 100
    , _wpLoadedAmmo = 100
    , _wpReloadTime = 20
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = const aSelf
    , _itUseModifiers =
        [ ammoCheckI
        , hammerCheckI
        ]
    , _itLeftClickUse = Just $ hammerCheckL $ shootL aSelfL
    , _wpSpread = 0.05
    , _wpRange = 20
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2[(-2,-2),(-2,2),(2,2),(2,0),(0,0),(0,-2)]
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    }
boosterGun :: Item
boosterGun = defaultGun 
    { _itName = "BOOSTER"
    , _itIdentity = Blinker
    , _wpMaxAmmo = 100
    , _wpLoadedAmmo = 100
    , _wpReloadTime = 20
    , _wpReloadState = 0
    , _itUseRate = 0
    , _itUseTime = 0
    , _itUse = \_ -> boostSelf 10
    , _itLeftClickUse = Just $ boostSelfL 10
    , _wpSpread = 0.05
    , _wpRange = 20
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-2,-2),(-2,2),(2,2),(2,0),(0,0),(0,-2)]
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itEffect = resetAttachmentID
    }

aTeslaArc :: Creature -> World -> World
aTeslaArc cr w 
    = teslaGunFlashAt (pos +.+ 5 *.* unitVectorAtAngle dir)
    $ set randGen g w
    & particles %~ (makeTeslaArcAt col pos dir :)
  where 
    pos = _crPos cr +.+ (_crRad cr +1) *.* unitVectorAtAngle dir
    dir = _crDir cr
    (colid,g) = randomR (0::Int,3) $ _randGen w
    col = chooseColor colid
    chooseColor 0 = white
    chooseColor 1 = azure
    chooseColor 2 = blue
    chooseColor _ = cyan

remoteShellPic' :: Projectile -> Picture
remoteShellPic' pj
    | t > 40 = onLayerL [levLayer CrLayer - 2] 
                        $ uncurryV translate pos $ rotate dir $ remoteShellPic t
    | otherwise = uncurryV translate pos $ rotate dir $ remoteShellPic t
  where
    t = _pjTimer pj
    pos = _pjPos pj
    dir = _pjDir pj

remoteShellPic 
    :: Int -- ^ Timer
    -> Picture
remoteShellPic t 
    | rem (t+200) 20 < 9 = setDepth 20 $ polygon $ map toV2 [(-6,4),(-6,-4),(6,-4),(8,0),(6,4)]
    | otherwise = pictures 
        [ setDepth 20 $ polygon $ map toV2 [(-6,4),(-6,-4),(6,-4),(8,0),(6,4)]
        , setLayer 1 . setDepth 20.5 . color col $ circleSolid 3
        ]
  where 
    col | t > (-99) = green
        | otherwise  = red

shellExplosionAt :: Point2 -> World -> World
shellExplosionAt = makeExplosionAt

aGasCloud :: Creature -> World -> World
aGasCloud cr w = insertCloud $ set randGen g w
  where 
    (a,g) = randomR (-0.1,0.1) (_randGen w)
    dir = _crDir cr + a
    pos = _crPos cr +.+ ((_crRad cr + 2.9) *.* unitVectorAtAngle (_crDir cr))
    vel = (_crPos cr -.- _crOldPos cr) +.+ 10 *.* unitVectorAtAngle dir
    insertCloud = makeGasCloud pos vel

aFlame :: Creature -> World -> World
aFlame cr w = w & particles %~ (aFlameParticle t pos vel (Just cid) :)
  where 
    (t,_) = randomR (99,101) (_randGen w)
    cid = _crID cr
    dir = _crDir cr
    pos = _crPos cr +.+ ((_crRad cr + 2.9) *.* unitVectorAtAngle (_crDir cr))
    vel = (_crPos cr -.- _crOldPos cr) +.+ 4 *.* unitVectorAtAngle dir
    
aSelf :: Creature -> World -> World
aSelf = blinkAction

aSelfL :: Creature -> Int -> World -> World
aSelfL cr _ = blinkAction cr

reflect :: Float -> Float -> Float
reflect a b = a + 2*(a-b)


pointToItem :: Applicative f =>
                     ItemPos -> (Item -> f Item) -> World -> f World
pointToItem (InInv cid invid) = creatures . ix cid . crInv . ix invid
pointToItem (OnFloor flid) = floorItems . ix flid . flIt

retireRemoteRocket :: Int -> Int -> Int -> World -> World
retireRemoteRocket itid 0 pjid w = 
    set (pointToItem (_itemPositions w IM.! itid) . itAttachment . scopePos) (V2 0 0)
    $ set (pointToItem (_itemPositions w IM.! itid) . itUse) (const fireRemoteLauncher)
    (w & projectiles %~ IM.delete pjid)
retireRemoteRocket itid t pjid w = setScope w 
    & projectiles . ix pjid . pjUpdate .~ (\_ -> retireRemoteRocket itid (t-1) pjid)
  where
    setScope w' = case _itemPositions w' IM.! itid of
        InInv cid invid -> w' 
            & creatures . ix cid . crInv . ix invid . itAttachment 
                    . scopePos .~ (pos -.- _crPos (_creatures w' IM.! cid))
        _   -> w'
    pos = fromMaybe (V2 0 0) $ w ^? projectiles . ix pjid . pjPos
retireRemoteBomb :: Int -> Int -> Int -> World -> World
retireRemoteBomb itid 0 pjid w = w
    & pointToItem (_itemPositions w IM.! itid) %~ 
        ( (itAttachment . scopePos .~ V2 0 0)
        . (itZoom .~ defaultItZoom)
        . (itUse .~ const throwRemoteBomb)
        )
    & projectiles %~ IM.delete pjid
retireRemoteBomb itid t pjid w = setScope w 
    & projectiles . ix pjid . pjUpdate .~ (\_ -> retireRemoteBomb itid (t-1) pjid)
  where
    setScope w' = case _itemPositions w' IM.! itid of
        InInv cid invid -> w' 
            & creatures . ix cid . crInv . ix invid . itAttachment 
                    . scopePos .~ (pos -.- _crPos (_creatures w' IM.! cid))
        _   -> w'
    pos = fromMaybe (V2 0 0) $ w ^? projectiles . ix pjid . pjPos

moveRemoteBomb :: Int -> Int -> Int -> World -> World
moveRemoteBomb itid time pID w 
    | time < -4 = updatePicture
                $ set (projectiles .ix pID.pjUpdate) (\_ -> moveRemoteBomb itid (f time) pID)
                    w
    | time < 2 = case hitWl of
            Just (p,_) -> soundStart (Tap 0) p tapQuietS Nothing $ halfV updatedWorld
            _      -> halfV updatedWorld
    | otherwise = case hitWl of
            Just (p,_) -> soundStart (Tap 0) p tapQuietS Nothing updatedWorld
            _      -> updatedWorld
  where 
    updatedWorld 
        = updateV $ set (projectiles . ix pID . pjPos) finalPos
            $ updatePicture
            $ set (projectiles .ix pID.pjUpdate) (\_ -> moveRemoteBomb itid (time-1) pID)
                w
    pj     = _projectiles w IM.! pID
    oldPos = _pjPos pj
    newPos = _pjVel pj +.+ oldPos
    -- this is hacky, should use a version of collidePointWalls' that collides
    -- circles and walls
    invShift x = x -.- 5 *.* normalizeV (_pjVel pj)
    hitWl  = collideCircWalls'' oldPos newPos 4 $ wallsNearPoint newPos w
    finalPos = maybe newPos (invShift . fst) hitWl
    setV v = set (projectiles . ix pID . pjVel) v
    updateV = maybe id (setV . snd) hitWl
    halfV = projectiles . ix pID . pjVel %~ (0.5 *.*)
    f x | x < -369 = -10
        | otherwise = x - 1
    updatePicture = 
        set (projectiles . ix pID . pjDraw)
            (\_ -> onLayer PtLayer $ uncurryV translate newPos $ remoteBombPic time)
        . lowLightDirected 
            (withAlpha 0.1 red) 
            newPos 
            (50 *.* unitVectorAtAngle (negate $ degToRad (10 * fromIntegral time)))
            [-0.2,-0.15,-0.1,-0.05,0,0.05,0.1,0.15,0.2]

setRemoteScope :: Int -> Projectile -> World -> World
setRemoteScope itid pj w' = case w' ^? itemPositions . ix itid of
    Just (InInv cid' invid )
        -> w' & creatures . ix cid' . crInv . ix invid . itAttachment 
                . scopePos .~ (_pjPos pj -.- _crPos (_creatures w' IM.! cid'))
    _   -> w'

setRemoteBombScope :: Int -> Projectile -> World -> World
setRemoteBombScope itid pj w' = case _itemPositions w' IM.! itid of
    InInv cid invid 
        -> w' & creatures . ix cid . crInv . ix invid . itAttachment 
                . scopePos .~ (_pjPos pj -.- _crPos (_creatures w' IM.! cid))
              & creatures . ix cid . crInv . ix invid . itAimZoom 
                    .~ (defaultItZoom {_itZoomMax = 0.5, _itZoomMin = 0.5})
    _   -> w'
                        
grenade :: Item
grenade = Throwable
    { _itName = "GRENADE " ++ show fuseTime
    , _itIdentity = Grenade
    , _itMaxStack = 8
    , _itAmount = 1
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _twMaxRange = 150
    , _twAccuracy = 30
    , _itUse = \_ -> throwGrenade makeExplosionAt
    , _itUseModifiers =
        [ useTimeCheckI
        ]
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itZoom       = defaultItZoom
    , _itAimZoom    = defaultItZoom {_itZoomMax = f fuseTime, _itZoomMin = f fuseTime}
    , _itEquipPict = pictureWeaponOnAim $ grenadePic fuseTime
    , _itID = Nothing
    , _itUseRate = 25 
    , _itUseTime = 0
    , _itAttachment = ItFuse fuseTime
    , _itInvColor = white
    , _itInvDisplay = basicWeaponDisplay
    , _itEffect = wpRecock
    , _itHammer = HammerUp
    , _itScroll = changeFuse
    , _itAimStance = OneHand
    }
  where 
    fuseTime = 50
    f x = 50 / fromIntegral x

flameGrenade :: Item
flameGrenade = grenade {
     _itName = "FLMGREN " ++ show fuseTime
     , _itUse = \_ -> throwGrenade makeFlameExplosionAt
    }
  where 
    fuseTime = 50 :: Int
    --f x = 50 / fromIntegral x

teslaGrenade :: Item
teslaGrenade = grenade {
     _itName = "TLSGREN " ++ show fuseTime
     , _itUse = \_ -> throwGrenade makeTeslaExplosionAt
    }
  where 
    fuseTime = 50 :: Int
    --f x = 50 / fromIntegral x

defaultThrowable :: Item
defaultThrowable = grenade
remoteBomb :: Item
remoteBomb = defaultThrowable
    { _itName = "REMOTEBOMB"
    , _itIdentity = RemoteBomb
    , _itMaxStack = 1
    , _itAmount = 1
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _twMaxRange = 150
    , _twAccuracy = 30
    , _itUse = const throwRemoteBomb
    , _itUseModifiers =
        [ hammerCheckI
        ]
    , _itAttachment = ItScope (V2 0 0) 0 1 True
    , _itEquipPict = pictureWeaponOnAim $ (,) emptySH remoteBombUnarmedPic
    }

fireRemoteLauncher :: Creature -> World -> World
fireRemoteLauncher cr w = setLocation 
    $ resetFire 
    $ resetName 
    $ soundStart (CrWeaponSound cid 0) pos tap4S Nothing
    $ over projectiles addRemRocket w
  where
    i = IM.newKey $ _projectiles w
    cid = _crID cr
    dir = _crDir cr
    pos = _crPos cr +.+ rotateV dir (V2 (_crRad cr + 1) 0)
    addRemRocket = IM.insert i $ Shell 
        { _pjPos = pos
        , _pjStartPos = pos
        , _pjVel = rotateV dir (V2 1 0)
        , _pjDraw = remoteShellPic'
        , _pjID = i
        , _pjUpdate = \pj -> decTimMvVel pj . setRemoteScope itid pj . moveRemoteShell cid itid pj
        , _pjAcc = V2 0 0
        , _pjDir = dir
        , _pjSpin = 0
        , _pjPayload = makeExplosionAt
        , _pjTimer = 50
        }
    j = _crInvSel cr
    newitid = IM.newKey $ _itemPositions w
    maybeitid = cr ^? crInv . ix j . itID . _Just
    resetFire = set (creatures . ix cid . crInv . ix j . itUse) 
        $ \_ _ -> explodeRemoteRocket itid i
    resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTEROCKET"
    setLocation :: World -> World
    setLocation w' = case maybeitid of
        Nothing -> w' & creatures . ix cid . crInv . ix j . itID ?~ newitid
                      & itemPositions %~ IM.insert newitid (InInv cid j)
        _ -> w'
    itid = fromMaybe newitid maybeitid

moveRemoteShell :: Int -> Int -> Projectile -> World -> World
moveRemoteShell cid itid pj w 
    | time > 40 = if circOnSomeWall oldPos 4 w
        then doExplosion w
        else w
    | time >= 20 = case thingHit of
        Just _  -> doExplosion w
        Nothing -> w & projectiles . ix i . pjDir .~ newdir
    | time > -99 = case thingHit of
        Just _  -> doExplosion $ stopSoundFrom (ShellSound i) w
        Nothing -> w & set randGen g
            & projectiles . ix i %~ 
                ( ( pjVel %~ ( (accel +.+) . (frict *.*) ) )
                . ( pjDir .~ newdir )
                )
            & soundContinue (ShellSound i) newPos missileLaunchS (Just 1)
            & smokeGen
            & makeFlameletTimed oldPos 20 (0.5 *.* rotateV (pi+sparkD) accel) Nothing 3 10
    | time > -200 = case thingHit of
        Just _  -> doExplosion $ stopSoundFrom (ShellSound i) w
        Nothing -> w
    | otherwise = doExplosion $ stopSoundFrom (ShellSound i) w
  where 
    time = _pjTimer pj
    i = _pjID pj
    oldPos = _pjPos pj
    vel    = _pjVel pj
    newPos = oldPos +.+ vel
    newdir 
        | SDL.ButtonRight `S.member` _mouseButtons w
            && w ^? creatures . ix cid . crInvSel == w ^? itemPositions . ix itid . itInvId
                  = _cameraRot w + argV (_mousePos w)
        | otherwise = _pjDir pj
    accel    = rotateV newdir (V2 2 0)
    (frict,g)  = randomR (0.6,0.9) $ _randGen w
    (sparkD,_) = randomR (-0.5,0.5) $ _randGen w
    hitCr    = fst <$> collideCircCrsPoint oldPos newPos 4 w
    hitWl    = fst <$> collideCircWalls'' oldPos newPos 2 (wallsNearPoint newPos w)
    thingHit = hitCr <|> hitWl
    r1 = _randGen w & evalState (randInCirc 10)
    smokeGen = shellTrailCloud $ addZ 20 $ oldPos +.+ r1 +.+ 30 *.* normalizeV (oldPos -.- newPos)
    doExplosion = explodeRemoteRocket itid i

explodeRemoteRocket 
    :: Int  -- ^ Item id
    -> Int  -- ^ Projectile id
    -> World 
    -> World
explodeRemoteRocket itid pjid w
    = set (projectiles . ix pjid . pjUpdate) (\_ -> retireRemoteRocket itid 30 pjid)
    $ set (projectiles . ix pjid . pjDraw) (const blank)
    $ set (itPoint . itUse) (\_ _ -> id)
    $ resetName
    $ makeExplosionAt (_pjPos (_projectiles w IM.! pjid)) w
  where 
    resetName = set (itPoint . itName) "REMOTELAUNCHER"
    itPoint = pointToItem $ _itemPositions w IM.! itid

throwRemoteBomb :: Creature -> World -> World
throwRemoteBomb cr w = setLocation 
    $ removePict 
    $ resetFire 
    $ resetName 
    $ over projectiles addG w
  where 
    cid = _crID cr
    addG = IM.insert i $ Projectile 
        { _pjPos = p
        , _pjStartPos = p
        , _pjVel = v
        , _pjDraw = const blank
        , _pjID = i
        , _pjUpdate = \_ -> moveRemoteBomb itid 50 i
        }
    i = newProjectileKey w
    v' = 0.02 / _cameraZoom w *.* rotateV (_cameraRot w) ( _mousePos w)
    v   | magV v' > 6 = 6 *.* normalizeV v'
        | otherwise   = v'
    j = _crInvSel cr
    resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTE"
    removePict = set (creatures . ix cid . crInv . ix j . itEquipPict) $ \ _ _ -> emptyBlank
    resetFire = set (creatures . ix cid . crInv . ix j . itUse) 
        $ \_ -> explodeRemoteBomb itid i
    p' = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr) 0)
    p | circOnSomeWall p' 4 w = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr-4) 0)
      | otherwise             = p'
    maybeitid = w ^? creatures . ix cid . crInv . ix j . itID . _Just
    setLocation :: World -> World
    setLocation w' = case maybeitid of
        Nothing -> w' & creatures . ix cid . crInv . ix j . itID ?~ newitid
                      & itemPositions %~ IM.insert newitid (InInv cid j)
        _ -> w'
    newitid = IM.newKey $ _itemPositions w
    itid = fromMaybe newitid maybeitid

explodeRemoteBomb :: Int -> Int -> Creature -> World -> World
explodeRemoteBomb itid pjid cr w
    = set (projectiles . ix pjid . pjUpdate) (\_ -> retireRemoteBomb itid 30 pjid)
--        $ set (projectiles . ix pjid . pjDraw) (\_ -> blank)
        $ set (creatures . ix cid . crInv . ix j . itUse) (\_ -> const id)
        $ resetName
        $ resetPict
--        $ resetScope
        $ makeExplosionAt (_pjPos (_projectiles w IM.! pjid)) w
        -- - $ makeShrapnelBombAt (_pjPos (_projectiles w IM.! pjid)) w
  where 
    cid = _crID cr
    resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTEBOMB"
    resetPict = set (creatures . ix cid . crInv . ix j . itEquipPict ) 
                    (pictureWeaponOnAim $ (,) emptySH remoteBombUnarmedPic)
--    resetScope = creatures . ix cid . crInv . ix j . itScope . _Just . scopePos .~ (0,0)
    j = _crInvSel $ _creatures w IM.! cid
remoteBombPic 
    :: Int -- ^ time
    -> Picture
remoteBombPic _ = pictures 
    [ color (dark $ dark orange) $ circleSolid 5
    ]

remoteBombUnarmedPic :: Picture
remoteBombUnarmedPic = color (dark $ dark orange) $ circleSolid 5

spreadGunSpread,autogunSpread :: Float
spreadGunSpread = 0.5
autogunSpread = 0.07
pipe :: Item
pipe = Craftable
    { _itIdentity = Generic
    , _itName = "PIPE"
    , _itMaxStack = 3
    , _itAmount = 3
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _itEquipPict = \_ _ -> emptyBlank
    , _itID = Nothing
    , _itInvDisplay = _itName
    , _itInvColor = green
    , _itAimStance = LeaveHolstered
    }
{- |
Sends out pulses that display walls.  -}
radar :: Item
radar = defaultGun 
    { _itName = "RADAR"
    , _itIdentity = Generic
    , _wpMaxAmmo = 100
    , _wpLoadedAmmo = 100
    , _wpReloadTime = 200
    , _wpReloadState = 0
    , _itUseRate = 120
    , _itUseTime = 0
    , _itUse = const aRadarPulse
    , _itUseModifiers =
        [ ammoUseCheckI
        ]
    , _wpSpread = autogunSpread
    , _wpRange = 20
    , _itHammer = HammerUp
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color blue $ polygon $ rectNESW 5 5 (-5) (-5)
    , _itAmount = 1
    , _itMaxStack = 1
    , _itAimingRange = 1
    , _itZoom       = defaultItZoom { _itZoomMax = 1}
    , _itAimZoom    = defaultItZoom { _itZoomMax = 1}
    , _itEquipPict = pictureWeaponOnAim $ (,) emptySH $ color blue $ polygon $ rectNESW 5 5 (-5) (-5)
    }
{- |
Sends out pulses that display creatures.  -}
sonar :: Item
sonar = defaultGun 
    { _itName = "SONAR"
    , _itIdentity = Generic
    , _wpMaxAmmo = 100
    , _wpLoadedAmmo = 100
    , _wpReloadTime = 200
    , _wpReloadState = 0
    , _itUseRate = 120
    , _itUseTime = 0
    , _itUse = const aSonarPulse
    , _itUseModifiers =
        [ ammoUseCheckI
        ]
    , _wpRange = 20
    , _itHammer = HammerUp
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color blue $ polygon $ rectNESW 5 5 (-5) (-5)
    , _itAmount = 1
    , _itMaxStack = 1
    , _itAimingRange = 1
    , _itZoom       = defaultItZoom {_itZoomMax = 1}
    , _itAimZoom    = defaultItZoom {_itZoomMax = 1}
    , _itEquipPict = pictureWeaponOnAim $ (,) emptySH $ color blue $ polygon $ rectNESW 5 5 (-5) (-5)
    }
{- |
Automatically sends out pulses that display creatures.  -}
autoSonar :: Item
autoSonar = defaultEquipment
    { _itIdentity = Generic
    , _itName = "AUTOSONAR"
    , _itMaxStack = 1
    , _itAmount = 1
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color yellow $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _itEquipPict = \_ _ -> (,) emptySH blank
    , _itEffect = autoSonarEffect
    , _itID = Nothing
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itZoom = defaultItZoom {_itZoomMax = 1}
    , _itAimZoom = defaultItZoom {_itZoomMax = 1}
    }
{- |
Automatically sends out pulses that display walls.  -}
autoRadar :: Item
autoRadar = defaultEquipment
    { _itIdentity = Generic
    , _itName = "AUTORADAR"
    , _itMaxStack = 1
    , _itAmount = 1
    , _itFloorPict = (,) emptySH $ onLayer FlItLayer $ color yellow $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _itEquipPict = \_ _ -> (,) emptySH blank
    , _itEffect = autoRadarEffect
    , _itID = Nothing
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itZoom = defaultItZoom {_itZoomMax = 1}
    , _itAimZoom = defaultItZoom {_itZoomMax = 1}
    }
{- |
Creates a creature next to the creature using the item.  -}
spawnGun :: Creature -> Item
spawnGun cr = defaultGun
    { _itName       = "SPAWNER"
    , _wpMaxAmmo    = 1
    , _wpLoadedAmmo = 1
    , _wpReloadTime = 80
    , _wpReloadState = 0
    , _itUseRate   = 100
    , _itUse       = \_ -> spawnCrNextTo cr
    , _itUseModifiers =
        [ ammoCheckI
        , hammerCheckI
        ]
    }
spawnCrNextTo 
    :: Creature -- ^ Creature to spawn
    -> Creature -- ^ existing creature that will be spawned next to
    -> World 
    -> World
spawnCrNextTo cr sCr w = w & creatures %~ IM.insert k newCr
  where
    k = IM.newKey $ _creatures w
    newCr = cr
        & crID  .~ k
        & crPos .~ _crPos sCr +.+ unitVectorAtAngle (_crDir sCr)
        & crOldPos .~ _crPos sCr +.+ unitVectorAtAngle (_crDir sCr)

