module Dodge.WorldEvent.Shockwave ( makeShockwaveAt , inverseShockwaveAt ) where import Dodge.Data import Dodge.Base import Geometry import Picture import qualified Data.IntMap.Strict as IM import Control.Lens makeShockwaveAt :: [Int] -- ^ IDs of invulnerable creatures. -> Point2 -- ^ Center of shockwave. -> Float -- ^ Maximal radius. -> Int -- ^ Damage caused per frame. -> Float -- ^ Amount of pushback per frame. -> Color -- ^ Color of shockwave. -> World -- ^ Start world. -> World makeShockwaveAt is p rad dam push col = over particles' ((:) theShockwave) where theShockwave = shockwaveAt is p rad dam push col 10 shockwaveAt :: [Int] -- ^ IDs of invulnerable creatures. -> Point2 -> Float -> Int -> Float -> Color -> Int -> Particle' shockwaveAt is p rad dam push col maxtime = Shockwave' { _ptDraw = drawShockwave , _ptUpdate' = mvShockwave' is , _btColor' = col , _btPos' = p , _btRad' = rad , _btDam' = dam , _btPush' = push , _btMaxTime' = maxtime , _btTimer' = maxtime } drawShockwave :: Particle' -> Picture drawShockwave pt = pic where pic = onLayer PtLayer $ uncurry translate p $ color (_btColor' pt) $ thickCircle rad thickness p = _btPos' pt r = _btRad' pt thickness = tFraction**2 * r rad = r - (3/4) * r * tFraction tFraction = fromIntegral t / fromIntegral (_btMaxTime' pt) t = _btTimer' pt mvShockwave' :: [Int] -- ^ IDs of invulnerable creatures. -> World -> Particle' -> (World, Maybe Particle') mvShockwave' is w pt | _btTimer' pt <= 0 = (w, Nothing) | otherwise = (dams w , Just $ set btTimer' (t - 1) pt) -- $ set ptDraw (const pic) pt) where r = _btRad' pt p = _btPos' pt push = _btPush' pt dam = _btDam' pt t = _btTimer' pt tFraction = fromIntegral t / fromIntegral (_btMaxTime' pt) rad = r - (3/4) * r * tFraction dams = over creatures (IM.map damCr) . flip (foldr damageBlocks) hitBlocks hitBlocks = wallsOnCirc p rad $ wallsNearPoint p w damageBlocks wall w = case wall ^? blHP of Just hp -> foldr (\j -> over (walls . ix j . blHP) (\y -> y - 1)) w (_blIDs wall) _ -> w damCr cr | _crID cr `elem` is = cr | dist (_crPos cr) p < rad + _crRad cr = over (crState . crDamage) ((:) $ PushDam dam (25 * push *.* safeNormalizeV (_crPos cr -.- p))) cr | otherwise = cr ------------------------------------------------- inverseShockwaveAt :: Point2 -> Float -> Int -> Float -> Float -> World -> World inverseShockwaveAt p rad dam push pushexp = over particles' ((:) theShockwave) where theShockwave = Particle' { _ptDraw = const blank , _ptUpdate' = moveInverseShockWave 10 p rad push pushexp } moveInverseShockWave :: Int -> Point2 -> Float -> Float -> Float -> World -> Particle' -> (World, Maybe Particle') moveInverseShockWave 0 _ _ _ _ w _ = (w, Nothing) moveInverseShockWave t p r push pushexp w pt = (dams w, Just $ newupdate $ newpic pt ) where newupdate = set ptUpdate' $ moveInverseShockWave (t-1) p r push pushexp newpic = set ptDraw (const $ onLayer PtLayer $ uncurry translate p $ color cyan $ thickCircle rad thickness) rad = r - (4/40) * r * fromIntegral (10 - t) thickness = (fromIntegral (10 - t))**2 * rad / 40 dams = over creatures (IM.map damCr) . flip (foldr damageBlocks) hitBlocks hitBlocks = wallsOnCirc p rad $ wallsNearPoint p w damageBlocks wall w = case wall ^? blHP of Just hp -> foldr (\j -> over (walls . ix j . blHP) (\y -> y - 1)) w (_blIDs wall) _ -> w damCr cr | dist (_crPos cr) p < rad + _crRad cr = over (crState . crDamage) ((:) $ PushDam 1 (25 *.* safeNormalizeV (p -.- _crPos cr))) cr | otherwise = cr