{-# LANGUAGE TupleSections #-} {- | Weapon effects when pulling the trigger. -} module Dodge.Item.Weapon.TriggerType ( useAmmoAmount, useAllAmmo, useAmmoUpTo, lockInvFor, withMuzFlareI, withMuzPos, withMuzPosShift, crAtMuzPos, withOldDir, trigDoAlso, trigDoAlso', withTempLight, withItem, withItemUpdate, withItemUpdate', ammoUseCheck, rateIncAB, torqueBefore, torqueBeforeAtLeast, 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, duplicateItem, duplicateLoadedBarrels, duplicateLoaded, duplicateOffsets, duplicateOffsetsV2, duplicateOffsetsFocus, hammerCheckI, hammerCheckL, shootL, useTimeCheck, ammoCheckI, applyInaccuracy, modClock, ammoHammerCheck, ) where import Data.Foldable import Data.Maybe import Dodge.Base import Dodge.Creature.HandPos import Dodge.Creature.Test import Dodge.Data.World import Dodge.Inventory.Lock import Dodge.LightSource import Dodge.Reloading import Dodge.SoundLogic import Dodge.WorldEvent import Geometry import LensHelp import RandomHelp import Sound.Data type ChainEffect = (Item -> Creature -> World -> World) -> Item -> Creature -> World -> World lockInvFor :: Int -> ChainEffect lockInvFor i f it cr = f it cr . (cWorld . lWorld . delayedEvents .:~ (i, UnlockInv (_crID cr))) . lockInv (_crID cr) trigDoAlso' :: (Item -> Item) -> (Creature -> Creature) -> (Item -> Creature -> World -> World) -> ChainEffect trigDoAlso' fit fcr f g itm cr = g itm cr . f (fit itm) (fcr 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 & cWorld . lWorld . creatures . ix (_crID cr) %~ crCancelReloading where ic = _heldConsumption $ _itUse 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) | otherwise = case it ^? itUse . heldHammer of Just HammerUp -> eff it cr $ w & cWorld . lWorld . creatures . ix (_crID cr) %~ crCancelReloading _ -> w where ic = _heldConsumption $ _itUse it itUseCharge :: Int -> Item -> Item itUseCharge x = itUse . leftConsumption . arLoaded %~ (max 0 . subtract x) itUseAmmo :: Int -> Item -> Item itUseAmmo x = itUse . heldConsumption . laLoaded %~ (max 0 . subtract x) {- | Fires at an increasing rate. Has different effect after first fire. Applies ammo check and use cooldown check. -} rateIncAB :: -- | Extra effect on first fire ChainEffect -> -- | Extra effect on continued fire ChainEffect -> ChainEffect rateIncAB exeffFirst exeffCont eff item cr w | repeatFire = w & pointItem %~ ( (itUse . heldDelay . rateMax .~ max fastRate (currentRate - 1)) . itUseAmmo 1 . (itUse . heldDelay . rateTime .~ currentRate) ) & exeffCont eff item cr | firstFire = w & pointItem %~ ( (itUse . heldDelay . rateMax .~ startRate - 1) . itUseAmmo 1 . (itUse . heldDelay . rateTime .~ startRate) ) & exeffFirst eff item cr | otherwise = w where fastRate = _rateMinMax . _heldDelay $ _itUse item startRate = _rateMaxMax . _heldDelay $ _itUse item cid = _crID cr itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change pointItem = cWorld . lWorld . creatures . ix cid . crInv . ix itRef currentRate = _rateMax (_heldDelay (_itUse item)) repeatFire = crWeaponReady cr && _rateTime (_heldDelay (_itUse item)) == 1 firstFire = crWeaponReady cr && _rateTime (_heldDelay (_itUse item)) == 0 -- | Apply effect after a warm up. -- note this is quite unsafe, requires the item to have the correct delay type withWarmUp :: -- | warm up sound id SoundID -> ChainEffect withWarmUp soundID f item cr w | curWarmUp < maxWarmUp && crWeaponReady cr = w & pointerToItem . itUse . heldDelay . warmTime +~ 2 & soundContinue (CrWeaponSound cid 0) (_crPos cr) soundID (Just 2) | otherwise = w & pointerToItem . itUse . heldDelay . warmTime .~ maxWarmUp & f item cr where cid = _crID cr itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change pointerToItem = cWorld . lWorld . creatures . ix cid . crInv . ix itRef curWarmUp = _warmTime . _heldDelay $ _itUse item maxWarmUp = _warmMax . _heldDelay $ _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 :: -- | Sound id SoundID -> 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 :: -- | Sound id SoundID -> 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 :: -- | Sound id SoundID -> -- | Frames to play Int -> 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 :: -- | volume Float -> -- | Sound id SoundID -> -- | Frames to play Int -> ChainEffect withSoundForVol vol soundid playTime f item cr = soundContinueVol vol (CrWeaponSound (_crID cr) 0) (_crPos cr) soundid (Just playTime) . f item cr afterRecoil :: -- | Recoil amount Float -> ChainEffect afterRecoil recoilAmount eff item cr = eff item (pushback cr) . over (cWorld . lWorld . 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 (cWorld . lWorld . 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 :: -- | Maximal possible side push amount Float -> ChainEffect withSidePushI maxSide eff item cr w = eff item (push cr) $ w & cWorld . lWorld . 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 :: -- | Maximal possible side push amount Float -> ChainEffect withSidePushAfterI maxSide eff item cr w = over (cWorld . lWorld . 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 . (cWorld . lWorld . creatures . ix (_crID cr) . crInv . ix itRef . itUse . heldConsumption . laLoaded .~ 0) where itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change useAmmoUpTo :: Int -> ChainEffect useAmmoUpTo amAmount eff item cr = eff item cr . ( cWorld . lWorld . creatures . ix (_crID cr) . crInv . ix itRef . itUse . heldConsumption . laLoaded %~ (max 0 . subtract amAmount) ) where itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change useAmmoAmount :: Int -> ChainEffect useAmmoAmount amAmount eff item cr = eff item cr . (cWorld . lWorld . creatures . ix (_crID cr) . crInv . ix itRef . itUse . heldConsumption . laLoaded -~ amAmount) where itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change {- | Applies a world effect after an item use cooldown check. -} useTimeCheck :: ChainEffect useTimeCheck f item cr w = case item ^? itUse . heldDelay . rateTime of Just 0 -> f item cr $ setUseTime w _ -> w where cid = _crID cr setUseTime = cWorld . lWorld . creatures . ix cid . crInv . ix itRef . itUse . heldDelay . rateTime +~ userate itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change userate = fromMaybe 0 $ item ^? itUse . heldDelay . rateMax -- | Applies a world effect after a hammer position check. hammerCheckI :: ChainEffect hammerCheckI f it cr w = case it ^? itUse . heldHammer of Just HammerUp | _laPrimed (_heldConsumption (_itUse it)) -> f it cr w _ -> w -- | 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 . heldDelay . rateTime .~ _rateMax (_heldDelay (_itUse item))) ) -- | reloadCondition = fromMaybe w $ startReloadingWeapon cr w | otherwise = w where cid = _crID cr itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change pointerToItem = cWorld . lWorld . creatures . ix cid . crInv . ix itRef fireCondition = crWeaponReady cr && _rateTime (_heldDelay (_itUse item)) == 0 && _laLoaded (_heldConsumption (_itUse item)) > 0 -- reloadCondition = _laLoaded (_itConsumption item) == 0 {- | Applies a world effect after a hammer position check. Arbitrary inventory position. -} hammerCheckL :: -- | Underlying effect (Item -> Creature -> World -> World) -> Item -> Creature -> World -> World hammerCheckL f itm cr w = case itm ^? itUse . leftHammer of Just HammerUp -> f itm cr w _ -> w {- | Applies a world effect after an ammo check. Arbitrary inventory position -- cannot be replaced with ammoUseCheck. -} shootL :: -- | Underlying effect (Item -> Creature -> World -> World) -> Item -> Creature -> World -> World shootL f item cr w | fireCondition = f item cr w & pointerToItem %~ ( itUseCharge 1 . (itUse . leftDelay . rateTime .~ _rateMax (_leftDelay (_itUse item))) ) -- | reloadCondition = fromMaybe w $ startReloadingWeapon cr w | otherwise = w where cid = _crID cr invid = _ipInvID $ _itLocation item pointerToItem = cWorld . lWorld . creatures . ix cid . crInv . ix invid fireCondition = _rateTime (_leftDelay (_itUse item)) == 0 && _arLoaded (_leftConsumption (_itUse item)) > 0 -- reloadCondition = _laLoaded (_itConsumption item) == 0 withItem :: (Item -> ChainEffect) -> ChainEffect withItem g f it = g it f it -- not ideal withItemUpdate' :: (Item -> Item) -> ChainEffect withItemUpdate' up f it cr = f (up it) cr . (cWorld . lWorld . creatures . ix (_crID cr) . crInv . ix itRef %~ up) where itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change withItemUpdate :: (Item -> Item) -> (Item -> ChainEffect) -> ChainEffect withItemUpdate up g f it cr = g it f it cr . (cWorld . lWorld . creatures . ix (_crID cr) . crInv . ix itRef %~ up) where itRef = cr ^?! crManipulation . manObject . inInventory . ispItem -- unsafe!! TODO change withTempLight :: Int -> Float -> V3 Float -> ChainEffect withTempLight time rad col eff item cr = eff item cr . over (cWorld . lWorld . 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 | (w ^. cWorld . lWorld . lClock) `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 + _aimMuzPos dimPort - _aimHandlePos dimPort) 0 0 where dimPort = _heldAim $ _itUse 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 crAtMuzPos :: ChainEffect crAtMuzPos f it cr = f it (cr & crPos +.+.~ (aimingMuzzlePos cr it *.* unitVectorAtAngle (_crDir cr))) {- | Applies the effect to a randomly rotated creature, - rotation amount given by inaccuracy in itParams -} applyInaccuracy :: ChainEffect applyInaccuracy f it = withRandomDirI acc f it where acc = _brlInaccuracy . _gunBarrels $ _itParams it -- | Applies the effect to a randomly rotated creature. withRandomDirI :: -- | Max possible rotation Float -> 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 :: -- | The fraction of the old direction Float -> 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 :: -- | Max possible rotation Float -> ChainEffect torqueBefore torque feff item cr w | cid == 0 = feff item (cr & crDir +~ rot) $ w & randGen .~ g & cWorld . lWorld . creatures . ix cid . crDir +~ rot & wCam . camRot +~ rot | otherwise = feff item cr $ set randGen g $ over (cWorld . lWorld . 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 :: -- | Minimal possible rotation Float -> -- | Extra possible rotation Float -> ChainEffect torqueBeforeAtLeast minTorque exTorque feff item cr w | cid == 0 = feff item (cr & crDir +~ rot') $ w & randGen .~ g & cWorld . lWorld . creatures . ix cid . crDir +~ rot' & wCam . camRot +~ rot' | otherwise = feff item cr $ set randGen g $ over (cWorld . lWorld . 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 (wCam . camRot) (+ rot) $ feff item cr w | otherwise = set randGen g $ over (cWorld . lWorld . 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 $ _heldConsumption (_itUse item) sideEffectOnFrame :: Int -> (Item -> Creature -> WdWd) -> ChainEffect sideEffectOnFrame i sf f it cr w = f it cr w & cWorld . lWorld . delayedEvents .:~ (i, sf it cr) torqueSideEffect :: Float -> Item -> Creature -> World -> World torqueSideEffect torque _ cr w | cid == 0 = set randGen g $ over (wCam . camRot) (+ rot) w | otherwise = set randGen g $ over (cWorld . lWorld . 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] -> HeldMod -> ChainEffect --repeatOnFrames is hm f it cr w = f it cr w repeatOnFrames is hm f it cr w = f it cr $ w & cWorld . lWorld . delayedEvents .++~ (is <&> (,WdWdFromItCrixWdWd (it & itUse . heldMods .~ hm) (_crID cr) ItCrWdItemEffect)) -- where -- f' = 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 $ _heldConsumption (_itUse 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 $ _heldConsumption (_itUse 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 ^. input) (w ^. wCam)) (_crPos cr) < aimingMuzzlePos cr item = argV ( _crPos cr +.+ aimingMuzzlePos cr item *.* unitVectorAtAngle (_crDir cr) -.- (_crPos cr +.+ pos) ) | otherwise = argV (mouseWorldPos (w ^. input) (w ^. wCam) -.- (_crPos cr +.+ pos)) duplicateItem :: (Item -> [Item]) -> ChainEffect duplicateItem fit eff itm cr w = foldr f w (fit itm) where f itm' = eff itm' cr duplicateOffsetsV2 :: [Point2] -> ChainEffect duplicateOffsetsV2 xs eff item cr w = foldr f w poss where poss = map (rotateV (_crDir cr)) xs f pos = eff item (cr & crPos +.+.~ 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)