236 lines
8.3 KiB
Haskell
236 lines
8.3 KiB
Haskell
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
|