{-# LANGUAGE TupleSections #-}
{- |
Weapon effects when pulling the trigger.  -}
module Dodge.Item.Weapon.TriggerType
    ( useAmmoAmount
    , useAllAmmo
    , useAmmoUpTo
    , lockInvFor
    , withMuzFlareI
    , withMuzPos
    , withMuzPosShift
    , withOldDir
    , trigDoAlso
    , withTempLight
    , withItem
    , withItemUpdate
    , ammoUseCheck
    , rateIncAB
    , torqueBefore
    , torqueBeforeAtLeast
--    , torqueAfterI
    , withTorqueAfter
    , torqueSideEffect
    , withRandomItemParams
    , withRandomItemUpdate
    , withSoundStart
    , withSoundItemChoiceStart
    , withSoundContinue
    , withSoundForI
    , withSoundForVol
    , withSmoke
    , withThickSmokeI
    , withThinSmokeI
    , withPositionOffset
    , withPositionWallCheck
    , withPosDirWallCheck
    , withRandomOffset
    , withRandomDirI
    , withRecoil
    , afterRecoil
    , withSidePushAfterI
    , withSidePushI
    , withWarmUp
    , spreadNumI
    , spreadLoaded
    , repeatOnFrames
    , sideEffectOnFrame
    , duplicateLoadedBarrels
    , duplicateLoaded
    , duplicateOffsets
    , duplicateOffsetsFocus
    , hammerCheckI
    , hammerCheckL
    , shootL
    , useTimeCheck
    , ammoCheckI
    , applyInaccuracy
    , modClock
    , ammoHammerCheck
    ) where
import Dodge.Data
import Dodge.Base.Collide
import Dodge.SoundLogic
import Dodge.Reloading
import Dodge.WorldEvent
import RandomHelp
import Dodge.Inventory.Lock
import Dodge.Creature.HandPos
import Dodge.Creature.Test
--import Dodge.Default
import Dodge.Base
import Sound.Data
import Geometry
--import Geometry.Vector3D
import Dodge.LightSource
--import Data.Preload
import LensHelp

--import Control.Lens
import Data.Maybe
--import qualified Data.IntMap.Strict as IM
import Data.Foldable

type ChainEffect 
     = (Item -> Creature -> World -> World)
    -> Item
    -> Creature
    -> World 
    -> World

lockInvFor :: Int -> ChainEffect
lockInvFor i f it cr = f it cr . (delayedEvents .:~ (i, unlockInv (_crID cr)))
    . lockInv (_crID cr)

-- Note that this uses the "base" creature and item values
trigDoAlso 
    :: (Item -> Creature -> World -> World)
    -> ChainEffect
trigDoAlso afterEff eff item cr = afterEff item cr . eff item cr

withSmoke :: Int -> Point4 -> Float -> Int -> Float -> ChainEffect
withSmoke num col rad t alt eff item cr w = eff item cr 
    $ foldl' (flip $ smokeCloudAt col rad t alt . (+.+.+ pos) . (*8)) w ps
  where
    dir = _crDir cr
    pos = addZ 0 $ _crPos cr +.+ (_crRad cr + 15) *.* unitVectorAtAngle dir
    ps = replicateM num randOnUnitSphere & evalState $ _randGen w

withThinSmokeI :: ChainEffect
withThinSmokeI eff item cr w = eff item cr 
    $ foldl' (flip $ makeThinSmokeAt . (+.+.+ pos) . (* 8)) w ps
  where
    dir = _crDir cr
    pos = addZ 0 $ _crPos cr +.+ (_crRad cr +0.5) *.* unitVectorAtAngle dir
    ps = replicateM 5 randOnUnitSphere & evalState $ _randGen w

withThickSmokeI :: ChainEffect
withThickSmokeI eff item cr w = eff item cr 
    $ foldl' (flip $ makeThickSmokeAt . (+.+.+ pos) . (*8)) w ps
  where
    dir = _crDir cr
    pos = addZ 0 $ _crPos cr +.+ (_crRad cr + 15) *.* unitVectorAtAngle dir
    ps = replicateM 20 randOnUnitSphere & evalState $ _randGen w
-- TODO  create a trigger that does different things on first and continued
-- fire.
ammoCheckI :: ChainEffect
ammoCheckI eff itm cr w
    | _laLoaded ic <= 0 || not (_laPrimed ic) = w
    | otherwise = eff itm cr $ w & creatures . ix (_crID cr) %~ crCancelReloading
  where
    ic = _itConsumption itm

-- combined ammo and hammer check: want to be able to auto-reload even if the
-- hammer is down?
ammoHammerCheck :: ChainEffect
ammoHammerCheck eff it cr w
    | _laLoaded ic <= 0 || not (_laPrimed ic) = w -- fromMaybe w (startReloadingWeapon cr w)
        & setHammerDown
    | otherwise = case it ^? itUse . useHammer . hammerPosition of
        Just HammerUp -> eff it cr $ setHammerDown w & creatures . ix (_crID cr) %~ crCancelReloading
        _             -> setHammerDown w
  where 
    ic = _itConsumption it
    cid = _crID cr
    setHammerDown = creatures . ix cid . crInv . ix (crSel cr) . itUse
        . useHammer . hammerPosition .~ HammerDown

itUseCharge :: Int -> Item -> Item
itUseCharge x = itConsumption . arLoaded %~ (max 0 . subtract x) 

itUseAmmo :: Int -> Item -> Item
itUseAmmo x = itConsumption . laLoaded %~ (max 0 . subtract x)

{- | Fires at an increasing rate.
Has different effect after first fire.
Applies ammo check and use cooldown check.  -}
rateIncAB
    :: ChainEffect  -- ^ Extra effect on first fire
    -> ChainEffect  -- ^ Extra effect on continued fire
    -> ChainEffect
rateIncAB exeffFirst exeffCont eff item cr w
    | repeatFire = w
        & pointItem %~ ( (itUse . useDelay . rateMax .~ max fastRate (currentRate - 1))
            . itUseAmmo 1
            . (itUse . useDelay . rateTime .~ currentRate) )
        & exeffCont eff item cr
    | firstFire = w
        & pointItem %~ ( (itUse . useDelay . rateMax .~ startRate - 1)
            . itUseAmmo 1
            . (itUse . useDelay . rateTime .~ startRate) )
        & exeffFirst eff item cr
    | otherwise = w
  where 
    fastRate  = _rateMinMax . _useDelay $ _itUse item
    startRate = _rateMaxMax . _useDelay $ _itUse item
    cid = _crID cr
    itRef = crSel cr
    pointItem = creatures . ix cid . crInv . ix itRef
    currentRate = _rateMax (_useDelay (_itUse item))
    repeatFire = crWeaponReady cr && _rateTime (_useDelay (_itUse item)) == 1 
    firstFire  = crWeaponReady cr && _rateTime (_useDelay (_itUse item)) == 0 
{- | Apply effect after a warm up.  -}
-- note this is quite unsafe, requires the item to have the correct delay type
withWarmUp
    :: SoundID -- ^ warm up sound id
    -> ChainEffect
withWarmUp soundID f item cr w
    | curWarmUp < maxWarmUp && crWeaponReady cr = w
        & pointerToItem . itUse . useDelay . warmTime +~ 2
        & soundContinue (CrWeaponSound cid 0) (_crPos cr) soundID (Just 2)
    | otherwise = w
        & pointerToItem . itUse . useDelay . warmTime .~ maxWarmUp
        & f item cr
  where 
    cid = _crID cr
    itRef = crSel cr
    pointerToItem = creatures . ix cid . crInv . ix itRef
    curWarmUp = _warmTime . _useDelay $ _itUse item
    maxWarmUp = _warmMax  . _useDelay $ _itUse item
withSoundItemChoiceStart
    :: (Item -> SoundID)
    -> ChainEffect
withSoundItemChoiceStart soundf f it cr 
    = soundMultiFrom [CrWeaponSound cid 0,CrWeaponSound cid 1,CrWeaponSound cid 2] (_crPos cr) 
        (soundf it) Nothing 
    . f it cr
  where
    cid = _crID cr
{- | Adds a sound to a creature based world effect. 
The sound is emitted from the creature's position. -}
withSoundStart
    :: SoundID -- ^ Sound id
    -> ChainEffect
withSoundStart soundid f item cr 
    = soundMultiFrom [CrWeaponSound cid 0,CrWeaponSound cid 1,CrWeaponSound cid 2] (_crPos cr) soundid Nothing
    . f item cr
  where
    cid = _crID cr
{- | Adds a sound to a creature based world effect. 
The sound is emitted from the creature's position. -}
withSoundContinue
    :: SoundID -- ^ Sound id
    -> ChainEffect
withSoundContinue soundid f item cr 
    = soundContinue (CrWeaponSound cid 0) (_crPos cr) soundid Nothing
    . f item cr
  where
    cid = _crID cr
{- | Adds a sound to a creature based world effect. 
The sound is emitted from the creature's position. -}
withSoundForI
    :: SoundID -- ^ Sound id
    -> Int -- ^ Frames to play
    -> ChainEffect
withSoundForI soundid playTime f item cr 
    = soundContinue (CrWeaponSound (_crID cr) 0) (_crPos cr) soundid (Just playTime)
    . f item cr

{- | Adds a sound to a creature based world effect. 
The sound is emitted from the creature's position. -}
withSoundForVol
    :: Float -- ^ volume
    -> SoundID -- ^ Sound id
    -> Int -- ^ Frames to play
    -> ChainEffect
withSoundForVol vol soundid playTime f item cr 
    = soundContinueVol vol (CrWeaponSound (_crID cr) 0) (_crPos cr) soundid (Just playTime)
    . f item cr

afterRecoil
    :: Float    -- ^ Recoil amount
    -> ChainEffect
afterRecoil recoilAmount eff item cr = eff item (pushback cr) . over (creatures . ix cid) pushback
  where 
    cid = _crID cr
    pushback = over crPos (+.+ rotateV (_crDir cr) (V2 ((-recoilAmount) / _crMass cr ) 0))
withRecoil :: ChainEffect
withRecoil eff it cr = eff it cr . over (creatures . ix cid) pushback
  where 
    cid = _crID cr
    recoilAmount = fromMaybe 0 $ it ^? itParams . recoil
    pushback = over crPos (+.+ rotateV (_crDir cr) (V2 ((-recoilAmount) / _crMass cr ) 0))
{- | Pushes a creature sideways by a random amount.
Applied before the underlying effect.  -}
withSidePushI
    :: Float    -- ^ Maximal possible side push amount
    -> ChainEffect
withSidePushI maxSide eff item cr w = eff item (push cr) $ w 
    & creatures . ix cid %~ push
    & randGen .~ g
  where 
    cid = _crID cr
    push = over crPos (+.+ rotateV (_crDir cr) (V2 0 (pushAmount / _crMass cr)))
    (pushAmount, g) = randomR (-maxSide,maxSide) $ _randGen w
-- consider unifying the pushes using a direction vector
{- | Pushes a creature sideways by a random amount.
Applied after the underlying effect.  -}
withSidePushAfterI
    :: Float    -- ^ Maximal possible side push amount
    -> ChainEffect
withSidePushAfterI maxSide eff item cr w = over (creatures . ix cid) push . eff item cr $ w 
    & randGen .~ g
  where 
    cid = _crID cr
    push = over crPos (+.+ rotateV (_crDir cr) (V2 0 (pushAmount / _crMass cr)))
    (pushAmount, g) = randomR (-maxSide,maxSide) $ _randGen w
useAllAmmo :: ChainEffect
useAllAmmo eff item cr = eff item cr
    . (creatures . ix (_crID cr) . crInv . ix (crSel cr) . itConsumption . laLoaded .~ 0)
useAmmoUpTo :: Int -> ChainEffect
useAmmoUpTo amAmount eff item cr = eff item cr
    . (creatures . ix (_crID cr) . crInv . ix (crSel cr) . itConsumption . laLoaded 
        %~ (max 0 . subtract amAmount))
useAmmoAmount :: Int -> ChainEffect
useAmmoAmount amAmount eff item cr = eff item cr
    . (creatures . ix (_crID cr) . crInv . ix (crSel cr) . itConsumption . laLoaded -~ amAmount)
{- | 
Applies a world effect after an item use cooldown check. -}
useTimeCheck :: ChainEffect
useTimeCheck f item cr w = case item ^? itUse . useDelay . rateTime of
    Just 0 -> f item cr $ setUseTime w
    _      -> w
  where
    cid = _crID cr
    setUseTime = creatures . ix cid . crInv . ix (crSel cr) . itUse . useDelay . rateTime +~ userate
    userate = fromMaybe 0 $ item ^? itUse . useDelay . rateMax
{- | Applies a world effect after a hammer position check. -}
hammerCheckI :: ChainEffect
hammerCheckI f it cr w = case it ^? itUse . useHammer . hammerPosition of
    Just HammerUp | _laPrimed (_itConsumption it) -> f it cr $ setHammerDown w
    _             -> setHammerDown w
  where 
    cid = _crID cr
    setHammerDown = creatures . ix cid . crInv . ix (crSel cr) . itUse
        . useHammer . hammerPosition .~ HammerDown
{- | Applies a world effect after an ammo check. -}
-- this should be made "safe" incase there is no _rateTime
ammoUseCheck  :: ChainEffect
ammoUseCheck f item cr w 
    | fireCondition = f item cr w & pointerToItem %~
        ( itUseAmmo 1
        . (itUse . useDelay . rateTime .~ _rateMax ( _useDelay (_itUse item)) ))
--    | reloadCondition = fromMaybe w $ startReloadingWeapon cr w
    | otherwise = w
  where 
    cid = _crID cr
    itRef = crSel cr
    pointerToItem = creatures . ix cid . crInv . ix itRef
    fireCondition = crWeaponReady cr
        && _rateTime (_useDelay (_itUse item)) == 0
        && _laLoaded (_itConsumption item) > 0
--    reloadCondition = _laLoaded (_itConsumption item) == 0
{- | Applies a world effect after a hammer position check.
Arbitrary inventory position. -}
hammerCheckL
    :: (Item -> Creature -> World -> World)  -- ^ Underlying effect
    -> Item -> Creature -> World -> World
hammerCheckL f itm cr w = case itm ^? itUse . useHammer . hammerPosition of
    Just HammerUp -> f itm cr $ setHammerDown w
    _             -> setHammerDown w
  where 
    cid = _crID cr
    invid = fromJust $ _itInvPos itm
    setHammerDown = creatures . ix cid . crInv . ix invid 
        . itUse . useHammer . hammerPosition .~ HammerDown
{- | Applies a world effect after an ammo check. 
Arbitrary inventory position -- cannot be replaced with ammoUseCheck. -}
shootL 
    :: (Item -> Creature -> World -> World) 
            -- ^ Underlying effect
    -> Item
    -> Creature
    -> World
    -> World
shootL f item cr w 
    | fireCondition = f item cr w & pointerToItem %~
        ( itUseCharge 1
        . (itUse . useDelay . rateTime .~ _rateMax (_useDelay (_itUse item))) )
--    | reloadCondition = fromMaybe w $ startReloadingWeapon cr w
    | otherwise = w
  where 
    cid = _crID cr
    invid = fromJust $ _itInvPos item
    pointerToItem = creatures . ix cid . crInv . ix invid
    fireCondition = _rateTime (_useDelay (_itUse item)) == 0
        && _arLoaded (_itConsumption item) > 0
--    reloadCondition = _laLoaded (_itConsumption item) == 0
withItem :: (Item -> ChainEffect) -> ChainEffect
withItem g f it = g it f it
withItemUpdate :: (Item -> Item) -> (Item -> ChainEffect) -> ChainEffect
withItemUpdate up g f it cr = g it f it cr . (creatures . ix (_crID cr) . crInv . ix (crSel cr) %~ up)
withTempLight :: Int -> Float -> V3 Float -> ChainEffect
withTempLight time rad col eff item cr = eff item cr 
    . over tempLightSources (theTLS :)
  where
    theTLS = tlsTimeRadColPos time rad col (V3 x y 10)
    V2 x y = _crPos cr +.+ 15 *.* unitVectorAtAngle (_crDir cr)

modClock :: Int -> ChainEffect -> ChainEffect
modClock n chainEff eff it cr w
    | _worldClock w `mod` n == 0 = chainEff eff it cr w
    | otherwise = eff it cr w

withMuzFlareI :: ChainEffect
withMuzFlareI f it cr w = makeTlsTimeRadColPos 2 100 (V3 1 1 0.5) flashPos
    . muzFlareAt (V4 5 5 0 2) flarePos cdir
    $ f it cr w
  where 
    flarePos = addZ 0 (_crPos cr) +.+.+ rotate3z cdir (muzzleOffset cr it +.+.+ V3 0 0 20)
    flashPos = addZ 0 (_crPos cr) +.+.+ rotate3z cdir (muzzleOffset cr it +.+.+ V3 5 0 20)
--    muzzleOffset = V3 (_muzPos (_dimPortage (_itDimension it))) 0 0
    cdir = _crDir cr

muzzleOffset :: Creature -> Item -> Point3
muzzleOffset cr it = V3 (holdOffset + 5 + _muzPos dimPort - _handlePos dimPort) 0 0
  where
    dimPort = _dimPortage $ _itDimension it
    holdOffset
        | crInAimStance OneHand cr = 10
        | otherwise = 0

withMuzPos :: (Point3 -> World -> World) -> ChainEffect
withMuzPos = withMuzPosShift (V2 0 0)
withMuzPosShift :: Point2 -> (Point3 -> World -> World) -> ChainEffect
withMuzPosShift p g f it cr w = g (pos `v2z` 20)
    $ f it cr w
  where
    pos = _crPos cr 
            +.+ rotateV dir p
            +.+ aimingMuzzlePos cr it *.* unitVectorAtAngle dir
    dir = _crDir cr

{- | Applies the effect to a randomly rotated creature,
 - rotation amount given by inaccuracy in itParams  -}
applyInaccuracy :: ChainEffect
applyInaccuracy f it cr w = f it (cr & crDir +~ a) $ set randGen g w
  where 
    acc = _brlInaccuracy . _gunBarrels $ _itParams it
    (a, g) = randomR (-acc,acc) $ _randGen w
{- | Applies the effect to a randomly rotated creature.  -}
withRandomDirI
    :: Float    -- ^ Max possible rotation
    -> ChainEffect
withRandomDirI acc f it cr w = f it (cr & crDir +~ a) $ set randGen g w
  where 
    (a, g) = randomR (-acc,acc) $ _randGen w
withOldDir
    :: Float    -- ^ The fraction of the old direction
    -> ChainEffect
withOldDir aFrac eff item cr
    = eff item (cr & crDir %~ tweenAngles aFrac (_crOldDir cr))

withRandomItemUpdate
    :: State StdGen (Item -> Item)
    -> ChainEffect
withRandomItemUpdate randItUp eff it cr w = eff (f it) cr $ w & randGen .~ g
  where
    (f,g) = runState randItUp (_randGen w)

withRandomItemParams :: State StdGen (ItemParams -> ItemParams) -> ChainEffect
withRandomItemParams rip eff it cr w = eff (it & itParams %~ f) cr $ w & randGen .~ g
  where
    (f,g) = runState rip (_randGen w)

withPositionOffset
    :: (Item -> Creature -> World -> (Point2,Float))
    -> ChainEffect
withPositionOffset h f it cr w = f it (cr & crPos .+.+~ p & crDir +~ a) w
  where
    (p,a) = h it cr w

withPositionWallCheck
    :: (Item -> Creature -> World -> Point2)
    -> ChainEffect
withPositionWallCheck h f it cr w 
    | hasLOS p (_crPos cr) w  = f it (cr & crPos .~ p) w
    | otherwise = w
  where
    p = h it cr w
withPosDirWallCheck
    :: (Item -> Creature -> World -> (Point2,Float))
    -> ChainEffect
withPosDirWallCheck h f it cr w 
    | hasLOS p (_crPos cr) w  = f it (cr & crPos .~ p & crDir .~ a) w
    | otherwise = w
  where
    (p,a) = h it cr w

{- | Apply the effect to a translated creature. -}
withRandomOffset :: ChainEffect
withRandomOffset f item cr w = f item (cr & crPos %~ (+.+ offV)) $ set randGen g w
  where 
    (offsetVal , g) = randomR (-offsetAmount,offsetAmount) $ _randGen w
    offV = rotateV (_crDir cr) (V2 0 offsetVal)
    offsetAmount = fromMaybe 0 $ item ^? itParams . randomOffset
-- | Rotates a creature 
torqueBefore
    :: Float    -- ^ Max possible rotation
    -> ChainEffect
torqueBefore torque feff item cr w 
    | cid == 0  = feff item (cr & crDir +~ rot) $ w
        & randGen                    .~ g 
        & creatures . ix cid . crDir +~ rot
        & cameraRot                  +~ rot
    | otherwise = feff item cr $ set randGen g $ over (creatures . ix cid . crDir) (+rot) w
  where 
    cid = _crID cr
    (rot, g) = randomR (-torque,torque) $ _randGen w
-- | Rotates a creature with minimum rotation
-- Rotates the player creature before applying the effect, other creatures after.
torqueBeforeAtLeast
    :: Float    -- ^ Minimal possible rotation
    -> Float    -- ^ Extra possible rotation
    -> ChainEffect
torqueBeforeAtLeast minTorque exTorque feff item cr w 
    | cid == 0  = feff item (cr & crDir +~ rot') $ w
        & randGen                    .~ g 
        & creatures . ix cid . crDir +~ rot'
        & cameraRot                  +~ rot'
    | otherwise = feff item cr $ set randGen g $ over (creatures . ix cid . crDir) (+rot') w
  where 
    cid = _crID cr
    (rot, g) = randomR (-exTorque,exTorque) $ _randGen w
    rot' | rot < 0   = rot - minTorque
         | otherwise = rot + minTorque
-- | Rotate a randomly creature after applying an effect.
withTorqueAfter :: ChainEffect
withTorqueAfter feff item cr w 
--    | cid == 0  = rotateScope $ set randGen g $ over cameraRot (+rot) $ feff item cr w
    | cid == 0  = set randGen g $ over cameraRot (+rot) $ feff item cr w
    | otherwise = set randGen g $ over (creatures . ix cid . crDir) (+rot) $ feff item cr w
  where 
    cid = _crID cr
    (rot, g) = randomR (-torque,torque) $ _randGen w
    torque = fromMaybe 0 $ item ^? itParams. torqueAfter

spreadNumI :: ChainEffect
spreadNumI eff item cr w = foldr f w dirs
  where
    dirs = zipWith (+) 
        [-spread,-spread+(2*spread/fromIntegral numBul)..spread]
        (randomRs (0,spread/fromIntegral numBul) (_randGen w))
    f dir = eff item (cr & crDir +~ dir)
    spread = _spreadAngle . _brlSpread . _gunBarrels $ _itParams item
    numBul = _brlNum . _gunBarrels $ _itParams item
spreadLoaded :: ChainEffect
spreadLoaded eff item cr w = foldr f w dirs
  where
    cd = 0.5 * spread * fromIntegral (numBulLoaded -1)
    dirs = subtract cd . (spread *) . fromIntegral <$> [0 .. numBulLoaded - 1]
    f dir = eff item (cr & crDir +~ dir)
    spread = _spreadAngle . _brlSpread . _gunBarrels $ _itParams item
    numBulLoaded = _laLoaded $ _itConsumption item

sideEffectOnFrame :: Int 
    -> (Item -> Creature -> World -> World)
    -> ChainEffect
sideEffectOnFrame i sf f it cr w = f it cr w
    & delayedEvents .:~ (i, f')
  where
    f' w' = fromMaybe w' $ do
        cr' <- w' ^? creatures . ix (_crID cr)
        return $ sf it cr' w'

torqueSideEffect :: Float -> Item -> Creature -> World -> World
torqueSideEffect torque _ cr w
    | cid == 0  = set randGen g $ over cameraRot (+rot) w
    | otherwise = set randGen g $ over (creatures . ix cid . crDir) (+rot) w
  where 
    cid = _crID cr
    (rot, g) = randomR (-torque,torque) $ _randGen w

-- pump the updated creature into the chain in later frames
repeatOnFrames :: [Int] -> ChainEffect
repeatOnFrames is f it cr w = f it cr w
    & delayedEvents .++~ (is <&> (, f'))
  where
    f' w' = fromMaybe w' $ do
        cr' <- w' ^? creatures . ix (_crID cr)
        return $ f it cr' w'

duplicateLoaded :: ChainEffect
duplicateLoaded eff it cr w = foldr f w [1..numBul]
  where
    f _ = eff it cr
    numBul = _laLoaded $ _itConsumption it

duplicateLoadedBarrels :: ChainEffect
duplicateLoadedBarrels eff item cr w = foldr f w poss
  where
    cp :: Float
    cp = -0.5 * (fromIntegral numBar - 1)
    poss :: [V2 Float]
    poss = map (rotateV (_crDir cr) . V2 0 . (*5) . (+ cp) . fromIntegral) [0 .. numBul - 1]
    f pos = eff item (cr & crPos %~ (+.+ pos))
    numBar = _brlNum . _gunBarrels $ _itParams item
    numBul = _laLoaded $ _itConsumption item

duplicateOffsetsFocus :: [Float] -> ChainEffect
duplicateOffsetsFocus xs eff item cr w = foldr f w poss
  where
    poss :: [V2 Float]
    poss = map (rotateV (_crDir cr) . V2 0 ) xs
    f pos = eff item $ cr 
        & crPos %~ (+.+ pos)
        & crDir .~ thedir pos
    thedir pos 
        | dist (mouseWorldPos w) (_crPos cr) < aimingMuzzlePos cr item
                    = argV (_crPos cr +.+ aimingMuzzlePos cr item *.* unitVectorAtAngle (_crDir cr)
                                -.- (_crPos cr +.+ pos))
        | otherwise = argV (mouseWorldPos w -.- (_crPos cr +.+ pos))

duplicateOffsets :: [Float] -> ChainEffect
duplicateOffsets xs eff item cr w = foldr f w poss
  where
    poss :: [V2 Float]
    poss = map (rotateV (_crDir cr) . V2 0 ) xs
    f pos = eff item (cr & crPos %~ (+.+ pos))
