{- | Definitions of weapons. In progress: move out effects into other modules. -} module Dodge.Item.Weapon where import Dodge.Data import Dodge.Data.SoundOrigin import Dodge.Base import Dodge.Zone import Dodge.Picture.Layer import Dodge.SoundLogic import Dodge.SoundLogic.LoadSound import Dodge.Creature.Action import Dodge.WorldEvent import Dodge.Default --import Dodge.Default.Shell import Dodge.Item.Draw --import Dodge.Particle.Bullet.HitEffect import Dodge.Item.Data import Dodge.Item.Weapon.InventoryDisplay import Dodge.Item.Weapon.TriggerType import Dodge.Item.Weapon.ExtraEffect import Dodge.Item.Weapon.UseEffect import Dodge.Item.Weapon.Laser import Dodge.Item.Weapon.Bullet import Dodge.Item.Weapon.AmmoParams import Dodge.Item.Weapon.Remote import Dodge.Item.Attachment.Data import Dodge.Default.Weapon import Dodge.Item.Weapon.Booster import Dodge.Item.Weapon.Grenade --import Dodge.Item.Attachment.Data import Dodge.Item.Attachment import Geometry import Geometry.Vector3D import Picture import qualified IntMapHelp as IM import Shape import ShapePicture --import Data.List --import Data.Char import Data.Maybe import Data.Function --import qualified Data.ByteString as B import qualified Data.Sequence as Seq import Control.Lens --import Control.Monad import System.Random pistol :: Item pistol = defaultGun { _itName = "PISTOL" , _itIdentity = Pistol , _wpMaxAmmo = 15 , _wpLoadedAmmo = 15 , _wpReloadTime = 40 , _wpReloadState = 0 , _itUseRate = 8 , _itUseTime = 0 , _itUse = useAmmoParams , _itUseModifiers = [ ammoCheckI , hammerCheckI , useTimeCheckI , withSoundStart tap3S , useAmmo 1 , randSpreadDir , withMuzFlareI ] , _itLeftClickUse = Nothing , _wpSpread = 0.02 , _wpRange = 20 , _itHammer = HammerUp , _itFloorPict = pistolPic , _itAmount = 1 , _itMaxStack = 1 , _itAimingSpeed = 1 , _itAimingRange = 0 , _itZoom = defaultItZoom , _itEquipPict = pictureWeaponAim pistolPic , _itAttachment = NoItAttachment , _itID = Nothing , _itEffect = wpRecock , _itInvDisplay = basicWeaponDisplay , _itInvColor = white , _itTargeting = Nothing , _itWorldTrigger = Nothing , _wpAmmo = basicBullet } pistolPic :: Item -> SPic pistolPic it = ( colorSH green (prismPoly (map (addZ 3) $ rectNESW 3 3 (-3) (-3)) (map (addZ 0) $ rectNESW 5 3 (-5) (-7)) ) <> translateSH (V3 (-4) 5.5 4) (rotateSH pi $ bulletClip am) , mempty ) where am = loadedAmmo it bulletClip :: Int -> Shape bulletClip x = rotateSHx (negate $ pi/4) . upperPrismPoly 2 $ rectNESW 3 0 (3 - 0.25 * am) (-5) where am = fromIntegral x effectGun :: String -> (Creature -> World -> World) -> Item effectGun name eff = defaultGun { _itName = name ++ "Gun" , _itUse = const eff } autoEffectGun :: String -> (Creature -> World -> World) -> Item autoEffectGun name eff = defaultAutoGun { _itName = name ++ "Gun" , _itUse = const eff } rezGun :: Item rezGun = defaultGun { _itName = "REANIMATOR" , _wpMaxAmmo = 50 , _wpLoadedAmmo = 50 , _wpReloadTime = 80 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = const aTeslaArc , _itUseModifiers = [] , _wpSpread = 0.001 , _wpRange = 20 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ color chartreuse $ pictures [polygon $ rectNESW 7 3 1 (-3) ,polygon $ rectNESW (-1) 3 (-7) (-3) ] , _itAimingSpeed = 0.4 , _itAimingRange = 0 , _itEquipPict = pictureWeaponOnAim $ \_ -> (,) emptySH $ color chartreuse $ pictures [polygon $ rectNESW 7 3 1 (-3) ,polygon $ rectNESW (-1) 3 (-7) (-3) ] } teslaGun :: Item teslaGun = defaultGun { _itName = "TESLA" , _itIdentity = TeslaGun , _wpMaxAmmo = 200 , _wpLoadedAmmo = 200 , _wpReloadTime = 80 , _wpReloadState = 0 , _itUseRate = 0 , _itAimStance = TwoHandFlat , _itUseTime = 0 , _itUse = const aTeslaArc , _itUseModifiers = [ ammoCheckI , useTimeCheckI , withTempLight 1 100 (V3 0 0 1) , withSoundForI elecCrackleS 1 , useAmmo 1 ] , _wpSpread = 0.001 , _wpRange = 20 , _itFloorPict = teslaGunPic , _itAimingSpeed = 0.4 , _itZoom = defaultItZoom , _itAimingRange = 0 , _itEquipPict = pictureWeaponOnAim teslaGunPic } teslaGunPic :: Item -> SPic teslaGunPic _ = ( colorSH blue $ upperPrismPoly 5 (rectNESW xb y xa (-y)) ++ upperPrismPoly 5 (rectNESW (-xa) y (-xb) (-y)) , mempty ) where xa = 1 xb = 9 y = 4 lasGun :: Item lasGun = defaultAutoGun { _itName = "LASGUN ////" , _itIdentity = LasGun , _wpMaxAmmo = 200 , _wpLoadedAmmo = 200 , _wpReloadTime = 80 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = const aLaser , _itUseModifiers = [ ammoCheckI , useTimeCheckI , withTempLight 1 100 (V3 1 1 0) , withSoundForI tone440sawtoothS 1 , useAmmo 1 ] , _wpSpread = 0.001 , _wpRange = 20 , _itFloorPict = lasGunPic , _itAimingSpeed = 0.4 , _itAimingRange = 1 , _itEquipPict = pictureWeaponOnAim lasGunPic , _itAttachment = ItCharMode $ Seq.fromList "/VZ" , _itScroll = scrollCharMode , _itInvDisplay = basicWeaponDisplay } lasGunPic :: Item -> SPic lasGunPic it = ( colorSH blue $ upperPrismPoly 4 (rectNESW 3 15 1 (-15)) <> upperPrismPoly 4 (rectNESW (-1) 15 (-3) (-15)) -- <> (upperPrismPoly 4 $ rectNESW (5) l (-5) (l-2)) <> upperPrismPoly 1 (rectNESW 3 15 (-3) (-15)) , setLayer 1 . color col . setDepth 1.1 . polygon $ rectNESW 1 15 (-1) (-15) ) where amFrac = fractionLoadedAmmo it col = brightX 2 1.5 $ mixColors amFrac (1-amFrac) green red forceFieldGun :: Item forceFieldGun = defaultGun { _itName = "FORCEFIELD" , _itIdentity = ForceFieldGun , _wpMaxAmmo = 100 , _wpLoadedAmmo = 100 , _wpReloadTime = 40 , _wpReloadState = 0 , _itUseRate = 10 , _itUseTime = 0 , _itUse = undefined , _wpSpread = 0.02 , _wpRange = 20 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-4,-4),(-4,4),(4,4),(4,0),(0,0),(0,-4)] , _itAimingSpeed = 0.4 , _itAimingRange = 0.5 } -- grapGun = defaultGun -- { _itName = "grapGun" -- , _itIdentity = GrapGun -- , _wpMaxAmmo = 1 -- , _wpLoadedAmmo = 1 -- , _wpReloadTime = 40 -- , _wpReloadState = 0 -- , _itUseRate = 10 -- , _itUseTime = 0 -- , _itUse = grapFire -- , _wpSpread = 0.002 -- , _wpRange = 20 -- , _wpIsAuto = False -- , _itFloorPict = onLayer FlItLayer $ polygon [(-4,-4),(-4,4),(4,4),(4,0),(0,0),(0,-4)] -- , _itAmount = 1 -- , _itMaxStack = 1 -- , _itAimingSpeed = 1 -- , _itAimingRange = 0.5 -- } tractorGun :: Item tractorGun = defaultAutoGun { _itName = "TRACTORGUN" , _itIdentity = TractorGun , _wpMaxAmmo = 10000 , _wpLoadedAmmo = 10000 , _wpReloadTime = 40 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = aTractorBeam , _itUseModifiers = [ ammoUseCheckI ] , _wpSpread = 0.00001 , _wpRange = 20 , _itFloorPict = tractorGunSPic , _itAimingSpeed = 0.4 , _itAimingRange = 0.5 , _itEquipPict = pictureWeaponOnAim tractorGunSPic } -- TODO own picture for tractor gun tractorGunSPic :: Item -> SPic tractorGunSPic = lasGunPic removeItAttachment :: Int -> Int -> World -> World removeItAttachment i _ w = w & creatures . ix i . crInv . ix itRef . itAttachment .~ NoItAttachment where cr = _creatures w IM.! i itRef = _crInvSel cr hvAutoGun :: Item hvAutoGun = defaultAutoGun { _itName = "AUTO-HV" , _itIdentity = HvAutoGun , _wpMaxAmmo = 100 , _wpLoadedAmmo = 100 , _wpReloadTime = 200 , _wpReloadState = 0 , _itUseRate = 25 , _itUseTime = 0 , _itUse = useAmmoParams , _itUseModifiers = [ ammoCheckI , rateIncABI 24 7 (torqueBeforeAtLeast 0.1 0.1) (torqueAfterI 0.2) , withSoundStart bangEchoS , withThinSmokeI , withMuzFlareI ] , _wpRange = 20 , _itFloorPict = hvAutoGunPic , _itAimingSpeed = 0.2 , _itAimingRange = 1 , _itEquipPict = pictureWeaponOnAim hvAutoGunPic , _wpAmmo = hvBullet } hvAutoGunPic :: Item -> SPic hvAutoGunPic it = ( colorSH orange (prismPoly (map (addZ 5) $ rectNESW 3 12 (-3) (-8)) (map (addZ 0) $ rectNESW 5 12 (-5) (-12)) ) <> bulletClip am , mempty ) where am = loadedAmmo it ltAutoGun :: Item ltAutoGun = defaultAutoGun { _itName = "AUTO-LT" , _itIdentity = LtAutoGun , _wpMaxAmmo = 25 , _wpLoadedAmmo = 25 , _wpReloadTime = 80 , _wpReloadState = 0 , _itUseRate = 3 , _itUseTime = 0 , _itUse = useAmmoParams , _itUseModifiers = [ ammoCheckI , useTimeCheckI , withSoundStart tap1S , useAmmo 1 , withRandomDirI 0.1 , torqueAfterI 0.2 , withSidePushI 50 , modClock 2 withMuzFlareI ] , _wpSpread = 0.5 , _wpRange = 20 , _itFloorPict = ltAutoGunPic , _itAimingSpeed = 1 , _itAimingRange = 0 , _itZoom = defaultItZoom , _itEquipPict = pictureWeaponOnAim ltAutoGunPic , _itAimStance = OneHand , _wpAmmo = ltBullet } ltAutoGunPic :: Item -> SPic ltAutoGunPic it = ( colorSH green $ prismPoly (map (addZ 5) $ mirrorXAxis [V2 (-2) 3, V2 5 3, V2 10 0]) (map (addZ 0) $ mirrorXAxis [V2 (-5) 5, V2 5 5, V2 10 0]) <> bulletClip am , mempty ) where am = loadedAmmo it miniGun :: Item miniGun = defaultAutoGun { _itName = "MINI-G" , _itIdentity = MiniGun , _wpMaxAmmo = 1500 , _wpLoadedAmmo = 1500 , _wpReloadTime = 200 , _wpReloadState = 0 , _wpMaxWarmUp = 100 , _itUseRate = 0 , _itUseTime = 0 , _itUse = useAmmoParamsVelMod vm4 , _itUseModifiers = [ ammoCheckI , withWarmUpI crankSlowS , useTimeCheckI --, afterRecoil recoilAmount , withSoundForI mini1S 2 --, withThinSmokeI , torqueAfterI 0.05 , withSidePushI 53 , afterRecoil recoilAmount , withRandomOffsetI 12 , trigDoAlso (useAmmoParamsVelMod vm1) --, torqueBeforeForcedI 0.001 , withSidePushI 52 , afterRecoil recoilAmount , withRandomOffsetI 11 , withOldDir od1 , trigDoAlso (useAmmoParamsVelMod vm2) --, torqueBeforeForcedI 0.001 , withSidePushI 51 , afterRecoil recoilAmount , withRandomOffsetI 10 , withOldDir od2 , trigDoAlso (useAmmoParamsVelMod vm3) , useAmmo 4 --, torqueBeforeForcedI 0.001 , withSidePushI 50 , afterRecoil recoilAmount , withRandomOffsetI 9 , withMuzFlareI , withOldDir od3 , withSmoke 1 black 20 200 5 ] , _wpRange = 20 , _itFloorPict = miniGunPictItem , _itAimingSpeed = 0.4 , _itAimingRange = 1 , _itEquipPict = pictureWeaponAim miniGunPictItem , _wpAmmo = basicBullet } where recoilAmount = 5 [vm1,vm2,vm3,vm4] = [ 0.25 , 0.5 , 0.75 , 1 ] [od1,od2,od3] = [ 0.25, 0.5, 0.9] --[ 0.75, 0.5, 0.25] --[ 0, 0, 0] miniGunPictItem :: Item -> SPic miniGunPictItem it = miniGunPict spin (loadedAmmo it) where spin = (-10) * _wpLoadedAmmo it + _wpCurWarmUp it miniGunPict :: Int -> Int -> SPic miniGunPict spin am = ( colorSH red (rotateSHx a barrels) <> colorSH red (upperPrismPoly 5 $ rectNESW 8 8 (-8) 4) <> colorSH red (prismPoly (map (addZ 5) $ rectNESW 2 12 (-2) (-12)) (map (addZ 0) $ rectNESW 4 12 (-4) (-16)) ) <> clip (-1) 0 <> clip (-7) 0 , mempty ) where aBarrel = translateSH (V3 7 0 9) $ prismPoly (map (addZ 5) $ rectNESW 2 12 (-2) (-1)) (map (addZ 0) $ rectNESW 4 12 (-4) (-5)) barrels = concatMap (\an -> aBarrel & rotateSHx an) [0,0.5* pi, pi, 1.5*pi] a = fromIntegral spin / 100 x = fromIntegral am / 10 clip y z = translateSH (V3 0 (-1) z) (rotateSHx (negate $ pi/4) . upperPrismPoly 2 $ rectNESW (negate $ 3 + 0.25 * x) y 3 (y-5)) spreadGun :: Item spreadGun = defaultGun { _itName = "SPREAD" , _itIdentity = SpreadGun , _wpMaxAmmo = 5 , _wpLoadedAmmo = 5 , _wpReloadTime = 80 , _wpReloadState = 0 , _itUseRate = 20 , _itUseTime = 0 --, _itUse = \_ -> spreadNumVelWthHiteff spreadGunSpread 9 (V2 30 0) 2 basicBulletEffect , _itUse = useAmmoParams , _itUseModifiers = [ ammoCheckI , hammerCheckI , useTimeCheckI , withSoundStart shotgunS , useAmmo 1 , withRecoilI 100 , withMuzFlareI , spreadNumI ] , _wpSpread = spreadGunSpread , _wpRange = 20 , _itFloorPict = spreadGunPic , _itAimingSpeed = 1 , _itAimingRange = 0 , _itEquipPict = pictureWeaponOnAim spreadGunPic , _wpAmmo = basicBullet , _wpNumBarrels = 50 } spreadGunPic :: Item -> SPic spreadGunPic it = ( colorSH green (upperPrismPoly 5 $ map toV2[(-3,0),(3,6),(3,-6)]) <> bulletClip am , mempty ) where am = loadedAmmo it multGun :: Item multGun = defaultGun { _itName = "MULTGUN" , _itIdentity = MultGun , _wpMaxAmmo = 2 , _wpLoadedAmmo = 2 , _wpReloadTime = 40 , _wpReloadState = 0 , _itUseRate = 20 , _itUseTime = 0 , _itUse = useAmmoParams , _itUseModifiers = [ ammoCheckI , hammerCheckI , useTimeCheckI , withSoundStart shotgunS , useAmmo 1 , withRecoilI 200 , withMuzFlareI , numI ] , _wpSpread = spreadGunSpread , _wpRange = 20 , _itFloorPict = multGunSPic , _itAimingSpeed = 0.4 , _itAimingRange = 1 , _itZoom = defaultItZoom , _itAimZoom = defaultItZoom {_itZoomFac = 1.5} , _itEquipPict = pictureWeaponOnAim multGunSPic , _itAimStance = TwoHandTwist , _wpAmmo = basicBullet , _wpNumBarrels = 5 } multGunSPic :: Item -> SPic multGunSPic it = ( colorSH red ( upperPrismPoly 2 (rectNESW 4 8 (-4) (-12)) <> translateSHf 8 0 (concatMap barrel [12,7,2,-3,-8] <> upperPrismPoly 2 (map toV2 [(-1.5,12),(-2,12),(-2,-12),(-1.5,-12)]) ) ) <> bulletClip am , mempty ) where barrel y = prismPoly (map (addZ 5) $ rectNSEW y (y-4) 2 0 ) (map (addZ 0) $ rectNSEW y (y-4) 2 (-2)) am = loadedAmmo it longGun :: Item longGun = defaultGun { _itName = "LONGGUN" , _itIdentity = LongGun , _wpMaxAmmo = 1 , _wpLoadedAmmo = 1 , _wpReloadTime = 100 , _wpReloadState = 0 , _wpReloadType = PassiveReload skwareFadeTwoSecS , _itUseRate = 100 , _itUseTime = 0 , _itUse = useAmmoParams , _itUseModifiers = [ ammoCheckI , hammerCheckI , useTimeCheckI , withSoundStart bangEchoS , useAmmo 1 , withThickSmokeI , torqueAfterI 0.05 , withMuzFlareI ] , _wpRange = 200 , _itFloorPict = longGunSPic , _itAimingSpeed = 0.2 , _itAimingRange = 1 , _itZoom = defaultItZoom , _itAimZoom = defaultItZoom {_itZoomMax = 0.5, _itZoomMin = 0.5} , _itEquipPict = pictureWeaponOnAim longGunSPic , _itScroll = zoomLongGun , _itAttachment = ItScope (V2 0 0) 0 1 False , _itEffect = itemLaserScopeEffect , _wpAmmo = hvBullet , _itAimStance = TwoHandTwist } longGunSPic :: Item -> SPic longGunSPic _ = ( colorSH orange $ upperPrismPoly 5 $ rectWH 12 2 , mempty ) zoomLongGun :: Float -> Creature -> Item -> Item zoomLongGun x _ | x > 0 = startZoomInLongGun | x < 0 = startZoomOutLongGun | otherwise = id startZoomInLongGun :: Item -> Item startZoomInLongGun = itAttachment . scopeZoomChange %~ \x -> max 5 (x + 5) startZoomOutLongGun :: Item -> Item startZoomOutLongGun = itAttachment . scopeZoomChange %~ \x -> min (-5) (x-5) poisonSprayer :: Item poisonSprayer = defaultAutoGun { _itName = "POISON" , _itIdentity = PoisonSprayer , _wpMaxAmmo = 500 , _wpLoadedAmmo = 500 , _wpReloadTime = 100 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = const aGasCloud , _itUseModifiers = [ ammoCheckI , useTimeCheckI , withSoundForI foamSprayLoopS 5 , useAmmo 1 , spreadNumI ] , _wpRange = 8 , _itFloorPict = flamerPic , _itAimingSpeed = 0.2 , _itAimingRange = 0 , _itZoom = defaultItZoom , _itEquipPict = pictureWeaponOnAim flamerPic , _wpSpread = 0.1 , _wpNumBarrels = 3 } flamer :: Item flamer = defaultAutoGun { _itName = "FLAMER" , _itIdentity = Flamethrower , _wpMaxAmmo = 250 , _wpLoadedAmmo = 250 , _wpReloadTime = 100 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = \_ -> randWalkAngle 0.2 0.01 aFlame , _itUseModifiers = [ ammoUseCheckI , withSidePushI 5 --, withTempLight 1 100 (V3 1 0 0) , withSidePushAfterI 20 ] , _wpSpread = 0 , _wpRange = 8 , _itFloorPict = flamerPic , _itAimingSpeed = 0.5 , _itAimingRange = 0 , _itZoom = defaultItZoom , _itAimZoom = defaultItZoom {_itZoomMax = 5, _itZoomMin = 1.5} , _itEquipPict = pictureWeaponOnAim flamerPic , _itAttachment = NoItAttachment } flamerPic :: Item -> SPic flamerPic it = ( colorSH yellow $ translateSHf tx ty (upperPrismPoly tz $ polyCirc 3 r) ++ upperPrismPoly 5 (rectNESW 2 9 (-2) (-9)) , color black $ translate3 (V3 tx ty (tz+0.01)) $ circleSolid (r * am) ) where tx = - 5 ty = - 6 tz = 3 r = 5 am = fractionLoadedAmmo2 it blinkGun :: Item blinkGun = defaultGun { _itName = "BLINKER" , _itIdentity = Blinker , _wpMaxAmmo = 100 , _wpLoadedAmmo = 100 , _wpReloadTime = 20 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = const aSelf , _itUseModifiers = [ ammoCheckI , hammerCheckI ] , _itLeftClickUse = Just $ hammerCheckL $ shootL aSelfL , _wpSpread = 0.05 , _wpRange = 20 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2[(-2,-2),(-2,2),(2,2),(2,0),(0,0),(0,-2)] , _itAimingSpeed = 1 , _itAimingRange = 0 } boosterGun :: Item boosterGun = defaultGun { _itName = "BOOSTER" , _itIdentity = Blinker , _wpMaxAmmo = 100 , _wpLoadedAmmo = 100 , _wpReloadTime = 20 , _wpReloadState = 0 , _itUseRate = 0 , _itUseTime = 0 , _itUse = \_ -> boostSelf 10 , _itLeftClickUse = Just $ boostSelfL 10 , _wpSpread = 0.05 , _wpRange = 20 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-2,-2),(-2,2),(2,2),(2,0),(0,0),(0,-2)] , _itAimingSpeed = 1 , _itAimingRange = 0 , _itEffect = resetAttachmentID } aTeslaArc :: Creature -> World -> World aTeslaArc cr w = teslaGunFlashAt (pos +.+ 5 *.* unitVectorAtAngle dir) $ set randGen g w & particles %~ (makeTeslaArcAt col pos dir :) where pos = _crPos cr +.+ (_crRad cr +1) *.* unitVectorAtAngle dir dir = _crDir cr (colid,g) = randomR (0::Int,3) $ _randGen w col = chooseColor colid chooseColor 0 = white chooseColor 1 = azure chooseColor 2 = blue chooseColor _ = cyan shellExplosionAt :: Point2 -> World -> World shellExplosionAt = makeExplosionAt aGasCloud :: Creature -> World -> World aGasCloud cr w = insertCloud $ set randGen g w where (a,g) = randomR (-0.1,0.1) (_randGen w) dir = _crDir cr + a pos = _crPos cr +.+ ((_crRad cr + 2.9) *.* unitVectorAtAngle (_crDir cr)) vel = (_crPos cr -.- _crOldPos cr) +.+ 10 *.* unitVectorAtAngle dir insertCloud = makeGasCloud pos vel aFlame :: Creature -> World -> World aFlame cr w = w & particles %~ (aFlameParticle t pos vel (Just cid) :) where (t,_) = randomR (99,101) (_randGen w) cid = _crID cr dir = _crDir cr pos = _crPos cr +.+ ((_crRad cr + 2.9) *.* unitVectorAtAngle (_crDir cr)) vel = (_crPos cr -.- _crOldPos cr) +.+ 4 *.* unitVectorAtAngle dir aSelf :: Creature -> World -> World aSelf = blinkAction aSelfL :: Creature -> Int -> World -> World aSelfL cr _ = blinkAction cr reflect :: Float -> Float -> Float reflect a b = a + 2*(a-b) retireRemoteBomb :: Int -> Int -> Int -> World -> World retireRemoteBomb itid 0 pjid w = w & pointToItem (_itemPositions w IM.! itid) %~ ( (itAttachment . scopePos .~ V2 0 0) . (itZoom .~ defaultItZoom) . (itUse .~ const throwRemoteBomb) ) & props %~ IM.delete pjid retireRemoteBomb itid t pjid w = setScope w & props . ix pjid . pjUpdate .~ (\_ -> retireRemoteBomb itid (t-1) pjid) where setScope w' = case _itemPositions w' IM.! itid of InInv cid invid -> w' & creatures . ix cid . crInv . ix invid . itAttachment . scopePos .~ (pos -.- _crPos (_creatures w' IM.! cid)) _ -> w' pos = fromMaybe (V2 0 0) $ w ^? props . ix pjid . pjPos moveRemoteBomb :: Int -> Int -> Int -> World -> World moveRemoteBomb itid time pID w | time < -4 = updatePicture $ set (props .ix pID.pjUpdate) (\_ -> moveRemoteBomb itid (f time) pID) w | time < 2 = case hitWl of Just (p,_) -> soundStart (Tap 0) p tapQuietS Nothing $ halfV updatedWorld _ -> halfV updatedWorld | otherwise = case hitWl of Just (p,_) -> soundStart (Tap 0) p tapQuietS Nothing updatedWorld _ -> updatedWorld where updatedWorld = updateV $ set (props . ix pID . pjPos) finalPos $ updatePicture $ set (props .ix pID.pjUpdate) (\_ -> moveRemoteBomb itid (time-1) pID) w pj = _props w IM.! pID oldPos = _pjPos pj newPos = _pjVel pj +.+ oldPos -- this is hacky, should use a version of collidePointWalls' that collides -- circles and walls invShift x = x -.- 5 *.* normalizeV (_pjVel pj) hitWl = collideCircWalls oldPos newPos 4 $ wallsNearPoint newPos w finalPos = maybe newPos (invShift . fst) hitWl setV v = set (props . ix pID . pjVel) v updateV = maybe id (setV . snd) hitWl halfV = props . ix pID . pjVel %~ (0.5 *.*) f x | x < -369 = -10 | otherwise = x - 1 updatePicture = set (props . ix pID . prDraw) (\_ -> (,) mempty $ onLayer PtLayer $ uncurryV translate newPos $ remoteBombPic time) . lowLightDirected (withAlpha 0.1 red) newPos (50 *.* unitVectorAtAngle (negate $ degToRad (10 * fromIntegral time))) [-0.2,-0.15,-0.1,-0.05,0,0.05,0.1,0.15,0.2] setRemoteBombScope :: Int -> Prop -> World -> World setRemoteBombScope itid pj w' = case _itemPositions w' IM.! itid of InInv cid invid -> w' & creatures . ix cid . crInv . ix invid . itAttachment . scopePos .~ (_pjPos pj -.- _crPos (_creatures w' IM.! cid)) & creatures . ix cid . crInv . ix invid . itAimZoom .~ (defaultItZoom {_itZoomMax = 0.5, _itZoomMin = 0.5}) _ -> w' grenade :: Item grenade = Throwable { _itName = "GRENADE " ++ show fuseTime , _itIdentity = Grenade , _itMaxStack = 8 , _itAmount = 1 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)] , _twMaxRange = 150 , _twAccuracy = 30 , _itUse = \_ -> throwGrenade makeExplosionAt , _itUseModifiers = [ useTimeCheckI ] , _itAimingSpeed = 1 , _itAimingRange = 0 , _itZoom = defaultItZoom , _itAimZoom = defaultItZoom {_itZoomMax = f fuseTime, _itZoomMin = f fuseTime} , _itEquipPict = pictureWeaponOnAim $ \_ -> grenadePic fuseTime , _itID = Nothing , _itUseRate = 25 , _itUseTime = 0 , _itAttachment = ItFuse fuseTime , _itInvColor = white , _itInvDisplay = basicWeaponDisplay , _itEffect = wpRecock , _itHammer = HammerUp , _itScroll = changeFuse , _itAimStance = OneHand } where fuseTime = 50 f x = 50 / fromIntegral x flameGrenade :: Item flameGrenade = grenade { _itName = "FLMGREN " ++ show fuseTime , _itUse = \_ -> throwGrenade makeFlameExplosionAt } where fuseTime = 50 :: Int --f x = 50 / fromIntegral x teslaGrenade :: Item teslaGrenade = grenade { _itName = "TLSGREN " ++ show fuseTime , _itUse = \_ -> throwGrenade makeTeslaExplosionAt } where fuseTime = 50 :: Int --f x = 50 / fromIntegral x defaultThrowable :: Item defaultThrowable = grenade remoteBomb :: Item remoteBomb = defaultThrowable { _itName = "REMOTEBOMB" , _itIdentity = RemoteBomb , _itMaxStack = 1 , _itAmount = 1 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)] , _twMaxRange = 150 , _twAccuracy = 30 , _itUse = const throwRemoteBomb , _itUseModifiers = [ hammerCheckI ] , _itAttachment = ItScope (V2 0 0) 0 1 True , _itEquipPict = pictureWeaponOnAim $ \_ -> (,) emptySH remoteBombUnarmedPic } throwRemoteBomb :: Creature -> World -> World throwRemoteBomb cr w = setLocation $ removePict $ resetFire $ resetName $ over props addG w where cid = _crID cr addG = IM.insert i $ Projectile { _pjPos = p , _pjStartPos = p , _pjVel = v , _prDraw = const mempty , _pjID = i , _pjUpdate = \_ -> moveRemoteBomb itid 50 i } i = newProjectileKey w v' = 0.02 / _cameraZoom w *.* rotateV (_cameraRot w) ( _mousePos w) v | magV v' > 6 = 6 *.* normalizeV v' | otherwise = v' j = _crInvSel cr resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTE" removePict = set (creatures . ix cid . crInv . ix j . itEquipPict) $ \ _ _ -> emptyBlank resetFire = set (creatures . ix cid . crInv . ix j . itUse) $ \_ -> explodeRemoteBomb itid i p' = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr) 0) p | circOnSomeWall p' 4 w = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr-4) 0) | otherwise = p' maybeitid = w ^? creatures . ix cid . crInv . ix j . itID . _Just setLocation :: World -> World setLocation w' = case maybeitid of Nothing -> w' & creatures . ix cid . crInv . ix j . itID ?~ newitid & itemPositions %~ IM.insert newitid (InInv cid j) _ -> w' newitid = IM.newKey $ _itemPositions w itid = fromMaybe newitid maybeitid explodeRemoteBomb :: Int -> Int -> Creature -> World -> World explodeRemoteBomb itid pjid cr w = set (props . ix pjid . pjUpdate) (\_ -> retireRemoteBomb itid 30 pjid) -- $ set (props . ix pjid . pjDraw) (\_ -> blank) $ set (creatures . ix cid . crInv . ix j . itUse) (\_ -> const id) $ resetName $ resetPict -- $ resetScope $ makeExplosionAt (_pjPos (_props w IM.! pjid)) w -- - $ makeShrapnelBombAt (_pjPos (_props w IM.! pjid)) w where cid = _crID cr resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTEBOMB" resetPict = set (creatures . ix cid . crInv . ix j . itEquipPict ) (pictureWeaponOnAim $ \_ -> (,) emptySH remoteBombUnarmedPic) -- resetScope = creatures . ix cid . crInv . ix j . itScope . _Just . scopePos .~ (0,0) j = _crInvSel $ _creatures w IM.! cid remoteBombPic :: Int -- ^ time -> Picture remoteBombPic _ = pictures [ color (dark $ dark orange) $ circleSolid 5 ] remoteBombUnarmedPic :: Picture remoteBombUnarmedPic = color (dark $ dark orange) $ circleSolid 5 spreadGunSpread,autogunSpread :: Float spreadGunSpread = 0.5 autogunSpread = 0.07 pipe :: Item pipe = Craftable { _itIdentity = Generic , _itName = "PIPE" , _itMaxStack = 3 , _itAmount = 3 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)] , _itEquipPict = \_ _ -> emptyBlank , _itID = Nothing , _itInvDisplay = _itName , _itInvColor = green , _itAimStance = LeaveHolstered } {- | Sends out pulses that display walls. -} radar :: Item radar = defaultGun { _itName = "RADAR" , _itIdentity = Generic , _wpMaxAmmo = 100 , _wpLoadedAmmo = 100 , _wpReloadTime = 200 , _wpReloadState = 0 , _itUseRate = 120 , _itUseTime = 0 , _itUse = const aRadarPulse , _itUseModifiers = [ ammoUseCheckI ] , _wpSpread = autogunSpread , _wpRange = 20 , _itHammer = HammerUp , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ color blue $ polygon $ rectNESW 5 5 (-5) (-5) , _itAmount = 1 , _itMaxStack = 1 , _itAimingRange = 1 , _itZoom = defaultItZoom { _itZoomMax = 1} , _itAimZoom = defaultItZoom { _itZoomMax = 1} , _itEquipPict = pictureWeaponOnAim $ \_ -> (,) emptySH $ color blue $ polygon $ rectNESW 5 5 (-5) (-5) } {- | Sends out pulses that display creatures. -} sonar :: Item sonar = defaultGun { _itName = "SONAR" , _itIdentity = Generic , _wpMaxAmmo = 100 , _wpLoadedAmmo = 100 , _wpReloadTime = 200 , _wpReloadState = 0 , _itUseRate = 120 , _itUseTime = 0 , _itUse = const aSonarPulse , _itUseModifiers = [ ammoUseCheckI ] , _wpRange = 20 , _itHammer = HammerUp , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ color blue $ polygon $ rectNESW 5 5 (-5) (-5) , _itAmount = 1 , _itMaxStack = 1 , _itAimingRange = 1 , _itZoom = defaultItZoom {_itZoomMax = 1} , _itAimZoom = defaultItZoom {_itZoomMax = 1} , _itEquipPict = pictureWeaponOnAim $ \_ -> (,) emptySH $ color blue $ polygon $ rectNESW 5 5 (-5) (-5) } {- | Automatically sends out pulses that display creatures. -} autoSonar :: Item autoSonar = defaultEquipment { _itIdentity = Generic , _itName = "AUTOSONAR" , _itMaxStack = 1 , _itAmount = 1 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ color yellow $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)] , _itEquipPict = \_ _ -> (,) emptySH blank , _itEffect = autoSonarEffect , _itID = Nothing , _itAimingSpeed = 1 , _itAimingRange = 0 , _itZoom = defaultItZoom {_itZoomMax = 1} , _itAimZoom = defaultItZoom {_itZoomMax = 1} } {- | Automatically sends out pulses that display walls. -} autoRadar :: Item autoRadar = defaultEquipment { _itIdentity = Generic , _itName = "AUTORADAR" , _itMaxStack = 1 , _itAmount = 1 , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ color yellow $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)] , _itEquipPict = \_ _ -> (,) emptySH blank , _itEffect = autoRadarEffect , _itID = Nothing , _itAimingSpeed = 1 , _itAimingRange = 0 , _itZoom = defaultItZoom {_itZoomMax = 1} , _itAimZoom = defaultItZoom {_itZoomMax = 1} } {- | Creates a creature next to the creature using the item. -} spawnGun :: Creature -> Item spawnGun cr = defaultGun { _itName = "SPAWNER" , _wpMaxAmmo = 1 , _wpLoadedAmmo = 1 , _wpReloadTime = 80 , _wpReloadState = 0 , _itUseRate = 100 , _itUse = \_ -> spawnCrNextTo cr , _itUseModifiers = [ ammoCheckI , hammerCheckI ] } spawnCrNextTo :: Creature -- ^ Creature to spawn -> Creature -- ^ existing creature that will be spawned next to -> World -> World spawnCrNextTo cr sCr w = w & creatures %~ IM.insert k newCr where k = IM.newKey $ _creatures w newCr = cr & crID .~ k & crPos .~ _crPos sCr +.+ unitVectorAtAngle (_crDir sCr) & crOldPos .~ _crPos sCr +.+ unitVectorAtAngle (_crDir sCr)