diff --git a/src/Dodge/Combine/Combinations.hs b/src/Dodge/Combine/Combinations.hs index 13dae21f6..051a9e480 100644 --- a/src/Dodge/Combine/Combinations.hs +++ b/src/Dodge/Combine/Combinations.hs @@ -3,6 +3,7 @@ module Dodge.Combine.Combinations where import Geometry import Dodge.Data import Dodge.WorldEvent.HitEffect +import Dodge.WorldEvent.SpawnParticle import Dodge.Particle.Bullet.HitEffect import Dodge.Item.Equipment import Dodge.Item.Craftable @@ -115,7 +116,8 @@ moduleCombinations = , ( ModBullet , bulletWeapons , [amod [INCENDIARYMODULE] "+INCENDIARY" (f $ destroyOnImpact bulIncCr bulIncWall) - ,amod [BOUNCEMODULE] "+BOUNCE" (f $ destroyOnImpact bulBounceArmCr' bulBounceWall) + ,amod [BOUNCEMODULE] "+BOUNCE" (f $ destroyOnImpact bulBounceArmCr bulBounceWall) + ,amod [STATICMODULE] "+STATIC" (f $ bulletDestroySpawn aStaticBall) ] ) , ( ModTarget diff --git a/src/Dodge/Combine/Data.hs b/src/Dodge/Combine/Data.hs index e62f3ab54..4dda90a68 100644 --- a/src/Dodge/Combine/Data.hs +++ b/src/Dodge/Combine/Data.hs @@ -110,6 +110,9 @@ data CombineType -- Modules -- bullet | INCENDIARYMODULE + | STATICMODULE + | FRAGMODULE + | FLASHMODULE | BOUNCEMODULE -- | TELEPORTMODULE diff --git a/src/Dodge/Item/Craftable.hs b/src/Dodge/Item/Craftable.hs index f86d7fbb5..ab87b24b7 100644 --- a/src/Dodge/Item/Craftable.hs +++ b/src/Dodge/Item/Craftable.hs @@ -21,6 +21,11 @@ makeTypeCraftNum i ct = defaultCraftable makeTypeCraft :: CombineType -> Item makeTypeCraft = makeTypeCraftNum 1 +makeModule :: CombineType -> Item +makeModule ct = makeTypeCraft ct + & itInvSize .~ 1 + & itInvColor .~ chartreuse + incendiaryModule :: Item incendiaryModule = makeTypeCraft INCENDIARYMODULE & itInvSize .~ 1 diff --git a/src/Dodge/Item/Weapon/Bullet.hs b/src/Dodge/Item/Weapon/Bullet.hs index df5d41430..92e3189c4 100644 --- a/src/Dodge/Item/Weapon/Bullet.hs +++ b/src/Dodge/Item/Weapon/Bullet.hs @@ -1,6 +1,7 @@ module Dodge.Item.Weapon.Bullet ( basicBullet , incBullet + , staticBullet , conBullet , bounceBullet , ltBullet @@ -8,6 +9,7 @@ module Dodge.Item.Weapon.Bullet ) where import Dodge.Data import Dodge.WorldEvent.HitEffect +import Dodge.WorldEvent.SpawnParticle import Dodge.Particle.Bullet.HitEffect import Geometry.Data @@ -27,6 +29,14 @@ incBullet = BulletAmmo , _amBulVel = V2 50 0 , _amBulTraj = BasicBulletTrajectory } +staticBullet :: AmmoType +staticBullet = BulletAmmo + { _amString = "STATIC" + , _amBulEff = bulletDestroySpawn aStaticBall + , _amBulWth = 2 + , _amBulVel = V2 50 0 + , _amBulTraj = BasicBulletTrajectory + } conBullet :: AmmoType conBullet = BulletAmmo { _amString = "CONCUSSIVE" diff --git a/src/Dodge/LockAndKey.hs b/src/Dodge/LockAndKey.hs index 1b4a9a72d..c8231fe37 100644 --- a/src/Dodge/LockAndKey.hs +++ b/src/Dodge/LockAndKey.hs @@ -22,7 +22,8 @@ lockRoomKeyItems :: RandomGen g => [ (Int -> State g (SubCompTree Room) , State lockRoomKeyItems = -- [(lasCenSensEdge, takeOne [LAUNCHER,LASGUN,SPARKGUN,FLATSHIELD] ) -- ,(sensorRoomRunPast Electrical, return SPARKGUN ) - [(sensorRoomRunPast Flaming, return FLAMESPITTER ) + [(sensorRoomRunPast Electrical, return STATICMODULE ) + --,(sensorRoomRunPast Flaming, takeOne [FLAMESPITTER, INCENDIARYMODULE] ) -- ,(sensorRoomRunPast Lasering, return LASGUN ) -- ,(const slowDoorRoomRunPast, return MINIGUN) -- ,(const longRoomRunPast, takeOne [SNIPERRIFLE,FLATSHIELD]) @@ -67,6 +68,14 @@ itemRooms = [rc [keyCard 0] ] ) + , (INCENDIARYMODULE , join $ takeOne + [rc [incendiaryModule] + ] + ) + , (STATICMODULE , join $ takeOne + [rc [makeModule STATICMODULE] + ] + ) ] where rc its = do diff --git a/src/Dodge/Particle/Bullet/HitEffect.hs b/src/Dodge/Particle/Bullet/HitEffect.hs index d7a90d2a7..ab51477a3 100644 --- a/src/Dodge/Particle/Bullet/HitEffect.hs +++ b/src/Dodge/Particle/Bullet/HitEffect.hs @@ -16,12 +16,17 @@ import LensHelp import System.Random import Control.Monad.State --- | Basic bullet hit creature effect. -bulHitCr :: Particle -> Point2 -> Creature -> World -> World -bulHitCr bt p cr w - | crIsArmouredFrom p cr - = colSpark 8 (_ptColor bt) p1 (argV (p1 -.- p)) $ addDamageArmoured w - | otherwise = addDamage . bulletHitSound p $ w + +crArmourSplit + :: (Particle -> Point2 -> Creature -> World -> World) -- | normal effect + -> (Particle -> Point2 -> Creature -> World -> World) -- | armoured effect + -> Particle -> Point2 -> Creature -> World -> World +crArmourSplit normalf armourf bt p cr + | crIsArmouredFrom p cr = armourf bt p cr + | otherwise = normalf bt p cr + +basicHitCr :: Particle -> Point2 -> Creature -> World -> World +basicHitCr bt p cr = addDamage . bulletHitSound p where sp = head $ _ptTrail bt bulVel = _ptVel bt @@ -29,24 +34,27 @@ bulHitCr bt p cr w mvDams = [ Damage PushDam 1 sp p ep . PushBackDamage $ 2 *.* bulVel ] addDamage = creatures . ix cid . crState . crDamage .++~ (Damage Piercing 100 sp p ep NoDamageEffect : mvDams) + cid = _crID cr + +-- | Basic bullet hit creature effect. +basicHitArmour :: Particle -> Point2 -> Creature -> World -> World +basicHitArmour bt p cr w = colSpark 8 (_ptColor bt) p1 (argV (p1 -.- p)) $ addDamageArmoured w + where + sp = head $ _ptTrail bt + bulVel = _ptVel bt + ep = sp +.+ bulVel + mvDams = [ Damage PushDam 1 sp p ep . PushBackDamage $ 2 *.* bulVel ] addDamageArmoured = creatures . ix cid . crState . crDamage .++~ mvDams cid = _crID cr p1 = p +.+ 2 *.* squashNormalizeV (p -.- _crPos cr) -bulletHitSound :: Point2 -> World -> World -bulletHitSound p = soundMultiFrom [CrHitSound 0] p hit1S (Just 10) -{- | Bounce off armoured creatures, otherwise do damage. -} -bulBounceArmCr' :: Particle -> Point2 -> Creature -> World -> World -bulBounceArmCr' bt p cr w - | crIsArmouredFrom p cr = addBouncer . addDamageArmoured $ w - | otherwise = addDamage . bulletHitSound p $ w +bounceArmour :: Particle -> Point2 -> Creature -> World -> World +bounceArmour bt p cr = addBouncer . addDamageArmoured where sp = head $ _ptTrail bt bulVel = _ptVel bt ep = sp +.+ bulVel mvDams = [ Damage PushDam 1 sp p ep . PushBackDamage $ 2 *.* bulVel ] - addDamage = creatures . ix cid . crState . crDamage .++~ - (Damage Piercing 100 sp p ep NoDamageEffect : mvDams) addDamageArmoured = creatures . ix cid . crState . crDamage .++~ mvDams cid = _crID cr newDir = squashNormalizeV (p -.- _crPos cr) @@ -56,6 +64,16 @@ bulBounceArmCr' bt p cr w bouncer = (aBulAt Nothing id (Just (_ptColor bt)) Nothing pOut reflectVel (_btDrag bt) (_ptHitEff bt) (_ptWidth bt) ) {_ptTimer = _ptTimer bt - 1} + +-- | Basic bullet hit creature effect. +bulHitCr :: Particle -> Point2 -> Creature -> World -> World +bulHitCr = crArmourSplit basicHitCr basicHitArmour + +bulletHitSound :: Point2 -> World -> World +bulletHitSound p = soundMultiFrom [CrHitSound 0] p hit1S (Just 10) +{- | Bounce off armoured creatures, otherwise do damage. -} +bulBounceArmCr :: Particle -> Point2 -> Creature -> World -> World +bulBounceArmCr = crArmourSplit basicHitCr bounceArmour {- | Bullet pass through creatures. -} bulPenCr' :: Particle -> Point2 -> Creature -> World -> World bulPenCr' bt p cr w = w @@ -77,11 +95,6 @@ hvBulHitCr :: Particle -> Point2 -> Creature -> World -> World hvBulHitCr bt p cr w = w & creatures . ix cid . crState . crDamage .++~ damageGamut 200 100 d1 sp p ep --- [Piercing 200 sp p ep --- ,Blunt 100 sp p ep --- ,TorqueDam 1 d1 --- ,PushDam 1 $ 3 *.* (ep -.- sp) --- ] & bulletHitSound ep where (d1,_) = randomR (-0.7,0.7) $ _randGen w @@ -97,6 +110,42 @@ damageGamut pdam bdam tor sp p ep = ,Damage PushDam 1 sp p ep . PushBackDamage $ 3 *.* (ep -.- sp) ] +bulletDestroySpawn :: (Point2 -> Particle) + -> Particle + -> [(Point2, Either Creature Wall)] + -> World + -> (World, Maybe Particle) +bulletDestroySpawn f = destroyOnImpact (bulDestroyCr f) (bulDestroyWall f) + +{- | Create a flamelet when hitting a creature. -} +bulDestroyCr :: (Point2 -> Particle) -> Particle -> Point2 -> Creature -> World -> World +bulDestroyCr f bt p cr w = w + & instantParticles .:~ (f v & ptPos .~ p) + & bulletHitSound p + & creatures . ix cid . crState . crDamage .:~ Damage Piercing 60 sp p ep NoDamageEffect + where + cid = _crID cr + sp = head $ _ptTrail bt + ep = sp +.+ _ptVel bt + v = evalState (randInCirc 1) $ _randGen w + +{- | Create flamelet on wall. -} +bulDestroyWall + :: (Point2 -> Particle) + -> Particle + -> Point2 -- Impact point + -> Wall + -> World + -> World +bulDestroyWall f bt p wl = damageWall (Damage Blunt 50 sp p ep NoDamageEffect) wl + . (instantParticles .:~ (f reflectVel & ptPos .~ pOut)) + where + ep = sp +.+ _ptVel bt + sp = head $ _ptTrail bt + pOut = p +.+ squashNormalizeV (sp -.- p) + wallV = uncurry (-.-) (_wlLine wl) + reflectVel = squashNormalizeV $ reflectIn wallV (_ptVel bt) + {- | Create a flamelet when hitting a creature. -} bulIncCr :: Particle -> Point2 -> Creature -> World -> World bulIncCr bt p cr w = w diff --git a/src/Dodge/WorldEvent/Flash.hs b/src/Dodge/WorldEvent/Flash.hs index da01aca5e..09e81a0c8 100644 --- a/src/Dodge/WorldEvent/Flash.hs +++ b/src/Dodge/WorldEvent/Flash.hs @@ -13,7 +13,7 @@ module Dodge.WorldEvent.Flash , laserGunFlashAt , teslaGunFlashAt , muzFlareAt - , flameFlicker + , ptFlicker ) where import Dodge.Data import Dodge.LightSource @@ -61,8 +61,9 @@ explosionFlashAt p = tempLightSources .:~ tlsTimeRadFunPos 20 150 intensityFunc | x < 10 = 1 / (10 - fromIntegral x) | otherwise = 1 -flameFlicker :: Particle -> World -> World -flameFlicker pt +ptFlicker :: Particle -> World -> World +ptFlicker pt | _ptTimer pt `mod` 7 == 0 = tempLightSources - .:~ tlsTimeRadColPos 1 70 (V3 0.5 0 0) (addZ 10 $ _ptPos pt) + .:~ tlsTimeRadColPos 1 70 (0.5 *.*.* xyzV4 (_ptColor pt)) (addZ 10 $ _ptPos pt) | otherwise = id + diff --git a/src/Dodge/WorldEvent/SpawnParticle.hs b/src/Dodge/WorldEvent/SpawnParticle.hs index 7336223f9..ca4184e62 100644 --- a/src/Dodge/WorldEvent/SpawnParticle.hs +++ b/src/Dodge/WorldEvent/SpawnParticle.hs @@ -3,6 +3,8 @@ module Dodge.WorldEvent.SpawnParticle ( makeGasCloud , aFlameParticle , makeFlamelet + , aStaticBall + , makeStaticBall ) where import Dodge.Data import Dodge.Base @@ -37,7 +39,7 @@ aFlameParticle t pos vel maycid = PtZ , _ptCrIgnore = maycid , _ptWidth = 4 , _ptTimer = t - , _ptHitEff = doFlameDamCrWall + , _ptHitEff = damageBothTypeAmount Flaming 20 , _ptZ = 20 } drawFlame @@ -80,7 +82,7 @@ moveFlame rotd w pt | otherwise = case thingsHitExceptCr (_ptCrIgnore pt) sp ep w of ((_,Left _):_) -> (doSound $ damwls damcrs , mvPt 0.7) ((p,Right wl):_) -> (doSound $ damwls damcrs , rfl wl p) - _ -> (flameFlicker pt . damwls $ doSound damcrs , mvPt 0.98) + _ -> (ptFlicker pt . damwls $ doSound damcrs , mvPt 0.98) where time = _ptTimer pt doSound = soundContinue Flame (V2 x y) fireLoudS (Just 2) @@ -109,6 +111,44 @@ moveFlame rotd w pt reflV wall = (0.3 *.* reflectIn (uncurry (-.-) . swap $ _wlLine wall) vel ) +.+ (0.2 *.* vel) +aStaticBall :: Point2 -> Particle +aStaticBall vel = PtZ + { _ptDraw = drawStaticBall + , _ptUpdate = moveStaticBall + , _ptVel = vel + , _ptColor = blue + , _ptPos = 0 + , _ptCrIgnore = Nothing + , _ptWidth = 3 + , _ptTimer = 20 + , _ptHitEff = damageBothTypeAmount Electrical 20 + , _ptZ = 20 + } + + +makeStaticBall + :: Point2 -- ^ Position + -> Float -- ^ z position + -> Point2 -- ^ Velocity + -> Maybe Int -- ^ Ignore creature id + -> Float -- ^ Size + -> Int -- ^ Timer + -> World + -> World +makeStaticBall (V2 x y) z vel maycid size time w = w + & instantParticles .:~ PtZ + { _ptDraw = drawStaticBall + , _ptUpdate = moveStaticBall + , _ptVel = vel + , _ptColor = blue + , _ptPos = V2 x y + , _ptCrIgnore = maycid + , _ptWidth = size + , _ptTimer = time + , _ptHitEff = damageBothTypeAmount Electrical 20 + , _ptZ = z + } + makeFlamelet :: Point2 -- ^ Position -> Float -- ^ z position @@ -121,27 +161,42 @@ makeFlamelet makeFlamelet (V2 x y) z vel maycid size time w = w & randGen .~ g & instantParticles .:~ PtZ - { _ptDraw = drawFlameletZ rot - , _ptUpdate = moveFlamelet - , _ptVel = vel - , _ptColor = red - , _ptPos = V2 x y - , _ptCrIgnore = maycid - , _ptWidth = size - , _ptTimer = time - , _ptHitEff = doFlameDamCrWall + { _ptDraw = drawFlameletZ rot + , _ptUpdate = moveFlamelet + , _ptVel = vel + , _ptColor = red + , _ptPos = V2 x y + , _ptCrIgnore = maycid + , _ptWidth = size + , _ptTimer = time + , _ptHitEff = damageBothTypeAmount Flaming 20 , _ptZ = z } where (rot ,g) = randomR (0,3) $ _randGen w -doFlameDamCrWall :: Particle +damageBothTypeAmount :: DamageType + -> Int + -> Particle -> [(Point2, Either Creature Wall)] -> World -> (World, Maybe Particle) -doFlameDamCrWall = destroyOnImpact - (doFlameDam 1) - (\_ p -> damageWall (Damage Flaming 19 p p p NoDamageEffect)) +damageBothTypeAmount dt amount = destroyOnImpact + (\pt p cr -> creatures . ix (_crID cr) . crState . crDamage .:~ thedam pt p) + (\pt p -> damageWall (thedam pt p)) + where + thedam pt p = Damage dt amount sp p ep NoDamageEffect + where + sp = _ptPos pt + ep = sp +.+ _ptVel pt + +drawStaticBall + :: Particle + -> Picture +drawStaticBall pt = setLayer 1 + . setDepth (_ptZ pt + 20) + . uncurryV translate (_ptPos pt) + $ circleSolidCol (_ptColor pt) (withAlpha 0.5 white) (_ptWidth pt+5) drawFlameletZ :: Float -- ^ Rotation @@ -188,30 +243,33 @@ drawFlameletZ rot pt = pictures s1 = (*) 2 $ log $ 2 + fromIntegral time / 40 s2 = 0.5 * (sc + s1) +moveStaticBall :: World -> Particle -> (World, Maybe Particle) +moveStaticBall = moveFlamelet {- Update of a flamelet. Applies movement and attaches damage to nearby creatures. -} -- This should be unified in many ways with moveFlame moveFlamelet :: World -> Particle -> (World, Maybe Particle) moveFlamelet w pt | _ptTimer pt <= 0 = ( w, Nothing) - | otherwise = (flameFlicker pt $ damwls damcrs, mvPt) + | otherwise = (ptFlicker pt $ damageInRadius 5 mvPt w, Just mvPt) where - sp = _ptPos pt - vel = _ptVel pt - ep = sp +.+ vel - size = _ptWidth pt - mvPt = Just $ pt & ptTimer -~ 1 - & ptPos .~ ep - & ptCrIgnore .~ Nothing - & ptVel .~ 0.8 *.* vel - damcrs = w & creatures %~ IM.map damifclose - damwls w' = foldr (\wl -> fst . hiteff [(ep,Right wl)]) w' $ IM.filter closeWls $ wallsNearPoint ep w - closeWls wl = uncurry circOnSeg (_wlLine wl) ep 5 + mvPt = pt + & ptTimer -~ 1 + & ptPos .~ _ptPos pt +.+ _ptVel pt + & ptCrIgnore .~ Nothing + & ptVel .*.*~ 0.8 + +-- | Note damgeInRadius by itself never destroys the particle +damageInRadius :: Float -> Particle -> World -> World +damageInRadius size pt w = damwls damcrs + where + p = _ptPos pt + damcrs = foldr (\cr -> fst . hiteff [(p,Left cr)]) w $ IM.filter isClose $ _creatures w + damwls w' = foldr (\wl -> fst . hiteff [(p,Right wl)]) w' $ IM.filter closeWls $ wallsNearPoint p w + closeWls wl = uncurry circOnSeg (_wlLine wl) p size + isClose cr = dist p (_crPos cr) < _crRad cr + size hiteff = _ptHitEff pt pt - isClose cr = dist ep (_crPos cr) < _crRad cr + size - damifclose cr - | isClose cr = cr & crState . crDamage .:~ Damage Flaming 3 sp ep ep NoDamageEffect - | otherwise = cr + -- | At writing the radius is half the size of the effect area makeGasCloud :: Point2 -- ^ Position diff --git a/src/Geometry/Vector.hs b/src/Geometry/Vector.hs index 41bbe3eab..5ee0b24f4 100644 --- a/src/Geometry/Vector.hs +++ b/src/Geometry/Vector.hs @@ -161,3 +161,7 @@ xV2 (V2 x _) = x yV2 :: Point2 -> Float {-# INLINE yV2 #-} yV2 (V2 _ y) = y + +xyzV4 :: V4 a -> V3 a +{-# INLINE xyzV4 #-} +xyzV4 (V4 x y z _) = V3 x y z diff --git a/src/Geometry/Vector3D.hs b/src/Geometry/Vector3D.hs index 5e5e3bd42..60ce2b5fb 100644 --- a/src/Geometry/Vector3D.hs +++ b/src/Geometry/Vector3D.hs @@ -89,6 +89,10 @@ addZ :: Float -> Point2 -> Point3 {-# INLINE addZ #-} addZ z (V2 x y) = V3 x y z +v2z :: Point2 -> Float -> Point3 +{-# INLINE v2z #-} +v2z (V2 x y) z = V3 x y z + stripZ :: Point3 -> Point2 {-# INLINE stripZ #-} stripZ (V3 x y _) = V2 x y