module Dodge.Bullet ( updateBullet, ) where import Linear import Dodge.Item.Weapon.Bullet import System.Random import Data.Foldable import Data.Maybe import Dodge.Creature.Test import Dodge.Data.World import Dodge.EnergyBall import Dodge.Movement.Turn import Dodge.WorldEvent.SpawnParticle import Dodge.WorldEvent.ThingsHit import Geometry import qualified IntMapHelp as IM import LensHelp import qualified ListHelp as List import Data.Bifunctor updateBullet :: World -> Bullet -> (World, Maybe Bullet) updateBullet w bu | magV (_buVel bu) < 1 || _buTimer bu <= 0 = (endspawn w, Nothing) | otherwise = second (fmap updateBulVel) . hitEffFromBul w $ applyMagnetsToBul bu w where endspawn = fromMaybe id $ do partspawn <- useBulletPayload bu return $ partspawn p p = _buPos bu -- do we want this to drain energy from the deflection source? applyMagnetsToBul :: Bullet -> World -> Bullet applyMagnetsToBul bu = foldl' (flip doMagnetBuBu) bu . _oldMagnets . _lWorld . _cWorld doMagnetBuBu :: Magnet -> Bullet -> Bullet doMagnetBuBu mg bu = if isclose then case _mgField mg of MagnetAlign -> bu & buVel %~ vecTurnTo (10 * pi / (d+40)) bpos mgalignpos MagnetDeflect -> bu & buVel %~ vecTurnTo (10 * pi / (d+40)) bpos mgdeflectpos MagnetAttract -> bu & buVel %~ vecTurnTo (10 * pi / (d+40)) bpos mpos MagnetRepulse -> bu & buVel %~ vecTurnTo (10 * pi / (d+40)) bpos (2*bpos - mpos) else bu where bvel = bu ^. buVel mpos = mg ^. mgPos bpos = bu ^. buPos mgdeflectpos | isLHS mpos (mpos + bvel) bpos = bpos + vNormal (bpos - mpos) | otherwise = bpos - vNormal (bpos - mpos) mgalignpos | dotV bvel (bpos - mpos) > 0 = bpos - mpos | otherwise = mpos - bpos d = dist (bu ^. buPos) (mg ^. mgPos) isclose = d < 100 updateBulVel :: Bullet -> Bullet updateBulVel bt = case _buTrajectory bt of BasicBulletTrajectory -> bt & buVel .*.*~ _buDrag bt MagnetTrajectory tpos -> bt & buVel %~ (clipV 20 . (+.+ 5 *.* normalizeV (tpos -.- _buPos bt))) FlechetteTrajectory tpos -> bt & buVel %~ vecTurnTo 0.2 (_buPos bt) tpos BezierTrajectory spos tpos xpos -> let bf tm = bQuadToF (spos, xpos, tpos) $ (100 - tm) * 0.05 in bt & buVel .~ bf (fromIntegral $ t - 1) -.- bf (fromIntegral t) where t = _buTimer bt -- NOTE THE FOLLOWING HAS BEZIER CURE/FLECHETTE STUFF THAT MIGHT BE USEFUL --shootBullet :: LabelDoubleTree ComposeLinkType Item -> Creature -> World -> World --shootBullet itm cr w = fromMaybe (error "cannot find bullet ammo when expected to") $ do -- atype <- itm ^? ldtValue . itUse . heldAmmoTypes . ix 0 -- leftitms <- itm ^? ldtLeft -- mag <- lookup (AmmoInLink 0 atype) leftitms -- thebullet <- mag ^? ldtValue . itUse . amagParams . ampBullet -- return $ w -- & randGen .~ g' -- & cWorld . lWorld . instantBullets -- .:~ ( thebullet -- & buPos .~ _crPos cr -- & buTrajectory %~ settrajectory -- & buVel %~ (rotateV dir . (muzvel *.*)) -- & buDrag *~ drag -- ) -- where -- it = itm ^. ldtValue -- sp = _crPos cr +.+ (muzlength ^?! _head . _x) *.* unitVectorAtAngle dir -- dir = _crDir cr -- (drag,g) = case _rifling (_heldParams $ _itUse it) of -- ConstFloat x -> (x, _randGen w) -- UniRandFloat x y -> randomR (x,y) $ _randGen w -- (muzvel,g') = case _muzVel $ _heldParams $ _itUse it of -- ConstFloat x -> (x,g) -- UniRandFloat x y -> randomR (x,y) $ _randGen w -- muzlength = aimingMuzzlePos cr it -- settrajectory traj = case traj of -- BasicBulletTrajectory -> BasicBulletTrajectory -- MagnetTrajectory{} -> fromMaybe BasicBulletTrajectory $ do -- tpos <- cr ^? crTargeting . ctPos . _Just -- return $ MagnetTrajectory tpos -- FlechetteTrajectory{} -> fromMaybe BasicBulletTrajectory $ do -- tpos <- cr ^? crTargeting . ctPos . _Just -- return $ FlechetteTrajectory tpos -- BezierTrajectory{} -> fromMaybe BasicBulletTrajectory $ do -- tpos <- cr ^? crTargeting . ctPos . _Just -- return $ BezierTrajectory sp tpos (mouseWorldPos (w ^. input) (w ^. wCam)) bounceDir :: (Point2, Either Creature Wall) -> Maybe Point2 bounceDir (_, Right wl) | _wlBouncy wl = Just $ uncurry (-.-) (_wlLine wl) bounceDir (p, Left cr) | crIsArmouredFrom p cr = Just $ vNormal $ p -.- _crPos cr bounceDir _ = Nothing useBulletPayload :: Bullet -> Maybe (Point2 -> World -> World) useBulletPayload bu = case _buPayload bu of BulSpark -> Nothing BulFlak -> Just (makeFlak bu) BulFrag -> Just makeFragBullets BulGas -> Just (`makeGasCloud` V2 0 0) BulBall IncBall -> Just incBallAt BulBall ConcBall -> Just $ \p -> cWorld . lWorld . shockwaves .:~ concBall p BulBall TeslaBall -> Just makeStaticBall BulBall FlashBall -> Just makeFlashBall makeFragBullets :: Point2 -> World -> World makeFragBullets p w = w & cWorld . lWorld . bullets .++~ bus where bus = zipWith f (take 10 as) (take 10 ss) as = randomRs (0, 2 * pi) $ _randGen w ss = randomRs (5, 15) $ _randGen w f a s = defaultBullet & buVel .~ s *.* unitVectorAtAngle a & buDrag .~ 0.8 & buPos .~ p & buOldPos .~ p & buWidth .~ 1 & buDamages .~ [ Damage PIERCING 5 0 0 0 $ PushBackDamage 2 ] makeFlak :: Bullet -> Point2 -> World -> World makeFlak bu _ w = w & cWorld . lWorld . bullets .++~ [f x | x <- xs] where s = min 10 (0.5 * magV (_buVel bu)) xs = take 5 $ randomRs (-s,s) $ _randGen w f x = bu & buVel %~ g x & buTimer .~ 97 & buPayload .~ BulSpark & buWidth .~ 0.5 & buDamages .~ [ Damage PIERCING 25 0 0 0 $ PushBackDamage 2 ] g x v = v +.+ x *.* normalizeV (vNormal v) hitEffFromBul :: World -> Bullet -> (World, Maybe Bullet) hitEffFromBul w bu = case _buEffect bu of PenetrateBullet -> movePenBullet bu hitstream w BounceBullet -> case List.safeHead hitstream of Nothing -> (w, moveBullet bu) Just (hp, crwl) -> fromMaybe (expireAndDamage bu hitstream w) $ do dir <- bounceDir (hp, crwl) return ( w , Just $ bu & buPos .~ hp +.+ normalizeV (_buPos bu -.- hp) & buVel %~ reflectIn dir & buTrajectory .~ BasicBulletTrajectory & buTimer -~ 1 ) DestroyBullet -> expireAndDamage bu hitstream w where ep = sp +.+ _buVel bu sp = _buPos bu hitstream = thingsHit sp ep w setFromToDams :: Bullet -> Point2 -> [Damage] setFromToDams bu p = map f (_buDamages bu) where f = (dmFrom .~ _buPos bu) . (dmAt .~ p) . (dmTo .~ _buPos bu +.+ _buVel bu) damageThingHit :: Bullet -> (Point2, Either Creature Wall) -> World -> World damageThingHit bu (p, crwl) = case crwl of Left cr -> cWorld . lWorld . creatures . ix (_crID cr) . crState . csDamage .++~ dams Right wl -> cWorld . lWorld . wallDamages %~ IM.insertWith (++) (_wlID wl) dams where dams = setFromToDams bu p expireAndDamage :: Bullet -> [(Point2, Either Creature Wall)] -> World -> (World, Maybe Bullet) expireAndDamage bt things w = case List.safeHead things of Nothing -> (w, moveBullet bt) Just x -> (damageThingHit bt x w, destroyAt (fst x) bt) moveBullet :: Bullet -> Maybe Bullet moveBullet pt = Just $ pt & buPos %~ (+.+ _buVel pt) & buOldPos .~ _buPos pt & buTimer -~ 1 destroyAt :: Point2 -> Bullet -> Maybe Bullet destroyAt hitp pt = Just $ pt & buPos .~ hitp +.+ normalizeV (p -.- hitp) & buOldPos .~ p & buVel .~ 0 where p = _buPos pt movePenBullet :: Bullet -> [(Point2, Either Creature Wall)] -> World -> (World, Maybe Bullet) movePenBullet bu hitstream w = case hitstream of [] -> (w, moveBullet bu) ((p, crwl) : strm) -> if penThing crwl then first (damageThingHit bu (p, crwl)) $ movePenBullet bu strm w else expireAndDamage bu hitstream w penThing :: Either Creature Wall -> Bool penThing (Left _) = True penThing (Right wl) = _wlPenetrable wl