Add source files, commit before reverting pictures to lists
This commit is contained in:
+13
-35
@@ -6,9 +6,6 @@ import Shaders
|
|||||||
import Render
|
import Render
|
||||||
import Shapes
|
import Shapes
|
||||||
|
|
||||||
import Dodge.Rendering
|
|
||||||
import Dodge.RenderPicture
|
|
||||||
import Dodge.Picture
|
|
||||||
import Dodge.Prototypes
|
import Dodge.Prototypes
|
||||||
import Dodge.Data
|
import Dodge.Data
|
||||||
import Dodge.Initialisation
|
import Dodge.Initialisation
|
||||||
@@ -17,7 +14,11 @@ import Dodge.Layout
|
|||||||
import Dodge.LoadSound
|
import Dodge.LoadSound
|
||||||
import Dodge.Update
|
import Dodge.Update
|
||||||
import Dodge.KeyEvents
|
import Dodge.KeyEvents
|
||||||
import Dodge.Preload
|
import Dodge.Rendering
|
||||||
|
|
||||||
|
import Picture
|
||||||
|
import Picture.Render
|
||||||
|
import Picture.Preload
|
||||||
|
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
@@ -38,31 +39,6 @@ initWorld = initialWorld
|
|||||||
pps = [(0,0),(50,0),(0,25)]
|
pps = [(0,0),(50,0),(0,25)]
|
||||||
ppt = [(0,0),(-50,0),(20,25)]
|
ppt = [(0,0),(-50,0),(20,25)]
|
||||||
|
|
||||||
tSh = TextShape
|
|
||||||
[((0.0,0.0,0),(1,0,1,1),(s,1))
|
|
||||||
,((0.5,0.0,0),(1,1,1,1),(e,1))
|
|
||||||
,((0.5,1.0,0),(1,1,1,1),(e,0))
|
|
||||||
,((0.0,1.0,0),(1,1,1,1),(s,0))
|
|
||||||
]
|
|
||||||
where x = 1/128
|
|
||||||
s = 20 * x
|
|
||||||
e = s + x
|
|
||||||
tSh' = TShape'
|
|
||||||
[0.0,0.0,0,1,0,1,1,s,1
|
|
||||||
,0.5,0.0,0,1,1,1,1,e,1
|
|
||||||
,0.5,1.0,0,1,1,1,1,e,0
|
|
||||||
,0.0,1.0,0,1,1,1,1,s,0
|
|
||||||
] TriangleFan
|
|
||||||
where x = 1/128
|
|
||||||
s = 20 * x
|
|
||||||
e = s + x
|
|
||||||
tSha = TShape'
|
|
||||||
[0.0,0.0,0,1,1,1,1
|
|
||||||
,50 ,0.0,0,1,1,1,1
|
|
||||||
,50 ,50 ,0,1,1,1,1
|
|
||||||
,0.0,1.0,0,1,1,1,1
|
|
||||||
] TriangleFan
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
-- loadedSounds <- loadSounds
|
-- loadedSounds <- loadSounds
|
||||||
@@ -70,15 +46,17 @@ main = do
|
|||||||
"windowName"
|
"windowName"
|
||||||
800 600
|
800 600
|
||||||
doPreload'
|
doPreload'
|
||||||
|
cleanUpPreload
|
||||||
(initializeWorld $ generateFromTree lev1 $ initWorld)
|
(initializeWorld $ generateFromTree lev1 $ initWorld)
|
||||||
(-- \((bs:fs:ts:_),tes) w -> --do render setparams w egFade
|
(-- \((bs:fs:ts:_),tes) w -> --do render setparams w egFade
|
||||||
\preData w -> --do render setparams w egFade
|
\preData w -> --do render setparams w egFade
|
||||||
--renderPicture preData (draw blank w)
|
renderPicture' preData (draw blank w)
|
||||||
renderPicture' preData (pictures' [--scale' 0.5 0.9 $ polygon' [(0,0),(0,0.5),(0.5,0.5),(0.5,0.0)]
|
-- renderPicture' preData
|
||||||
scale' 0.0005 0.0005 $ text' "asdf"
|
-- (pictures [scale 0.5 0.9 $ polygon [(0,0),(0,-0.5),(-0.5,-0.5),(-0.5,0.0)]
|
||||||
]
|
-- ,scale 0.5 0.9 $ polygon [(0,0),(0,-0.5),(-0.5,-0.5),(-0.5,0.0)]
|
||||||
)
|
-- ,color red $ scale 0.0005 0.0005 $ text "asdf"
|
||||||
-- renderTex'' preData tSh
|
-- ]
|
||||||
|
-- )
|
||||||
)
|
)
|
||||||
handleEvent
|
handleEvent
|
||||||
(\w -> Just $ update w)
|
(\w -> Just $ update w)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ dependencies:
|
|||||||
- monad-loops
|
- monad-loops
|
||||||
- JuicyPixels
|
- JuicyPixels
|
||||||
- vector
|
- vector
|
||||||
|
- dlist
|
||||||
|
- deepseq
|
||||||
|
|
||||||
library:
|
library:
|
||||||
source-dirs: src
|
source-dirs: src
|
||||||
@@ -48,8 +50,15 @@ executables:
|
|||||||
source-dirs: app
|
source-dirs: app
|
||||||
ghc-options:
|
ghc-options:
|
||||||
- -threaded
|
- -threaded
|
||||||
|
- -O2
|
||||||
- -rtsopts
|
- -rtsopts
|
||||||
- -with-rtsopts=-N
|
- -with-rtsopts=-N
|
||||||
|
- -fno-liberate-case
|
||||||
|
- -fno-state-hack
|
||||||
|
- -funfolding-use-threshold1000
|
||||||
|
- -funfolding-keeness-factor1000
|
||||||
|
- -fllvm
|
||||||
|
- -optlo-O3
|
||||||
dependencies:
|
dependencies:
|
||||||
- loop
|
- loop
|
||||||
|
|
||||||
|
|||||||
+1966
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,740 @@
|
|||||||
|
--{-# LANGUAGE Strict #-}
|
||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
module Dodge.Base where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad.State
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Function
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Bifunctor
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.IntSet as IS
|
||||||
|
import qualified Data.Set as S
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
--
|
||||||
|
you :: World -> Creature
|
||||||
|
you w = _creatures w IM.! _yourID w
|
||||||
|
|
||||||
|
aCrPos :: Int -> World -> Point2
|
||||||
|
aCrPos i w = _crPos $ _creatures w IM.! i
|
||||||
|
|
||||||
|
yourItem :: World -> Item
|
||||||
|
yourItem w = _crInv (you w) IM.! _crInvSel (you w)
|
||||||
|
|
||||||
|
yourItemRef w = (creatures . ix (_yourID w) . crInv . ix (_crInvSel (you w)))
|
||||||
|
|
||||||
|
halfWidth,halfHeight :: World -> Float
|
||||||
|
halfWidth w = _windowX w / 2
|
||||||
|
halfHeight w = _windowY w / 2
|
||||||
|
|
||||||
|
hasLOS :: Point2 -> Point2 -> World -> Bool
|
||||||
|
{-# INLINE hasLOS #-}
|
||||||
|
hasLOS p1 p2 w = (not $ collidePointWallsSimple p1 p2 nearbyWalls)
|
||||||
|
&& (not $ collidePointSmoke p1 p2 nearbySmoke)
|
||||||
|
where nearbyWalls = wallsAlongLine p1 p2 w
|
||||||
|
nearbySmoke = _smoke w -- smokeAlongLine p1 p2 w
|
||||||
|
|
||||||
|
smokeLOS :: Point2 -> Point2 -> World -> Bool
|
||||||
|
smokeLOS p1 p2 w = not $ collidePointSmoke p1 p2 nearbySmoke
|
||||||
|
where nearbySmoke = _smoke w
|
||||||
|
|
||||||
|
hasLOSIndirect :: Point2 -> Point2 -> World -> Bool
|
||||||
|
hasLOSIndirect p1 p2 w = case collidePointIndirect p1 p2 $ wallsAlongLine p1 p2 w
|
||||||
|
of Just _ -> False
|
||||||
|
Nothing -> True && smokeLOS p1 p2 w
|
||||||
|
|
||||||
|
isWalkable :: Point2 -> Point2 -> World -> Bool
|
||||||
|
isWalkable p1 p2 w = not $ collidePointWalkable p1 p2 nearbyWalls
|
||||||
|
where nearbyWalls = wallsAlongLine p1 p2 w
|
||||||
|
|
||||||
|
canSee :: Int -> Int -> World -> Bool
|
||||||
|
canSee i j w = hasLOS p1 p2 w
|
||||||
|
--canSee i j w = not $ collidePointWallsSimple p1 p2
|
||||||
|
-- nearbyWalls
|
||||||
|
where p1 = _crPos (_creatures w IM.! i)
|
||||||
|
p2 = _crPos (_creatures w IM.! j)
|
||||||
|
nearbyWalls = wallsAlongLine p1 p2 w
|
||||||
|
|
||||||
|
canSeePoint :: Int -> Point2 -> World -> Bool
|
||||||
|
canSeePoint i p w = hasLOS p1 p w
|
||||||
|
--canSeePoint i p w = case collidePointWalls p1 p nearbyWalls
|
||||||
|
-- of Just _ -> False
|
||||||
|
-- Nothing -> True
|
||||||
|
where nearbyWalls = wallsAlongLine p1 p w
|
||||||
|
p1 = _crPos (_creatures w IM.! i)
|
||||||
|
|
||||||
|
|
||||||
|
pathToPointFireable :: Int -> Point2 -> World -> Bool
|
||||||
|
pathToPointFireable i p w = not $ collidePointWallsSimple (_crPos (_creatures w IM.! i))
|
||||||
|
p
|
||||||
|
walls
|
||||||
|
where walls = IM.filter (not . isJust . \wl -> wl ^? blHP)
|
||||||
|
$ wallsAlongLine p1 p w
|
||||||
|
p1 = _crPos (_creatures w IM.! i)
|
||||||
|
|
||||||
|
canSeePointAll :: Int -> Point2 -> World -> Bool
|
||||||
|
canSeePointAll i targPos w = and $ map (flip (canSeePoint i) w . (\p -> (targPos +.+ radius *.* p)))
|
||||||
|
[(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
where cr = _creatures w IM.! i
|
||||||
|
cpos = _crPos cr
|
||||||
|
radius = _crRad cr
|
||||||
|
|
||||||
|
canSeeAny :: Int -> Int -> World -> Bool
|
||||||
|
canSeeAny fromID toID w = or $ map (flip (canSeePoint fromID) w . (\p -> (cpos +.+ radius *.* p)))
|
||||||
|
[(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
where cr = _creatures w IM.! toID
|
||||||
|
cpos = _crPos cr
|
||||||
|
radius = _crRad cr
|
||||||
|
|
||||||
|
canSeeAll :: Int -> Int -> World -> Bool
|
||||||
|
canSeeAll fromID toID w = and $ map (flip (canSeePoint fromID) w . (\p -> (cpos +.+ radius *.* p)))
|
||||||
|
[(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
where cr = _creatures w IM.! toID
|
||||||
|
cpos = _crPos cr
|
||||||
|
radius = _crRad cr
|
||||||
|
|
||||||
|
canWalk :: Int -> Int -> World -> Bool
|
||||||
|
canWalk i j w = not $ collidePointWalkable ipos jpos $ wallsAlongLine ipos jpos w
|
||||||
|
where ipos = _crPos (_creatures w IM.! i)
|
||||||
|
jpos = _crPos (_creatures w IM.! j)
|
||||||
|
|
||||||
|
canSeeIndirect :: Int -> Int -> World -> Bool
|
||||||
|
canSeeIndirect i j w = case collidePointIndirect ipos jpos $ wallsAlongLine ipos jpos w
|
||||||
|
of Just _ -> False
|
||||||
|
Nothing -> True && smokeLOS ipos jpos w
|
||||||
|
where ipos = _crPos (_creatures w IM.! i)
|
||||||
|
jpos = _crPos (_creatures w IM.! j)
|
||||||
|
|
||||||
|
canSeeFire :: Point2 -> Point2 -> World -> Bool
|
||||||
|
canSeeFire p p' w = (not $ collidePointFireVision p p' $ wallsAlongLine p p' w)
|
||||||
|
&& smokeLOS p p' w
|
||||||
|
|
||||||
|
canSeeFireVision :: Int -> Int -> World -> Bool
|
||||||
|
canSeeFireVision i j w = (not $ collidePointFireVision ipos jpos $ wallsAlongLine ipos jpos w)
|
||||||
|
&& smokeLOS ipos jpos w
|
||||||
|
where ipos = _crPos (_creatures w IM.! i)
|
||||||
|
jpos = _crPos (_creatures w IM.! j)
|
||||||
|
|
||||||
|
canSeeFireVisionAny :: Int -> Int -> World -> Bool
|
||||||
|
canSeeFireVisionAny i j w = (not $ and $ fmap ($ (wallsAlongLine (_crPos icr) (_crPos jcr) w) )
|
||||||
|
$ zipWith collidePointFireVision ips jps
|
||||||
|
) && smokeLOS (_crPos icr) (_crPos jcr) w
|
||||||
|
where icr = _creatures w IM.! i
|
||||||
|
jcr = _creatures w IM.! j
|
||||||
|
ips = map (\p -> (_crPos icr +.+ _crRad icr *.* p)) [(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
jps = map (\p -> (_crPos jcr +.+ _crRad jcr *.* p)) [(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
|
||||||
|
|
||||||
|
canSeeFireVisionAll :: Int -> Int -> World -> Bool
|
||||||
|
canSeeFireVisionAll i j w = (not $ or $ fmap ($ (wallsAlongLine (_crPos icr) (_crPos jcr) w) )
|
||||||
|
$ zipWith collidePointFireVision ips jps
|
||||||
|
) && smokeLOS (_crPos icr) (_crPos jcr) w
|
||||||
|
where icr = _creatures w IM.! i
|
||||||
|
jcr = _creatures w IM.! j
|
||||||
|
ips = map (\p -> (_crPos icr +.+ _crRad icr *.* p)) [(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
jps = map (\p -> (_crPos jcr +.+ _crRad jcr *.* p)) [(1,0),(0,1),(-1,0),(0,-1)]
|
||||||
|
|
||||||
|
-- looks for first collision of a point with walls
|
||||||
|
-- if found, returns wall
|
||||||
|
-- I'm sure there is a better way of doing this, one that propagates Nothing in a nice way
|
||||||
|
|
||||||
|
--wallsOnLine :: Point2 -> Point2 -> IM.IntMap Wall -> [Wall]
|
||||||
|
--wallsOnLine p1 p2 ws = hitWalls
|
||||||
|
-- where hitPoint w = myIntersectSegSeg p1 p2 (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
-- hitWalls = filter (\w -> Nothing /= hitPoint w) (IM.elems ws)
|
||||||
|
|
||||||
|
wallsOnLine :: Point2 -> Point2 -> IM.IntMap Wall -> [Wall]
|
||||||
|
wallsOnLine p1 p2 ws = hitWalls
|
||||||
|
where hitPoint w = intersectSegSeg' p1 p2 (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
hitWalls = filter (\w -> Nothing /= hitPoint w) (IM.elems ws)
|
||||||
|
|
||||||
|
wallOnLine :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe Wall
|
||||||
|
wallOnLine p1 p2 ws
|
||||||
|
= listToMaybe $ sortBy f hitWalls
|
||||||
|
where hitPoint w = intersectSegSeg' p1 p2 (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
--where hitPoint w = myIntersectSegSeg p1 p2 (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
hitWalls = filter (\w -> Nothing /= hitPoint w) (IM.elems ws)
|
||||||
|
f w1 w2 = compare (magV (p1 -.- fromJust (hitPoint w1))) (magV (p1 -.- fromJust (hitPoint w2)))
|
||||||
|
|
||||||
|
wallsOnCirc :: Point2 -> Float -> IM.IntMap Wall -> [Wall]
|
||||||
|
wallsOnCirc p r wls = IM.elems $ IM.filter f wls
|
||||||
|
where f wl = circOnLine (_wlLine wl !! 0) (_wlLine wl !! 1) p r
|
||||||
|
|
||||||
|
wallsNearPoint :: Point2 -> World -> IM.IntMap Wall
|
||||||
|
wallsNearPoint p w = IM.unions [f b $ f a $ _wallsZone w | a<-[x-1,x,x+1] , b<-[y-1,y,y+1]]
|
||||||
|
where (x,y) = zoneOfPoint p
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
-- possible BUG, was associated with thingsHitLongLine
|
||||||
|
wallsAlongLine :: Point2 -> Point2 -> World -> IM.IntMap Wall
|
||||||
|
{-# INLINE wallsAlongLine #-}
|
||||||
|
--wallsAlongLine a b w = IM.unions [f y $ f x $ _wallsZone w | (x,y) <- zoneOfLine a b]
|
||||||
|
-- where f i m = case IM.lookup i m of Just val -> val
|
||||||
|
-- _ -> IM.empty
|
||||||
|
wallsAlongLine a b w = IM.foldrWithKey' g IM.empty kps
|
||||||
|
where g x s = IM.union (IM.unions (IM.restrictKeys (f x $ _wallsZone w) s))
|
||||||
|
kps = zoneOfLine' a b
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
wallsNearZone' :: IM.IntMap IS.IntSet -> World -> IM.IntMap Wall
|
||||||
|
{-# INLINE wallsNearZone' #-}
|
||||||
|
wallsNearZone' im w = IM.foldrWithKey' g IM.empty im
|
||||||
|
where g x s = IM.union (IM.unions (IM.restrictKeys (f x $ _wallsZone w) s))
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
|
||||||
|
wallsAlongCirc :: Point2 -> Float -> World -> IM.IntMap Wall
|
||||||
|
wallsAlongCirc p r w = IM.unions [f y $ f x $ _wallsZone w | (x,y) <- zoneOfCircle p r]
|
||||||
|
where f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
allWalls :: World -> IM.IntMap Wall
|
||||||
|
allWalls w = IM.unions $ concatMap IM.elems $ IM.elems $ _wallsZone w
|
||||||
|
|
||||||
|
creaturesNearPoint :: Point2 -> World -> IM.IntMap Creature
|
||||||
|
creaturesNearPoint p w = IM.unions [f b $ f a $ _creaturesZone w | a<-[x-1,x,x+1] , b<-[y-1,y,y+1]]
|
||||||
|
where (x,y) = zoneOfPoint p
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
cloudsNearPoint :: Point2 -> World -> IM.IntMap Cloud
|
||||||
|
cloudsNearPoint p w = IM.unions [f b $ f a $ _cloudsZone w | a<-[x-1,x,x+1] , b<-[y-1,y,y+1]]
|
||||||
|
where (x,y) = zoneOfPoint p
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
-- possible BUG, occurs when used in thingsHitLongLine
|
||||||
|
creaturesAlongLine :: Point2 -> Point2 -> World -> IM.IntMap Creature
|
||||||
|
--creaturesAlongLine a b w = IM.unions [f y $ f x $ _creaturesZone w | (x,y) <- zoneOfLine a b]
|
||||||
|
-- where f i m = case IM.lookup i m of Just val -> val
|
||||||
|
-- _ -> IM.empty
|
||||||
|
creaturesAlongLine a b w = IM.foldrWithKey' g IM.empty kps
|
||||||
|
where g x s = IM.union (IM.unions (IM.restrictKeys (f x $ _creaturesZone w) s))
|
||||||
|
kps = zoneOfLine' a b
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
zoneSize :: Float
|
||||||
|
zoneSize = 50
|
||||||
|
--zoneSize = 100
|
||||||
|
|
||||||
|
floorHun :: Float -> Int
|
||||||
|
floorHun x = floor $ x / zoneSize
|
||||||
|
zoneOfPoint :: Point2 -> (Int,Int)
|
||||||
|
zoneOfPoint (x,y) = (floorHun x, floorHun y)
|
||||||
|
|
||||||
|
zoneNearPoint :: Point2 -> [(Int,Int)]
|
||||||
|
zoneNearPoint (x',y') = [(a,b) | a<-[x-1,x,x+1] , b<-[y-1,y,y+1]]
|
||||||
|
where x = floorHun x'
|
||||||
|
y = floorHun y'
|
||||||
|
|
||||||
|
zoneAroundPoint :: Point2 -> [(Int,Int)]
|
||||||
|
zoneAroundPoint (x',y') = [(a,b) | a<-[x-3..x+3] , b<-[y-3..y+3]]
|
||||||
|
where x = floorHun x'
|
||||||
|
y = floorHun y'
|
||||||
|
|
||||||
|
zoneAroundPoint' :: Int -> Point2 -> IM.IntMap IS.IntSet
|
||||||
|
zoneAroundPoint' i (x',y') = IM.fromSet (const ys) xs
|
||||||
|
where x = floorHun x'
|
||||||
|
y = floorHun y'
|
||||||
|
xs = IS.fromAscList [x-i..x+i]
|
||||||
|
ys = IS.fromAscList [y-i..y+i]
|
||||||
|
|
||||||
|
-- the laser seemed to be occasionally missing creatures,
|
||||||
|
-- if this reoccurs, maybe change
|
||||||
|
-- divide line factor from 2 to 1.5
|
||||||
|
bres :: Point2 -> Point2 -> [(Int,Int)]
|
||||||
|
bres a b = bresenham (zoneOfPoint a) (zoneOfPoint b)
|
||||||
|
|
||||||
|
bresx :: Point2 -> Point2 -> [(Int,Int)]
|
||||||
|
bresx a b = bresenham (x-1,y-1) (x'-1,y'-1)
|
||||||
|
where (x,y) = zoneOfPoint a
|
||||||
|
(x',y') = zoneOfPoint b
|
||||||
|
|
||||||
|
zoneOfLine :: Point2 -> Point2 -> [(Int,Int)]
|
||||||
|
zoneOfLine (aa,ab) (ba,bb) = nub $ concatMap f
|
||||||
|
$ bresenham (zoneOfPoint (aa,ab)) (zoneOfPoint (ba,bb))
|
||||||
|
where f (x,y) = [(p,r) | p <-[x-1,x,x+1] , r<-[y-1,y,y+1]]
|
||||||
|
--zoneOfLine (aa,ab) (ba,bb) = nub $ concatMap f
|
||||||
|
-- $ bresenham (zoneOfPoint (aa-n,ab-n)) (zoneOfPoint (ba-n,bb-n))
|
||||||
|
-- where f (x,y) = [(p,r) | p <-[x,x+1] , r<-[y,y+1]]
|
||||||
|
-- n = zoneSize * 0.5
|
||||||
|
|
||||||
|
expandLine :: [(Int,Int)] -> IM.IntMap IS.IntSet
|
||||||
|
{-# INLINE expandLine #-}
|
||||||
|
expandLine xs = IM.map expandSet
|
||||||
|
$ IM.unionsWith IS.union [im, IM.mapKeysMonotonic (+1) im, IM.mapKeysMonotonic (+2) im]
|
||||||
|
where im = IM.fromListWith IS.union $ map (\(a,b)->(a,IS.singleton b)) xs
|
||||||
|
expandSet s = IS.insert (mk+2) $ IS.insert (mk+1) s
|
||||||
|
--expandSet s = s
|
||||||
|
where mk = IS.findMax s
|
||||||
|
|
||||||
|
zoneOfLine' :: Point2 -> Point2 -> IM.IntMap IS.IntSet
|
||||||
|
{-# INLINE zoneOfLine' #-}
|
||||||
|
zoneOfLine' a b = expandLine $ bresenham (x-1,y-1) (x'-1,y'-1)
|
||||||
|
where (x,y) = zoneOfPoint a
|
||||||
|
(x',y') = zoneOfPoint b
|
||||||
|
|
||||||
|
--zoneOfLine a b = concatMap zoneNearPoint $ divideLine (2 * zoneSize) a b
|
||||||
|
--zoneOfLine a b = concatMap zoneNearPoint $ divideLine zoneSize a b
|
||||||
|
|
||||||
|
zoneOfCircle :: Point2 -> Float -> [(Int,Int)]
|
||||||
|
zoneOfCircle p r = concatMap zoneNearPoint $ divideCircle (1.5 * zoneSize) p r
|
||||||
|
|
||||||
|
-- looking at this again, I am not convinced it deals correctly with the
|
||||||
|
-- rotation of the world
|
||||||
|
zoneOfScreen :: World -> [(Int,Int)]
|
||||||
|
zoneOfScreen w = [(a,b) | a <- [x - n .. x + n]
|
||||||
|
, b <- [y - n .. y + n]
|
||||||
|
]
|
||||||
|
where (x,y) = zoneOfPoint $ _cameraPos w
|
||||||
|
n = ceiling $ wh / (_cameraZoom w * zoneSize)
|
||||||
|
wh = max (_windowX w) (_windowY w)
|
||||||
|
|
||||||
|
zoneOfDoubleScreen :: World -> [(Int,Int)]
|
||||||
|
zoneOfDoubleScreen w = [(a,b) | a <- [x - n .. x + n]
|
||||||
|
, b <- [y - n .. y + n]
|
||||||
|
]
|
||||||
|
where (x,y) = zoneOfPoint $ _cameraPos w
|
||||||
|
n = (ceiling $ wh / (_cameraZoom w * zoneSize)) * 2
|
||||||
|
wh = max (_windowX w) (_windowY w)
|
||||||
|
|
||||||
|
zoneOfSight :: World -> [(Int,Int)]
|
||||||
|
zoneOfSight w = [(a,b) | a <- [minimum xs .. maximum xs]
|
||||||
|
, b <- [minimum ys .. maximum ys]
|
||||||
|
]
|
||||||
|
where (xs,ys) = unzip $ map zoneOfPoint $ screenPolygon w ++ [_cameraCenter w]
|
||||||
|
|
||||||
|
screenPolygon :: World -> [Point2]
|
||||||
|
screenPolygon w = [tr,tl,bl,br]
|
||||||
|
where scRot = rotateV (_cameraRot w)
|
||||||
|
scZoom p | _cameraZoom w /= 0 = (1/_cameraZoom w) *.* p
|
||||||
|
scTran p = p +.+ _cameraPos w
|
||||||
|
tr = scTran $ scRot $ scZoom ( halfWidth w, halfHeight w)
|
||||||
|
tl = scTran $ scRot $ scZoom (-halfWidth w, halfHeight w)
|
||||||
|
br = scTran $ scRot $ scZoom ( halfWidth w,-halfHeight w)
|
||||||
|
bl = scTran $ scRot $ scZoom (-halfWidth w,-halfHeight w)
|
||||||
|
|
||||||
|
wallsNearZones :: [(Int,Int)] -> World -> IM.IntMap Wall
|
||||||
|
wallsNearZones is w = IM.unions [f b $ f a $ _wallsZone w | (a,b) <- is]
|
||||||
|
where f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
|
||||||
|
ixZone :: IM.IntMap (IM.IntMap a) -> Point2 -> a
|
||||||
|
ixZone z (x,y) = z IM.! floorHun x IM.! floorHun y
|
||||||
|
|
||||||
|
ixNZ :: IM.IntMap (IM.IntMap a) -> Point2 -> [a]
|
||||||
|
ixNZ z p = lookLookups (zoneNearPoint p) z
|
||||||
|
|
||||||
|
lookLookup :: Int -> Int -> (IM.IntMap (IM.IntMap a)) -> Maybe a
|
||||||
|
lookLookup i j z = case IM.lookup i z of
|
||||||
|
Just z' -> IM.lookup j z'
|
||||||
|
Nothing -> Nothing
|
||||||
|
|
||||||
|
lookLookups :: [(Int,Int)] -> (IM.IntMap (IM.IntMap a)) -> [a]
|
||||||
|
lookLookups xs z = mapMaybe (flip (uncurry lookLookup) z) xs
|
||||||
|
|
||||||
|
-- looks for first collision of a point with walls
|
||||||
|
-- if found, gives point and reflection velocity
|
||||||
|
collidePointWalls :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
||||||
|
collidePointWalls p1 p2 ws = listToMaybe $ sortBy f $ IM.elems $ IM.mapMaybe
|
||||||
|
(( \(x:y:_) -> fmap (flip (,)
|
||||||
|
(reflectIn (x -.- y) (p2 -.- p1))
|
||||||
|
. (+.+ errorNormalizeV 39 (vNormal (x -.- y)))
|
||||||
|
) (intersectSegSeg' p1 p2 x y)
|
||||||
|
) . _wlLine
|
||||||
|
) ws
|
||||||
|
where f (a,_) (b,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
-- looks for if a point collides with walls
|
||||||
|
collidePointWallsSimple :: Point2 -> Point2 -> IM.IntMap Wall -> Bool
|
||||||
|
collidePointWallsSimple p1 p2 = any $ isJust . ( \(x:y:_) -> intersectSegSeg' p1 p2 x y)
|
||||||
|
. _wlLine
|
||||||
|
|
||||||
|
collidePointWalkable :: Point2 -> Point2 -> IM.IntMap Wall -> Bool
|
||||||
|
collidePointWalkable p1 p2 ws = any (isJust
|
||||||
|
. ( \(x:y:_) -> intersectSegSeg' p1 p2 x y)
|
||||||
|
. _wlLine
|
||||||
|
) unwalkableWalls
|
||||||
|
where unwalkableWalls = IM.filter notDoor ws
|
||||||
|
notDoor (AutoDoor {}) = False
|
||||||
|
notDoor _ = True
|
||||||
|
|
||||||
|
furthestPointWalkable :: Point2 -> Point2 -> IM.IntMap Wall -> Point2
|
||||||
|
furthestPointWalkable p1 p2 ws = head $ (sortBy (compare `on` dist p1) $ IM.elems
|
||||||
|
$ IM.mapMaybe ( ( \(x:y:_) -> intersectSegSeg' p1 p2 x y)
|
||||||
|
. _wlLine
|
||||||
|
) ws
|
||||||
|
) ++ [p2]
|
||||||
|
|
||||||
|
collidePointIndirect :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe Point2
|
||||||
|
collidePointIndirect p1 p2 ws = listToMaybe $ sortBy (compare `on` dist p1) $ IM.elems
|
||||||
|
$ IM.mapMaybe ( ( \(x:y:_) -> intersectSegSeg' p1 p2 x y)
|
||||||
|
. _wlLine
|
||||||
|
) notWindows
|
||||||
|
where notWindows = IM.filter (not . _wlIsSeeThrough) ws
|
||||||
|
|
||||||
|
collidePointFire :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe Point2
|
||||||
|
collidePointFire p1 p2 ws = listToMaybe $ sortBy (compare `on` dist p1) $ IM.elems
|
||||||
|
$ IM.mapMaybe ( ( \(x:y:_) -> intersectSegSeg' p1 p2 x y)
|
||||||
|
. _wlLine
|
||||||
|
) notWindows
|
||||||
|
where notWindows = IM.filter (\wl -> not (_wlIsSeeThrough wl && isJust (wl ^? blHP))
|
||||||
|
) ws
|
||||||
|
|
||||||
|
collidePointFireVision :: Point2 -> Point2 -> IM.IntMap Wall -> Bool
|
||||||
|
collidePointFireVision p1 p2 ws = any ( isJust
|
||||||
|
. ( \(x:y:_) -> intersectSegSeg' p1 p2 x y)
|
||||||
|
. _wlLine
|
||||||
|
)
|
||||||
|
$ IM.filter notBlockWindow ws
|
||||||
|
where notBlockWindow wl = case wl ^? blHP of
|
||||||
|
Just _ -> not $ _wlIsSeeThrough wl
|
||||||
|
Nothing -> True
|
||||||
|
|
||||||
|
collidePointSmoke :: Point2 -> Point2 -> [Smoke] -> Bool
|
||||||
|
collidePointSmoke a b = any $ isJust . uncurry (intersectSegSeg' a b) . smokePerpLine a b
|
||||||
|
|
||||||
|
smokePerpLine :: Point2 -> Point2 -> Smoke -> (Point2,Point2)
|
||||||
|
smokePerpLine a b sm = (p +.+ orth, p -.- orth)
|
||||||
|
where
|
||||||
|
p = _smPos sm
|
||||||
|
orth = _smRad sm *.* safeNormalizeV (vNormal (a -.- b))
|
||||||
|
|
||||||
|
-- shit this is ugly
|
||||||
|
lineOfThickness :: Float -> [Point2] -> Picture
|
||||||
|
lineOfThickness t = pictures . f
|
||||||
|
where f (x:y:ys)
|
||||||
|
| x == y = f (x:ys)
|
||||||
|
| otherwise
|
||||||
|
= polygon [x +.+ n x y, x -.- n x y, y -.- n x y, y +.+ n x y] : f (y:ys)
|
||||||
|
f _ = []
|
||||||
|
n a b = (t*0.5) *.* errorNormalizeV 42 (vNormal (a -.- b))
|
||||||
|
|
||||||
|
wedgeOfThickness :: Float -> Point2 -> Point2 -> Picture
|
||||||
|
wedgeOfThickness t x y
|
||||||
|
| x == y = blank
|
||||||
|
| otherwise = pictures [uncurry translate x $ circleSolid (0.5*t)
|
||||||
|
,polygon [x +.+ n x y, x -.- n x y, y]
|
||||||
|
]
|
||||||
|
where n a b = (t*0.5) *.* errorNormalizeV 4200 (vNormal (a -.- b))
|
||||||
|
|
||||||
|
insertInZoneWith :: Int -> Int -> (a -> a -> a) -> a -> IM.IntMap (IM.IntMap a)
|
||||||
|
-> IM.IntMap (IM.IntMap a)
|
||||||
|
insertInZoneWith x y fun obj = IM.insertWith f x $ IM.singleton y obj
|
||||||
|
where-- f :: IM.IntMap a -> IM.IntMap a -> IM.IntMap a
|
||||||
|
f _ = IM.insertWith fun y obj
|
||||||
|
|
||||||
|
insertIMInZone :: Int -> Int -> Int -> a -> IM.IntMap (IM.IntMap (IM.IntMap a))
|
||||||
|
-> IM.IntMap (IM.IntMap (IM.IntMap a))
|
||||||
|
insertIMInZone x y obid obj = IM.insertWith f x $ IM.singleton y $ IM.singleton obid obj
|
||||||
|
where f _ = IM.insertWith g y $ IM.singleton obid obj
|
||||||
|
g _ = IM.insert obid obj
|
||||||
|
|
||||||
|
adjustIMZone :: (a -> a) -> Int -> Int -> Int -> IM.IntMap (IM.IntMap (IM.IntMap a))
|
||||||
|
-> IM.IntMap (IM.IntMap (IM.IntMap a))
|
||||||
|
adjustIMZone f x y n m = IM.adjust f' x m
|
||||||
|
where f' = IM.adjust f'' y
|
||||||
|
f'' = IM.adjust f n
|
||||||
|
|
||||||
|
|
||||||
|
newKey :: IM.IntMap a -> Int
|
||||||
|
newKey m = case IM.lookupMax m of
|
||||||
|
Just (n,_) -> n+1
|
||||||
|
Nothing -> 0
|
||||||
|
|
||||||
|
newParticleKey :: World -> Int
|
||||||
|
newParticleKey w = case IM.lookupMax (_particles w) of
|
||||||
|
Just (n,_) -> n+1
|
||||||
|
Nothing -> 0
|
||||||
|
newCrKey :: World -> Int
|
||||||
|
newCrKey w = case IM.lookupMax (_creatures w) of
|
||||||
|
Just (n,_) -> n+1
|
||||||
|
Nothing -> 0
|
||||||
|
|
||||||
|
insertNewKey :: a -> IM.IntMap a -> IM.IntMap a
|
||||||
|
insertNewKey x m = case IM.lookupMax m of
|
||||||
|
Nothing -> IM.singleton 0 x
|
||||||
|
Just (k,_) -> IM.insert (k+1) x m
|
||||||
|
|
||||||
|
reflectPointCreature :: Point2 -> Point2 -> Creature -> Maybe (Point2, Point2, Int)
|
||||||
|
reflectPointCreature p1 p2 cr =
|
||||||
|
case collidePointCirc p1 p2 (_crRad cr) (_crPos cr) of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just p3 -> Just ( p1
|
||||||
|
, errorNormalizeV 35 (ssaTriPoint p2 (_crPos cr) p1 (_crRad cr) -.- _crPos cr)
|
||||||
|
+.+ (_crPos cr -.- _crOldPos cr)
|
||||||
|
--, errorNormalizeV 36 $
|
||||||
|
-- ssaTriPoint p1 (_crPos cr) p2 (_crRad cr)
|
||||||
|
-- -.- _crOldPos cr
|
||||||
|
, _crID cr)
|
||||||
|
|
||||||
|
reflectPointCreatures :: Point2 -> Point2 -> IM.IntMap Creature -> Maybe (Point2,Point2,Int)
|
||||||
|
reflectPointCreatures p1 p2 cs = listToMaybe $ sortBy f $ IM.elems $ IM.mapMaybe
|
||||||
|
(reflectPointCreature p1 p2) cs
|
||||||
|
where f (a,_,_) (b,_,_) = compare (magV (a -.- p1)) (magV (b -.- p1))
|
||||||
|
|
||||||
|
|
||||||
|
reflectCircCreature :: Float -> Point2 -> Point2 -> Creature -> Maybe (Point2, Point2, Int)
|
||||||
|
reflectCircCreature rad p1 p2 cr =
|
||||||
|
case collidePointCirc p1 p2 (rad + _crRad cr) (_crPos cr) of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just p3 -> Just ( p1
|
||||||
|
, errorNormalizeV 37 (ssaTriPoint p2 (_crPos cr) p1 (_crRad cr) -.- _crPos cr)
|
||||||
|
+.+ (_crPos cr -.- _crOldPos cr)
|
||||||
|
--, errorNormalizeV 38 $
|
||||||
|
-- ssaTriPoint p1 (_crPos cr) p2 (_crRad cr)
|
||||||
|
-- -.- _crOldPos cr
|
||||||
|
, _crID cr)
|
||||||
|
|
||||||
|
reflectCircCreatures :: Float -> Point2 -> Point2 -> IM.IntMap Creature -> Maybe (Point2,Point2,Int)
|
||||||
|
reflectCircCreatures rad p1 p2 cs = listToMaybe $ sortBy f $ IM.elems $ IM.mapMaybe
|
||||||
|
(reflectCircCreature rad p1 p2) cs
|
||||||
|
where f (a,_,_) (b,_,_) = compare (magV (a -.- p1)) (magV (b -.- p1))
|
||||||
|
|
||||||
|
-- collides a point with forcefields
|
||||||
|
-- if found, returns point of collision, deflection if required, and the id
|
||||||
|
collidePointFFs = undefined
|
||||||
|
collidePointFF = undefined
|
||||||
|
--
|
||||||
|
--collidePointFFs :: Point2 -> Point2 -> StdGen -> IM.IntMap ForceField
|
||||||
|
-- -> Maybe (Point2,(Maybe (Point2,StdGen),Int))
|
||||||
|
--collidePointFFs p1 p2 g fs = listToMaybe $ sortBy f $ IM.elems
|
||||||
|
-- $ IM.mapMaybe (collidePointFF p1 p2 g) fs
|
||||||
|
-- where f (a,_) (b,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
--
|
||||||
|
--collidePointFF :: Point2 -> Point2 -> StdGen -> ForceField
|
||||||
|
-- -> Maybe (Point2,(Maybe (Point2,StdGen),Int))
|
||||||
|
--collidePointFF p1 p2 g ff = fmap f ip
|
||||||
|
-- where (p3:p4:_) = _ffLine ff
|
||||||
|
-- ip = intersectSegSeg' p1 p2 p3 p4
|
||||||
|
-- ref = (_ffDeflect ff) <*> Just g <*> Just (p2 -.- p1) <*> Just ff
|
||||||
|
-- f p = (p, (ref, _ffID ff))
|
||||||
|
--
|
||||||
|
|
||||||
|
-- looks for first collision of a point with walls
|
||||||
|
-- if found, gives point and reflection velocity, reflection damped in normal
|
||||||
|
collidePointWalls' :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
||||||
|
collidePointWalls' p1 p2 ws
|
||||||
|
= listToMaybe $ sortBy f $ IM.elems
|
||||||
|
$ IM.mapMaybe
|
||||||
|
(( \(x:y:_) -> fmap (flip (,) (reflectInParam 0.5 (x -.- y) (p2 -.- p1))
|
||||||
|
. (+.+ errorNormalizeV 40 (vNormal (x -.- y)))
|
||||||
|
)
|
||||||
|
(intersectSegSeg' p1 p2 x y)
|
||||||
|
) . _wlLine
|
||||||
|
) ws
|
||||||
|
where f (a,_) (b,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
|
||||||
|
-- looks for first collision of a circle with walls
|
||||||
|
-- if found, gives point and reflection velocity, reflection damped in normal
|
||||||
|
collideCircWalls' :: Point2 -> Point2 -> Float -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
||||||
|
collideCircWalls' p1 p2 rad ws
|
||||||
|
= listToMaybe $ sortBy f $ IM.elems
|
||||||
|
$ IM.mapMaybe
|
||||||
|
(( \(x:y:_) -> fmap (flip (,) (reflectInParam 0.5 (x -.- y) (p2 -.- p1))
|
||||||
|
. (+.+ errorNormalizeV 40 (vNormal (x -.- y)))
|
||||||
|
)
|
||||||
|
(intersectSegSeg' p1 p2 x y)
|
||||||
|
) . shiftByRad . _wlLine
|
||||||
|
) ws
|
||||||
|
where f (a,_) (b,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
shiftByRad (a:b:_) = map ((+.+) (rad *.* normalizeV (vNormal (a -.- b))))
|
||||||
|
[a +.+ rad *.* (normalizeV (a -.-b))
|
||||||
|
,b +.+ rad *.* (normalizeV (b -.-a))
|
||||||
|
]
|
||||||
|
-- this shifts the wall out, and for outer corners extends the wall
|
||||||
|
-- not sure what this does for inner corners, hopefully won't cause a problem
|
||||||
|
-- the alternative would be to separately bounce off corner points...
|
||||||
|
-- unfortunately, doesn't allow for collisions when the circle spawns on the
|
||||||
|
-- wall
|
||||||
|
|
||||||
|
-- looks for first collision of a point with walls
|
||||||
|
-- if found, gives point and normal of wall
|
||||||
|
collidePointWallsNorm :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Point2)
|
||||||
|
collidePointWallsNorm p1 p2 ws = listToMaybe $ sortBy f $ IM.elems $ IM.mapMaybe
|
||||||
|
(( \(x:y:_) -> fmap (flip (,) ( vNormal $ x -.- y ))
|
||||||
|
(intersectSegSeg' p1 p2 x y)
|
||||||
|
) . _wlLine
|
||||||
|
) ws
|
||||||
|
where f (a,_) (b,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
|
||||||
|
-- looks for first collision of a point with walls
|
||||||
|
-- if found, gives point and colour of wall
|
||||||
|
collidePointWallsCol :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Color)
|
||||||
|
collidePointWallsCol p1 p2 ws = listToMaybe $ sortBy f $ IM.elems $ IM.mapMaybe
|
||||||
|
( (\(m, c) -> fmap (flip (,) c) m)
|
||||||
|
. (\w -> (intersectSegSeg' p1 p2
|
||||||
|
(_wlLine w !! 0) (_wlLine w !! 1), _wlColor w))
|
||||||
|
) ws
|
||||||
|
where f (a,_) (b,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
-- looks for first collision of a point with walls
|
||||||
|
-- if found, gives point, and normal and colour of wall
|
||||||
|
collidePointWallsNormCol :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe (Point2,Point2,Color)
|
||||||
|
collidePointWallsNormCol p1 p2 ws
|
||||||
|
= listToMaybe $ sortBy f $ IM.elems $ IM.mapMaybe m ws
|
||||||
|
where f (a,_,_) (b,_,_) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
ls w = let (x:y:_) = _wlLine w
|
||||||
|
in (intersectSegSeg' p1 p2 x y, vNormal (x -.- y), _wlColor w)
|
||||||
|
m w = let (a1,a2,a3) = ls w
|
||||||
|
in fmap (\a4 -> (a4,a2,a3)) a1
|
||||||
|
|
||||||
|
--returns the first creature, if any, that a point intersects with
|
||||||
|
collidePointCreatures :: Point2 -> Point2 -> World -> Maybe Int
|
||||||
|
collidePointCreatures p1 p2 w = fmap fst $ listToMaybe $ sortBy (csnd) $ IM.toList $
|
||||||
|
IM.mapMaybe (\x ->
|
||||||
|
collidePointCirc' p1 p2 (_crRad x) (_crPos x)
|
||||||
|
)
|
||||||
|
(_creatures w)
|
||||||
|
where csnd (_,a) (_,b) = compare a b
|
||||||
|
--as for collidePointCreatures, only increases the radius of creatures by a
|
||||||
|
--fixed amount, thus collides a moving circle with creaures
|
||||||
|
collideCircCreatures :: Point2 -> Point2 -> Float -> World -> Maybe Int
|
||||||
|
collideCircCreatures p1 p2 rad w = fmap fst $ listToMaybe $ sortBy (csnd) $ IM.toList $
|
||||||
|
IM.mapMaybe (\x ->
|
||||||
|
collidePointCirc' p1 p2 (rad + _crRad x) (_crPos x)
|
||||||
|
)
|
||||||
|
(_creatures w)
|
||||||
|
where csnd (_,a) (_,b) = compare a b
|
||||||
|
|
||||||
|
|
||||||
|
--returns the first creature, if any, that a point intersects with, gives point
|
||||||
|
--in creature on line
|
||||||
|
collidePointCrsPoint :: Point2 -> Point2 -> World -> Maybe (Point2,Int)
|
||||||
|
collidePointCrsPoint p1 p2 w = fmap f $ listToMaybe $ sortBy (csndsnd) $ IM.toList $
|
||||||
|
IM.mapMaybe (\x ->
|
||||||
|
collidePointCirc'' p1 p2 (_crRad x) (_crPos x)
|
||||||
|
)
|
||||||
|
(_creatures w)
|
||||||
|
where csndsnd (_,(_,a)) (_,(_,b)) = compare a b
|
||||||
|
f (cID,(p,_)) = (p,cID)
|
||||||
|
collideCircCrsPoint :: Point2 -> Point2 -> Float -> World -> Maybe (Point2,Int)
|
||||||
|
collideCircCrsPoint p1 p2 rad w = fmap f $ listToMaybe $ sortBy (csndsnd) $ IM.toList $
|
||||||
|
IM.mapMaybe (\x ->
|
||||||
|
collidePointCirc'' p1 p2 (rad + _crRad x) (_crPos x)
|
||||||
|
)
|
||||||
|
(_creatures w)
|
||||||
|
where csndsnd (_,(_,a)) (_,(_,b)) = compare a b
|
||||||
|
f (cID,(p,_)) = (p,cID)
|
||||||
|
|
||||||
|
|
||||||
|
-- makes a creatures not hittable
|
||||||
|
collidePointCrsWithoutPoint :: Int -> Point2 -> Point2 -> World -> Maybe (Point2,Int)
|
||||||
|
collidePointCrsWithoutPoint cid p1 p2 w = fmap f $ listToMaybe $ sortBy (csndsnd) $ IM.toList $
|
||||||
|
IM.mapMaybe (\x ->
|
||||||
|
collidePointCirc'' p1 p2 (_crRad x) (_crPos x)
|
||||||
|
)
|
||||||
|
(IM.delete cid $ _creatures w)
|
||||||
|
where csndsnd (_,(_,a)) (_,(_,b)) = compare a b
|
||||||
|
f (cID,(p,_)) = (p,cID)
|
||||||
|
|
||||||
|
circOnSomeWall :: Point2 -> Float -> World -> Bool
|
||||||
|
circOnSomeWall p rad w = any (\(x:y:_) -> circOnLine x y p rad)
|
||||||
|
$ fmap _wlLine $ IM.elems $ wallsNearPoint p w
|
||||||
|
|
||||||
|
crsNearLine :: Float -> [Point2] -> World -> Bool
|
||||||
|
crsNearLine d (p1:p2:_) w = any (\c -> circOnLine p1 p2 (_crPos c) (d + _crRad c))
|
||||||
|
$ IM.filter (\cr -> _crMass cr > 4) $ _creatures w
|
||||||
|
|
||||||
|
crsNearPoint :: Float -> Point2 -> World -> Bool
|
||||||
|
crsNearPoint d p w = any (\c -> dist (_crPos c) p < (d + _crRad c)) (_creatures w)
|
||||||
|
|
||||||
|
crsOnLine :: Point2 -> Point2 -> World -> [Creature]
|
||||||
|
crsOnLine p1 p2 w = IM.elems
|
||||||
|
$ IM.filter (\cr -> circOnLine p1 p2 (_crPos cr) (_crRad cr))
|
||||||
|
$ _creatures w
|
||||||
|
|
||||||
|
crsOnThickLine :: Float -> Point2 -> Point2 -> World -> [Creature]
|
||||||
|
crsOnThickLine thickness p1 p2 w = IM.elems
|
||||||
|
$ IM.filter (\cr -> circOnLine p1 p2 (_crPos cr) (_crRad cr + thickness))
|
||||||
|
$ _creatures w
|
||||||
|
|
||||||
|
nearestCrInRad :: Point2 -> Float -> World -> Maybe Creature
|
||||||
|
nearestCrInRad p r w = let crs = IM.filter (\cr -> dist p (_crPos cr) < r) $ _creatures w
|
||||||
|
sortedCrs = sortBy (compare `on` (dist p . _crPos)) $ IM.elems crs
|
||||||
|
in listToMaybe sortedCrs
|
||||||
|
|
||||||
|
nearestCrInTri :: Point2 -> Float -> Float -> World -> Maybe Creature
|
||||||
|
nearestCrInTri p dir x w
|
||||||
|
= let crs = IM.filter (\cr -> errorPointInPolygon 1 (_crPos cr) tri) $ _creatures w
|
||||||
|
sortedCrs = sortBy (compare `on` (dist p . _crPos)) $ IM.elems crs
|
||||||
|
in listToMaybe sortedCrs
|
||||||
|
where tri = [p
|
||||||
|
,p +.+ rotateV (dir-pi/4) (x,0)
|
||||||
|
,p +.+ rotateV (dir+pi/4) (x,0)
|
||||||
|
]
|
||||||
|
|
||||||
|
nearestCrInFront :: Point2 -> Float -> Float -> World -> Maybe Creature
|
||||||
|
nearestCrInFront p dir x w
|
||||||
|
= let crs = IM.filter (\cr -> errorPointInPolygon 2 (_crPos cr) rec) $ _creatures w
|
||||||
|
sortedCrs = sortBy (compare `on` (dist p . _crPos)) $ IM.elems crs
|
||||||
|
in listToMaybe sortedCrs
|
||||||
|
where rec = [p
|
||||||
|
,pR
|
||||||
|
,pR1
|
||||||
|
,pL1
|
||||||
|
,pL
|
||||||
|
]
|
||||||
|
pR = p +.+ rotateV (dir - pi*(3/8)) (x/2,0)
|
||||||
|
pL = p +.+ rotateV (dir + pi*(3/8)) (x/2,0)
|
||||||
|
pR1 = pR +.+ rotateV dir (x/2,0)
|
||||||
|
pL1 = pL +.+ rotateV dir (x/2,0)
|
||||||
|
|
||||||
|
crInPolygon :: Creature -> [Point2] -> Bool
|
||||||
|
crInPolygon cr xs = errorPointInPolygon 3 (_crPos cr) xs
|
||||||
|
|
||||||
|
onLayer :: Layer -> Picture -> Picture
|
||||||
|
onLayer l = setDepth (fromIntegral (levLayer l) / 100)
|
||||||
|
|
||||||
|
--onLayer :: Layer -> Picture -> [(Picture, [Int])]
|
||||||
|
--onLayer l p = [(p, [levLayer l])]
|
||||||
|
|
||||||
|
onLayerL :: [Int] -> Picture -> Picture
|
||||||
|
onLayerL is = setDepth (sum $ zipWith (/) (map fromIntegral is) $ map (\x->100**x) [1..])
|
||||||
|
|
||||||
|
levLayer :: Layer -> Int
|
||||||
|
levLayer BgLayer = 20
|
||||||
|
levLayer PressPlateLayer = 45
|
||||||
|
levLayer CorpseLayer = 50
|
||||||
|
levLayer FlItLayer = 55
|
||||||
|
levLayer CrLayer = 60
|
||||||
|
levLayer WlLayer = 65
|
||||||
|
levLayer GloomLayer = 67
|
||||||
|
levLayer UPtLayer = 70
|
||||||
|
levLayer PtLayer = 72
|
||||||
|
levLayer HPtLayer = 73
|
||||||
|
levLayer ShadowLayer = 75
|
||||||
|
levLayer LabelLayer = 80
|
||||||
|
levLayer InvLayer = 85
|
||||||
|
levLayer MenuLayer = 90
|
||||||
|
|
||||||
|
mouseWorldPos :: World -> Point2
|
||||||
|
mouseWorldPos w = _cameraPos w +.+ (1/_cameraZoom w) *.* rotateV (_cameraRot w) (_mousePos w)
|
||||||
|
|
||||||
|
logistic :: Float -> Float -> Float -> (Float -> Float)
|
||||||
|
logistic x0 l k x = l / (1 + exp (k*(x0 - x)))
|
||||||
|
|
||||||
|
wallLOS :: [Point2] -> Point2 -> Point2 -> Bool
|
||||||
|
{-# INLINE wallLOS #-}
|
||||||
|
wallLOS !(x:y:_) !c !p = isRHS c x y || isLHS p x' y' || isLHS c p x || isRHS c p y
|
||||||
|
where n = 10 *.* (normV . vNormal $ y -.- x)
|
||||||
|
x' = x +.+ n
|
||||||
|
y' = y +.+ n
|
||||||
|
|
||||||
|
wallsLOS :: Foldable t => t [Point2] -> Point2 -> Point2 -> Bool
|
||||||
|
{-# INLINE wallsLOS #-}
|
||||||
|
wallsLOS !ls !c !p = all (\l -> wallLOS l c p) ls
|
||||||
|
|
||||||
|
translateDrawing = translate
|
||||||
|
rotateDrawing = rotate
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
module Dodge.Block where
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture.Data
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
|
||||||
|
import Data.Function
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import System.Random
|
||||||
|
import Control.Monad.State
|
||||||
|
|
||||||
|
updateBlocks :: World -> World
|
||||||
|
updateBlocks w = (\w' -> seq (_wallsZone w') w') $ flip (foldr removeFromZone) deadPanes
|
||||||
|
$ over walls (\wls -> wls `seq` IM.filter (not . blockIsDead) wls)
|
||||||
|
degradeBlocks
|
||||||
|
-- w
|
||||||
|
where degradeBlocks = deadBlocks `seq` foldr killBlock w deadBlocks
|
||||||
|
removeFromZone :: Wall -> World -> World
|
||||||
|
removeFromZone bl = over (wallsZone . ix x . ix y) (IM.delete (_wlID bl))
|
||||||
|
where (x,y) = zoneOfPoint $ pHalf (_wlLine bl !! 0) (_wlLine bl !! 1)
|
||||||
|
deadPanes = filter blockIsDead (IM.elems $ _walls w)
|
||||||
|
deadBlocks = nubBy ((==) `on` _blIDs) deadPanes
|
||||||
|
blockIsDead wl = case wl ^? blHP of Just x -> x <= 0
|
||||||
|
Nothing -> False
|
||||||
|
|
||||||
|
|
||||||
|
killBlock :: Wall -> World -> World
|
||||||
|
killBlock bl w = f bl .
|
||||||
|
flip (foldr unshadow)
|
||||||
|
-- flip (foldr (\i -> set (walls . ix i . blVisible) (True)))
|
||||||
|
(_blShadows bl)
|
||||||
|
$ w
|
||||||
|
where
|
||||||
|
f bl@(Block {_blDegrades = (x:xs)}) = degradeBlock bl . hitSound bl
|
||||||
|
f bl = hitSound' bl
|
||||||
|
hitSound bl | _wlIsSeeThrough bl = soundMultiFrom sos (soundid+8) 25 0
|
||||||
|
| otherwise = soundMultiFrom sos soundid 25 0
|
||||||
|
hitSound' bl | _wlIsSeeThrough bl = soundMultiFrom sos (soundid+4) 25 0
|
||||||
|
| otherwise = soundMultiFrom sos soundid 25 0
|
||||||
|
sos = [BlockDegradeSound 0,BlockDegradeSound 1]
|
||||||
|
(soundid,_) = randomR (29,32) $ _randGen w
|
||||||
|
unshadow :: Int -> World -> World
|
||||||
|
unshadow bid w = case w ^? walls . ix bid of
|
||||||
|
Just b -> let (x,y) = zoneOfPoint $ pHalf (_wlLine b !! 0) (_wlLine b !! 1)
|
||||||
|
in w & wallsZone . ix x . ix y . ix bid . blVisible %~ \_ -> True
|
||||||
|
Nothing -> w
|
||||||
|
|
||||||
|
degradeBlock :: Wall -> World -> World
|
||||||
|
degradeBlock bl w = let blid = _wlID bl
|
||||||
|
bls = map (\i -> _walls w IM.! i) (_blIDs $ _walls w IM.! blid)
|
||||||
|
ps = reverse $ orderPolygon $ nub $ concatMap _wlLine bls
|
||||||
|
(newPs,g) = runState (shrinkPolygon 0.5 ps) $ _randGen w
|
||||||
|
(x:xs) = _blDegrades bl
|
||||||
|
in addBlock newPs (x + _blHP bl) (_wlColor bl) (_wlIsSeeThrough bl) xs $ set randGen g w
|
||||||
|
|
||||||
|
pushPointTowardsBy :: RandomGen g => Float -> Point2 -> [Point2] -> State g Point2
|
||||||
|
pushPointTowardsBy x p ps = do
|
||||||
|
xs <- sequence $ take (length ps) $ repeat $ state $ randomR (0, x / (fromIntegral $ length ps))
|
||||||
|
let toAdd p' y = y *.* (p' -.- p)
|
||||||
|
return $ p +.+ foldr1 (+.+) (zipWith toAdd ps xs)
|
||||||
|
|
||||||
|
shrinkPolygon :: RandomGen g => Float -> [Point2] -> State g [Point2]
|
||||||
|
shrinkPolygon x ps = sequence $ map (flip (pushPointTowardsBy x) ps) ps
|
||||||
|
|
||||||
|
|
||||||
|
addBlock :: [Point2] -> Int -> Color -> Bool -> [Int] -> World -> World
|
||||||
|
addBlock (p:ps) hp col isSeeThrough degradability w
|
||||||
|
| hp <= 0 && degradability == [] = w
|
||||||
|
| hp <= 0 = addBlock (p:ps) (head degradability + hp) col isSeeThrough (tail degradability) w
|
||||||
|
| otherwise = over wallsZone (flip (IM.foldr wallInZone) blocks)
|
||||||
|
$ over walls (IM.union blocks) w
|
||||||
|
--addBlock (p:p':ps) w = over walls (IM.insert i b) w
|
||||||
|
where
|
||||||
|
lines = zip (p:ps) (ps ++ [p])
|
||||||
|
i = newKey $ _walls w
|
||||||
|
is = [i.. i + length lines-1]
|
||||||
|
blocks = IM.fromList $ zip is
|
||||||
|
$ zipWith (\j (a,b) -> Block { _wlLine = [a,b]
|
||||||
|
, _wlID = j
|
||||||
|
-- , _wlColor = greyN 0.5
|
||||||
|
, _wlColor = col
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _blIDs = is
|
||||||
|
, _blHP = hp
|
||||||
|
, _wlIsSeeThrough = isSeeThrough
|
||||||
|
, _blVisible = True
|
||||||
|
, _blShadows = []
|
||||||
|
, _blDegrades = degradability
|
||||||
|
}
|
||||||
|
) is lines
|
||||||
|
wallInZone wl | dist (_wlLine wl !! 0) (_wlLine wl !! 1) <= 2*zoneSize
|
||||||
|
= insertIMInZone x y wlid wl
|
||||||
|
| otherwise = flip (foldr (\(a,b) -> insertIMInZone a b wlid wl)) ips
|
||||||
|
where (x,y) = zoneOfPoint $ (pHalf (_wlLine wl !! 0) (_wlLine wl !! 1))
|
||||||
|
wlid = _wlID wl
|
||||||
|
ips = map zoneOfPoint $ divideLine (2*zoneSize) (_wlLine wl !! 0) (_wlLine wl !! 1)
|
||||||
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
module Dodge.Camera where
|
||||||
|
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
|
||||||
|
import qualified SDL as SDL
|
||||||
|
|
||||||
|
updateCamera :: World -> World
|
||||||
|
updateCamera = rotCam . moveCamera . updateAimTime . updateScopeZoom
|
||||||
|
|
||||||
|
updateAimTime :: World -> World
|
||||||
|
updateAimTime w
|
||||||
|
| SDL.ButtonRight `S.member` _mouseButtons w
|
||||||
|
= w & cameraAimTime %~ \t -> min (t+1) 10
|
||||||
|
| otherwise = w & cameraAimTime %~ \t -> max (t-1) 0
|
||||||
|
|
||||||
|
moveCamera :: World -> World
|
||||||
|
moveCamera w = w & cameraPos .~ idealPos
|
||||||
|
& cameraCenter .~ sightFrom
|
||||||
|
where aimRangeFactor | _cameraZoom w == 0 = 0
|
||||||
|
| otherwise = (fromMaybe 0 $ yourItem w ^? itAimingRange) / _cameraZoom w
|
||||||
|
aimTimeFactor = fromIntegral (w ^. cameraAimTime) / 10
|
||||||
|
aimingMult | SDL.ButtonRight `S.member` _mouseButtons w = 1
|
||||||
|
| otherwise = 0
|
||||||
|
ypos = _crPos $ you w
|
||||||
|
idealOffset = rotateV (_cameraRot w) (aimRangeFactor * aimingMult *.* _mousePos w)
|
||||||
|
currentOffset = currentPos -.- camCenter
|
||||||
|
idealPos = camCenter +.+ rotateV (_cameraRot w)
|
||||||
|
-- (aimRangeFactor * aimTimeFactor *.* _mousePos w)
|
||||||
|
(aimRangeFactor * aimingMult *.* _mousePos w)
|
||||||
|
currentPos = _cameraPos w
|
||||||
|
camCenter = ypos +.+ scope
|
||||||
|
isCam :: Bool
|
||||||
|
isCam = fromMaybe False $ yourItem w ^? itAttachment . _Just . scopeIsCamera
|
||||||
|
scope = fromMaybe (0,0) $ yourItem w ^? itAttachment . _Just . scopePos
|
||||||
|
sightFrom | isCam = camCenter
|
||||||
|
| otherwise = ypos
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
updateScopeZoom :: World -> World
|
||||||
|
updateScopeZoom w
|
||||||
|
| SDL.ButtonRight `S.member` _mouseButtons w = case w ^? scppoint of
|
||||||
|
Just x
|
||||||
|
| x > 9 -> zoomInLongGun $ zoomInLongGun
|
||||||
|
$ w & creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopeZoomChange -~ 2
|
||||||
|
| x > 0 -> zoomInLongGun
|
||||||
|
$ w & creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopeZoomChange -~ 1
|
||||||
|
| x < -9 -> zoomOutLongGun $ zoomOutLongGun
|
||||||
|
$ w & creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopeZoomChange +~ 2
|
||||||
|
| x < 0 -> zoomOutLongGun $ zoomOutLongGun
|
||||||
|
$ w & creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopeZoomChange +~ 1
|
||||||
|
| otherwise -> w
|
||||||
|
_ -> w
|
||||||
|
| otherwise = w & creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just %~ updateScope
|
||||||
|
where scppoint = creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopeZoomChange
|
||||||
|
updateScope (ItScope _ _ _ bl) = ItScope (0,0) 0 1 bl
|
||||||
|
updateScope otherAtt = otherAtt
|
||||||
|
|
||||||
|
zoomSpeed = 39/40
|
||||||
|
|
||||||
|
zoomInLongGun :: World -> World
|
||||||
|
zoomInLongGun w | currentZoom < 8 = over (wpPointer . itAttachment . _Just . scopePos)
|
||||||
|
(\p -> p +.+ (4-(4*zoomSpeed)) / currentZoom *.* mousep)
|
||||||
|
$ over (wpPointer . itAttachment . _Just . scopeZoom) (\z -> z / zoomSpeed)
|
||||||
|
w
|
||||||
|
| otherwise = w
|
||||||
|
where wpPointer = creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
wp = _crInv (_creatures w IM.! 0) IM.! (_crInvSel (_creatures w IM.! 0))
|
||||||
|
Just currentZoom = wp ^? itAttachment . _Just . scopeZoom
|
||||||
|
mousep = rotateV (_cameraRot w) $ _mousePos w
|
||||||
|
|
||||||
|
zoomOutLongGun :: World -> World
|
||||||
|
zoomOutLongGun w | currentZoom > 0.5 = over (wpPointer . itAttachment . _Just . scopePos)
|
||||||
|
-- (\p -> p -.- 2/(2 * currentZoom) *.* _mousePos w)
|
||||||
|
-- (\p -> p -.- 2*zoomSpeed/currentZoom *.* p)
|
||||||
|
(\p -> p)
|
||||||
|
$ over (wpPointer . itAttachment . _Just . scopeZoom) (\z -> z * zoomSpeed)
|
||||||
|
w
|
||||||
|
| otherwise = w
|
||||||
|
where wpPointer = creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
wp = _crInv (_creatures w IM.! 0) IM.! (_crInvSel (_creatures w IM.! 0))
|
||||||
|
Just currentZoom = wp ^? itAttachment . _Just . scopeZoom
|
||||||
|
currentCursorDisplacement = fromJust $ _itAttachment wp
|
||||||
|
|
||||||
|
rotCam = rotateCameraL . rotateCameraR . zoomCamIn . zoomCamOut . zoomCam
|
||||||
|
|
||||||
|
rotateCameraL :: World -> World
|
||||||
|
rotateCameraL w | SDL.KeycodeQ `S.member` _keys w
|
||||||
|
= w & cameraRot +~ 0.015
|
||||||
|
& creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopePos %~ rotateV 0.015
|
||||||
|
| otherwise = w
|
||||||
|
rotateCameraR :: World -> World
|
||||||
|
rotateCameraR w | SDL.KeycodeE `S.member` _keys w
|
||||||
|
= w & cameraRot -~ 0.015
|
||||||
|
& creatures . ix 0 . crInv . ix (_crInvSel (_creatures w IM.! 0))
|
||||||
|
. itAttachment . _Just . scopePos %~ rotateV (-0.015)
|
||||||
|
| otherwise = w
|
||||||
|
zoomCamIn :: World -> World
|
||||||
|
zoomCamIn w | SDL.KeycodeJ `S.member` _keys w
|
||||||
|
= w {_cameraZoom = _cameraZoom w + 0.01}
|
||||||
|
| otherwise = w
|
||||||
|
zoomCamOut :: World -> World
|
||||||
|
zoomCamOut w | SDL.KeycodeK `S.member` _keys w
|
||||||
|
= w {_cameraZoom = max (_cameraZoom w - 0.01) 0.01}
|
||||||
|
| otherwise = w
|
||||||
|
|
||||||
|
-- the 395 here limits the max zoom out, is 25 less than the current screen
|
||||||
|
-- size, thus matches up with it
|
||||||
|
zoomCam :: World -> World
|
||||||
|
--zoomCam w = w
|
||||||
|
zoomCam w = over cameraZoom changeZoom w
|
||||||
|
where maxViewDistance = 800
|
||||||
|
camPos = _cameraCenter w
|
||||||
|
camRot = _cameraRot w
|
||||||
|
wallZoom = min (halfWidth w / (horizontalMax+10) )
|
||||||
|
(halfHeight w / (verticalMax+10) )
|
||||||
|
idealZoom | SDL.ButtonRight `S.member` _mouseButtons w
|
||||||
|
= scZoom *
|
||||||
|
(
|
||||||
|
min (fromMaybe 20 $ yourItem w ^? itZoom . itAimZoomMax)
|
||||||
|
$ max (fromMaybe 0.2 $ yourItem w ^? itZoom . itAimZoomMin)
|
||||||
|
(wallZoom * fromMaybe 1 (yourItem w ^? itZoom . itAimZoomFac))
|
||||||
|
)
|
||||||
|
| otherwise
|
||||||
|
= min (fromMaybe 20 $ yourItem w ^? itZoom . itZoomMax)
|
||||||
|
$ max (fromMaybe 0.2 (yourItem w ^? itZoom . itZoomMin))
|
||||||
|
(wallZoom * fromMaybe 1 (yourItem w ^? itZoom . itZoomFac))
|
||||||
|
changeZoom curZoom | curZoom > idealZoom + 0.01 = 0.05 * (19*curZoom + idealZoom)
|
||||||
|
| curZoom < idealZoom - 0.01 = 0.05 * (19*curZoom + idealZoom)
|
||||||
|
| otherwise = idealZoom
|
||||||
|
-- zs = take 20 [-maxViewDistance,0 - 0.9*maxViewDistance..]
|
||||||
|
zs = take 9 [-maxViewDistance,0 - 0.8*maxViewDistance..]
|
||||||
|
rRays = rotF [( maxViewDistance,y) | y <- zs]
|
||||||
|
lRays = rotF [(-maxViewDistance,y) | y <- zs]
|
||||||
|
tRays = rotF [(y, maxViewDistance) | y <- zs]
|
||||||
|
bRays = rotF [(y,-maxViewDistance) | y <- zs]
|
||||||
|
rotF = map (h . (+.+) camPos . rotateV (_cameraRot w))
|
||||||
|
h p = fromMaybe p $ collidePointIndirect camPos p $ wallsAlongLine camPos p w
|
||||||
|
h' x p = dotV (rotateV camRot x) (p -.- camPos)
|
||||||
|
horizontalMax = maximum $ map (h' (1,0)) rRays ++ map (h' (-1,0)) lRays
|
||||||
|
verticalMax = maximum $ map (h' (0,1)) tRays ++ map (h' (0,-1)) bRays
|
||||||
|
scZoom = fromMaybe 1 $ yourItem w ^? itAttachment . _Just . scopeZoom
|
||||||
|
|
||||||
|
dirRays :: Float -> [Point2]
|
||||||
|
dirRays dir = take 11 $ iterate (rotateV (0.5 * pi / 10)) $ rotateV (dir-pi*0.25) (600,0)
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
module Dodge.CreatureActions where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
import Dodge.WorldActions
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Applicative
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.List
|
||||||
|
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
moveForwardSpeed :: Float -> Int -> World -> World
|
||||||
|
moveForwardSpeed x n w = over (creatures . ix n . crPos) (+.+ p) w
|
||||||
|
where p = (*.*) x $ unitVectorAtAngle $ _crDir $ _creatures w IM.! n
|
||||||
|
|
||||||
|
mvForward :: Float -> Int -> World -> World
|
||||||
|
mvForward speed cid w = stepForward cid speed $ over (creatures . ix cid . crPos) (+.+ p) w
|
||||||
|
where p = (*.*) (speed * equipFactor) $ unitVectorAtAngle $ _crDir $ _creatures w IM.! cid
|
||||||
|
equipFactor = product $ map equipSpeed $ IM.elems $ _crInv $ _creatures w IM.! cid
|
||||||
|
|
||||||
|
crStrafeLeft :: Float -> Creature -> Creature
|
||||||
|
crStrafeLeft speed cr = stepForward' s2 $ over crPos (+.+ p) cr
|
||||||
|
where p = (*.*) s2 $ unitVectorAtAngle $ (_crDir cr + pi/2)
|
||||||
|
equipFactor = product $ map equipSpeed $ IM.elems $ _crInv cr
|
||||||
|
s2 = (speed * equipFactor)
|
||||||
|
crStrafeRight :: Float -> Creature -> Creature
|
||||||
|
crStrafeRight speed cr = stepForward' s2 $ over crPos (+.+ p) cr
|
||||||
|
where p = (*.*) s2 $ unitVectorAtAngle $ (_crDir cr - pi/2)
|
||||||
|
equipFactor = product $ map equipSpeed $ IM.elems $ _crInv cr
|
||||||
|
s2 = (speed * equipFactor)
|
||||||
|
|
||||||
|
crMvForward :: Float -> Creature -> Creature
|
||||||
|
crMvForward speed cr = over crPos (+.+ p) cr
|
||||||
|
where p = (*.*) (speed * equipFactor) $ unitVectorAtAngle $ _crDir cr
|
||||||
|
equipFactor = product $ map equipSpeed $ IM.elems $ _crInv cr
|
||||||
|
|
||||||
|
crMvBy :: Point2 -> Creature -> Creature
|
||||||
|
crMvBy p' cr = stepForward' (magV p) $ over crPos (+.+ p) cr
|
||||||
|
where p = (*.*) (equipFactor * aimingFactor) $ rotateV (_crDir cr) p'
|
||||||
|
equipFactor = product $ map equipSpeed $ IM.elems $ _crInv cr
|
||||||
|
aimingFactor | (_posture $ _stance $ _crState cr) == Aiming
|
||||||
|
= fromMaybe 1 $ it ^? itAimingSpeed
|
||||||
|
| otherwise = 1
|
||||||
|
it = _crInv cr IM.! _crInvSel cr
|
||||||
|
|
||||||
|
stepForward :: Int -> Float -> World -> World
|
||||||
|
stepForward cid speed = over (creatures . ix cid . crState . stance . carriage) f
|
||||||
|
where f (w@Walking {}) = w {_stepToAdd = ceiling speed}
|
||||||
|
f s = s
|
||||||
|
|
||||||
|
stepForward' :: Float -> Creature -> Creature
|
||||||
|
stepForward' speed cr = over (crState . stance . carriage) f cr
|
||||||
|
where f (w@Walking {}) = w {_stepToAdd = ceiling speed}
|
||||||
|
f s = s
|
||||||
|
|
||||||
|
equipSpeed NoItem = 1
|
||||||
|
equipSpeed it | _itIdentity it == FrontArmour = 0.75
|
||||||
|
| _itIdentity it == FlameShield = 0.75
|
||||||
|
| otherwise = 1
|
||||||
|
|
||||||
|
turnTo :: Point2 -> Int -> World -> World
|
||||||
|
turnTo p n w = set (creatures . ix n . crDir) dirToTarget w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
dirToTarget = argV (p -.- _crPos cr)
|
||||||
|
|
||||||
|
turnToward :: Point2 -> Int -> World -> World
|
||||||
|
turnToward p n w | isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= f $ over (creatures . ix n . crDir)
|
||||||
|
(normalizeAngle . (+ (0.03 + r*m))) w
|
||||||
|
| otherwise = f $ over (creatures . ix n . crDir)
|
||||||
|
(\x->normalizeAngle (x - (0.03 + r*m))) w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
dirToTarget = argV (p -.- _crPos cr)
|
||||||
|
(r,g) = randomR (-0.1,0.1) (_randGen w)
|
||||||
|
m = diffAngles dirToTarget (_crDir cr)
|
||||||
|
f = set randGen g
|
||||||
|
|
||||||
|
turnTowardAngle :: Float -> Int -> World -> World
|
||||||
|
turnTowardAngle a n w
|
||||||
|
| isLeftOfA a (_crDir cr) = f $ over (creatures . ix n . crDir)
|
||||||
|
(normalizeAngle . (+ (0.03 + r*m))) w
|
||||||
|
| otherwise = f $ over (creatures . ix n . crDir)
|
||||||
|
(\x->normalizeAngle (x - (0.03 + r*m))) w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
(r,g) = randomR (-0.1,0.1) (_randGen w)
|
||||||
|
m = diffAngles a (_crDir cr)
|
||||||
|
f = set randGen g
|
||||||
|
|
||||||
|
turnToward'' :: Float -> Point2 -> Int -> World -> World
|
||||||
|
turnToward'' turnSpeed p n w
|
||||||
|
| isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= f $ over (creatures . ix n . crDir) (normalizeAngle. (+ (turnSpeed + r))) w
|
||||||
|
| otherwise = f $ over (creatures . ix n . crDir) (\x->normalizeAngle (x - (turnSpeed + r))) w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
dirToTarget = argV (p -.- _crPos cr)
|
||||||
|
(r,g) = randomR (-0.1,0.1) (_randGen w)
|
||||||
|
f = set randGen g
|
||||||
|
|
||||||
|
crTurnAndStrafeIn :: Float -> Float -> Point2 -> Creature -> Creature
|
||||||
|
crTurnAndStrafeIn strafeSpeed turnSpeed p cr
|
||||||
|
| vToTarg == (0,0) = cr -- this should deal with the angleVV error
|
||||||
|
| errorAngleVV 3 vToTarg (unitVectorAtAngle (_crDir cr)) <= turnSpeed
|
||||||
|
= set crDir dirToTarget cr
|
||||||
|
| isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= over crDir (normalizeAngle . (+ turnAmount ))
|
||||||
|
$ crStrafeLeft strafeSpeed cr
|
||||||
|
| otherwise = over crDir (\x->normalizeAngle (x - turnAmount))
|
||||||
|
$ crStrafeRight strafeSpeed cr
|
||||||
|
where vToTarg = p -.- _crPos cr
|
||||||
|
dirToTarget = argV vToTarg
|
||||||
|
turnAmount = turnSpeed
|
||||||
|
|
||||||
|
crTurnTo :: Point2 -> Creature -> Creature
|
||||||
|
crTurnTo p cr = set crDir dirToTarget cr
|
||||||
|
where dirToTarget = argV (p -.- _crPos cr)
|
||||||
|
|
||||||
|
crTurnTowardSpeed :: Float -> Point2 -> Creature -> Creature
|
||||||
|
crTurnTowardSpeed turnSpeed p cr
|
||||||
|
| vToTarg == (0,0) = cr -- this should deal with the angleVV error
|
||||||
|
| errorAngleVV 3 vToTarg (unitVectorAtAngle (_crDir cr)) <= turnSpeed
|
||||||
|
= set crDir dirToTarget cr
|
||||||
|
| isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= over crDir (normalizeAngle . (+ turnAmount )) cr
|
||||||
|
| otherwise = over crDir (\x->normalizeAngle (x - turnAmount)) cr
|
||||||
|
where vToTarg = p -.- _crPos cr
|
||||||
|
dirToTarget = argV vToTarg
|
||||||
|
turnAmount = turnSpeed
|
||||||
|
|
||||||
|
turnTowardSpeed :: Float -> Point2 -> Int -> World -> World
|
||||||
|
turnTowardSpeed turnSpeed p n w
|
||||||
|
| vToTarg == (0,0) = w -- this should deal with the angleVV error
|
||||||
|
| errorAngleVV 3 vToTarg (unitVectorAtAngle (_crDir cr)) <= turnSpeed
|
||||||
|
= set (creatures . ix n . crDir) dirToTarget w
|
||||||
|
| isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= over (creatures . ix n . crDir) (normalizeAngle . (+ turnAmount )) w
|
||||||
|
| otherwise = over (creatures . ix n . crDir) (\x->normalizeAngle (x - turnAmount)) w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
vToTarg = p -.- _crPos cr
|
||||||
|
dirToTarget = argV vToTarg
|
||||||
|
turnAmount = turnSpeed
|
||||||
|
|
||||||
|
turnLRS :: Int -> World -> World
|
||||||
|
turnLRS n w = f $ over (creatures . ix n . crDir) (+ (fromIntegral r * pi / 2)) w
|
||||||
|
where (r,g) = randomR (negate 1::Int,1) (_randGen w)
|
||||||
|
f = set randGen g
|
||||||
|
|
||||||
|
turnToward' :: Point2 -> Int -> World -> World
|
||||||
|
turnToward' p n w | isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= over (creatures . ix n . crDir) (+ 0.03) w
|
||||||
|
| otherwise = over (creatures . ix n . crDir) (\x-> x - 0.03) w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
dirToTarget = argV (p -.- _crPos cr)
|
||||||
|
|
||||||
|
overturnToward :: Point2 -> Int -> World -> World
|
||||||
|
overturnToward p n w
|
||||||
|
| isLeftOfA dirToTarget (_crDir cr)
|
||||||
|
= over (creatures . ix n . crDir) (normalizeAngle. (+ 0.03 )) w
|
||||||
|
| otherwise = over (creatures . ix n . crDir) (\x->normalizeAngle (x - 0.03 )) w
|
||||||
|
where cr = _creatures w IM.! n
|
||||||
|
dirToTarget = argV (p -.- _crPos cr)
|
||||||
|
|
||||||
|
moveForward :: Int -> World -> World
|
||||||
|
moveForward n w = over (creatures . ix n . crPos) (+.+ p) w
|
||||||
|
where p = unitVectorAtAngle $ _crDir $ _creatures w IM.! n
|
||||||
|
|
||||||
|
moveToward :: Point2 -> Int -> World -> World
|
||||||
|
moveToward p n w
|
||||||
|
| angle < 10 = moveForwardSpeed 0.1 n w
|
||||||
|
| angle < 20 = moveForwardSpeed 0.02 n w
|
||||||
|
| angle < 40 = moveForwardSpeed 0.01 n w
|
||||||
|
| otherwise = w
|
||||||
|
where angle = errorAngleVV 5 (p -.- curPos) (unitVectorAtAngle (_crDir (_creatures w IM.! n)))
|
||||||
|
curPos = _crPos (_creatures w IM.! n)
|
||||||
|
|
||||||
|
moveBy :: Int -> Point2 -> World -> World
|
||||||
|
moveBy n v = over (creatures . ix n . crPos) (+.+ v)
|
||||||
|
|
||||||
|
reloadWeapon :: Int -> World -> Maybe World
|
||||||
|
reloadWeapon cid w = if _unlimitedAmmo w then reloadWeaponUnlimited cid w
|
||||||
|
else reloadWeaponAmmoCheck cid w
|
||||||
|
|
||||||
|
reloadWeaponUnlimited :: Int -> World -> Maybe World
|
||||||
|
reloadWeaponUnlimited cid w =
|
||||||
|
let cr = _creatures w IM.! cid
|
||||||
|
it = _crInv cr IM.! _crInvSel cr
|
||||||
|
itRef = creatures . ix cid . crInv . ix (_crInvSel cr)
|
||||||
|
in case it of
|
||||||
|
Weapon {_wpMaxAmmo=maxA,_wpLoadedAmmo=lA,_wpReloadState=rS,_wpReloadTime=rT}
|
||||||
|
| lA < maxA && rS == 0 -> Just $ set ( itRef . wpLoadedAmmo) maxA
|
||||||
|
$ set ( itRef . wpReloadState) rT w
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
reloadWeaponAmmoCheck :: Int -> World -> Maybe World
|
||||||
|
reloadWeaponAmmoCheck cid w =
|
||||||
|
let cr = _creatures w IM.! cid
|
||||||
|
it = _crInv cr IM.! _crInvSel cr
|
||||||
|
itRef = creatures . ix cid . crInv . ix (_crInvSel cr)
|
||||||
|
in case it of
|
||||||
|
Weapon {_wpMaxAmmo=maxA,_wpLoadedAmmo=lA,_wpAmmoType=aT
|
||||||
|
,_wpReloadState=rS ,_wpReloadTime=rT}
|
||||||
|
-> do guard $ lA < maxA && rS == 0 && M.findWithDefault 0 aT (_crAmmo cr) > 0
|
||||||
|
toAdd <- Just $ min (_crAmmo (you w) M.! aT) (maxA - lA)
|
||||||
|
return $ over (creatures . ix cid . crAmmo) (M.adjust (\x -> x - toAdd) aT)
|
||||||
|
$ over ( itRef . wpLoadedAmmo) (\x -> x + toAdd)
|
||||||
|
$ set ( itRef . wpReloadState) rT w
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
|
||||||
|
useItemContinuous :: Int -> World -> World
|
||||||
|
useItemContinuous n w = case (_crInv cr IM.! _crInvSel cr) ^? itHammer of
|
||||||
|
Just HammerUp -> useItem1 n $ setHammerDown w
|
||||||
|
Just NoHammer -> useItem1 n w
|
||||||
|
_ -> setHammerDown w
|
||||||
|
where
|
||||||
|
cr = _creatures w IM.! n
|
||||||
|
setHammerDown = creatures . ix n . crInv . ix (_crInvSel cr) . itHammer .~ HammerDown
|
||||||
|
|
||||||
|
tryUseItem :: Int -> World -> World
|
||||||
|
tryUseItem cid w = case w ^? creatures . ix cid of
|
||||||
|
Just cr -> useItem2 cid (_crInv cr IM.! _crInvSel cr) w
|
||||||
|
Nothing -> w
|
||||||
|
|
||||||
|
useItem1 :: Int -> World -> World
|
||||||
|
useItem1 n w = useItem2 n it w
|
||||||
|
where c = (_creatures w IM.! n)
|
||||||
|
it = _crInv c IM.! _crInvSel c
|
||||||
|
|
||||||
|
useItem2 :: Int -> Item -> World -> World
|
||||||
|
useItem2 n (Consumable {_cnEffect=eff }) w = fromMaybe w $ fmap (rmInvItem n) $ eff n w
|
||||||
|
useItem2 n (Weapon {_wpFire=eff}) w = eff n w
|
||||||
|
useItem2 n (Throwable {_twFire = eff}) w = eff n w
|
||||||
|
useItem2 _ _ w = w
|
||||||
|
|
||||||
|
rmInvItem :: Int -> World -> World
|
||||||
|
rmInvItem n w =
|
||||||
|
let i = _crInvSel (_creatures w IM.! n)
|
||||||
|
item = _crInv (_creatures w IM.! n) IM.! i
|
||||||
|
itRef = creatures . ix n . crInv . ix i
|
||||||
|
in case item of
|
||||||
|
Consumable {_itAmount = 1} -> set itRef NoItem w
|
||||||
|
Craftable {_itAmount = 1} -> set itRef NoItem w
|
||||||
|
Equipment {_itAmount = 1} -> set itRef NoItem w
|
||||||
|
Throwable {_itAmount = 1} -> set itRef NoItem w
|
||||||
|
Consumable {_itAmount = x} -> over (itRef . itAmount) (\y-> y-1) w
|
||||||
|
Craftable {_itAmount = x} -> over (itRef . itAmount) (\y-> y-1) w
|
||||||
|
Equipment {_itAmount = x} -> over (itRef . itAmount) (\y-> y-1) w
|
||||||
|
Throwable {_itAmount = x} -> over (itRef . itAmount) (\y-> y-1) w
|
||||||
|
_ -> set itRef NoItem w
|
||||||
|
|
||||||
|
dropItem :: World -> World
|
||||||
|
dropItem w = case yourItem w of
|
||||||
|
NoItem -> w
|
||||||
|
it -> rmInvItem (_yourID w) $ over floorItems (IM.insert flid theflit)
|
||||||
|
$ updateLocation
|
||||||
|
$ soundOnce putDownSound
|
||||||
|
$ set randGen g w
|
||||||
|
where (rot, g) = randomR (-pi,pi) $ _randGen w
|
||||||
|
offset = _crRad (you w) *.* unitVectorAtAngle rot
|
||||||
|
updateLocation w = case it ^? itID of
|
||||||
|
Just (Just i) -> w & itemPositions . ix i .~ OnFloor flid
|
||||||
|
_ -> w
|
||||||
|
flid = newKey $ _floorItems w
|
||||||
|
theflit = FlIt {_flIt = set itAmount 1 it
|
||||||
|
,_flItPos = offset +.+ _crPos (you w)
|
||||||
|
,_flItRot = rot
|
||||||
|
,_flItID = flid}
|
||||||
|
|
||||||
|
useItemAmmoCheck :: Int -> World -> World
|
||||||
|
useItemAmmoCheck n w
|
||||||
|
| hasAmmo = useItem1 n w
|
||||||
|
| otherwise = w
|
||||||
|
where c = _creatures w IM.! n
|
||||||
|
hasAmmo = case _crInv c IM.! _crInvSel c of
|
||||||
|
Weapon {_wpLoadedAmmo=0, _wpAmmoType=ammoT}
|
||||||
|
-> case _crAmmo c M.!? ammoT of
|
||||||
|
Just x -> x > 0
|
||||||
|
_ -> False
|
||||||
|
Weapon {} -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
|
|
||||||
|
closestItem :: World -> Maybe Int
|
||||||
|
closestItem w = fmap fst $ listToMaybe $ sortBy cf $ IM.toList
|
||||||
|
$ fmap (dist yourP . _flItPos) $ _floorItems w
|
||||||
|
where yourP = _crPos $ you w
|
||||||
|
cf (_,x) (_,y) = compare x y
|
||||||
|
|
||||||
|
pickUpItem' :: FloorItem -> World -> World
|
||||||
|
pickUpItem' flit w = case maybeInvSlot of
|
||||||
|
Nothing -> w
|
||||||
|
Just i -> soundOnce pickUpSound
|
||||||
|
$ updateItLocation i
|
||||||
|
$ w & floorItems %~ IM.delete (_flItID flit)
|
||||||
|
& creatures . ix 0 . crInv . ix i %~ addItem' it
|
||||||
|
where
|
||||||
|
it = _flIt flit
|
||||||
|
maybeInvSlot = checkInvSlots it (_crInv (_creatures w IM.! 0))
|
||||||
|
updateItLocation invid w'
|
||||||
|
= case _itID it of Nothing -> w'
|
||||||
|
Just j -> w' & itemPositions . ix j .~ InInv 0 invid
|
||||||
|
|
||||||
|
checkInvSlots :: Item -> IM.IntMap Item -> Maybe Int
|
||||||
|
checkInvSlots it its = fmap fst $ find cond $ IM.toList its
|
||||||
|
where cond (_,NoItem) = True
|
||||||
|
cond (_,it' ) = itNotFull it' && _itName it == _itName it'
|
||||||
|
|
||||||
|
addItem' :: Item -> Item -> Item
|
||||||
|
addItem' it NoItem = it
|
||||||
|
addItem' it it' = it' & itAmount +~ 1
|
||||||
|
|
||||||
|
numInventorySlots :: Int
|
||||||
|
numInventorySlots = 9
|
||||||
|
|
||||||
|
itNotFull :: Item -> Bool
|
||||||
|
itNotFull it = _itMaxStack it > _itAmount it
|
||||||
|
|
||||||
|
createItemAt :: Point2 -> FloorItem -> World -> World
|
||||||
|
createItemAt p it w = over floorItems (IM.insert i (set flItPos p
|
||||||
|
$ set flItID i it)) w
|
||||||
|
where i = newKey (_floorItems w)
|
||||||
|
|
||||||
|
blinkAction :: Int -> World -> World
|
||||||
|
blinkAction n w = soundOnce teleSound $ set (creatures . ix n . crPos) p3
|
||||||
|
$ blinkShockwave p3
|
||||||
|
$ inverseShockwaveAt cp 40 2 2 2
|
||||||
|
w
|
||||||
|
where p1 = _cameraPos w +.+ (1 / _cameraZoom w) *.* rotateV (_cameraRot w) (_mousePos w)
|
||||||
|
cp = _crPos $ _creatures w IM.! n
|
||||||
|
p2 = collidePointWalls cp p1 $ wallsAlongLine cp p1 w
|
||||||
|
r = 1.5 * _crRad (_creatures w IM.! n)
|
||||||
|
p3 = fromMaybe p1 (fmap ((\p -> moveAmountToward p r cp) . fst) p2)
|
||||||
|
|
||||||
|
blinkShockwave :: Point2 -> World -> World
|
||||||
|
blinkShockwave p = makeShockwaveAt p 40 1 2 cyan
|
||||||
|
|
||||||
|
moveAmountToward :: Point2 -> Float -> Point2 -> Point2
|
||||||
|
moveAmountToward p1 am p2
|
||||||
|
= p1 +.+ am *.* errorNormalizeV 44 (p2 -.- p1)
|
||||||
|
|
||||||
|
facePointAI :: Point2 -> Int -> World -> World
|
||||||
|
facePointAI p n w = set (creatures . ix n . crDir)
|
||||||
|
(argV (p -.- _crPos (_creatures w IM.! n)) )
|
||||||
|
w
|
||||||
|
|
||||||
|
reverseDir :: Int -> World -> World
|
||||||
|
reverseDir n = over (creatures . ix n . crDir) (+ pi)
|
||||||
|
|
||||||
|
turnBy :: Float -> Int -> World -> World
|
||||||
|
turnBy a n = over (creatures . ix n . crDir) (+ a)
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
module Dodge.CreatureState where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
import Dodge.RandomHelp
|
||||||
|
import Dodge.WorldActions
|
||||||
|
import Dodge.WallCreatureCollisions
|
||||||
|
import Dodge.CreatureActions
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Char
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
import Data.Graph.Inductive.PatriciaTree
|
||||||
|
import Data.Graph.Inductive.Query.SP
|
||||||
|
import Codec.BMP
|
||||||
|
import qualified Data.ByteString as B
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.State
|
||||||
|
import Control.Monad
|
||||||
|
import qualified SDL as SDL
|
||||||
|
import qualified SDL.Mixer as Mix
|
||||||
|
import System.Random
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Foreign.ForeignPtr
|
||||||
|
import Control.Concurrent
|
||||||
|
---}
|
||||||
|
|
||||||
|
type CRUpdate = World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
|
||||||
|
|
||||||
|
-- the movement is updated before the ai in order to correctly set the oldpos
|
||||||
|
-- the whole of this update cycle could do with a rethink, it is becoming
|
||||||
|
-- convoluted
|
||||||
|
stateUpdate :: CRUpdate -> CRUpdate
|
||||||
|
stateUpdate u w (f,g) cr = case u w (f,g) (updateMovement g cr) of
|
||||||
|
((f',g') , maybeCr) -> ( (invSideEff cr . movementSideEff cr . dropifdead . f'
|
||||||
|
, g')
|
||||||
|
, fmap (updateReloadCounter . reducePastDamage . doDamage)
|
||||||
|
$ crOrCorpse =<< maybeCr
|
||||||
|
)
|
||||||
|
where
|
||||||
|
crOrCorpse cr | cr ^. crHP > 0 = Just cr
|
||||||
|
| otherwise = Nothing
|
||||||
|
dropifdead | cr ^.crHP > 0 = id
|
||||||
|
| otherwise = stopSoundFrom (CrWeaponSound (_crID cr))
|
||||||
|
. over decorations addCorpse
|
||||||
|
. insertIt
|
||||||
|
crBeforeDeath = colCrWall w $ cr
|
||||||
|
-- addCorpse = insertNewKey $ uncurry translateDrawing (_crPos crBeforeDeath)
|
||||||
|
addCorpse = insertNewKey $ uncurry translateDrawing (_crOldPos cr)
|
||||||
|
$ rotateDrawing (radToDeg (_crDir cr))
|
||||||
|
(_crCorpse cr)
|
||||||
|
maybeIt = evalState (maybeTakeOne $ IM.elems (_crInv cr)) (_randGen w)
|
||||||
|
insertIt = case maybeIt of
|
||||||
|
-- Just it -> createItemAt (offset +.+ _crPos crBeforeDeath)
|
||||||
|
Just it -> createItemAt (offset +.+ _crOldPos cr)
|
||||||
|
(FlIt {_flIt=it,_flItPos=(0,0),_flItRot=rot,_flItID=0})
|
||||||
|
Nothing -> id
|
||||||
|
offset = _crRad cr *.* unitVectorAtAngle rot
|
||||||
|
(rot,_) = randomR (-pi,pi) g
|
||||||
|
|
||||||
|
|
||||||
|
setOldPos :: Creature -> Creature
|
||||||
|
setOldPos cr = set crOldPos (_crPos cr) cr
|
||||||
|
|
||||||
|
doDamage :: Creature -> Creature
|
||||||
|
doDamage cr = set (crState . crDamage) [] $ over (crState . crPastDamage) (+ hpLost)
|
||||||
|
damagedCr
|
||||||
|
where dams = _crDamage $ _crState cr
|
||||||
|
startHP = _crHP cr
|
||||||
|
damagedCr = snd $ (_crApplyDamage (_crState cr)) dams cr
|
||||||
|
-- applyDamage (cr ^. crState . crDamage) cr
|
||||||
|
afterHP = _crHP damagedCr
|
||||||
|
hpLost = startHP - afterHP -- note this can be negative
|
||||||
|
-- applyDamage :: DamageType -> Creature -> Creature
|
||||||
|
-- applyDamage dt cr = snd $ (cr ^. crState . crApplyDamage) dt cr
|
||||||
|
|
||||||
|
sumDamage :: Creature -> DamageType -> Int -> Int
|
||||||
|
sumDamage cr dm x = x + _dmAmount dm
|
||||||
|
|
||||||
|
reducePastDamage :: Creature -> Creature
|
||||||
|
reducePastDamage = over (crState . crPastDamage) rdpdam
|
||||||
|
where rdpdam x | x > 500 = x - 55
|
||||||
|
| x > 200 = x - 25
|
||||||
|
| x > 20 = x - 5
|
||||||
|
| x > 0 = x - 1
|
||||||
|
| otherwise = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
movementSideEff :: Creature -> World -> World
|
||||||
|
movementSideEff cr w
|
||||||
|
| hasJetPack
|
||||||
|
= case cr ^? crState . stance . carriage of
|
||||||
|
Just (Boosting v)
|
||||||
|
-> makeFlamelet (oldPos +.+ (_crRad cr + 3) *.* (unitVectorAtAngle $ _crDir cr + pi))
|
||||||
|
(momentum +.+ 1 *.* rotateV randDir (vInverse v))
|
||||||
|
66
|
||||||
|
Nothing
|
||||||
|
1
|
||||||
|
-- $ smokeGen v
|
||||||
|
$ set randGen g
|
||||||
|
w
|
||||||
|
_ -> w
|
||||||
|
| otherwise = case cr ^? crState . stance . carriage of
|
||||||
|
Just (Walking x y) -> takeStep x y w
|
||||||
|
_ -> w
|
||||||
|
where hasJetPack = any (\it -> it ^? itIdentity == Just JetPack) $ _crInv cr
|
||||||
|
oldPos = _crOldPos cr
|
||||||
|
momentum' = 0.97 *.* (_crPos cr -.- _crOldPos cr)
|
||||||
|
momentum'' | magV momentum' > 3 = 3 *.* normalizeV momentum'
|
||||||
|
| otherwise = momentum'
|
||||||
|
momentum = momentum'' +.+ 0.01 *.* unitVectorAtAngle randAng
|
||||||
|
(randDir,g) = randomR (-0.5,0.5) $ _randGen w
|
||||||
|
(randAng,_) = randomR (0,2*pi) $ _randGen w
|
||||||
|
|
||||||
|
(v1:v2:v3:_) = fst $ runState ((sequence . repeat . randInCirc) 0.1) $ _randGen w
|
||||||
|
(r1:r2:r3:_) = map ((*.*) 100) (v2:v3:v1:[])
|
||||||
|
(t1:t2:t3:_) = randomRs ( 15,20) $ _randGen w
|
||||||
|
(s1:s2:s3:_) = map (\s -> fromIntegral s * 0.1 - 1) (t3:t2:t1:[])
|
||||||
|
smokeGen v = makeSmokeAt'' v1 s1 t1
|
||||||
|
(r1 +.+ oldPos +.+ ((_crRad cr + 3) *.* vInverse (unitVectorAtAngle $ _crDir cr)))
|
||||||
|
|
||||||
|
crHasMoved = dist (_crPos cr) (_crOldPos cr) > 0.5
|
||||||
|
takeStep x y | crHasMoved && x < 20 && x + y >= 20 = soundMultiFrom footor 22 3 0
|
||||||
|
| crHasMoved && x < 80 && x + y >= 80 = soundMultiFrom footor 23 3 0
|
||||||
|
| otherwise = id
|
||||||
|
footor = [FootstepSound 0,FootstepSound 1]
|
||||||
|
|
||||||
|
invSideEff :: Creature -> World -> World
|
||||||
|
invSideEff cr w = weaponReloadSounds cr $ IM.foldrWithKey f w (_crInv cr)
|
||||||
|
where f i it w' = case it ^? itEffect . itInvEffect of
|
||||||
|
Nothing -> w'
|
||||||
|
Just g -> g cr i w'
|
||||||
|
|
||||||
|
weaponReloadSounds :: Creature -> World -> World
|
||||||
|
weaponReloadSounds cr w = case _crInv cr IM.!? _crInvSel cr of
|
||||||
|
Just (Weapon {_wpReloadState = 0})
|
||||||
|
-> w
|
||||||
|
Just (Weapon {_wpReloadState = 1})
|
||||||
|
-> stopSoundFrom (CrReloadSound cid) w
|
||||||
|
Just (Weapon {})
|
||||||
|
-> soundFrom (CrReloadSound cid) reloadSound (1) 0 w
|
||||||
|
_
|
||||||
|
-> w
|
||||||
|
where cid = _crID cr
|
||||||
|
|
||||||
|
updateMovement :: StdGen -> Creature -> Creature
|
||||||
|
updateMovement g cr
|
||||||
|
| isFrictionless cr = over crPos (+.+ momentum)
|
||||||
|
$ setOldPos
|
||||||
|
cr
|
||||||
|
| otherwise
|
||||||
|
= case cr ^? crState . stance . carriage of
|
||||||
|
Just (Walking x y)
|
||||||
|
-> set (crState . stance . carriage) (Walking ((x+y)`mod`120) 0)
|
||||||
|
$ setOldPos
|
||||||
|
cr
|
||||||
|
_ -> set (crState . stance . carriage) (Walking 0 0)
|
||||||
|
$ setOldPos
|
||||||
|
cr
|
||||||
|
where
|
||||||
|
momentum' = 0.97 *.* (_crPos cr -.- _crOldPos cr)
|
||||||
|
momentum'' | magV momentum' > 3 = 3 *.* normalizeV momentum'
|
||||||
|
| otherwise = momentum'
|
||||||
|
momentum = momentum'' +.+ 0.01 *.* unitVectorAtAngle randAng
|
||||||
|
(randAng,_) = randomR (0,2*pi) g
|
||||||
|
|
||||||
|
isFrictionless :: Creature -> Bool
|
||||||
|
isFrictionless cr = case cr ^? crState . stance . carriage of
|
||||||
|
Just (Boosting _) -> True
|
||||||
|
Just (Floating) -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
|
updateReloadCounter :: Creature -> Creature
|
||||||
|
updateReloadCounter cr = over (crInv . ix iSel . wpFireState) decreaseToZero
|
||||||
|
. over (crInv . ix iSel . wpReloadState) decreaseToZero
|
||||||
|
$ cr
|
||||||
|
where iSel = _crInvSel cr
|
||||||
|
|
||||||
|
decreaseToZero :: Int -> Int
|
||||||
|
decreaseToZero x | x > 0 = x - 1
|
||||||
|
| otherwise = 0
|
||||||
|
|
||||||
|
updateBarrel ::
|
||||||
|
World -> (World -> World,StdGen) -> Creature -> ((World -> World , StdGen), Maybe Creature)
|
||||||
|
updateBarrel w (f,g) cr | _crHP cr > 0 = ((f, g), newCr)
|
||||||
|
| otherwise = ((f, g), Nothing)
|
||||||
|
-- | otherwise = ((makeExplosionAt (_crPos cr) . stopSounds , g'), Nothing)
|
||||||
|
where damages = _crDamage $ _crState cr
|
||||||
|
newCr = Just $ doDamage cr
|
||||||
|
|
||||||
|
-- it is easy to leave off the "f" here
|
||||||
|
-- should find some better way of doing all this that is less prone to error
|
||||||
|
updateExpBarrel ::
|
||||||
|
World -> (World -> World,StdGen) -> Creature -> ((World -> World , StdGen), Maybe Creature)
|
||||||
|
updateExpBarrel w (f,g) cr
|
||||||
|
| _crHP cr > 0 = ((f . foldr (.) id pierceSparks . hiss, g'), newCr)
|
||||||
|
| otherwise = ((f . makeExplosionAt (_crPos cr) . stopSounds , g'), Nothing)
|
||||||
|
where damages = _crDamage $ _crState cr
|
||||||
|
pierceSparks :: [World -> World]
|
||||||
|
pierceSparks
|
||||||
|
= zipWith4 (\p a colid time-> (createBarrelSpark time colid (_crPos cr +.+ p) (a + argV p))
|
||||||
|
(Just $ _crID cr))
|
||||||
|
poss as colids times
|
||||||
|
as = randomRs (-0.7,0.7) $ g
|
||||||
|
colids = randomRs (0,11) $ g
|
||||||
|
times = randomRs (2,5) $ g
|
||||||
|
(g',_) = split g
|
||||||
|
poss = _piercedPoints $ _crSpState $ _crState cr
|
||||||
|
--newCr = Just $ doDamage $ applyFuseDamage cr -- $ foldr perforate cr damages
|
||||||
|
newCr = Just $ applyFuseDamage $ set (crState . crDamage) [] $ damToExpBarrel damages cr
|
||||||
|
perforate :: DamageType -> Creature -> Creature
|
||||||
|
perforate (Piercing amount sp int ep) cr = over (crState . crSpState . piercedPoints)
|
||||||
|
((:) $ int -.- _crPos cr) cr
|
||||||
|
perforate _ cr = cr
|
||||||
|
applyFuseDamage cr = over crHP (\hp -> hp - length (_piercedPoints
|
||||||
|
$ _crSpState $ _crState cr))
|
||||||
|
cr
|
||||||
|
hiss | poss == [] = id
|
||||||
|
| otherwise = soundMultiFrom [BarrelHiss 0,BarrelHiss 1] 41 50 1
|
||||||
|
stopSounds = stopSoundFrom (BarrelHiss 0) . stopSoundFrom (BarrelHiss 1)
|
||||||
|
|
||||||
|
damToExpBarrel :: [DamageType] -> Creature -> Creature
|
||||||
|
damToExpBarrel ds cr = foldr damToExpBarrel' (foldr damToExpBarrel' cr pierceDam) otherDam
|
||||||
|
where (pierceDam,otherDam) = partition isPierce ds
|
||||||
|
isPierce (Piercing {}) = True
|
||||||
|
isPierce _ = False
|
||||||
|
|
||||||
|
damToExpBarrel' :: DamageType -> Creature -> Creature
|
||||||
|
damToExpBarrel' (Piercing amount sp int ep) cr
|
||||||
|
= over (crState . crSpState . piercedPoints) ((:) $ int -.- _crPos cr)
|
||||||
|
$ over crHP (\hp -> hp - div amount 200) cr
|
||||||
|
damToExpBarrel' (PoisonDam {}) cr = cr
|
||||||
|
damToExpBarrel' (SparkDam {}) cr = cr
|
||||||
|
damToExpBarrel' (PushDam {_dmPushBack = v}) cr = cr Control.Lens.& crPos %~ (+.+) (1 / _crMass cr *.* v)
|
||||||
|
damToExpBarrel' dt cr = cr Control.Lens.& crHP -~ _dmAmount dt
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
module Dodge.Critters where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Weapons
|
||||||
|
import Dodge.AIs
|
||||||
|
import Dodge.CreatureState
|
||||||
|
import Dodge.Prototypes
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.Smoke
|
||||||
|
|
||||||
|
import Picture
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Char
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
import Data.Graph.Inductive.PatriciaTree
|
||||||
|
import Codec.BMP
|
||||||
|
import qualified Data.ByteString as B
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.State
|
||||||
|
import Control.Monad
|
||||||
|
import System.Random
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import Foreign.ForeignPtr
|
||||||
|
import Control.Concurrent
|
||||||
|
---}
|
||||||
|
|
||||||
|
--colouredEnemy col = pictures [color (greyN 0.8) $ circleSolid 10, color col $ circLine 10]
|
||||||
|
colouredEnemy col = pictures [color col $ circleSolid 10, circLine 10]
|
||||||
|
|
||||||
|
|
||||||
|
-- armouredSwarmCrit :: Int -> Creature
|
||||||
|
-- armouredSwarmCrit n = (basicCreature n)
|
||||||
|
-- -- { _crUpdate = encircleAI' n
|
||||||
|
-- { _crUpdate = swarmAI lineOrth n
|
||||||
|
-- , _crHP = 30
|
||||||
|
-- , _crPict = equipOnTop goalPict
|
||||||
|
-- , _crState = basicState {_goals = [[]], _faction = EncircleFlock}
|
||||||
|
-- , _crInv = IM.fromList [(0,frontArmour)]
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- swarmCrit :: Int -> Creature
|
||||||
|
-- swarmCrit n = (basicCreature n)
|
||||||
|
-- -- { _crUpdate = encircleAI' n
|
||||||
|
-- { _crUpdate = swarmAI lineOrth n
|
||||||
|
-- , _crHP = 30
|
||||||
|
-- , _crPict = equipOnTop goalPict
|
||||||
|
-- , _crState = basicState {_goals = [[]], _faction = EncircleFlock}
|
||||||
|
-- , _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- encircleCrit :: Int -> Creature
|
||||||
|
-- encircleCrit n = (basicCreature n)
|
||||||
|
-- -- { _crUpdate = encircleAI' n
|
||||||
|
-- { _crUpdate = swarmAI lineOrth n
|
||||||
|
-- , _crHP = 30
|
||||||
|
-- , _crPict = equipOnTop goalPict
|
||||||
|
-- , _crState = basicState {_goals = [[]], _faction = EncircleFlock}
|
||||||
|
-- , _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
spawnerCrit :: Creature
|
||||||
|
spawnerCrit = basicCreature
|
||||||
|
{ _crUpdate = stateUpdate $ spawnerAI chaseCrit
|
||||||
|
, _crHP = 300
|
||||||
|
, _crPict = enemyPict blue
|
||||||
|
, _crState = basicState {_goals = [[WaitFor 0]]
|
||||||
|
}
|
||||||
|
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
}
|
||||||
|
--
|
||||||
|
-- guardMelee :: Int -> Creature
|
||||||
|
-- guardMelee n = (basicCreature n)
|
||||||
|
-- { _crUpdate = meleeAI' n
|
||||||
|
-- , _crHP = 30
|
||||||
|
-- , _crPict = equipOnTop goalPict
|
||||||
|
-- , _crState = basicState {_goals = [[InitGuard]]
|
||||||
|
-- ,_faction = ChaseCritters}
|
||||||
|
-- , _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
-- }
|
||||||
|
smallChaseCrit :: Creature
|
||||||
|
smallChaseCrit = basicCreature
|
||||||
|
{ _crUpdate = stateUpdate chaseAI
|
||||||
|
, _crHP = 1
|
||||||
|
, _crRad = 4
|
||||||
|
--, _crPict = equipOnTop goalPict
|
||||||
|
, _crPict = enemyPict green
|
||||||
|
, _crState = basicState {_goals = [[Wait]]
|
||||||
|
,_faction = ChaseCritters}
|
||||||
|
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 4
|
||||||
|
}
|
||||||
|
chaseCrit :: Creature
|
||||||
|
chaseCrit = basicCreature
|
||||||
|
{ _crUpdate = stateUpdate chaseAI
|
||||||
|
, _crHP = 300
|
||||||
|
-- , _crPict = goalPict -- enemyPict green
|
||||||
|
, _crPict = enemyPict green
|
||||||
|
, _crState = basicState {_goals = [[Wait]]
|
||||||
|
,_faction = ChaseCritters}
|
||||||
|
, _crInv = IM.empty
|
||||||
|
-- IM.fromList [(0,frontArmour)]
|
||||||
|
}
|
||||||
|
armourChaseCrit :: Creature
|
||||||
|
armourChaseCrit = basicCreature
|
||||||
|
{ _crUpdate = stateUpdate chaseAI
|
||||||
|
, _crHP = 300
|
||||||
|
, _crPict = enemyPict green
|
||||||
|
, _crState = basicState {_goals = [[Wait]]}
|
||||||
|
, _crInv = IM.fromList [(0,frontArmour)]
|
||||||
|
}
|
||||||
|
miniGunCrit :: Creature
|
||||||
|
miniGunCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate miniAI
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,miniGun)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[InitGuard]]}
|
||||||
|
, _crHP = 500
|
||||||
|
}
|
||||||
|
longCrit :: Creature
|
||||||
|
longCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate sniperAI
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,longGun),(1,medkit 100)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[InitGuard]]}
|
||||||
|
, _crHP = 300
|
||||||
|
}
|
||||||
|
multGunCrit :: Creature
|
||||||
|
multGunCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate (twitchMissAI 300 350)
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,multGun),(1,medkit 100)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[InitGuard]]}
|
||||||
|
, _crHP = 300
|
||||||
|
}
|
||||||
|
launcherCrit :: Creature
|
||||||
|
launcherCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate (launcherAI 150 200)
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,launcher),(1,medkit 100)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[Init]]}
|
||||||
|
, _crHP = 300
|
||||||
|
}
|
||||||
|
spreadGunCrit :: Creature
|
||||||
|
spreadGunCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate chargeAI
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,spreadGun),(1,medkit 100)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[Init]]}
|
||||||
|
, _crHP = 300
|
||||||
|
}
|
||||||
|
pistolCrit :: Creature
|
||||||
|
pistolCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate (dodgeAI 150 200)
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,pistol),(1,medkit 100)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[InitGuard]]}
|
||||||
|
, _crHP = 500
|
||||||
|
}
|
||||||
|
autoCrit :: Creature
|
||||||
|
autoCrit = basicCreature
|
||||||
|
{ _crPict = enemyPict red
|
||||||
|
--, _crUpdate = checkDeadStopSound n . shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crUpdate = stateUpdate basicShooterAI
|
||||||
|
--, _crUpdate = shooterFootsteps n . makeStateAI autoShooterAI n
|
||||||
|
, _crInv = IM.fromList [(0,autoGun),(1,medkit 100)]
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crRad = 10
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
--, _crState = ShooterWait
|
||||||
|
, _crState = basicState {_goals = [[InitGuard]]}
|
||||||
|
, _crHP = 300
|
||||||
|
}
|
||||||
|
addArmour :: Creature -> Creature
|
||||||
|
addArmour = over crInv insarmour
|
||||||
|
where insarmour xs = IM.insert i frontArmour xs
|
||||||
|
where i = newKey xs
|
||||||
|
--closeShooterCrit :: Int -> Creature
|
||||||
|
--closeShooterCrit n = (basicCreature n)
|
||||||
|
-- { _crPos = startPos
|
||||||
|
-- , _crPict = equipOnTop goalPict
|
||||||
|
-- , _crUpdate = closeShooterAI n
|
||||||
|
-- , _crInv = IM.fromList [(0,ltAutoGun)]
|
||||||
|
-- , _crInvSel = 0
|
||||||
|
-- , _crRad = 10
|
||||||
|
-- , _crAmmo = M.fromList [(PistolBullet, 1000)]
|
||||||
|
-- , _crState = basicState {_goals = [[InitGuard]]}
|
||||||
|
-- , _crHP = 50
|
||||||
|
-- }
|
||||||
|
-- where startPos = (20*fromIntegral n-550,0)
|
||||||
|
barrel :: Creature
|
||||||
|
barrel = basicCreature
|
||||||
|
{ _crUpdate = updateBarrel
|
||||||
|
, _crHP = 500
|
||||||
|
, _crPict = \ _ -> onLayer CrLayer $ pictures [ color orange $ circleSolid 10
|
||||||
|
, color (greyN 0.5) $ circleSolid 8
|
||||||
|
]
|
||||||
|
, _crState = basicState {_goals = [[Wait]]
|
||||||
|
,_faction = ChaseCritters
|
||||||
|
,_crSpState = Barrel []
|
||||||
|
}
|
||||||
|
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
}
|
||||||
|
|
||||||
|
explosiveBarrel :: Creature
|
||||||
|
explosiveBarrel = basicCreature
|
||||||
|
{ _crUpdate = updateExpBarrel
|
||||||
|
, _crHP = 400
|
||||||
|
, _crPict = \ _ -> onLayer CrLayer $ pictures [ color orange $ circleSolid 10
|
||||||
|
, color red $ circleSolid 8
|
||||||
|
]
|
||||||
|
, _crState = basicState {_goals = [[Wait]]
|
||||||
|
,_faction = ChaseCritters
|
||||||
|
,_crSpState = Barrel []
|
||||||
|
,_crApplyDamage = \_ c -> (id, c)
|
||||||
|
}
|
||||||
|
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
|
||||||
|
}
|
||||||
|
|
||||||
|
equipOnTop :: (Creature -> Picture) -> Creature -> Drawing
|
||||||
|
--equipOnTop f cr = onLayer CrLayer $ pictures $ fst (drawEquipment cr) ++ [f cr] ++ snd (drawEquipment cr)
|
||||||
|
equipOnTop f cr = onLayer CrLayer (f cr) <> drawEquipment cr
|
||||||
|
|
||||||
|
drawEquipment :: Creature -> Drawing
|
||||||
|
drawEquipment cr = mconcat $ map f $ IM.toList (_crInv cr)
|
||||||
|
where f (i,it) = case it ^? itEquipPict of
|
||||||
|
Just g -> g cr i
|
||||||
|
_ -> blank
|
||||||
|
|
||||||
|
--drawEquipment :: Creature -> ([Picture],[Picture])
|
||||||
|
--drawEquipment cr = (map fst p1, map fst p2)
|
||||||
|
-- where f (k,it) = join $ (it ^? itEquipPict) <*> (pure cr) <*> (pure k)
|
||||||
|
-- picts = sortBy (compare `on` snd) $ mapMaybe f $ IM.toList (_crInv cr)
|
||||||
|
-- (p1,p2) = partition (\ x -> snd x < 0) picts
|
||||||
|
|
||||||
|
frontArmouredPict = const $ pictures [ color (greyN 0.8) $ circleSolid 20
|
||||||
|
, color red $ circLine 20
|
||||||
|
-- ,color (greyN 0.1) $ thickArc 0 90 20 5
|
||||||
|
-- ,color (greyN 0.1) $ thickArc 270 360 20 5
|
||||||
|
]
|
||||||
|
|
||||||
|
--packCrits :: (Int -> World -> World) -> [Int] -> [Creature]
|
||||||
|
--packCrits ai is = [(basicCreature i)
|
||||||
|
-- { _crPos = (150,10+20*fromIntegral i)
|
||||||
|
-- , _crPict = swarmPict
|
||||||
|
-- , _crUpdate = ai i
|
||||||
|
-- --, _crUpdate = swarmAI is i
|
||||||
|
-- , _crRad = 3
|
||||||
|
-- , _crHP = 1
|
||||||
|
-- , _crMass = 2
|
||||||
|
-- }
|
||||||
|
-- | i <- is]
|
||||||
|
|
||||||
|
--queenBeeCrit :: Int -> Creature
|
||||||
|
--queenBeeCrit cID = (basicCreature cID)
|
||||||
|
-- { _crRad = 20
|
||||||
|
-- , _crCorpse = color (greyN 0.5) $ circleSolid 20
|
||||||
|
-- , _crPict = const $ pictures [color black $ circSolid 20
|
||||||
|
-- ,color yellow $ circLine 20
|
||||||
|
-- ]
|
||||||
|
-- , _crUpdate = checkDead cID . makeStateAI queenBeeAI cID
|
||||||
|
-- , _crState = QueenBee 0 20
|
||||||
|
-- , _crMass = 20
|
||||||
|
-- }
|
||||||
|
|
||||||
|
--antCrit :: Int -> Creature
|
||||||
|
--antCrit cID = (basicCreature cID)
|
||||||
|
-- { _crPos = (0,100 + 3 * fromIntegral cID)
|
||||||
|
-- , _crUpdate = checkDead cID . makeStateAI (antAI 2) cID
|
||||||
|
-- , _crState = AntMove 50
|
||||||
|
-- , _crPict = antPic
|
||||||
|
-- , _crRad = 3
|
||||||
|
-- , _crHP = 1
|
||||||
|
-- , _crMass = 2
|
||||||
|
-- , _crCorpse = color (greyN 0.5) $ circSolid 3
|
||||||
|
-- }
|
||||||
|
-- where i = evalState (state (randomR (1::Int,100))) (mkStdGen cID)
|
||||||
|
--antPic = const $ pictures [ translate (-1) 0 $ circleSolid 2
|
||||||
|
-- , translate (2) 0 $ circleSolid 1
|
||||||
|
-- , translate (1) 0 $ circleSolid 1
|
||||||
|
-- , line [(3,3),(-3,-3)]
|
||||||
|
-- , line [(-3,3),(3,-3)]
|
||||||
|
-- , line [(0,-3),(0,3)]
|
||||||
|
-- ]
|
||||||
|
|
||||||
|
flamerPict = const $ pictures [color (light $ light $ light $ dim blue) $ circleSolid 10, circLine 10]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
goalPict cr = let r = _crRad cr in case _crState cr of
|
||||||
|
CrSt {_goals = gls ,_crDamage = crDam }
|
||||||
|
-- | crDam > _crHP cr -> color white $ circleSolid r
|
||||||
|
| otherwise -> case head gls of
|
||||||
|
[] -> sizeColEnemy r white
|
||||||
|
(MoveToFor p i:_) -> dGoals $ sizeColEnemy r green
|
||||||
|
(WaitFor x:_) -> dGoals $ sizeColEnemy r yellow
|
||||||
|
(FireAt p:_) -> dGoals $ sizeColEnemy r red
|
||||||
|
(Reload:_) -> dGoals $ sizeColEnemy r orange
|
||||||
|
(PathTo p:_) -> dGoals $ sizeColEnemy r cyan
|
||||||
|
(SubPathTo p i _:_) -> dGoals $ sizeColEnemy r blue
|
||||||
|
(Search i:_) -> dGoals $ sizeColEnemy r black
|
||||||
|
_ -> dGoals $ sizeColEnemy r magenta
|
||||||
|
where dGoals p = pictures [p, rotate (radToDeg (_crDir cr)) $ scale 0.1 0.1 $ color white $ text $ show gls]
|
||||||
|
_ -> sizeColEnemy r (light $ dim green)
|
||||||
|
|
||||||
|
startCr :: Creature
|
||||||
|
startCr = basicCreature
|
||||||
|
{ _crPos = (0,0)
|
||||||
|
, _crOldPos = (0,0)
|
||||||
|
, _crDir = 0
|
||||||
|
, _crID = 0
|
||||||
|
, _crPict = basicCrPict $ greyN 0.8
|
||||||
|
|
||||||
|
, _crUpdate = stateUpdate yourControl
|
||||||
|
, _crRad = 10
|
||||||
|
, _crMass = 10
|
||||||
|
, _crHP = 10000
|
||||||
|
, _crMaxHP = 1500
|
||||||
|
, _crInv = IM.fromList (zip [0..20]
|
||||||
|
([pistol,autoGun,launcher,lasGun,grenade
|
||||||
|
,ltAutoGun,flamer,multGun,spreadGun,remoteLauncher
|
||||||
|
,longGun
|
||||||
|
,hvAutoGun
|
||||||
|
,teslaGun
|
||||||
|
,latchkey 0
|
||||||
|
]
|
||||||
|
++ repeat NoItem))
|
||||||
|
-- startInv
|
||||||
|
, _crAmmo = M.fromList [(PistolBullet, 0),(LiquidFuel, 100),(Battery, 20000),(Shell,200)]
|
||||||
|
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ pictures [color (greyN 0.8) $ circleSolid 10, circLine 10]
|
||||||
|
}
|
||||||
|
|
||||||
|
smokeGenGun = effectGun "smoke" $ \_ -> spawnSmokeAtCursor
|
||||||
|
|
||||||
|
enemyPict :: Color -> Creature -> Drawing
|
||||||
|
enemyPict col = equipOnTop $ sizeEnemy (light $ light $ light $ light col)
|
||||||
|
|
||||||
|
sizeEnemy col cr
|
||||||
|
| pdam > 200 = color red $ circleSolid $ _crRad cr
|
||||||
|
| pdam > 100 = color white $ circleSolid $ _crRad cr
|
||||||
|
| mod pdam 2 == 1 = color white $ circleSolid $ _crRad cr
|
||||||
|
| otherwise = pictures [color col $ circleSolid $ _crRad cr
|
||||||
|
, circLine $ _crRad cr ]
|
||||||
|
where pdam = _crPastDamage $ _crState cr
|
||||||
|
|
||||||
|
sizeColEnemy r col = pictures [color col $ circleSolid r, circLine r]
|
||||||
|
|
||||||
|
basicCrPict :: Color -> Creature -> Drawing
|
||||||
|
basicCrPict col cr = onLayer CrLayer naked <> drawEquipment cr
|
||||||
|
where naked | pdam > 200 = color red $ circleSolid $ _crRad cr
|
||||||
|
| pdam > 100 = color white $ circleSolid $ _crRad cr
|
||||||
|
| mod pdam 2 == 1 = color white $ circleSolid $ _crRad cr
|
||||||
|
-- | crDam > _crHP cr && odd (crDam - _crHP cr)
|
||||||
|
-- = [color white $ circleSolid $ _crRad cr]
|
||||||
|
| otherwise = pictures [color col $ circleSolid $ _crRad cr, circLine $ _crRad cr]
|
||||||
|
-- , rotate (radToDeg $ _crDir cr) $ scale 0.2 0.2 $ color white $ text $ show $ _crPos cr
|
||||||
|
pdam = _crPastDamage $ _crState cr
|
||||||
@@ -0,0 +1,715 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
--{-# LANGUAGE BangPatterns #-}
|
||||||
|
{-# LANGUAGE StrictData #-}
|
||||||
|
module Dodge.Data
|
||||||
|
( module Dodge.Data
|
||||||
|
, Point2 (..)
|
||||||
|
)
|
||||||
|
where
|
||||||
|
-- imports {{{
|
||||||
|
|
||||||
|
import Picture.Data
|
||||||
|
|
||||||
|
import Geometry.Data
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad.State
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import Data.Graph.Inductive
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import qualified SDL.Mixer as Mix
|
||||||
|
import SDL (Keycode, MouseButton)
|
||||||
|
import Graphics.Rendering.OpenGL (PrimitiveMode (..),GLfloat,Program,VertexArrayObject,BufferObject)
|
||||||
|
import Codec.Picture (Image,PixelRGBA8)
|
||||||
|
|
||||||
|
import qualified Data.DList as DL
|
||||||
|
|
||||||
|
--}}}
|
||||||
|
-- datatypes {{{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
data World = World
|
||||||
|
{ _keys :: !(S.Set Keycode)
|
||||||
|
, _mouseButtons :: !(S.Set MouseButton)
|
||||||
|
, _cameraPos :: !Point2
|
||||||
|
, _cameraAimTime :: Int
|
||||||
|
, _cameraRot :: !Float
|
||||||
|
, _cameraZoom :: !Float
|
||||||
|
, _cameraCenter :: !Point2
|
||||||
|
, _creatures :: IM.IntMap Creature
|
||||||
|
, _creaturesZone :: IM.IntMap (IM.IntMap (IM.IntMap Creature))
|
||||||
|
, _itemPositions :: IM.IntMap ItemPos
|
||||||
|
, _clouds :: IM.IntMap Cloud
|
||||||
|
, _cloudsZone :: IM.IntMap (IM.IntMap (IM.IntMap Cloud))
|
||||||
|
, _particles :: IM.IntMap Particle
|
||||||
|
, _particles' :: ![Particle']
|
||||||
|
, _afterParticles' :: ![Particle']
|
||||||
|
, _smoke :: [Smoke]
|
||||||
|
, _walls :: !(IM.IntMap Wall)
|
||||||
|
, _wallsZone :: (IM.IntMap (IM.IntMap (IM.IntMap Wall)))
|
||||||
|
, _forceFields :: IM.IntMap ForceField
|
||||||
|
, _floorItems :: IM.IntMap FloorItem
|
||||||
|
, _randGen :: StdGen
|
||||||
|
, _mousePos :: !(Float,Float)
|
||||||
|
, _testString :: String
|
||||||
|
, _yourID :: !Int
|
||||||
|
, _worldEvents :: !(World -> World)
|
||||||
|
, _pressPlates :: IM.IntMap PressPlate
|
||||||
|
, _buttons :: IM.IntMap Button
|
||||||
|
, _soundQueue :: [Int]
|
||||||
|
-- , _loadedSounds :: IM.IntMap Mix.Chunk
|
||||||
|
, _sounds :: M.Map SoundOrigin Sound
|
||||||
|
, _decorations :: IM.IntMap Drawing
|
||||||
|
, _corpses :: IM.IntMap (IM.IntMap [Corpse])
|
||||||
|
, _lbClickMousePos :: (Float,Float)
|
||||||
|
, _lbRotation :: Float
|
||||||
|
, _pathGraph :: ~(Gr Point2 Float)
|
||||||
|
, _pathGraph' :: ~[(Point2,Point2)]
|
||||||
|
, _pathPoints :: ~(IM.IntMap (IM.IntMap [(Int,Point2)]))
|
||||||
|
, _pathInc :: ~(M.Map Point2 [Point2])
|
||||||
|
, _unlimitedAmmo :: Bool
|
||||||
|
, _storedLevel :: Maybe World
|
||||||
|
, _menuState :: MenuState
|
||||||
|
, _worldState :: M.Map WorldState Bool
|
||||||
|
, _windowX :: Float
|
||||||
|
, _windowY :: Float
|
||||||
|
, _mapDisplay :: (Bool, Float)
|
||||||
|
, _lightSources :: !(IM.IntMap LightSource)
|
||||||
|
, _tempLightSources :: ![TempLightSource]
|
||||||
|
} --deriving (Show)
|
||||||
|
|
||||||
|
type Drawing = Picture
|
||||||
|
|
||||||
|
data Corpse = Corpse
|
||||||
|
{ _cpPos :: Point2
|
||||||
|
, _cpPict :: Drawing
|
||||||
|
, _cpRes :: Creature
|
||||||
|
}
|
||||||
|
|
||||||
|
data Layer = PtLayer | CrLayer | WlLayer | BgLayer | ShadowLayer
|
||||||
|
| FlItLayer | LabelLayer | InvLayer | MenuLayer
|
||||||
|
| PressPlateLayer | CorpseLayer
|
||||||
|
| UPtLayer
|
||||||
|
| HPtLayer
|
||||||
|
| GloomLayer
|
||||||
|
|
||||||
|
data Cloud = Cloud
|
||||||
|
{ _clID :: Int
|
||||||
|
, _clPos :: Point2
|
||||||
|
, _clVel :: Point2
|
||||||
|
, _clPict :: Cloud -> Drawing
|
||||||
|
, _clRad :: Float
|
||||||
|
, _clTimer :: Int
|
||||||
|
, _clEffect :: Cloud -> World -> World
|
||||||
|
}
|
||||||
|
|
||||||
|
data LightSource
|
||||||
|
= LS
|
||||||
|
{ _lsID :: !Int
|
||||||
|
, _lsPos :: !Point2
|
||||||
|
, _lsDir :: !Float
|
||||||
|
, _lsRad :: !Float
|
||||||
|
, _lsIntensity :: !Float
|
||||||
|
}
|
||||||
|
|
||||||
|
data TempLightSource =
|
||||||
|
TLS { _tlsPos :: !Point2
|
||||||
|
, _tlsRad :: !Float
|
||||||
|
, _tlsIntensity :: !Float
|
||||||
|
, _tlsUpdate :: World -> TempLightSource -> (World, Maybe TempLightSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
data Creature = Creature
|
||||||
|
{ _crPos :: Point2
|
||||||
|
, _crOldPos :: Point2
|
||||||
|
, _crDir :: Float
|
||||||
|
, _crID :: Int
|
||||||
|
, _crPict :: Creature -> Drawing
|
||||||
|
, _crUpdate :: World -> (World -> World,StdGen) -> Creature
|
||||||
|
-> ((World -> World,StdGen), Maybe Creature)
|
||||||
|
, _crRad :: Float
|
||||||
|
, _crMass :: Float
|
||||||
|
, _crHP :: Int
|
||||||
|
, _crMaxHP :: Int
|
||||||
|
, _crInv :: IM.IntMap Item
|
||||||
|
, _crInvSel :: Int
|
||||||
|
, _crAmmo :: M.Map Ammo Int
|
||||||
|
, _crState :: CreatureState
|
||||||
|
, _crCorpse :: Drawing
|
||||||
|
}
|
||||||
|
|
||||||
|
data CreatureState
|
||||||
|
= CrSt { _goals :: [[Goal]]
|
||||||
|
, _stance :: Stance
|
||||||
|
, _faction :: Faction
|
||||||
|
, _crDamage :: [DamageType]
|
||||||
|
, _crPastDamage :: Int
|
||||||
|
, _crSpState :: CrSpState
|
||||||
|
, _crApplyDamage :: [DamageType] -> Creature -> (World -> World,Creature)
|
||||||
|
}
|
||||||
|
-- deriving (Eq,Show)
|
||||||
|
data CrSpState = Barrel { _piercedPoints :: [Point2]}
|
||||||
|
| GenCr
|
||||||
|
deriving (Eq,Show,Ord)
|
||||||
|
data Goal = MoveTo Point2
|
||||||
|
| MoveToFor Point2 Int
|
||||||
|
| MoveFire Point2 Point2
|
||||||
|
| PathTo Point2
|
||||||
|
| PathAlong [Point2]
|
||||||
|
| SubPathTo Point2 Int Point2
|
||||||
|
| Wait
|
||||||
|
| WaitFor Int
|
||||||
|
| WaitForID Int Int
|
||||||
|
| FireAt Point2
|
||||||
|
| FireAtID Int Point2
|
||||||
|
| Fire
|
||||||
|
| Reload
|
||||||
|
| IncreaseAlert Int
|
||||||
|
| Guard Point2 Point2
|
||||||
|
| Search Int
|
||||||
|
| SearchNear Point2
|
||||||
|
| AimAt Point2 Int
|
||||||
|
| InitGuard
|
||||||
|
| Init
|
||||||
|
| MeleeAttack Int
|
||||||
|
| InitTrackYou
|
||||||
|
| TrackYou
|
||||||
|
| Track Int
|
||||||
|
| TrackFor Int Int
|
||||||
|
| TurnByFor Float Int
|
||||||
|
| TurnTo Point2
|
||||||
|
| TurnToward Point2
|
||||||
|
| TurnTowardAngle Float
|
||||||
|
| StrafeLeftAround Int Point2
|
||||||
|
| StrafeRightAround Int Point2
|
||||||
|
| StrafeLeftFor Int
|
||||||
|
| StrafeRightFor Int
|
||||||
|
| StrafeLeftForSpeed Int Float
|
||||||
|
| StrafeRightForSpeed Int Float
|
||||||
|
| StrafeLeftFire
|
||||||
|
| StrafeRightFire
|
||||||
|
| MoveForwardFor Int
|
||||||
|
| MoveForwardFire
|
||||||
|
| MoveBackwardFor Int
|
||||||
|
| MoveByFor Point2 Int
|
||||||
|
| MoveBackwardFire
|
||||||
|
| GoalID Int Goal
|
||||||
|
| AtRange Float
|
||||||
|
| AtRanges Float Float
|
||||||
|
| RepeatAction Int Goal
|
||||||
|
| MakeJudgement
|
||||||
|
| SetPosture Posture
|
||||||
|
deriving (Eq,Show)
|
||||||
|
data Stance = Stance {_carriage :: Carriage
|
||||||
|
,_posture :: Posture
|
||||||
|
}
|
||||||
|
deriving (Eq,Show)
|
||||||
|
data Carriage = Walking {_stepCycle :: Int
|
||||||
|
,_stepToAdd :: Int
|
||||||
|
}
|
||||||
|
| Floating
|
||||||
|
| Boosting Point2
|
||||||
|
deriving (Eq,Show)
|
||||||
|
data Posture = Aiming
|
||||||
|
| AtEase
|
||||||
|
deriving (Eq,Show)
|
||||||
|
data Mind = ZombieMind
|
||||||
|
| HumanMind
|
||||||
|
deriving (Eq,Show)
|
||||||
|
data Faction = GenericFaction Int
|
||||||
|
| ZombieFaction
|
||||||
|
| EncircleFlock
|
||||||
|
| ChaseCritters
|
||||||
|
| SpawnedBy Int
|
||||||
|
| NoFaction
|
||||||
|
deriving (Eq,Show)
|
||||||
|
|
||||||
|
-- NOTE: walls must be drawn counterclockwise
|
||||||
|
|
||||||
|
data WorldState = DoorNumOpen Int
|
||||||
|
| CrNumAlive Int
|
||||||
|
deriving (Eq,Ord)
|
||||||
|
|
||||||
|
data MenuState
|
||||||
|
= LevelMenu Int
|
||||||
|
| PauseMenu
|
||||||
|
| GameOverMenu
|
||||||
|
| InGame
|
||||||
|
|
||||||
|
data Sound = Sound { _soundTime :: Int
|
||||||
|
, _soundFadeTime :: Int
|
||||||
|
, _soundChannel :: Maybe Int
|
||||||
|
, _soundPos :: Maybe Point2
|
||||||
|
, _soundType :: Int
|
||||||
|
}
|
||||||
|
deriving (Eq,Ord,Show)
|
||||||
|
|
||||||
|
data Button = Button
|
||||||
|
{ _btPict :: Drawing
|
||||||
|
, _btPos :: Point2
|
||||||
|
, _btRot :: Float
|
||||||
|
, _btEvent :: Button -> World -> World
|
||||||
|
, _btID :: Int
|
||||||
|
, _btText :: String
|
||||||
|
, _btState :: ButtonState
|
||||||
|
}
|
||||||
|
|
||||||
|
data ButtonState = BtOn | BtOff | BtNoLabel
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
data PressPlate = PressPlate { _ppPict :: Drawing
|
||||||
|
, _ppPos :: Point2
|
||||||
|
, _ppRot :: Float
|
||||||
|
, _ppEvent :: PressPlate -> World -> World
|
||||||
|
, _ppID :: Int
|
||||||
|
, _ppText :: String
|
||||||
|
}
|
||||||
|
|
||||||
|
data FloorItem = FlIt { _flIt :: Item , _flItPos :: Point2 , _flItRot :: Float, _flItID :: Int}
|
||||||
|
| FlAm { _flAm :: Ammo , _flItPos :: Point2 , _flAmNum :: Int , _flItID :: Int}
|
||||||
|
|
||||||
|
data ItemPos = InInv { _itCrId :: Int
|
||||||
|
, _itInvId :: Int
|
||||||
|
}
|
||||||
|
| OnFloor { _itFlID :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
data Item = Weapon
|
||||||
|
{ _itName :: String
|
||||||
|
, _wpMaxAmmo :: Int
|
||||||
|
, _wpLoadedAmmo :: Int
|
||||||
|
, _wpAmmoType :: Ammo
|
||||||
|
, _wpReloadTime :: Int
|
||||||
|
, _wpReloadState :: Int
|
||||||
|
, _wpFireRate :: Int
|
||||||
|
, _wpFireState :: Int
|
||||||
|
, _wpFire :: Int -> World -> World
|
||||||
|
, _wpSpread :: Float
|
||||||
|
, _wpRange :: Float
|
||||||
|
, _itHammer :: HammerPosition
|
||||||
|
, _itFloorPict :: Drawing
|
||||||
|
, _itMaxStack :: Int
|
||||||
|
, _itAmount :: Int
|
||||||
|
, _itAimingSpeed :: Float
|
||||||
|
, _itAimingRange :: Float
|
||||||
|
, _itZoom :: ItZoom
|
||||||
|
, _itEquipPict :: Creature -> Int -> Drawing
|
||||||
|
, _itScrollUp :: Int -> World -> World
|
||||||
|
, _itScrollDown :: Int -> World -> World
|
||||||
|
, _itIdentity :: ItemIdentity
|
||||||
|
, _itAttachment :: Maybe ItAttachment
|
||||||
|
, _itID :: Maybe Int
|
||||||
|
, _itEffect :: ItEffect
|
||||||
|
, _itInvDisplay :: Item -> String
|
||||||
|
, _itInvColor :: Color
|
||||||
|
}
|
||||||
|
| Consumable
|
||||||
|
{ _itName :: String
|
||||||
|
, _itMaxStack :: Int
|
||||||
|
, _itAmount :: Int
|
||||||
|
, _cnEffect :: Int -> World -> Maybe World
|
||||||
|
, _itFloorPict :: Drawing
|
||||||
|
, _itEquipPict :: Creature -> Int -> Drawing
|
||||||
|
, _itIdentity :: ItemIdentity
|
||||||
|
, _itID :: Maybe Int
|
||||||
|
, _itInvDisplay :: Item -> String
|
||||||
|
, _itInvColor :: Color
|
||||||
|
, _itEffect :: ItEffect
|
||||||
|
, _itHammer :: HammerPosition
|
||||||
|
}
|
||||||
|
| Craftable
|
||||||
|
{ _itName :: String
|
||||||
|
, _itMaxStack :: Int
|
||||||
|
, _itAmount :: Int
|
||||||
|
, _itFloorPict :: Drawing
|
||||||
|
, _itEquipPict :: Creature -> Int -> Drawing
|
||||||
|
, _itIdentity :: ItemIdentity
|
||||||
|
, _itID :: Maybe Int
|
||||||
|
, _itInvDisplay :: Item -> String
|
||||||
|
, _itInvColor :: Color
|
||||||
|
}
|
||||||
|
| Equipment
|
||||||
|
{ _itName :: String
|
||||||
|
, _itMaxStack :: Int
|
||||||
|
, _itAmount :: Int
|
||||||
|
, _itFloorPict :: Drawing
|
||||||
|
, _itEquipPict :: Creature -> Int -> Drawing
|
||||||
|
, _itIdentity :: ItemIdentity
|
||||||
|
, _itEffect :: ItEffect
|
||||||
|
, _itID :: Maybe Int
|
||||||
|
, _itAimingSpeed :: Float
|
||||||
|
, _itAimingRange :: Float
|
||||||
|
, _itZoom :: ItZoom
|
||||||
|
, _itInvDisplay :: Item -> String
|
||||||
|
, _itInvColor :: Color
|
||||||
|
, _itHammer :: HammerPosition
|
||||||
|
}
|
||||||
|
| Throwable
|
||||||
|
{ _itName :: String
|
||||||
|
, _itMaxStack :: Int
|
||||||
|
, _itAmount :: Int
|
||||||
|
, _itFloorPict :: Drawing
|
||||||
|
, _twMaxRange :: Float
|
||||||
|
, _twAccuracy :: Float
|
||||||
|
, _twFire :: Int -> World -> World
|
||||||
|
, _itAimingSpeed :: Float
|
||||||
|
, _itAimingRange :: Float
|
||||||
|
, _itZoom :: ItZoom
|
||||||
|
, _itEquipPict :: Creature -> Int -> Drawing
|
||||||
|
, _itIdentity :: ItemIdentity
|
||||||
|
, _itID :: Maybe Int
|
||||||
|
, _itAttachment :: Maybe ItAttachment
|
||||||
|
, _itInvDisplay :: Item -> String
|
||||||
|
, _itInvColor :: Color
|
||||||
|
, _itEffect :: ItEffect
|
||||||
|
, _itHammer :: HammerPosition
|
||||||
|
, _itScrollUp :: Int -> World -> World
|
||||||
|
, _itScrollDown :: Int -> World -> World
|
||||||
|
}
|
||||||
|
| NoItem
|
||||||
|
|
||||||
|
data ItAttachment
|
||||||
|
= ItScope {_scopePos :: Point2
|
||||||
|
,_scopeZoomChange :: Int
|
||||||
|
,_scopeZoom :: Float
|
||||||
|
,_scopeIsCamera :: Bool
|
||||||
|
}
|
||||||
|
| ItFuse {_itFuseTime :: Int}
|
||||||
|
| ItPhaseV {_itPhaseV :: Float}
|
||||||
|
| ItMode {_itMode :: Int}
|
||||||
|
|
||||||
|
data ItEffect = NoItEffect
|
||||||
|
| ItInvEffect {_itInvEffect :: Creature -> Int -> World -> World
|
||||||
|
,_itEffectCounter :: Int
|
||||||
|
}
|
||||||
|
| ItEffect {_itInvEffect :: Creature -> Int -> World -> World
|
||||||
|
,_itFloorEffect :: Int -> World -> World
|
||||||
|
,_itEffectCounter :: Int
|
||||||
|
}
|
||||||
|
data ItZoom = ItZoom {_itAimZoomMax :: Float
|
||||||
|
,_itAimZoomMin :: Float
|
||||||
|
,_itAimZoomFac :: Float
|
||||||
|
,_itZoomMax :: Float
|
||||||
|
,_itZoomMin :: Float
|
||||||
|
,_itZoomFac :: Float
|
||||||
|
}
|
||||||
|
|
||||||
|
data IntID a = IntID Int a
|
||||||
|
|
||||||
|
data HammerPosition = HammerDown
|
||||||
|
| HammerReleased
|
||||||
|
| HammerUp
|
||||||
|
| NoHammer
|
||||||
|
|
||||||
|
data ItemIdentity
|
||||||
|
= Pistol
|
||||||
|
| SpreadGun
|
||||||
|
| MultGun
|
||||||
|
| HvAutoGun
|
||||||
|
| AutoGun
|
||||||
|
| LtAutoGun
|
||||||
|
| MiniGun
|
||||||
|
| Medkit25
|
||||||
|
| MagShield
|
||||||
|
| FrontArmour
|
||||||
|
| JetPack
|
||||||
|
| FlameShield
|
||||||
|
| Generic
|
||||||
|
| SparkGun
|
||||||
|
| ShatterGun
|
||||||
|
| LongGun
|
||||||
|
| Flamethrower
|
||||||
|
| Blinker
|
||||||
|
| Grenade
|
||||||
|
| RemoteBomb
|
||||||
|
| TeslaGun
|
||||||
|
| LasGun
|
||||||
|
| ForceFieldGun
|
||||||
|
| GrapGun
|
||||||
|
| TractorGun
|
||||||
|
| Launcher
|
||||||
|
| RemoteLauncher
|
||||||
|
| LightningGun
|
||||||
|
| PoisonSprayer
|
||||||
|
deriving (Eq,Show,Ord,Enum)
|
||||||
|
|
||||||
|
data Particle' =
|
||||||
|
Particle' { _ptPict' :: Drawing
|
||||||
|
, _ptUpdate' :: World -> Particle' -> (World, Maybe Particle')
|
||||||
|
}
|
||||||
|
| Bul' { _ptPict' :: Drawing
|
||||||
|
, _ptUpdate' :: World -> Particle' -> (World, Maybe Particle')
|
||||||
|
, _btVel' :: Point2
|
||||||
|
, _btColor' :: Color
|
||||||
|
, _btTrail' :: [Point2]
|
||||||
|
, _btPassThrough' :: Maybe Int
|
||||||
|
, _btWidth' :: Float
|
||||||
|
, _btTimer' :: Int
|
||||||
|
, _btHitEffect' :: HitEffect'
|
||||||
|
}
|
||||||
|
| Pt' { _ptPict' :: Drawing
|
||||||
|
, _ptUpdate' :: World -> Particle' -> (World, Maybe Particle')
|
||||||
|
, _btVel' :: Point2
|
||||||
|
, _btColor' :: Color
|
||||||
|
, _btPos' :: Point2
|
||||||
|
, _btPassThrough' :: Maybe Int
|
||||||
|
, _btWidth' :: Float
|
||||||
|
, _btTimer' :: Int
|
||||||
|
, _btHitEffect' :: HitEffect'
|
||||||
|
}
|
||||||
|
| Shockwave'
|
||||||
|
{ _ptPict' :: Drawing
|
||||||
|
, _ptUpdate' :: World -> Particle' -> (World, Maybe Particle')
|
||||||
|
, _btColor' :: Color
|
||||||
|
, _btPos' :: Point2
|
||||||
|
, _btRad' :: Float
|
||||||
|
, _btDam' :: Int
|
||||||
|
, _btPush' :: Float
|
||||||
|
, _btMaxTime' :: Int
|
||||||
|
, _btTimer' :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type HitEffect = Point2 -> Point2 -> (Point2, (Either3 Creature Wall ForceField)) -> World -> World
|
||||||
|
type HitEffect' = Particle' -> (Point2, (Either3 Creature Wall ForceField)) -> World -> World
|
||||||
|
|
||||||
|
data Particle =
|
||||||
|
Particle { _ptPos :: Point2
|
||||||
|
, _ptStartPos :: Point2
|
||||||
|
, _ptVel :: Point2
|
||||||
|
, _ptPict :: Drawing
|
||||||
|
, _ptID :: Int
|
||||||
|
, _ptUpdate :: World -> World
|
||||||
|
}
|
||||||
|
| Bullet { _ptPos :: Point2
|
||||||
|
, _ptVel :: Point2
|
||||||
|
, _ptPict :: Drawing
|
||||||
|
, _ptID :: Int
|
||||||
|
, _ptUpdate :: World -> World
|
||||||
|
, _btTrail :: [Point2]
|
||||||
|
, _btPassThrough :: Maybe Int
|
||||||
|
, _btColor :: Color
|
||||||
|
, _btWidth :: Float
|
||||||
|
-- , _btHitEffect :: Particle -> [(Point2, Either3 Creature Wall ForceField)] -> World -> World
|
||||||
|
}
|
||||||
|
| Bullet'
|
||||||
|
{ _ptPos :: Point2
|
||||||
|
, _ptVel :: Point2
|
||||||
|
, _ptPict :: Drawing
|
||||||
|
, _ptID :: Int
|
||||||
|
, _ptUpdate :: World -> World
|
||||||
|
}
|
||||||
|
|
||||||
|
data DamageType = Piercing {_dmAmount :: Int , _dmFrom :: Point2 , _dmAt :: Point2 , _dmTo :: Point2 }
|
||||||
|
| Blunt {_dmAmount :: Int , _dmFrom :: Point2 , _dmAt :: Point2 , _dmTo :: Point2 }
|
||||||
|
| SparkDam {_dmAmount :: Int , _dmFrom :: Point2 , _dmAt :: Point2 , _dmTo :: Point2 }
|
||||||
|
| Flaming {_dmAmount :: Int , _dmFrom :: Point2 , _dmAt :: Point2 , _dmTo :: Point2 }
|
||||||
|
| Lasering {_dmAmount :: Int , _dmFrom :: Point2 , _dmAt :: Point2 , _dmTo :: Point2 }
|
||||||
|
| Concussive {_dmAmount :: Int , _dmFrom :: Point2 , _dmPush :: Float
|
||||||
|
, _dmPushExp :: Float
|
||||||
|
, _dmPushRadius :: Float}
|
||||||
|
| TorqueDam {_dmAmount :: Int , _dmTorque :: Float }
|
||||||
|
| PushDam {_dmAmount :: Int , _dmPushBack :: Point2 }
|
||||||
|
| PoisonDam {_dmAmount :: Int}
|
||||||
|
deriving (Eq,Ord,Show)
|
||||||
|
|
||||||
|
data Either3 a b c = E3x1 a | E3x2 b | E3x3 c
|
||||||
|
|
||||||
|
data Smoke =
|
||||||
|
Smoke
|
||||||
|
{ _smPos :: Point2
|
||||||
|
, _smRad :: Float
|
||||||
|
, _smColor :: Color
|
||||||
|
, _smUpdate :: Smoke -> Maybe Smoke
|
||||||
|
, _smPs :: [Point2]
|
||||||
|
, _smTime :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
data WLID = WLID { _wlIDx :: Int, _wlIDy :: Int, _wlIDid :: Int}
|
||||||
|
|
||||||
|
data Wall = Wall
|
||||||
|
{ _wlLine :: [Point2] , _wlID :: Int
|
||||||
|
, _wlColor :: Color
|
||||||
|
, _wlDraw :: Maybe (Wall -> Drawing)
|
||||||
|
, _wlSeen :: Bool
|
||||||
|
, _wlIsSeeThrough :: Bool
|
||||||
|
-- , _wlID' :: WLID
|
||||||
|
}
|
||||||
|
| BlockAutoDoor
|
||||||
|
{ _wlLine :: [Point2]
|
||||||
|
, _wlID :: Int
|
||||||
|
, _doorMech :: World -> World
|
||||||
|
, _wlColor :: Color
|
||||||
|
, _wlDraw :: Maybe (Wall -> Drawing)
|
||||||
|
, _wlSeen :: Bool
|
||||||
|
, _blIDs :: [Int]
|
||||||
|
, _blHP :: Int
|
||||||
|
, _wlIsSeeThrough :: Bool
|
||||||
|
-- , _wlID' :: WLID
|
||||||
|
}
|
||||||
|
| AutoDoor
|
||||||
|
{ _wlLine :: [Point2] , _wlID :: Int
|
||||||
|
, _doorMech :: World -> World
|
||||||
|
, _wlColor :: Color
|
||||||
|
, _wlDraw :: Maybe (Wall -> Drawing)
|
||||||
|
, _wlSeen :: Bool
|
||||||
|
, _wlIsSeeThrough :: Bool
|
||||||
|
-- , _wlID' :: WLID
|
||||||
|
-- , _doorLine :: [Point2]
|
||||||
|
}
|
||||||
|
| Door
|
||||||
|
{ _wlLine :: [Point2] , _wlID :: Int
|
||||||
|
, _doorMech :: World -> World
|
||||||
|
, _wlColor :: Color
|
||||||
|
, _wlDraw :: Maybe (Wall -> Drawing)
|
||||||
|
, _wlSeen :: Bool
|
||||||
|
, _wlIsSeeThrough :: Bool
|
||||||
|
-- , _wlID' :: WLID
|
||||||
|
-- , _doorLine :: [Point2]
|
||||||
|
}
|
||||||
|
| Block
|
||||||
|
{ _wlLine :: [Point2]
|
||||||
|
, _wlID :: Int
|
||||||
|
, _wlColor :: Color
|
||||||
|
, _wlDraw :: Maybe (Wall -> Drawing)
|
||||||
|
, _wlSeen :: Bool
|
||||||
|
, _blIDs :: [Int]
|
||||||
|
, _blHP :: Int
|
||||||
|
, _wlIsSeeThrough :: Bool
|
||||||
|
, _blVisible :: Bool
|
||||||
|
, _blShadows :: [Int]
|
||||||
|
, _blDegrades :: [Int]
|
||||||
|
-- , _wlID' :: WLID
|
||||||
|
}
|
||||||
|
|
||||||
|
data Block' = Block'
|
||||||
|
{ _blPoly :: [Point2]
|
||||||
|
, _blLines :: [[Point2]]
|
||||||
|
, _blColor :: Color
|
||||||
|
, _blSeen :: Bool
|
||||||
|
, _blHP' :: [Int]
|
||||||
|
, _blVisible' :: Bool
|
||||||
|
, _blShadows' :: [Int]
|
||||||
|
, _blID :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
data ForceField = FF { _ffLine :: [Point2] , _ffID :: Int
|
||||||
|
, _ffColor :: Color
|
||||||
|
, _ffDeflect :: Maybe (StdGen -> Point2 -> ForceField -> (Point2,StdGen))
|
||||||
|
, _ffState :: FFState
|
||||||
|
}
|
||||||
|
data FFState = FFDestroyable { _ffsHP :: Int }
|
||||||
|
|
||||||
|
data Ammo = PistolBullet | LiquidFuel | Battery | Shell
|
||||||
|
deriving (Eq,Ord,Enum)
|
||||||
|
|
||||||
|
data SoundOrigin = InventorySound
|
||||||
|
| BackgroundSound
|
||||||
|
| OnceSound
|
||||||
|
| CrSound Int
|
||||||
|
| CrWeaponSound Int
|
||||||
|
| WallSound Int
|
||||||
|
| CrReloadSound Int
|
||||||
|
| Flamer
|
||||||
|
| ShellSound Int
|
||||||
|
| Flame
|
||||||
|
| LasSound
|
||||||
|
| FootstepSound Int
|
||||||
|
| BlockDegradeSound Int
|
||||||
|
| CrHitSound Int
|
||||||
|
| BarrelHiss Int
|
||||||
|
deriving (Eq,Ord,Show)
|
||||||
|
|
||||||
|
type Poly = [Point2]
|
||||||
|
data PSType = PutCrit Creature
|
||||||
|
| PutLS LightSource Drawing
|
||||||
|
| PutButton Button
|
||||||
|
| PutFlIt FloorItem
|
||||||
|
| PutPressPlate PressPlate
|
||||||
|
| PutAutoDoor Point2 Point2
|
||||||
|
| PutBlock [Int] Color [Point2]
|
||||||
|
| PutWindowBlock Point2 Point2
|
||||||
|
| PutTriggerDoor Color (World -> Bool) Point2 Point2
|
||||||
|
| PutBtDoor Color Point2 Float Point2 Point2
|
||||||
|
| PutSwitchDoor Color Point2 Float Point2 Point2
|
||||||
|
| RandPS (State StdGen PSType)
|
||||||
|
| PutNothing
|
||||||
|
| CollectivePS
|
||||||
|
{ _collectiveID :: Int
|
||||||
|
, _collectiveNum :: Int
|
||||||
|
, _collectiveFunction :: [Int] -> State StdGen [PSType]
|
||||||
|
}
|
||||||
|
| LabelPS { _psLabel :: Int, _ps :: PSType}
|
||||||
|
| PutWindow { _pwPoly :: [Point2]
|
||||||
|
, _pwColor :: Color
|
||||||
|
}
|
||||||
|
data PlacementSpot = PS
|
||||||
|
{ _psPos :: Point2
|
||||||
|
, _psRot :: Float
|
||||||
|
, _psType :: PSType
|
||||||
|
}
|
||||||
|
data Room = Room
|
||||||
|
{ _rmPolys :: [Poly]
|
||||||
|
, _rmLinks :: [(Point2,Float)]
|
||||||
|
, _rmPath :: [(Point2, Point2)]
|
||||||
|
, _rmPS :: [PlacementSpot]
|
||||||
|
, _rmBound :: Poly
|
||||||
|
}
|
||||||
|
|
||||||
|
data SubNode a = SN a | InLink | OutLink
|
||||||
|
|
||||||
|
data RoomLink = RL {_rlPos :: Point2, _rlRot :: Float}
|
||||||
|
|
||||||
|
makeLenses ''RoomLink
|
||||||
|
makeLenses ''World
|
||||||
|
makeLenses ''Cloud
|
||||||
|
makeLenses ''Creature
|
||||||
|
makeLenses ''CreatureState
|
||||||
|
makeLenses ''CrSpState
|
||||||
|
makeLenses ''Goal
|
||||||
|
makeLenses ''LightSource
|
||||||
|
makeLenses ''TempLightSource
|
||||||
|
makeLenses ''Stance
|
||||||
|
makeLenses ''Carriage
|
||||||
|
makeLenses ''Posture
|
||||||
|
makeLenses ''Item
|
||||||
|
makeLenses ''ItemPos
|
||||||
|
makeLenses ''ItEffect
|
||||||
|
makeLenses ''ItAttachment
|
||||||
|
makeLenses ''ItZoom
|
||||||
|
makeLenses ''FloorItem
|
||||||
|
makeLenses ''Particle
|
||||||
|
makeLenses ''Particle'
|
||||||
|
makeLenses ''Wall
|
||||||
|
makeLenses ''Smoke
|
||||||
|
makeLenses ''ForceField
|
||||||
|
makeLenses ''FFState
|
||||||
|
makeLenses ''PressPlate
|
||||||
|
makeLenses ''Button
|
||||||
|
makeLenses ''PSType
|
||||||
|
makeLenses ''PlacementSpot
|
||||||
|
makeLenses ''Room
|
||||||
|
makeLenses ''Sound
|
||||||
|
--
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
numColor :: Int -> Color
|
||||||
|
numColor 0 = (1,0,0,1)
|
||||||
|
numColor 1 = (0,1,0,1)
|
||||||
|
numColor 2 = (0,0,1,1)
|
||||||
|
numColor 3 = (1,1,0,1)
|
||||||
|
numColor 4 = (0,1,1,1)
|
||||||
|
numColor 5 = (1,0,1,1)
|
||||||
|
numColor 6 = (1,0,0.5,1)
|
||||||
|
numColor 7 = (0.5,0,1,1)
|
||||||
|
numColor 8 = (0,0.5,1,1)
|
||||||
|
numColor 9 = (0,1,0.5,1)
|
||||||
|
numColor 10 = (0.5,1,0,1)
|
||||||
|
numColor 11 = (1,0.5,0,1)
|
||||||
|
numColor 12 = (1,1,1,1)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
module Dodge.Debug where
|
||||||
|
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
|
||||||
|
import Geometry.Data
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
|
||||||
|
drawCircleAtFor :: Point2 -> Int -> World -> World
|
||||||
|
drawCircleAtFor p t w =
|
||||||
|
let n = newParticleKey w
|
||||||
|
in over particles ( IM.insert n
|
||||||
|
Particle { _ptPos = p
|
||||||
|
, _ptStartPos = p
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = onLayer PtLayer $ uncurry translate p $ color white $ circleSolid 20
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = ptTimer t n
|
||||||
|
} ) w
|
||||||
|
drawCircleAtForCol :: Point2 -> Int -> Color -> World -> World
|
||||||
|
drawCircleAtForCol p t col w =
|
||||||
|
let n = newParticleKey w
|
||||||
|
in over particles ( IM.insert n
|
||||||
|
Particle { _ptPos = p
|
||||||
|
, _ptStartPos = p
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = onLayer PtLayer $ uncurry translate p $ color col $ circleSolid 20
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = ptTimer t n
|
||||||
|
} ) w
|
||||||
|
drawLineForCol :: [Point2] -> Int -> Color -> World -> World
|
||||||
|
drawLineForCol ps t col w =
|
||||||
|
let n = newParticleKey w
|
||||||
|
in over particles ( IM.insert n
|
||||||
|
Particle { _ptPos = head ps
|
||||||
|
, _ptStartPos = head ps
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = onLayer PtLayer $ color col $ lineOfThickness 5 ps
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = ptTimer t n
|
||||||
|
} ) w
|
||||||
|
|
||||||
|
ptTimer :: Int -> Int -> World -> World
|
||||||
|
ptTimer 0 i = over particles (IM.delete i)
|
||||||
|
ptTimer time i = set (particles . ix i . ptUpdate) $ ptTimer (time - 1) i
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
module Dodge.Initialisation where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Prototypes
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Critters
|
||||||
|
import Dodge.Base
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Char
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
import Data.Graph.Inductive.PatriciaTree
|
||||||
|
import Codec.BMP
|
||||||
|
import qualified Data.ByteString as B
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.State
|
||||||
|
import Control.Monad
|
||||||
|
import System.Random
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
--import Graphics.Gloss
|
||||||
|
--import Graphics.Gloss.Interface.IO.Game
|
||||||
|
--import Graphics.Gloss.Geometry.Angle
|
||||||
|
--import Graphics.Gloss.Geometry.Line
|
||||||
|
--import qualified Graphics.Gloss.Data.Point.Arithmetic as PA
|
||||||
|
--import Graphics.Gloss.Data.Vector
|
||||||
|
--import Graphics.Gloss.Data.Point
|
||||||
|
import Foreign.ForeignPtr
|
||||||
|
import Control.Concurrent
|
||||||
|
import Data.Tree
|
||||||
|
|
||||||
|
initializeWorld :: World -> World
|
||||||
|
initializeWorld w = w
|
||||||
|
|
||||||
|
initialWorld = basicWorld
|
||||||
|
{ _keys = S.empty
|
||||||
|
, _cameraPos = (0,0)
|
||||||
|
, _cameraRot = 0
|
||||||
|
, _cameraZoom = 1
|
||||||
|
, _creatures = IM.fromList [(0,startCr)]
|
||||||
|
, _particles = IM.empty
|
||||||
|
, _walls = IM.empty
|
||||||
|
, _forceFields = IM.empty
|
||||||
|
, _floorItems = IM.empty
|
||||||
|
, _randGen = mkStdGen 2
|
||||||
|
, _mousePos = (0,0)
|
||||||
|
, _testString = []
|
||||||
|
, _yourID = 0
|
||||||
|
, _worldEvents = id
|
||||||
|
, _pressPlates = IM.empty
|
||||||
|
, _buttons = IM.empty
|
||||||
|
, _soundQueue = []
|
||||||
|
-- , _loadedSounds = IM.empty
|
||||||
|
, _sounds = M.empty
|
||||||
|
, _decorations = IM.empty
|
||||||
|
, _unlimitedAmmo = True
|
||||||
|
, _storedLevel = Nothing
|
||||||
|
, _menuState = LevelMenu 1
|
||||||
|
, _worldState = M.empty
|
||||||
|
, _windowX = 800
|
||||||
|
, _windowY = 840
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
module Dodge.KeyEvents where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.CreatureActions
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Char
|
||||||
|
import Data.List
|
||||||
|
import Data.Function
|
||||||
|
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
|
||||||
|
import SDL
|
||||||
|
|
||||||
|
--import Graphics.Gloss
|
||||||
|
--import Graphics.Gloss.Interface.IO.Game
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
pauseGame :: World -> World
|
||||||
|
pauseGame w = w {_menuState = PauseMenu}
|
||||||
|
|
||||||
|
handleEvent :: World -> Event -> Maybe World
|
||||||
|
handleEvent w e = case eventPayload e of
|
||||||
|
KeyboardEvent kev -> handleKeyEvent w kev
|
||||||
|
MouseMotionEvent mmev -> handleMouseMotionEvent w mmev
|
||||||
|
MouseButtonEvent mbev -> handleMouseButtonEvent w mbev
|
||||||
|
MouseWheelEvent mwev -> handleMouseWheelEvent w mwev
|
||||||
|
WindowSizeChangedEvent sev -> handleResizeEvent w sev
|
||||||
|
_ -> Just w
|
||||||
|
|
||||||
|
handleKeyEvent :: World -> KeyboardEventData -> Maybe World
|
||||||
|
handleKeyEvent w kev = case keyboardEventKeyMotion kev of
|
||||||
|
Released -> Just $ over keys (S.delete $ getKeycode kev) w
|
||||||
|
_ -> handlePressedKeyEvent (addKey (getKeycode kev) w) (keyboardEventRepeat kev) (getKeycode kev)
|
||||||
|
where getKeycode :: KeyboardEventData -> Keycode
|
||||||
|
getKeycode = keysymKeycode . keyboardEventKeysym
|
||||||
|
|
||||||
|
addKey :: Keycode -> World -> World
|
||||||
|
addKey k = over keys $ S.insert k
|
||||||
|
|
||||||
|
handlePressedKeyEvent :: World -> Bool -> Keycode -> Maybe World
|
||||||
|
handlePressedKeyEvent w True _ = Just w
|
||||||
|
handlePressedKeyEvent w _ keycode
|
||||||
|
= case keycode of
|
||||||
|
KeycodeEscape -> Nothing
|
||||||
|
-- KeycodeEscape -> Just $ pauseGame $ escapeMap w
|
||||||
|
KeycodeC -> Just $ pauseGame $ escapeMap w
|
||||||
|
KeycodeF -> Just $ dropItem w
|
||||||
|
KeycodeM -> Just $ toggleMap w
|
||||||
|
KeycodeP -> Just $ pauseGame $ escapeMap w
|
||||||
|
KeycodeR -> Just $ fromMaybe w $ reloadWeapon (_yourID w) w
|
||||||
|
KeycodeT -> Just $ testEvent w
|
||||||
|
KeycodeSpace -> Just $ spaceAction w
|
||||||
|
KeycodeQ -> Just $ w {_cameraRot = _cameraRot w + 0.01}
|
||||||
|
KeycodeE -> Just $ w {_cameraRot = _cameraRot w - 0.01}
|
||||||
|
_ -> Just w
|
||||||
|
|
||||||
|
handleMouseMotionEvent :: World -> MouseMotionEventData -> Maybe World
|
||||||
|
handleMouseMotionEvent w mmev
|
||||||
|
= Just $ set mousePos (fromIntegral x - 0.5*_windowX w
|
||||||
|
,0.5*_windowY w - fromIntegral y
|
||||||
|
) w
|
||||||
|
where P (V2 x y) = mouseMotionEventPos mmev
|
||||||
|
|
||||||
|
handleMouseButtonEvent :: World -> MouseButtonEventData -> Maybe World
|
||||||
|
handleMouseButtonEvent w mbev = case mouseButtonEventMotion mbev of
|
||||||
|
Released -> Just $ over mouseButtons (S.delete but) w
|
||||||
|
Pressed -> handlePressedMouseButton but $ over mouseButtons (S.insert but) w
|
||||||
|
where but = mouseButtonEventButton mbev
|
||||||
|
|
||||||
|
handlePressedMouseButton :: MouseButton -> World -> Maybe World
|
||||||
|
handlePressedMouseButton ButtonMiddle w = Just $ set lbClickMousePos (_mousePos w) w
|
||||||
|
handlePressedMouseButton _ w = Just w
|
||||||
|
|
||||||
|
handleMouseWheelEvent :: World -> MouseWheelEventData -> Maybe World
|
||||||
|
handleMouseWheelEvent w mwev =
|
||||||
|
case mouseWheelEventPos mwev of
|
||||||
|
V2 x y | y > 0 -> Just $ wheelUpEvent w
|
||||||
|
| y < 0 -> Just $ wheelDownEvent w
|
||||||
|
| otherwise -> Just $ w
|
||||||
|
-- case mouseWheelEventDirection mwev of
|
||||||
|
-- ScrollNormal -> Just $ wheelUpEvent w
|
||||||
|
-- ScrollFlipped -> Just $ wheelDownEvent w
|
||||||
|
|
||||||
|
|
||||||
|
handleResizeEvent :: World -> WindowSizeChangedEventData -> Maybe World
|
||||||
|
handleResizeEvent w sev = Just $ set windowX (fromIntegral x)
|
||||||
|
$ set windowY (fromIntegral y) w
|
||||||
|
where V2 x y = windowSizeChangedEventSize sev
|
||||||
|
|
||||||
|
toggleMap w = w & mapDisplay . _1 %~ not
|
||||||
|
escapeMap w = w & mapDisplay . _1 .~ False
|
||||||
|
|
||||||
|
wheelUpEvent :: World -> World
|
||||||
|
wheelUpEvent w
|
||||||
|
= case _mapDisplay w of
|
||||||
|
(True,z) -> w & mapDisplay . _2 .~ min 0.3 (z+(0.1*z))
|
||||||
|
_ | rbPressed -> fromMaybe w $ (yourItem w ^? itScrollUp) <*> pure (_crInvSel (you w))
|
||||||
|
<*> pure w
|
||||||
|
| lbPressed -> w {_cameraZoom = _cameraZoom w + 0.1}
|
||||||
|
| otherwise -> upInvPos w
|
||||||
|
where lbPressed = ButtonLeft `S.member` mbs
|
||||||
|
rbPressed = ButtonRight `S.member` mbs
|
||||||
|
mbs = _mouseButtons w
|
||||||
|
wheelDownEvent :: World -> World
|
||||||
|
wheelDownEvent w
|
||||||
|
= case _mapDisplay w of
|
||||||
|
(True,z) -> w & mapDisplay . _2 .~ max 0.05 (z-(0.1*z))
|
||||||
|
_ | rbPressed -> fromMaybe w $ (yourItem w ^? itScrollDown) <*> pure (_crInvSel (you w))
|
||||||
|
<*> pure w
|
||||||
|
| lbPressed -> w {_cameraZoom = max (_cameraZoom w - 0.1) 0.01}
|
||||||
|
| otherwise -> downInvPos w
|
||||||
|
where lbPressed = ButtonLeft `S.member` mbs
|
||||||
|
rbPressed = ButtonRight `S.member` mbs
|
||||||
|
mbs = _mouseButtons w
|
||||||
|
|
||||||
|
upInvPos :: World -> World
|
||||||
|
--upInvPos w = soundOnce 3 $ stopSoundFrom (CrReloadSound 0) $ set (creatures . ix (_yourID w) . crInvSel) x w
|
||||||
|
upInvPos w = stopSoundFrom (CrReloadSound 0) $ set (creatures . ix (_yourID w) . crInvSel) x w
|
||||||
|
where is = IM.keys $ _crInv $ _creatures w IM.! _yourID w
|
||||||
|
isis = is ++ is
|
||||||
|
x = before (_crInvSel (you w)) isis
|
||||||
|
downInvPos :: World -> World
|
||||||
|
--downInvPos w = playSoundFromFor InventorySound 3 2 $ stopSoundFrom (CrReloadSound 0) $ set (creatures . ix (_yourID w) . crInvSel) x w
|
||||||
|
downInvPos w = stopSoundFrom (CrReloadSound 0) $ set (creatures . ix (_yourID w) . crInvSel) x w
|
||||||
|
where is = IM.keys $ _crInv $ _creatures w IM.! _yourID w
|
||||||
|
isis = is ++ is
|
||||||
|
x = after (_crInvSel (you w)) isis
|
||||||
|
|
||||||
|
|
||||||
|
-- these are incomplete: should put in error case here!
|
||||||
|
before y (x:z:ys) | y == z = x
|
||||||
|
| otherwise = before y (z:ys)
|
||||||
|
after y (x:z:ys) | y == x = z
|
||||||
|
| otherwise = after y (z:ys)
|
||||||
|
|
||||||
|
mouseActionsCr :: S.Set MouseButton -> Creature -> Creature
|
||||||
|
mouseActionsCr keys cr
|
||||||
|
| rbPressed
|
||||||
|
= set ( crState . stance . posture) Aiming cr
|
||||||
|
| otherwise
|
||||||
|
= set ( crState . stance . posture) AtEase cr
|
||||||
|
where lbPressed = ButtonLeft `S.member` keys
|
||||||
|
rbPressed = ButtonRight `S.member` keys
|
||||||
|
-- wUp = MouseButton WheelUp `S.member` keys
|
||||||
|
-- -- wUp = Char 'q' `S.member` keys
|
||||||
|
-- wDown = MouseButton WheelDown `S.member` keys
|
||||||
|
|
||||||
|
mouseActionsWorld :: S.Set MouseButton -> World -> World
|
||||||
|
mouseActionsWorld keys w
|
||||||
|
| lbPressed && rbPressed
|
||||||
|
= useItemContinuous (_yourID w) w
|
||||||
|
| mbPressed
|
||||||
|
= set lbClickMousePos (_mousePos w) $ over cameraRot (\r-> r - rotation) w
|
||||||
|
| otherwise
|
||||||
|
= w
|
||||||
|
where lbPressed = ButtonLeft `S.member` keys
|
||||||
|
rbPressed = ButtonRight `S.member` keys
|
||||||
|
mbPressed = ButtonMiddle `S.member` keys
|
||||||
|
---wUp = MouseButton WheelUp `S.member` keys
|
||||||
|
---wDown = MouseButton WheelDown `S.member` keys
|
||||||
|
---theItem = _crInv (you w) IM.! _crInvSel (you w)
|
||||||
|
rotation = angleBetween (_mousePos w) (_lbClickMousePos w)
|
||||||
|
|
||||||
|
-- for now, left are floor items, right are buttons
|
||||||
|
closestActiveObject :: World -> Maybe (Either FloorItem Button)
|
||||||
|
closestActiveObject w = listToMaybe $ sortBy (compare `on` dist ypos . pos) $ actObjs
|
||||||
|
where ypos = _crPos $ you w
|
||||||
|
actObjs = filter (\obj -> dist ypos (pos obj) < 40 && hasLOS ypos (pos obj) w)
|
||||||
|
$ map Left (IM.elems $ _floorItems w) ++ map Right (IM.elems $ _buttons w)
|
||||||
|
pos (Right x) = _btPos x
|
||||||
|
pos (Left x) = _flItPos x
|
||||||
|
|
||||||
|
spaceAction :: World -> World
|
||||||
|
spaceAction w = case closestActiveObject w of
|
||||||
|
Just (Left flit) -> pickUpItem' flit w
|
||||||
|
Just (Right but) -> _btEvent but but w
|
||||||
|
Nothing -> w
|
||||||
|
|
||||||
|
testEvent :: World -> World
|
||||||
|
testEvent w = w
|
||||||
|
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
module Dodge.Layout where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Weapons
|
||||||
|
import Dodge.Critters
|
||||||
|
import Dodge.LevelGen
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.RandomHelp
|
||||||
|
import Dodge.Prototypes
|
||||||
|
import Dodge.Path
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
--
|
||||||
|
--import Graphics.Gloss
|
||||||
|
--import Graphics.Gloss.Data.Vector
|
||||||
|
--import Graphics.Gloss.Geometry.Line
|
||||||
|
--import Graphics.Gloss.Geometry.Angle
|
||||||
|
--
|
||||||
|
import Control.Monad.State
|
||||||
|
--import Control.Monad.Loops
|
||||||
|
import Control.Lens
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Tree
|
||||||
|
import Data.Either
|
||||||
|
import Data.Function
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
import Data.Graph.Inductive.Basic
|
||||||
|
import Data.Graph.Inductive.PatriciaTree
|
||||||
|
import Data.Graph.Inductive.NodeMap
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
generateFromTree :: State StdGen (Tree Room) -> World -> World
|
||||||
|
generateFromTree t w = zoning $ placeSpots plmnts
|
||||||
|
$ w {_walls = wallsFromTree tr, _randGen = g
|
||||||
|
-- ,_loadedSounds = lSounds
|
||||||
|
,_pathGraph = path
|
||||||
|
,_pathGraph' = pairGraph
|
||||||
|
,_pathPoints = foldr insertPoint IM.empty (labNodes path)
|
||||||
|
,_pathInc = pinc}
|
||||||
|
where tr = evalState t $ _randGen w
|
||||||
|
plmnts = concatMap _rmPS $ flatten tr
|
||||||
|
g = _randGen w
|
||||||
|
-- lSounds = _loadedSounds w
|
||||||
|
path = pairsToGraph dist pairGraph
|
||||||
|
pairGraph = makePath tr
|
||||||
|
insertPoint pp@(_,(x,y)) = insertInZoneWith (floorHun x) (floorHun y) (++) [pp]
|
||||||
|
pinc = M.fromList $ pairsToIncidence pairGraph
|
||||||
|
zoning w = set wallsZone (IM.foldr wallInZone IM.empty (_walls w))
|
||||||
|
w
|
||||||
|
wallInZone wl | dist (_wlLine wl !! 0) (_wlLine wl !! 1) <= 2*zoneSize
|
||||||
|
= insertIMInZone x y wlid wl
|
||||||
|
| otherwise = flip (foldr (\(a,b) -> insertIMInZone a b wlid wl)) ips
|
||||||
|
where (x,y) = zoneOfPoint $ (pHalf (_wlLine wl !! 0) (_wlLine wl !! 1))
|
||||||
|
wlid = _wlID wl
|
||||||
|
ips = map zoneOfPoint $ divideLine (2*zoneSize) (_wlLine wl !! 0) (_wlLine wl !! 1)
|
||||||
|
|
||||||
|
makePath :: Tree Room -> [(Point2,Point2)]
|
||||||
|
makePath = concat . map _rmPath . flatten
|
||||||
|
|
||||||
|
wallsFromTree :: Tree Room -> IM.IntMap Wall
|
||||||
|
wallsFromTree t = divideWalls $ foldr cutWalls IM.empty (concatMap _rmPolys $ flatten t)
|
||||||
|
|
||||||
|
divideWall :: Wall -> [Wall]
|
||||||
|
divideWall wl
|
||||||
|
= let (a:b:_) = _wlLine wl
|
||||||
|
--ps = divideLine (zoneSize * 2) a b
|
||||||
|
ps = divideLine (zoneSize * 2) a b
|
||||||
|
in map (\(x,y) -> wl {_wlLine = [x,y]}) $ zip (init ps) (tail ps)
|
||||||
|
|
||||||
|
divideWallIn :: Wall -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
divideWallIn wl wls =
|
||||||
|
let (wl':newWls) = divideWall wl
|
||||||
|
k = newKey wls
|
||||||
|
newWls' = zipWith (\i w -> w {_wlID = i}) [k..] newWls
|
||||||
|
in foldr (\w -> IM.insert (_wlID w) w) wls (wl':newWls')
|
||||||
|
|
||||||
|
divideWalls :: IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
divideWalls wls = IM.foldr divideWallIn wls wls
|
||||||
|
|
||||||
|
collapseTree :: Tree (Tree (Either Room Room)) -> Tree (Either Room Room)
|
||||||
|
collapseTree = g . expandTreeBy f
|
||||||
|
where f (Node (Right x) xs) = Node (Right (Right x)) $ map f xs
|
||||||
|
f (Node (Left x) xs) = Node (Left (Left x)) $ map f xs
|
||||||
|
g (Node (Right x) []) = Node (Right x) []
|
||||||
|
g (Node (Right x) xs) = Node (Left x) $ map g xs
|
||||||
|
g (Node y ys) = Node y $ map g ys
|
||||||
|
|
||||||
|
|
||||||
|
insertInZone :: Int -> Int -> a -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)
|
||||||
|
insertInZone x y obj = IM.insertWith f x $ IM.singleton y obj
|
||||||
|
where f _ = IM.insert y obj
|
||||||
|
|
||||||
|
allPairs :: Eq a => [a] -> [(a,a)]
|
||||||
|
allPairs xs = [(x,y) | x <- xs, y <- xs, x /= y]
|
||||||
|
|
||||||
|
treePost :: [a] -> a -> Tree a
|
||||||
|
treePost [] y = Node y []
|
||||||
|
treePost (x:xs) y = Node x [treePost xs y]
|
||||||
|
|
||||||
|
randomiseLinks :: RandomGen g => Room -> State g (Tree (Either Room Room))
|
||||||
|
randomiseLinks r = do
|
||||||
|
newLinks <- shuffle $ init $ _rmLinks r
|
||||||
|
return $ connectRoom $ r {_rmLinks = newLinks ++ [last $ _rmLinks r]}
|
||||||
|
randLinks :: RandomGen g => Room -> State g Room
|
||||||
|
randLinks r = do
|
||||||
|
newLinks <- shuffle $ init $ _rmLinks r
|
||||||
|
return $ r {_rmLinks = newLinks ++ [last $ _rmLinks r]}
|
||||||
|
|
||||||
|
filterLinks :: RandomGen g => ((Point2,Float) -> Bool) -> Room -> State g Room
|
||||||
|
filterLinks cond r = do
|
||||||
|
newLinks <- shuffle $ filter cond $ init $ _rmLinks r
|
||||||
|
return $ r {_rmLinks = newLinks ++ [last $ _rmLinks r]}
|
||||||
|
|
||||||
|
changeLinkTo :: RandomGen g => ((Point2,Float) -> Bool) -> Room -> State g Room
|
||||||
|
changeLinkTo cond r = do
|
||||||
|
l <- takeOne $ filter cond $ _rmLinks r
|
||||||
|
let newLinks = delete l (_rmLinks r) ++ [l]
|
||||||
|
return $ r {_rmLinks = newLinks}
|
||||||
|
|
||||||
|
|
||||||
|
-- Left elements get new children, Right elements inherit the children from the
|
||||||
|
-- mapped over node
|
||||||
|
composeTreeWith :: (a -> Tree (Either b b)) -> Tree a -> Tree (Either b b)
|
||||||
|
composeTreeWith f (Node x []) = f x
|
||||||
|
composeTreeWith f (Node x xs) = paste xs $ f x
|
||||||
|
where paste xs (Node (Right y) _) = Node (Left y) (map (composeTreeWith f) xs)
|
||||||
|
paste xs (Node (Left y) ys) = Node (Left y) (map (paste xs) ys)
|
||||||
|
|
||||||
|
doPolysIntersect :: [Point2] -> [Point2] -> Bool
|
||||||
|
doPolysIntersect (p:ps) (q:qs)
|
||||||
|
= any isJust $ (\(a,b) (c,d) -> intersectSegSeg' a b c d) <$> pair1s <*> pair2s
|
||||||
|
where pair1s = zip (p:ps) (ps++[p])
|
||||||
|
pair2s = zip (q:qs) (qs++[q])
|
||||||
|
doPolysIntersect [] _ = False
|
||||||
|
doPolysIntersect _ [] = False
|
||||||
|
|
||||||
|
boundClip :: Tree Room -> Bool
|
||||||
|
boundClip t = or $ map (uncurry doPolysIntersect) [(x,y) | x<- xs, y<-xs, x>y]
|
||||||
|
++ map f [(ps,qs) | ps <- xs, qs <-xs, ps/=qs]
|
||||||
|
where xs = map _rmBound $ flatten t
|
||||||
|
f ([],qs) = False
|
||||||
|
f ((p:_),qs) = pointInPolygon p qs
|
||||||
|
|
||||||
|
noBoundClip :: Tree Room -> Bool
|
||||||
|
noBoundClip = not . boundClip
|
||||||
|
|
||||||
|
connectRoom :: a -> Tree (Either a a)
|
||||||
|
connectRoom r = Node (Right r) []
|
||||||
|
deadRoom :: a -> Tree (Either a a)
|
||||||
|
deadRoom r = Node (Left r) []
|
||||||
|
|
||||||
|
onRoot :: (a -> a) -> Tree a -> Tree a
|
||||||
|
onRoot f (Node t ts) = Node (f t) ts
|
||||||
|
|
||||||
|
shiftRoomTree :: Tree Room -> Tree Room
|
||||||
|
shiftRoomTree (Node t []) = Node t []
|
||||||
|
shiftRoomTree (Node t ts) = Node t $ zipWith (\l -> shiftRoomTree . onRoot (shiftRoomBy l . f))
|
||||||
|
(_rmLinks t)
|
||||||
|
ts
|
||||||
|
where f r = shiftRoomBy ((0,0) -.- (rotateV (pi-a) p),0) $ shiftRoomBy ((0,0),pi-a) r
|
||||||
|
where (p,a) = last $ _rmLinks r
|
||||||
|
|
||||||
|
shiftRoomBy :: (Point2,Float) -> Room -> Room
|
||||||
|
shiftRoomBy shift@(pos,rot) r =
|
||||||
|
over rmPolys (fmap (map (shiftPointBy shift)))
|
||||||
|
$ over rmLinks (fmap (shiftLinkBy shift))
|
||||||
|
$ over rmPath (map (shiftPathPointBy shift))
|
||||||
|
$ over rmPS (fmap (shiftPSBy shift))
|
||||||
|
$ over rmBound (map (shiftPointBy shift))
|
||||||
|
r
|
||||||
|
|
||||||
|
shiftPathPointBy s (p1,p2) = (shiftPointBy s p1, shiftPointBy s p2)
|
||||||
|
|
||||||
|
shiftLinkBy (pos,rot) (p,r) = (shiftPointBy (pos,rot) p, r + rot)
|
||||||
|
shiftPSBy (pos,rot) ps = case ps of
|
||||||
|
PS {} -> over psPos (shiftPointBy (pos,rot))
|
||||||
|
$ over psRot (+rot)
|
||||||
|
ps
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,756 @@
|
|||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
module Dodge.LevelGen where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
import Dodge.Prototypes
|
||||||
|
import Dodge.Block
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import System.Random
|
||||||
|
--import Graphics.Gloss
|
||||||
|
--import Graphics.Gloss.Geometry.Line
|
||||||
|
--import Graphics.Gloss.Geometry.Angle
|
||||||
|
--import Graphics.Gloss.Data.Vector
|
||||||
|
import Control.Monad.State
|
||||||
|
import Data.List
|
||||||
|
import Data.Function
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Lens
|
||||||
|
import Data.Maybe
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Data.Graph.Inductive
|
||||||
|
import Data.Graph.Inductive.NodeMap
|
||||||
|
import Data.Tree
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
inSegSeg p1 p2 a1 a2 = myIntersectSegSeg p1' p2' a1' a2'
|
||||||
|
where p1' = p1 +.+ normalizeV (p1 -.- p2)
|
||||||
|
p2' = p2 +.+ normalizeV (p2 -.- p1)
|
||||||
|
a1' = a1 +.+ normalizeV (a1 -.- a2)
|
||||||
|
a2' = a2 +.+ normalizeV (a2 -.- a1)
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
--supposes that the quadrilateral points are given in anticlockwise order
|
||||||
|
-- cutOutQuad :: Point -> Point -> Point -> Point -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
-- cutOutQuad p1 p2 p3 p4 walls = cutLine p4 p1 . cutLine p3 p4 . cutLine p2 p3 . cutLine p1 p2 $ walls
|
||||||
|
|
||||||
|
collidePointAllWalls :: Point2 -> Point2 -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
collidePointAllWalls p1 p2 walls = IM.filter f walls
|
||||||
|
where f wall
|
||||||
|
= case myIntersectSegSeg p1 p2 (_wlLine wall !! 0) (_wlLine wall !! 1)
|
||||||
|
of Nothing -> False
|
||||||
|
_ -> True
|
||||||
|
|
||||||
|
-- returns walls that collide with lines, and the point of collision
|
||||||
|
|
||||||
|
collidePointAllWallsPoints :: Point2 -> Point2 -> IM.IntMap Wall -> IM.IntMap (Wall,Point2)
|
||||||
|
collidePointAllWallsPoints p1 p2 walls = IM.mapMaybe f walls
|
||||||
|
where f wall
|
||||||
|
= case myIntersectSegSeg p1 p2 (_wlLine wall !! 0) (_wlLine wall !! 1)
|
||||||
|
of Nothing -> Nothing
|
||||||
|
Just p -> Just (wall,p)
|
||||||
|
|
||||||
|
collidePointAllWallsPoints' :: Point2 -> Point2 -> IM.IntMap Wall -> IM.IntMap (Wall,[Point2])
|
||||||
|
collidePointAllWallsPoints' p1 p2 walls = IM.mapMaybe f walls
|
||||||
|
where f wall
|
||||||
|
= case myIntersectSegSeg p1 p2 (_wlLine wall !! 0) (_wlLine wall !! 1)
|
||||||
|
of Nothing -> Nothing
|
||||||
|
Just p -> Just (wall,[p])
|
||||||
|
|
||||||
|
collidePolygonWalls :: [Point2] -> IM.IntMap Wall -> IM.IntMap (Wall,[Point2])
|
||||||
|
collidePolygonWalls (p:ps) walls
|
||||||
|
= IM.unionsWith f (fmap g (zip (p:ps) (ps ++ [p])))
|
||||||
|
-- = fmap ($ walls) $ zipWith collidePointAllWallsPoints' (init ps) (tail ps)
|
||||||
|
--where f (x,xs) (_,ys) = (x,xs++ys)
|
||||||
|
where f (x,xs) (_,ys) = (x,xs++ys)
|
||||||
|
g (p1,p2) = collidePointAllWallsPoints' p1 p2 walls
|
||||||
|
|
||||||
|
-- -- assumes that walls meet in corners
|
||||||
|
-- there appears to be an issue when one wall point is inside and the other exactly on the polygon
|
||||||
|
-- this is probably an issue with collidePolygonwalls
|
||||||
|
existingWallChanges :: [Point2] -> (Wall,[Point2]) -> [Wall]
|
||||||
|
existingWallChanges poly (wall,ps) = catMaybes [w1,w2]
|
||||||
|
where wp1 = _wlLine wall !! 0
|
||||||
|
wp2 = _wlLine wall !! 1
|
||||||
|
c1 = head $ sortBy (compare `on` dist wp1) ps
|
||||||
|
c2 = head $ sortBy (compare `on` dist wp2) ps
|
||||||
|
--c1 = last ps
|
||||||
|
--c2 = last ps
|
||||||
|
w1 = if errorPointInPolygon 5 wp1 poly || wp1 == c1
|
||||||
|
then Nothing
|
||||||
|
else Just $ wall {_wlLine = [ wp1, c1 ] }
|
||||||
|
w2 = if errorPointInPolygon 6 wp2 poly || wp2 == c2
|
||||||
|
then Nothing
|
||||||
|
else Just $ wall {_wlLine = [ c2, wp2 ] }
|
||||||
|
|
||||||
|
|
||||||
|
removeWallsInPolygon :: [Point2] -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
removeWallsInPolygon ps walls = IM.filter (not . cond) walls
|
||||||
|
-- where cond wall = pointInsidePolygon (_wlLine wall !! 0) ps
|
||||||
|
-- && pointInsidePolygon (_wlLine wall !! 1) ps
|
||||||
|
where cond wall = pointInsidePolygon (_wlLine wall !! 0) ps
|
||||||
|
&& pointInsidePolygon (_wlLine wall !! 1) ps
|
||||||
|
|
||||||
|
pointInsidePolygon :: Point2 -> [Point2] -> Bool
|
||||||
|
pointInsidePolygon p (x:xs) = all (\l -> not (uncurry isRHS l (p +.+ normalizeV s))) pairs
|
||||||
|
|| any (\l -> uncurry isOnLine l p) pairs
|
||||||
|
where pairs = zip (x:xs) (xs ++ [x])
|
||||||
|
s = ((1/fromIntegral (length (x:xs))) *.* (foldr1 (+.+) (x:xs)))
|
||||||
|
-.- p
|
||||||
|
|
||||||
|
|
||||||
|
-- ok, now to find out which new walls need to be drawn
|
||||||
|
drawCutWall :: (Point2, Point2) -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
drawCutWall (p1,p2) walls =
|
||||||
|
case maybeW of Just w -> if errorIsLHS 200 (_wlLine w !! 0) (_wlLine w !! 1) p3
|
||||||
|
then walls
|
||||||
|
else IM.insert k newWall walls
|
||||||
|
Nothing -> IM.insert k newWall walls
|
||||||
|
where p3 = 0.5 *.* (p1 +.+ p2)
|
||||||
|
p4 = p3 +.+ 10000 *.* vNormal (p2 -.- p1)
|
||||||
|
maybeW = wallOnLine p3 p4 walls
|
||||||
|
k = case IM.lookupMax walls
|
||||||
|
of Nothing -> 0
|
||||||
|
Just (j,_) -> j + 1
|
||||||
|
newWall = basicWall {_wlLine = [p1,p2], _wlID = k}
|
||||||
|
|
||||||
|
|
||||||
|
drawCutWalls :: [Point2] -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
drawCutWalls (q:qs) walls = foldr drawCutWall walls (zip (q:qs) (qs++[q]))
|
||||||
|
-- where (q:qs) = reverse $ orderPolygon
|
||||||
|
-- -- $ intersectPolygonWalls ps walls
|
||||||
|
-- $ maybeToList $ intersectSegSeg (20,0) (100,900) (-200,400) (200,400)
|
||||||
|
-- -- $ (++) ps $ nub $ concat $ IM.elems $ fmap snd $ collidePolygonWalls ps walls
|
||||||
|
|
||||||
|
-- I have no idea why collidePolygonWalls is sometimes dropping an intersection
|
||||||
|
-- point between the walls and the polygon
|
||||||
|
-- Trying a workaround
|
||||||
|
intersectPolygonWalls :: [Point2] -> IM.IntMap Wall -> [Point2]
|
||||||
|
intersectPolygonWalls ps walls = nub $ concat $ IM.elems $ IM.map (intersectSegsWall ps) walls
|
||||||
|
--intersectPolygonWalls (p:ps) walls = nub $ concat [intersectSegWalls s1 s2 walls | (s1,s2) <- segs]
|
||||||
|
-- where segs = zip (p:ps) (ps ++ [p])
|
||||||
|
|
||||||
|
intersectSegWalls s1 s2 ws = concatMap (intersectSegWall s1 s2) $ IM.elems ws
|
||||||
|
|
||||||
|
intersectSegsWall (p:ps) wall = concat [intersectSegWall s1 s2 wall | (s1,s2) <- zip (p:ps) (ps++[p])]
|
||||||
|
|
||||||
|
-- finds intersection, or any segment points contained in the wall
|
||||||
|
intersectSegWall s1 s2 w = catMaybes [myIntersectSegSeg s1 s2 w1 w2
|
||||||
|
-- ,intersectSegPoint w1 w2 s1
|
||||||
|
-- ,intersectSegPoint w1 w2 s2
|
||||||
|
]
|
||||||
|
where w1 = _wlLine w !! 0
|
||||||
|
w2 = _wlLine w !! 1
|
||||||
|
|
||||||
|
intersectSegPoint s1 s2 p = if isOnLine s1 s2 p then Just p else Nothing
|
||||||
|
|
||||||
|
-- new procedure: line by line
|
||||||
|
-- then remove all walls inside the polygon
|
||||||
|
|
||||||
|
-- abuses the list nature of wlLine
|
||||||
|
cutWall1 :: Point2 -> Point2 -> Wall -> Wall
|
||||||
|
cutWall1 p1 p2 wall = case maybeCP of
|
||||||
|
Nothing -> wall
|
||||||
|
Just cp -> wall {_wlLine = [cp,w0,w1]}
|
||||||
|
where wl = _wlLine wall
|
||||||
|
w0 = wl !! 0
|
||||||
|
w1 = wl !! 1
|
||||||
|
w0' = w0 +.+ normalizeV (w0 -.- w1)
|
||||||
|
w1' = w1 +.+ normalizeV (w1 -.- w0)
|
||||||
|
p1' = p1 +.+ normalizeV (p1 -.- p2)
|
||||||
|
p2' = p2 +.+ normalizeV (p2 -.- p1)
|
||||||
|
pT = p1' +.+ vNormal (normalizeV (p1 -.- p2))
|
||||||
|
pB = p1' -.- vNormal (normalizeV (p1 -.- p2))
|
||||||
|
--maybeCP = intersectSegSeg p1' p2' w0' w1'-- <|> intersectSegSeg w0 w1 pT pB
|
||||||
|
--maybeCP = inSegSeg p1 p2 w0 w1-- <|> intersectSegSeg w0 w1 pT pB
|
||||||
|
maybeCP = inSegSeg p1 p2 w0 w1-- <|> intersectSegSeg w0 w1 pT pB
|
||||||
|
--maybeCP = intersectSegSeg p1 p2 w0 w1-- <|> intersectSegSeg w0 w1 pT pB
|
||||||
|
cutWalls1 :: Point2 -> Point2 -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
cutWalls1 p1 p2 ws = foldr splitAndAdd ws $ IM.map (cutWall1 p1 p2) ws
|
||||||
|
where splitAndAdd w ws
|
||||||
|
= case _wlLine w of
|
||||||
|
(cp:w0:w1:[]) -> IM.insert (newKey ws) (w {_wlLine = [cp,w1]
|
||||||
|
,_wlID = newKey ws})
|
||||||
|
$ IM.insert (_wlID w) (w {_wlLine = [w0,cp]}) ws
|
||||||
|
(x:y:[]) -> ws
|
||||||
|
|
||||||
|
cutWallsPoints :: Point2 -> Point2 -> IM.IntMap Wall -> [Point2]
|
||||||
|
cutWallsPoints p1 p2 ws = map head $ filter (\xs -> length xs > 2)
|
||||||
|
$ IM.elems $ IM.map (_wlLine . cutWall1 p1 p2) ws
|
||||||
|
|
||||||
|
cutWallsProcess :: [Point2] -> IM.IntMap Wall -> ([Point2], IM.IntMap Wall)
|
||||||
|
cutWallsProcess (p:ps) ws = foldr f ([],ws) (zip (p:ps) (ps++[p]))
|
||||||
|
where f (p1,p2) (as,ws') = (as ++ cutWallsPoints p1 p2 ws', cutWalls1 p1 p2 ws')
|
||||||
|
|
||||||
|
cutWalls :: [Point2] -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
cutWalls qs walls = -- IM.filter (not . wallIsZeroLength) $ fuseWalls $
|
||||||
|
drawCutWalls rs $ IM.filter (not.wallIsZeroLength)
|
||||||
|
$ removeWallsInPolygon ps -- cwals
|
||||||
|
(fuseWallsWith zs cwals)
|
||||||
|
where (zs,cwals) = cutWallsProcess ps walls
|
||||||
|
ps = orderPolygon qs
|
||||||
|
--ps = orderPolygon $ map perturbToWallPoints qs
|
||||||
|
rs = orderPolygon $ nub $ zs ++ qs
|
||||||
|
-- perturbToWallPoints p = fromMaybe p $ find (\p1 -> dist p p1 < 1) wps
|
||||||
|
|
||||||
|
|
||||||
|
fusePoint :: [Point2] -> Point2 -> Maybe Point2
|
||||||
|
fusePoint ps p = find (\q -> dist p q < 5) ps
|
||||||
|
|
||||||
|
pointIfNotClose :: [Point2] -> Point2 -> Maybe Point2
|
||||||
|
pointIfNotClose ps p = case fusePoint ps p of
|
||||||
|
Nothing -> Just p
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
fuseWall :: ([Point2], Wall) -> ([Point2], Wall)
|
||||||
|
fuseWall (ps, w) = (qs, w {_wlLine = newLine})
|
||||||
|
where wp0 = _wlLine w !! 0
|
||||||
|
wp0' = fromMaybe wp0 $ fusePoint ps wp0
|
||||||
|
ps0 = maybeToList (pointIfNotClose ps wp0) ++ ps
|
||||||
|
wp1 = _wlLine w !! 1
|
||||||
|
wp1' = fromMaybe wp1 $ fusePoint ps0 wp1
|
||||||
|
newLine = [wp0',wp1']
|
||||||
|
qs = catMaybes [pointIfNotClose ps wp0, pointIfNotClose ps0 wp1] ++ ps
|
||||||
|
|
||||||
|
fuseWalls :: IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
fuseWalls ws = snd $ IM.foldr fuseWalls' ([], IM.empty) ws
|
||||||
|
where fuseWalls' w (ps, ws) = let (qs, w') = fuseWall (ps, w)
|
||||||
|
in (qs, IM.insert (_wlID w') w' ws)
|
||||||
|
fuseWallsWith :: [Point2] -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
fuseWallsWith zs ws = snd $ IM.foldr fuseWalls' (zs, IM.empty) ws
|
||||||
|
where fuseWalls' w (ps, ws) = let (qs, w') = fuseWall (ps, w)
|
||||||
|
in (qs, IM.insert (_wlID w') w' ws)
|
||||||
|
|
||||||
|
wallIsZeroLength w = l !! 0 == l !! 1
|
||||||
|
where l = _wlLine w
|
||||||
|
|
||||||
|
treeTrunk :: [a] -> Tree a -> Tree a
|
||||||
|
treeTrunk [] t = t
|
||||||
|
treeTrunk (x:xs) t = Node x [treeTrunk xs t]
|
||||||
|
|
||||||
|
applyToRoot :: (a -> a) -> Tree a -> Tree a
|
||||||
|
applyToRoot f (Node x xs) = Node (f x) xs
|
||||||
|
|
||||||
|
placeSpots :: [PlacementSpot] -> World -> World
|
||||||
|
placeSpots pss w = foldr placeSpot w basicPlacements
|
||||||
|
where (collectivePlacements,basicPlacements) = partition typeIsCollective pss
|
||||||
|
typeIsCollective (ps@PS {}) = case _psType ps of CollectivePS {} -> True
|
||||||
|
_ -> False
|
||||||
|
typeIsCollective _ = False
|
||||||
|
|
||||||
|
groupedPlacements = groupBy ((==) `on` _collectiveID . _psType)
|
||||||
|
$ sortBy (compare `on` _collectiveID . _psType) collectivePlacements
|
||||||
|
|
||||||
|
placeSpot :: PlacementSpot -> World -> World
|
||||||
|
placeSpot ps w = case ps of
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutButton bt}
|
||||||
|
-> placeBt bt p rot w
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutFlIt fi}
|
||||||
|
-> placeFlIt fi p rot w
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutCrit cr}
|
||||||
|
-> placeCr cr p rot w
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutLS ls dec}
|
||||||
|
-> placeLS ls dec p rot w
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutPressPlate pp}
|
||||||
|
-> placePressPlate pp p rot w
|
||||||
|
PS {_psType = RandPS rgen}
|
||||||
|
-> placeSpot (set psType evaluatedType ps) (set randGen g w)
|
||||||
|
where (evaluatedType, g) = runState rgen (_randGen w)
|
||||||
|
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutTriggerDoor col f a b}
|
||||||
|
-> addTriggerDoor col f (shiftPointBy (p,rot) a) (shiftPointBy (p,rot) b) w
|
||||||
|
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutAutoDoor a b}
|
||||||
|
-> addAutoDoor (shiftPointBy (p,rot) a) (shiftPointBy (p,rot) b) w
|
||||||
|
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutBlock (hp:hps) col ps}
|
||||||
|
-> putBlock (map (shiftPointBy (p,rot)) ps) hp col False hps w
|
||||||
|
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutBtDoor c bp f a b}
|
||||||
|
-> addButtonDoor c (shiftPointBy (p,rot) bp) (f + rot)
|
||||||
|
(shiftPointBy (p,rot) a) (shiftPointBy (p,rot) b) w
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutSwitchDoor c bp f a b}
|
||||||
|
-> addSwitchDoor c (shiftPointBy (p,rot) bp) (f + rot)
|
||||||
|
(shiftPointBy (p,rot) a) (shiftPointBy (p,rot) b) w
|
||||||
|
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutWindowBlock a b}
|
||||||
|
-> putWindowBlock (shiftPointBy (p,rot) a) (shiftPointBy (p,rot) b) w
|
||||||
|
|
||||||
|
PS {_psPos = p, _psRot = rot, _psType = PutWindow { _pwPoly = ps, _pwColor = c }}
|
||||||
|
-> rmCrossPaths $ over walls (addWindow (q:qs) c) w
|
||||||
|
where (q:qs) = translateS p $ rotateS rot ps
|
||||||
|
rmCrossPaths w = foldr (uncurry removePathsCrossing) w $ zip (q:qs) (qs++[q])
|
||||||
|
|
||||||
|
_ -> w
|
||||||
|
|
||||||
|
class Shiftable a where
|
||||||
|
translateS :: Point2 -> a -> a
|
||||||
|
rotateS :: Float -> a -> a
|
||||||
|
|
||||||
|
instance {-# OVERLAPPING #-} Shiftable Point2 where
|
||||||
|
translateS = (+.+)
|
||||||
|
rotateS = rotateV
|
||||||
|
|
||||||
|
instance (Shiftable a,Shiftable b) => Shiftable (a, b) where
|
||||||
|
translateS p (x,y) = (translateS p x,translateS p y)
|
||||||
|
rotateS r (x,y) = ( rotateS r x, rotateS r y)
|
||||||
|
|
||||||
|
instance (Shiftable a) => Shiftable [a] where
|
||||||
|
translateS p x = map (translateS p) x
|
||||||
|
rotateS r x = map (rotateS r) x
|
||||||
|
|
||||||
|
instance Shiftable RoomLink where
|
||||||
|
translateS q (RL p r) = RL (p +.+ q) r
|
||||||
|
rotateS s (RL p r) = RL (rotateV s p) (r+s)
|
||||||
|
|
||||||
|
instance Shiftable PlacementSpot where
|
||||||
|
translateS p' (PS p r x) = PS (p +.+ p') r x
|
||||||
|
rotateS r' (PS p r x) = PS (rotateV r' p) (r + r') x
|
||||||
|
|
||||||
|
pairsToGraph :: (Ord a, Eq a, Eq b) => (a -> a -> b) -> [(a,a)] -> Gr a b
|
||||||
|
pairsToGraph f pairs = let nodes = nub (map fst pairs ++ map snd pairs)
|
||||||
|
pairs' = map (\(x,y)->(x,y,f x y)) pairs
|
||||||
|
in undir $ run_ Data.Graph.Inductive.empty $ insMapNodesM nodes >> insMapEdgesM pairs'
|
||||||
|
|
||||||
|
makeButton :: Color -> (World -> World) -> Button
|
||||||
|
makeButton c eff = Button
|
||||||
|
{ _btPict = onLayer WlLayer $ color c $ polygon $ rectNSEW 5 (-5) 10 (-10)
|
||||||
|
, _btPos = (0,0)
|
||||||
|
, _btRot = 0
|
||||||
|
, _btEvent = \b w -> eff . over buttons (IM.adjust turnOn (_btID b))
|
||||||
|
-- . set (buttons . ix (_btID b) . btPict)
|
||||||
|
-- (onLayer WlLayer $ color c $ polygon $ rectNSEW (-3) (-5) 10 (-10))
|
||||||
|
-- . set (buttons . ix (_btID b) . btState) BtNoLabel
|
||||||
|
-- . set (buttons . ix (_btID b) . btEvent) (\b -> return)
|
||||||
|
. soundOnce 1 $ w
|
||||||
|
, _btID = 0
|
||||||
|
, _btText = "Button"
|
||||||
|
--, _btText = "Button"
|
||||||
|
, _btState = BtOff
|
||||||
|
}
|
||||||
|
where
|
||||||
|
turnOn bt = bt {_btState = BtNoLabel, _btPict = onPict, _btEvent = (\_ -> id)}
|
||||||
|
onPict = (onLayer WlLayer $ color c $ polygon $ rectNSEW (-3) (-5) 10 (-10))
|
||||||
|
|
||||||
|
makeSwitch :: Color -> (World -> World) -> (World -> World) -> Button
|
||||||
|
makeSwitch c effOn effOff = Button
|
||||||
|
{ _btPict = offPict
|
||||||
|
, _btPos = (0,0)
|
||||||
|
, _btRot = 0
|
||||||
|
, _btEvent = flipSwitch
|
||||||
|
, _btID = 0
|
||||||
|
, _btText = "Switch"
|
||||||
|
, _btState = BtOff
|
||||||
|
}
|
||||||
|
where
|
||||||
|
flipSwitch b w = switchEffect b . soundOnce 1 $ w
|
||||||
|
switchEffect b = case _btState b of
|
||||||
|
BtOff -> effOn . over buttons (IM.adjust turnOn (_btID b))
|
||||||
|
BtOn -> effOff . over buttons (IM.adjust turnOff (_btID b))
|
||||||
|
offPict = onLayer WlLayer $ color c $ pictures [--translate (-8) 4 $ circleSolid 5
|
||||||
|
polygon $ rectNSEW (-2) (-5) (-10) (10)
|
||||||
|
,polygon [(-2,-5),(-10,4),(-6,4),(2,-5)]
|
||||||
|
]
|
||||||
|
onPict = onLayer WlLayer $ color c $ pictures [--translate (8) 4 $ circleSolid 5
|
||||||
|
polygon $ rectNSEW (-2) (-5) (-10) (10)
|
||||||
|
,polygon [(-2,-5), (6,4),( 10,4),(2,-5)]
|
||||||
|
]
|
||||||
|
turnOn :: Button -> Button
|
||||||
|
turnOn bt = bt {_btState = BtOn, _btPict = onPict}
|
||||||
|
turnOff :: Button -> Button
|
||||||
|
turnOff bt = bt {_btState = BtOff, _btPict = offPict}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- probably don't have to rebuild the entire graph, oh well
|
||||||
|
addButtonDoor :: Color -> Point2 -> Float -> Point2 -> Point2 -> World -> World
|
||||||
|
addButtonDoor c btp btr a b w = over buttons (IM.insert bid bt)
|
||||||
|
$ set pathPoints (foldr insertPoint IM.empty (labNodes newGraph))
|
||||||
|
$ set pathGraph newGraph
|
||||||
|
$ set pathGraph' newGraphPairs
|
||||||
|
$ addTriggerDoor c cond a b w
|
||||||
|
where bid = newKey $ _buttons w
|
||||||
|
cond w = BtNoLabel == (_btState $ _buttons w IM.! bid)
|
||||||
|
bt = (makeButton c eff) {_btPos = btp, _btRot = btr, _btID = bid}
|
||||||
|
(newGraphPairs,removedPairs) = partition (not . isJust . uncurry (intersectSegSeg' a b))
|
||||||
|
$ _pathGraph' w
|
||||||
|
newGraph = pairsToGraph dist newGraphPairs
|
||||||
|
insertPoint pp@(_,(x,y)) = insertInZoneWith (floorHun x) (floorHun y) (++) [pp]
|
||||||
|
eff w' = over pathGraph' (removedPairs ++)
|
||||||
|
. over pathGraph (flip run_ $ insMapEdgesM $ map f removedPairs) $ w'
|
||||||
|
f (x,y) = (x,y,dist x y)
|
||||||
|
|
||||||
|
addSwitchDoor :: Color -> Point2 -> Float -> Point2 -> Point2 -> World -> World
|
||||||
|
addSwitchDoor c btp btr a b w = over buttons (IM.insert bid bt)
|
||||||
|
$ set pathPoints (foldr insertPoint IM.empty (labNodes newGraph))
|
||||||
|
$ set pathGraph newGraph
|
||||||
|
$ set pathGraph' newGraphPairs
|
||||||
|
$ addTriggerDoor c cond a b w
|
||||||
|
where bid = newKey $ _buttons w
|
||||||
|
cond w = BtOn == (_btState $ _buttons w IM.! bid)
|
||||||
|
bt = (makeSwitch c openDoor closeDoor) {_btPos = btp, _btRot = btr, _btID = bid}
|
||||||
|
(newGraphPairs,removedPairs) = partition (not . isJust . uncurry (intersectSegSeg' a b))
|
||||||
|
$ _pathGraph' w
|
||||||
|
newGraph = pairsToGraph dist newGraphPairs
|
||||||
|
insertPoint pp@(_,(x,y)) = insertInZoneWith (floorHun x) (floorHun y) (++) [pp]
|
||||||
|
openDoor w' = over pathGraph' (removedPairs ++)
|
||||||
|
. over pathGraph (flip run_ $ insMapEdgesM $ map f removedPairs) $ w'
|
||||||
|
f (x,y) = (x,y,dist x y)
|
||||||
|
closeDoor w' = over pathGraph' (\pg -> pg \\ removedPairs)
|
||||||
|
. over pathGraph (flip run_ $ delMapEdgesM removedPairs) $ w'
|
||||||
|
|
||||||
|
addTriggerDoor :: Color -> (World -> Bool) -> Point2 -> Point2 -> World -> World
|
||||||
|
addTriggerDoor c cond a b = over walls (triggerDoor c cond a b)
|
||||||
|
|
||||||
|
triggerDoor :: Color -> (World -> Bool) -> Point2 -> Point2 -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
triggerDoor c cond a b wls = IM.union wls $ IM.fromList $ zip is $ mkTriggerDoor c cond a b is
|
||||||
|
where i = newKey wls
|
||||||
|
is = [i..]
|
||||||
|
|
||||||
|
mkTriggerDoor :: Color -> (World -> Bool) -> Point2 -> Point2 -> [Int] -> [Wall]
|
||||||
|
mkTriggerDoor c cond pl pr xs = addSound $ zipWith3 (triggerDoorPane c cond)
|
||||||
|
xs
|
||||||
|
[ [pld,hwd]
|
||||||
|
, [hwd,hwu]
|
||||||
|
, [hwu,plu]
|
||||||
|
, [plu,pld]
|
||||||
|
, [pru,hwu]
|
||||||
|
, [hwu,hwd]
|
||||||
|
, [hwd,prd]
|
||||||
|
, [prd,pru]
|
||||||
|
]
|
||||||
|
[ [plld,pld]
|
||||||
|
, [pld,plu]
|
||||||
|
, [plu,pllu]
|
||||||
|
, [pllu,plld]
|
||||||
|
, [prru,pru]
|
||||||
|
, [pru,prd]
|
||||||
|
, [prd,prrd]
|
||||||
|
, [prrd,prru]
|
||||||
|
]
|
||||||
|
where norm = 10 *.* errorNormalizeV 49 ( vNormal (pr -.- pl))
|
||||||
|
hw = 0.5 *.* (pl +.+ pr)
|
||||||
|
perp = 20 *.* normalizeV (pl -.- pr)
|
||||||
|
--perp = pl -.- hw
|
||||||
|
plu = pl +.+ norm
|
||||||
|
pld = pl -.- norm
|
||||||
|
pru = pr +.+ norm
|
||||||
|
prd = pr -.- norm
|
||||||
|
hwu = hw +.+ norm
|
||||||
|
hwd = hw -.- norm
|
||||||
|
pllu = plu +.+ perp
|
||||||
|
plld = pld +.+ perp
|
||||||
|
prru = pru -.- perp
|
||||||
|
prrd = prd -.- perp
|
||||||
|
addSound (x:xs) = f x : xs
|
||||||
|
f wl = over doorMech g wl
|
||||||
|
g dm w | dist wp pld > 2 && dist wp hwd > 2 = soundFrom (WallSound (head xs)) (fromIntegral doorSound) 1 0
|
||||||
|
$ dm w
|
||||||
|
| otherwise = dm w
|
||||||
|
where wp = (_wlLine $ _walls w IM.! (head xs)) !! 1
|
||||||
|
|
||||||
|
triggerDoorPane :: Color -> (World -> Bool) -> Int -> [Point2] -> [Point2] -> Wall
|
||||||
|
triggerDoorPane c cond n [a,b] [a',b'] = Door
|
||||||
|
{ _wlLine = [a,b]
|
||||||
|
, _wlID = n
|
||||||
|
, _doorMech = dm
|
||||||
|
, _wlColor = c
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _wlIsSeeThrough = False
|
||||||
|
-- , _doorLine = [a,b,a',b']
|
||||||
|
}
|
||||||
|
where
|
||||||
|
dm w | cond w = flip (foldr changeZonedWall) zoneps
|
||||||
|
$ over walls (IM.adjust openDoor n) w -- . wlLine . ix 0) (mvPointToward a')
|
||||||
|
| otherwise = flip (foldr changeZonedWall') zoneps
|
||||||
|
$ over walls (IM.adjust closeDoor n) w -- . wlLine . ix 0) (mvPointToward a)
|
||||||
|
zoneps | dist a b <= 2 * zoneSize = [zoneOfPoint $ pHalf a b]
|
||||||
|
| otherwise = map zoneOfPoint $ divideLine (2*zoneSize) a b
|
||||||
|
openDoor :: Wall -> Wall
|
||||||
|
openDoor wl = case _wlLine wl of
|
||||||
|
((!pa):(!pb):_) -> wl {_wlLine = [mvPointToward a' pa, mvPointToward b' pb]}
|
||||||
|
closeDoor :: Wall -> Wall
|
||||||
|
closeDoor wl = case _wlLine wl of
|
||||||
|
((!pa):(!pb):_) -> wl {_wlLine = [mvPointToward a pa, mvPointToward b pb]}
|
||||||
|
changeZonedWall (!x,!y)
|
||||||
|
= over wallsZone $ adjustIMZone openDoor x y n
|
||||||
|
changeZonedWall' (!x,!y)
|
||||||
|
= over wallsZone $ adjustIMZone closeDoor x y n
|
||||||
|
|
||||||
|
addAutoDoor :: Point2 -> Point2 -> World -> World
|
||||||
|
addAutoDoor a b = over walls (autoDoorAt a b)
|
||||||
|
|
||||||
|
removePathsCrossing :: Point2 -> Point2 -> World -> World
|
||||||
|
removePathsCrossing a b w = set pathGraph newGraph $ set pathGraph' pg'
|
||||||
|
$ set pathPoints (foldr insertPoint IM.empty (labNodes newGraph))
|
||||||
|
w
|
||||||
|
where pg' = filter (not . isJust . uncurry (intersectSegSeg' a b)) $ _pathGraph' w
|
||||||
|
insertPoint pp@(_,(x,y)) = insertInZoneWith (floorHun x) (floorHun y) (++) [pp]
|
||||||
|
newGraph = pairsToGraph dist pg'
|
||||||
|
|
||||||
|
putBlock :: [Point2] -> Int -> Color -> Bool -> [Int] -> World -> World
|
||||||
|
putBlock (p:ps) i c b is w = foldr (uncurry removePathsCrossing) wWithBlock pairs
|
||||||
|
where pairs = zip (p:ps) (ps ++ [p])
|
||||||
|
wWithBlock = addBlock (p:ps) i c b is w
|
||||||
|
|
||||||
|
putWindowBlock :: Point2 -> Point2 -> World -> World
|
||||||
|
--putWindowBlock a b w = foldr makeBlockAt w $ zip ps ns
|
||||||
|
putWindowBlock a b w = removePathsCrossing a b $ foldr makeBlockAt w $ zip ps ns
|
||||||
|
where d = dist a b
|
||||||
|
rot = argV (b -.- a)
|
||||||
|
numPoints' = floor (d / 20)
|
||||||
|
numPoints = numPoints'*2
|
||||||
|
ns = take (numPoints+1) [0..]
|
||||||
|
ps = map (\i -> a +.+ i/ (fromIntegral numPoints) *.* (b -.- a))
|
||||||
|
$ map fromIntegral ns
|
||||||
|
k = newKey $ _walls w
|
||||||
|
ksAtN i = [k+i*4,k+i*4 + 1 ,k+i*4 +2, k+i*4+3]
|
||||||
|
bSide = d / (fromIntegral numPoints)
|
||||||
|
ds = [(-bSide,-10),(-bSide,10),(bSide,10),(bSide,-10)]
|
||||||
|
polyAtP p = map ((+.+) p . rotateV rot) ds
|
||||||
|
hp = 1
|
||||||
|
degradeHP = [5,5]
|
||||||
|
winCol = withAlpha 0.5 cyan
|
||||||
|
makeBlockAt :: (Point2,Int) -> World -> World
|
||||||
|
makeBlockAt (p,i) w =
|
||||||
|
let (k0:k1:k2:k3:_) = ksAtN i
|
||||||
|
(bl:tl:tr:br:_) = polyAtP p
|
||||||
|
shadows | i == 0 = ksAtN 1
|
||||||
|
| i == numPoints - 1 = ksAtN $ numPoints - 2
|
||||||
|
| otherwise = ksAtN (i-1) ++ ksAtN (i+1)
|
||||||
|
seen | even i = True
|
||||||
|
| otherwise = False
|
||||||
|
isLeftmost | i == 0 = True
|
||||||
|
| otherwise = False
|
||||||
|
isRightmost | i == numPoints = True
|
||||||
|
| otherwise = False
|
||||||
|
l = Block
|
||||||
|
{ _wlLine = [bl,tl]
|
||||||
|
, _wlID = k0
|
||||||
|
, _wlColor = winCol
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _blIDs = ksAtN i
|
||||||
|
, _blHP = hp
|
||||||
|
, _wlIsSeeThrough = True
|
||||||
|
--, _blVisible = False
|
||||||
|
, _blVisible = isLeftmost
|
||||||
|
, _blShadows = shadows
|
||||||
|
, _blDegrades = degradeHP
|
||||||
|
}
|
||||||
|
t = Block
|
||||||
|
{ _wlLine = [tl,tr]
|
||||||
|
, _wlID = k1
|
||||||
|
, _wlColor = winCol
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _blIDs = ksAtN i
|
||||||
|
, _blHP = hp
|
||||||
|
, _wlIsSeeThrough = True
|
||||||
|
, _blVisible = seen
|
||||||
|
, _blShadows = shadows
|
||||||
|
, _blDegrades = degradeHP
|
||||||
|
}
|
||||||
|
r = Block
|
||||||
|
{ _wlLine = [tr,br]
|
||||||
|
, _wlID = k2
|
||||||
|
, _wlColor = winCol
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _blIDs = ksAtN i
|
||||||
|
, _blHP = hp
|
||||||
|
, _wlIsSeeThrough = True
|
||||||
|
--, _blVisible = False
|
||||||
|
, _blVisible = isRightmost
|
||||||
|
, _blShadows = shadows
|
||||||
|
, _blDegrades = degradeHP
|
||||||
|
}
|
||||||
|
b = Block
|
||||||
|
{ _wlLine = [br,bl]
|
||||||
|
, _wlID = k3
|
||||||
|
, _wlColor = winCol
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _blIDs = ksAtN i
|
||||||
|
, _blHP = hp
|
||||||
|
, _wlIsSeeThrough = True
|
||||||
|
, _blVisible = seen
|
||||||
|
, _blShadows = shadows
|
||||||
|
, _blDegrades = degradeHP
|
||||||
|
}
|
||||||
|
f = IM.insert k0 l . IM.insert k1 t . IM.insert k2 r . IM.insert k3 b
|
||||||
|
in over walls f w
|
||||||
|
|
||||||
|
autoDoorAt :: Point2 -> Point2 -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
autoDoorAt a b wls = IM.union wls $ IM.fromList $ zip is $ mkAutoDoor a b is
|
||||||
|
where i = newKey wls
|
||||||
|
is = [i..]
|
||||||
|
|
||||||
|
mkAutoDoor :: Point2 -> Point2 -> [Int] -> [Wall]
|
||||||
|
mkAutoDoor pl pr xs = addSound $ zipWith3 (autoDoorPane [pl,pr])
|
||||||
|
xs
|
||||||
|
[ [pld,hwd]
|
||||||
|
, [hwd,hwu]
|
||||||
|
, [hwu,plu]
|
||||||
|
, [pru,hwu]
|
||||||
|
, [hwu,hwd]
|
||||||
|
, [hwd,prd]
|
||||||
|
]
|
||||||
|
[ [plld,pld]
|
||||||
|
, [pld,plu]
|
||||||
|
, [plu,pllu]
|
||||||
|
, [prru,pru]
|
||||||
|
, [pru,prd]
|
||||||
|
, [prd,prrd]
|
||||||
|
]
|
||||||
|
where norm = 10 *.* errorNormalizeV 49 ( vNormal (pr -.- pl))
|
||||||
|
hw = 0.5 *.* (pl +.+ pr)
|
||||||
|
perp = 20 *.* normalizeV (pl -.- pr)
|
||||||
|
plu = pl +.+ norm
|
||||||
|
pld = pl -.- norm
|
||||||
|
pru = pr +.+ norm
|
||||||
|
prd = pr -.- norm
|
||||||
|
hwu = hw +.+ norm
|
||||||
|
hwd = hw -.- norm
|
||||||
|
pllu = plu +.+ perp
|
||||||
|
plld = pld +.+ perp
|
||||||
|
prru = pru -.- perp
|
||||||
|
prrd = prd -.- perp
|
||||||
|
addSound (x:xs) = f x : xs
|
||||||
|
f wl = over doorMech g wl
|
||||||
|
g dm w | dist wp pld > 1 && dist wp hwd > 1 = soundFrom (WallSound (head xs)) (fromIntegral doorSound) 1 0
|
||||||
|
$ dm w
|
||||||
|
| otherwise = dm w
|
||||||
|
where wp = (_wlLine $ _walls w IM.! (head xs)) !! 1
|
||||||
|
|
||||||
|
mvPointTowardAtSpeed :: Float -> Point2 -> Point2 -> Point2
|
||||||
|
mvPointTowardAtSpeed speed !ep !p
|
||||||
|
| dist p ep < speed = ep
|
||||||
|
| otherwise = p +.+ speed *.* normalizeV (ep -.- p)
|
||||||
|
|
||||||
|
mvPointToward :: Point2 -> Point2 -> Point2
|
||||||
|
mvPointToward ep p | dist p ep < 1 = ep
|
||||||
|
| otherwise = p +.+ normalizeV (ep -.- p)
|
||||||
|
|
||||||
|
drawAutoDoor :: Wall -> Drawing
|
||||||
|
drawAutoDoor wl = onLayerL [levLayer WlLayer, layer2]
|
||||||
|
$ pictures [color c $ polygon [x,x +.+ n2,y +.+ n2, y]
|
||||||
|
,color (dark c) $ line [x,y]
|
||||||
|
]
|
||||||
|
where
|
||||||
|
(x:y:_) = _wlLine wl
|
||||||
|
c = _wlColor wl
|
||||||
|
nm = errorNormalizeV 543 (y -.- x)
|
||||||
|
t = 5 *.* nm
|
||||||
|
n = vNormal t
|
||||||
|
n2 = 3 *.* n
|
||||||
|
layer2 | _wlIsSeeThrough wl = 0
|
||||||
|
| isJust $ wl ^? doorMech = 1
|
||||||
|
| otherwise = 2
|
||||||
|
|
||||||
|
autoDoorPane :: [Point2] -> Int -> [Point2] -> [Point2] -> Wall
|
||||||
|
autoDoorPane trigL n [a,b] [a',b'] = AutoDoor
|
||||||
|
{ _wlLine = [a,b]
|
||||||
|
, _wlID = n
|
||||||
|
, _doorMech = dm
|
||||||
|
, _wlColor = dim $ yellow
|
||||||
|
, _wlDraw = Just drawAutoDoor
|
||||||
|
, _wlSeen = False
|
||||||
|
, _wlIsSeeThrough = False
|
||||||
|
-- , _doorLine = [a,b,a',b']
|
||||||
|
}
|
||||||
|
where
|
||||||
|
dm w | crsNearLine 40 trigL w
|
||||||
|
= flip (foldr changeZonedWall) zoneps
|
||||||
|
$ over walls (IM.adjust openDoor n) w
|
||||||
|
| otherwise = flip (foldr changeZonedWall') zoneps
|
||||||
|
$ over walls (IM.adjust closeDoor n) w
|
||||||
|
mvP !ep !p = mvPointTowardAtSpeed 2 ep p
|
||||||
|
openDoor :: Wall -> Wall
|
||||||
|
openDoor wl = case _wlLine wl of
|
||||||
|
((!pa):(!pb):_) -> wl {_wlLine = [mvP a' pa, mvP b' pb]}
|
||||||
|
closeDoor :: Wall -> Wall
|
||||||
|
closeDoor wl = case _wlLine wl of
|
||||||
|
((!pa):(!pb):_) -> wl {_wlLine = [mvP a pa, mvP b pb]}
|
||||||
|
zoneps | dist a b <= 2 * zoneSize = [zoneOfPoint $ pHalf a b]
|
||||||
|
| otherwise = map zoneOfPoint $ divideLine (2*zoneSize) a b
|
||||||
|
changeZonedWall (!x,!y)
|
||||||
|
= over wallsZone $ adjustIMZone openDoor x y n
|
||||||
|
changeZonedWall' (!x,!y)
|
||||||
|
= over wallsZone $ adjustIMZone closeDoor x y n
|
||||||
|
|
||||||
|
shiftPointBy (pos,rot) p = pos +.+ rotateV rot p
|
||||||
|
|
||||||
|
addWindow :: [Point2] -> Color -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
addWindow qs c wls = foldr (addPane c) wls pairs
|
||||||
|
where (p:ps) = orderPolygon qs
|
||||||
|
pairs = zip (ps ++ [p]) (p:ps)
|
||||||
|
addPane :: Color -> (Point2,Point2) -> IM.IntMap Wall -> IM.IntMap Wall
|
||||||
|
addPane c (p0,p1) wls = IM.insert (newKey wls) (Wall { _wlLine = [p0,p1]
|
||||||
|
, _wlID = newKey wls
|
||||||
|
, _wlColor = c
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _wlIsSeeThrough = True
|
||||||
|
}
|
||||||
|
) wls
|
||||||
|
|
||||||
|
|
||||||
|
placeBt bt p rot w = over buttons addBT w
|
||||||
|
where addBT bts = IM.insert (newKey bts) (bt {_btPos = p, _btRot = rot, _btID = newKey bts}) bts
|
||||||
|
placeFlIt fi p rot w = over floorItems addFI w
|
||||||
|
where addFI fis = IM.insert (newKey fis) (fi {_flItPos = p, _flItRot = rot, _flItID = newKey fis}) fis
|
||||||
|
placePressPlate pp p rot w = over pressPlates addPP w
|
||||||
|
where addPP pps = IM.insert (newKey pps) (pp {_ppPos = p,_ppRot = rot}) pps
|
||||||
|
|
||||||
|
-- Left elements get new children, Right elements inherit the children from the
|
||||||
|
-- mapped over node
|
||||||
|
expandTreeBy :: (a -> Tree (Either b b)) -> Tree a -> Tree b
|
||||||
|
expandTreeBy f (Node x []) = fmap removeEither (f x)
|
||||||
|
where removeEither (Left y) = y
|
||||||
|
removeEither (Right y) = y
|
||||||
|
expandTreeBy f (Node x xs) = appendAndRemove $ f x
|
||||||
|
where appendAndRemove (Node (Left y) ys) = Node y (map appendAndRemove ys)
|
||||||
|
appendAndRemove (Node (Right y) _ ) = Node y (map (expandTreeBy f) xs)
|
||||||
|
|
||||||
|
expandTreeRand :: RandomGen g =>
|
||||||
|
(a -> State g (Tree (Either b b))) -> Tree a -> State g (Tree b)
|
||||||
|
expandTreeRand f (Node x []) = fmap (fmap removeEither) (f x)
|
||||||
|
where removeEither (Left y) = y
|
||||||
|
removeEither (Right y) = y
|
||||||
|
expandTreeRand f (Node x xs) = do
|
||||||
|
root <- f x
|
||||||
|
branches <- sequence $ map (expandTreeRand f) xs
|
||||||
|
return (appendAndRemove branches root)
|
||||||
|
where appendAndRemove :: [Tree a] -> Tree (Either a a) -> Tree a
|
||||||
|
appendAndRemove bran (Node (Left y) ys) = Node y (map (appendAndRemove bran) ys)
|
||||||
|
appendAndRemove bran (Node (Right y) _) = Node y bran
|
||||||
|
|
||||||
|
placeCr :: Creature -> Point2 -> Float -> World -> World
|
||||||
|
placeCr crF p rot w = over creatures addCr w
|
||||||
|
where addCr crs = IM.insert (newKey crs)
|
||||||
|
(crF {_crPos = p,_crOldPos = p,_crDir = rot,_crID = newKey crs})
|
||||||
|
crs
|
||||||
|
|
||||||
|
placeLS :: LightSource -> Drawing -> Point2 -> Float -> World -> World
|
||||||
|
placeLS ls dec p rot w = over lightSources addLS
|
||||||
|
$ over decorations addDec w
|
||||||
|
where addLS lss = IM.insert (newKey lss)
|
||||||
|
(ls {_lsPos = p,_lsDir = rot,_lsID = newKey lss})
|
||||||
|
lss
|
||||||
|
addDec decs = IM.insert (newKey decs)
|
||||||
|
(uncurry translate p $ rotate (radToDeg rot) dec)
|
||||||
|
decs
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
module Dodge.LightSources where
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
--import Graphics.Gloss
|
||||||
|
--import Graphics.Gloss.Data.Vector
|
||||||
|
--import Graphics.Gloss.Geometry.Line
|
||||||
|
--import Graphics.Gloss.Geometry.Angle
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
|
||||||
|
lightAt p i =
|
||||||
|
LS {_lsID = i
|
||||||
|
,_lsPos = p
|
||||||
|
,_lsDir = 0
|
||||||
|
,_lsRad = 300
|
||||||
|
,_lsIntensity = 0.25
|
||||||
|
}
|
||||||
|
|
||||||
|
basicLS = PutLS ls dec
|
||||||
|
where ls = lightAt (0,0) 0
|
||||||
|
dec = onLayer PtLayer $ color white $ circleSolid 8
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
module Dodge.LoadSound where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
import qualified SDL.Mixer as Mix
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
loadSounds :: IO (IM.IntMap Mix.Chunk)
|
||||||
|
loadSounds = do
|
||||||
|
-- pFireSound <- Mix.load "./data/sound/tap3.wav"
|
||||||
|
pFireSound <- Mix.load "./data/sound/tap3.wav"
|
||||||
|
click <- Mix.load "./data/sound/click1.wav"
|
||||||
|
reloadSound' <- Mix.load "./data/sound/reload1.wav"
|
||||||
|
tone440 <- Mix.load "./data/sound/tone440.wav"
|
||||||
|
pickupSound' <- Mix.load "./data/sound/pickUp.wav"
|
||||||
|
putdownSound' <- Mix.load "./data/sound/whiteNoiseFadeOut.wav"
|
||||||
|
fireSound' <- Mix.load "./data/sound/fire1.wav"
|
||||||
|
grenadeBang' <- Mix.load "./data/sound/grenade.wav"
|
||||||
|
tapQuiet' <- Mix.load "./data/sound/tapQuiet.wav"
|
||||||
|
twoStep' <- Mix.load "./data/sound/twoStep.wav"
|
||||||
|
healSound' <- Mix.load "./data/sound/heal.wav"
|
||||||
|
doorSound' <- Mix.load "./data/sound/slideDoor.wav"
|
||||||
|
twoStepSlow' <- Mix.load "./data/sound/twoStepSlow.wav"
|
||||||
|
knifeSound' <- Mix.load "./data/sound/knife.wav"
|
||||||
|
buzzSound' <- Mix.load "./data/sound/buzz.wav"
|
||||||
|
hitSound' <- Mix.load "./data/sound/hit1.wav"
|
||||||
|
autoGunSound' <- Mix.load "./data/sound/autoGun.wav"
|
||||||
|
shotgunSound' <- Mix.load "./data/sound/shotgun.wav"
|
||||||
|
teleSound' <- Mix.load "./data/sound/tele.wav"
|
||||||
|
longGunSound' <- Mix.load "./data/sound/longTap.wav"
|
||||||
|
launcherSound' <- Mix.load "./data/sound/tap4.wav"
|
||||||
|
smokeTrailSound' <- Mix.load "./data/sound/missileLaunch.wav"
|
||||||
|
foot1Sound' <- Mix.load "./data/sound/foot1.wav"
|
||||||
|
foot2Sound' <- Mix.load "./data/sound/foot2.wav"
|
||||||
|
lasSound' <- Mix.load "./data/sound/tone440sawtooth.wav"
|
||||||
|
teslaSound' <- Mix.load "./data/sound/Electrical-Welding.wav"
|
||||||
|
crankSlow' <- Mix.load "./data/sound/crankSlow.wav"
|
||||||
|
autoB' <- Mix.load "./data/sound/autoB.wav"
|
||||||
|
mini' <- Mix.load "./data/sound/mini1.wav"
|
||||||
|
impactA' <- Mix.load "./data/sound/Impact-A.wav"
|
||||||
|
impactB' <- Mix.load "./data/sound/Impact-B.wav"
|
||||||
|
impactC' <- Mix.load "./data/sound/Impact-C.wav"
|
||||||
|
impactD' <- Mix.load "./data/sound/Impact-D.wav"
|
||||||
|
glassShat1' <- Mix.load "./data/sound/Glass-Bottle-Shattering-A1.wav"
|
||||||
|
glassShat2' <- Mix.load "./data/sound/Glass-Bottle-Shattering-A2.wav"
|
||||||
|
glassShat3' <- Mix.load "./data/sound/Glass-Bottle-Shattering-A3.wav"
|
||||||
|
glassShat4' <- Mix.load "./data/sound/Glass-Bottle-Shattering-A4.wav"
|
||||||
|
glass1' <- Mix.load "./data/sound/Small-Piece-Of-Glass-Shattering-A1.wav"
|
||||||
|
glass2' <- Mix.load "./data/sound/Small-Piece-Of-Glass-Shattering-A2.wav"
|
||||||
|
glass3' <- Mix.load "./data/sound/Small-Piece-Of-Glass-Shattering-A3.wav"
|
||||||
|
glass4' <- Mix.load "./data/sound/Small-Piece-Of-Glass-Shattering-A4.wav"
|
||||||
|
foamSpray' <- Mix.load "./data/sound/foamSprayLoop.wav"
|
||||||
|
return $ IM.fromList $ zip [0..]
|
||||||
|
$ [ pFireSound
|
||||||
|
, click
|
||||||
|
, reloadSound'
|
||||||
|
, tone440
|
||||||
|
, pickupSound'
|
||||||
|
, putdownSound'
|
||||||
|
, fireSound'
|
||||||
|
, grenadeBang'
|
||||||
|
, tapQuiet'
|
||||||
|
, twoStep'
|
||||||
|
, healSound'
|
||||||
|
, doorSound'
|
||||||
|
, twoStepSlow'
|
||||||
|
, knifeSound'
|
||||||
|
, buzzSound'
|
||||||
|
, hitSound' --15
|
||||||
|
, autoGunSound'
|
||||||
|
, shotgunSound'
|
||||||
|
, teleSound'
|
||||||
|
, longGunSound'
|
||||||
|
, launcherSound'
|
||||||
|
, smokeTrailSound'
|
||||||
|
, foot1Sound'
|
||||||
|
, foot2Sound'
|
||||||
|
, lasSound'
|
||||||
|
, teslaSound' --25
|
||||||
|
, crankSlow'
|
||||||
|
, autoB'
|
||||||
|
, mini'
|
||||||
|
, impactA'
|
||||||
|
, impactB' --30
|
||||||
|
, impactC'
|
||||||
|
, impactD'
|
||||||
|
, glassShat1' --33
|
||||||
|
, glassShat2'
|
||||||
|
, glassShat3'
|
||||||
|
, glassShat4'
|
||||||
|
, glass1' --37
|
||||||
|
, glass2'
|
||||||
|
, glass3'
|
||||||
|
, glass4' --40
|
||||||
|
, foamSpray'
|
||||||
|
]
|
||||||
|
|
||||||
|
-- updateSound :: World -> IO World
|
||||||
|
-- updateSound w
|
||||||
|
-- = do forM_ (_soundQueue w) (\n -> Mix.playOn (-1) 0 (_loadedSounds w IM.! n))
|
||||||
|
-- fmap (set soundQueue creatureSounds) $ stepSoundTimers $ w
|
||||||
|
-- -- = return $ set soundQueue creatureSounds w
|
||||||
|
-- -- = do forM_ (_soundQueue w) (\n -> Mix.playOn (-1) (_loadedSounds w IM.! n) 0)
|
||||||
|
-- -- fmap (set soundQueue creatureSounds) $ return w
|
||||||
|
-- where creatureSounds = []
|
||||||
|
|
||||||
|
soundOnce :: Int -> World -> World
|
||||||
|
soundOnce i = over soundQueue ((:) i)
|
||||||
|
|
||||||
|
--stepSoundTimers :: World -> IO World
|
||||||
|
--stepSoundTimers w = M.foldrWithKey stepSoundTimer (return w) (_sounds w)
|
||||||
|
|
||||||
|
-- ok, this is hard to reason about-- bugs can occur because I am unsure when
|
||||||
|
-- everything is evaluated
|
||||||
|
--stepSoundTimer :: SoundOrigin -> Sound -> IO World -> IO World
|
||||||
|
--stepSoundTimer so s w =
|
||||||
|
-- case _soundChannel s of
|
||||||
|
-- Nothing -> do w1 <- w
|
||||||
|
-- let soundChunk = _loadedSounds w1 IM.! _soundType s
|
||||||
|
---- n <- Mix.playingCount
|
||||||
|
-- channel <- Mix.playOn (-1) (-1) soundChunk
|
||||||
|
-- -- w2 <- w -- Note that using w2 instead of w1 in the
|
||||||
|
-- -- final return breaks the sound sometimes, must be
|
||||||
|
-- -- something to do with order of evaluation
|
||||||
|
-- return $ set (sounds . ix so . soundChannel) (Just $ fromIntegral channel) w1
|
||||||
|
-- Just channel -> case _soundTime s of
|
||||||
|
-- x | x > 0 -> fmap (set (sounds . ix so . soundTime) (x-1)) w
|
||||||
|
-- | x <= 0 -> do Mix.fadeOut channel (fromIntegral $ _soundFadeTime s+ 1)
|
||||||
|
-- fmap (over sounds (M.delete so)) w
|
||||||
|
-- the sound fade doesn't seem to like being 0
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
module Dodge.Menu where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Rooms
|
||||||
|
import Dodge.Initialisation
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
|
||||||
|
import Data.Tree
|
||||||
|
import Data.Maybe
|
||||||
|
import qualified Data.Set as S
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.State
|
||||||
|
|
||||||
|
|
||||||
|
import System.Exit
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
--import Graphics.Gloss.Interface.IO.Game
|
||||||
|
import SDL
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
keyPressedDown :: Event -> Maybe Keycode
|
||||||
|
keyPressedDown e = case eventPayload e of
|
||||||
|
KeyboardEvent (KeyboardEventData _ Pressed False keysym) -> Just $ keysymKeycode keysym
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
menuEvents :: (Event -> World -> Maybe World) -> Event -> World -> Maybe World
|
||||||
|
menuEvents f e w = case _menuState w of
|
||||||
|
LevelMenu _ -> case keyPressedDown e of
|
||||||
|
Just KeycodeEscape -> Nothing
|
||||||
|
Just _ -> startLevel w >>= f e
|
||||||
|
_ -> Just w
|
||||||
|
PauseMenu -> case keyPressedDown e of
|
||||||
|
Just KeycodeEscape -> Nothing
|
||||||
|
Just KeycodeR -> return $ fromMaybe w $ _storedLevel w
|
||||||
|
Just KeycodeN -> Just $ putSound $ generateLevel 1
|
||||||
|
$ initialWorld {_randGen = _randGen w}
|
||||||
|
Just _ -> unpause w >>= f e
|
||||||
|
_ -> Just w
|
||||||
|
GameOverMenu -> case keyPressedDown e of
|
||||||
|
Just KeycodeEscape -> Nothing
|
||||||
|
Just KeycodeR -> Just $ fromMaybe w $ _storedLevel w
|
||||||
|
Just KeycodeN -> Just $ putSound $ generateLevel 1
|
||||||
|
$ initialWorld
|
||||||
|
{_randGen = _randGen w}
|
||||||
|
_ -> return w
|
||||||
|
_ -> f e w
|
||||||
|
where unpause w' = Just $ resumeSound $
|
||||||
|
w' {_menuState = InGame,_keys = S.empty}
|
||||||
|
startLevel = unpause . storeLevel
|
||||||
|
putSound = id -- set loadedSounds (_loadedSounds w)
|
||||||
|
|
||||||
|
|
||||||
|
--- menuCheckKeyEvents :: (Event -> World -> IO World) -> Event -> World -> IO World
|
||||||
|
--- menuCheckKeyEvents f e w = case _menuState w of
|
||||||
|
--- LevelMenu _ -> case e of
|
||||||
|
--- (EventKey (SpecialKey KeyEsc) Down _ _)
|
||||||
|
--- -> exitSuccess
|
||||||
|
--- (EventKey _ Down _ _) -> startLevel w >>= f e
|
||||||
|
--- _ -> return w
|
||||||
|
--- PauseMenu -> case e of
|
||||||
|
--- (EventKey (SpecialKey KeyEsc) Down _ _)
|
||||||
|
--- -> exitSuccess
|
||||||
|
--- (EventKey (Char 'r') Down _ _) -> return $ fromMaybe w $ _storedLevel w
|
||||||
|
--- (EventKey (Char 'n') Down _ _) -> fmap putSound $ generateLevel 1
|
||||||
|
--- $ initialWorld
|
||||||
|
--- {_randGen = _randGen w}
|
||||||
|
--- -- (EventKey (Char 'c') Down _ _) -> unpaused
|
||||||
|
--- -- (EventKey (Char 'p') Down _ _) -> unpaused
|
||||||
|
--- -- (EventKey (SpecialKey KeySpace) Down _ _) -> unpaused
|
||||||
|
--- (EventKey _ Down _ _) -> unpause w >>= f e
|
||||||
|
--- _ -> return w
|
||||||
|
--- GameOverMenu -> case e of
|
||||||
|
--- (EventKey (SpecialKey KeyEsc) Down _ _)
|
||||||
|
--- -> exitSuccess
|
||||||
|
--- (EventKey (Char 'r') Down _ _) -> return $ fromMaybe w $ _storedLevel w
|
||||||
|
--- (EventKey (Char 'n') Down _ _) -> fmap putSound $ generateLevel 1
|
||||||
|
--- $ initialWorld
|
||||||
|
--- {_randGen = _randGen w}
|
||||||
|
--- -- (EventKey (Char 'c') Down _ _) -> unpaused
|
||||||
|
--- _ -> return w
|
||||||
|
--- _ -> f e w
|
||||||
|
--- where unpause w' = do Mix.resume (-1)
|
||||||
|
--- return w' {_menuState = InGame,_keys = S.empty}
|
||||||
|
--- startLevel = unpause . storeLevel
|
||||||
|
--- putSound = set loadedSounds (_loadedSounds w)
|
||||||
|
--- -- there might be a better way to pass the loaded sounds around
|
||||||
|
|
||||||
|
updateRandGen :: World -> World
|
||||||
|
updateRandGen = over randGen (fst . split)
|
||||||
|
|
||||||
|
menuCheckUpdate :: (Float -> World -> IO World) -> Float -> World -> IO World
|
||||||
|
menuCheckUpdate f t w = case _menuState w of
|
||||||
|
InGame -> f t w
|
||||||
|
_ -> return w
|
||||||
|
|
||||||
|
storeLevel :: World -> World
|
||||||
|
storeLevel w = case _storedLevel w of
|
||||||
|
Nothing -> set storedLevel (Just w) w
|
||||||
|
_ -> w
|
||||||
|
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
module Dodge.Path where
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
import qualified Data.HashSet as HS
|
||||||
|
import qualified Data.Heap as HP
|
||||||
|
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
import Data.Graph.Inductive.PatriciaTree
|
||||||
|
import Data.Graph.Inductive.Query.SP
|
||||||
|
|
||||||
|
worldGraph :: World -> Point2 -> HS.HashSet Point2
|
||||||
|
worldGraph w p = HS.unions $ fmap (\q -> HS.fromList $ pointsAlong w p (p +.+ q))
|
||||||
|
[(200,0),(-200,0),(0,200),(0,-200)]
|
||||||
|
|
||||||
|
pointsAlong :: World -> Point2 -> Point2 -> [Point2]
|
||||||
|
pointsAlong w p q = divideLineFixed 50 p p'
|
||||||
|
where p' = furthestPointWalkable p q $ wallsAlongLine p q w
|
||||||
|
|
||||||
|
divideLineFixed :: Float -> Point2 -> Point2 -> [Point2]
|
||||||
|
divideLineFixed x a b = fmap (\i -> a +.+ i * x *.* normalizeV (b -.- a))
|
||||||
|
$ fmap fromIntegral ns
|
||||||
|
where numPoints = floor $ dist a b / x
|
||||||
|
ns = [1 .. numPoints]
|
||||||
|
|
||||||
|
-- ok, astar or something like it
|
||||||
|
|
||||||
|
type SearchedNodes = (HP.MinHeap (Float,(Float,[Point2])), [Point2])
|
||||||
|
|
||||||
|
stripRight :: Either a b -> b
|
||||||
|
stripRight (Right x) = x
|
||||||
|
|
||||||
|
stepPath :: (Point2 -> [Point2]) -> Point2
|
||||||
|
-> SearchedNodes -> Either [Point2] SearchedNodes
|
||||||
|
stepPath f p (nextNodes, seenNodes)
|
||||||
|
= case HP.view nextNodes of
|
||||||
|
Nothing -> Left []
|
||||||
|
Just ((_,(cost,(q:qs))), nextNodes')
|
||||||
|
| q == p -> Left (q:qs)
|
||||||
|
| otherwise -> let rs' = f q
|
||||||
|
rs = rs' \\ seenNodes
|
||||||
|
newNodes = map (\r -> (cost + dist q r + dist r p
|
||||||
|
, (cost + dist q r
|
||||||
|
, (r:q:qs)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) rs
|
||||||
|
in Right $ (foldr HP.insert nextNodes' newNodes
|
||||||
|
, rs ++ seenNodes
|
||||||
|
)
|
||||||
|
stepPath' :: (Point2 -> [Point2]) -> Point2
|
||||||
|
-> SearchedNodes -> [Point2]
|
||||||
|
stepPath' f p s = case stepPath f p s of Left ps -> ps
|
||||||
|
Right s' -> stepPath' f p s'
|
||||||
|
|
||||||
|
makePath' :: (Point2 -> [Point2]) -> Point2 -> Point2 -> [Point2]
|
||||||
|
makePath' f s e = stepPath' f e $ (HP.singleton (0,(0,[s])) , [])
|
||||||
|
|
||||||
|
makeNode :: Point2 -> SearchedNodes
|
||||||
|
makeNode e = (HP.singleton (0,(0,[e])) , [])
|
||||||
|
|
||||||
|
|
||||||
|
tp1,tp2,tp3 :: Point2
|
||||||
|
tp1 = (0,1)
|
||||||
|
tp2 = (0,20)
|
||||||
|
tp3 = (30,40)
|
||||||
|
|
||||||
|
f = incidenceToFunction $ pairsToIncidence [(tp1,tp2),(tp2,tp3)
|
||||||
|
,(tp2,tp1)
|
||||||
|
,(tp1,tp3)]
|
||||||
|
g = pairsToIncidence [(tp1,tp2),(tp2,tp3)
|
||||||
|
,(tp2,tp1)
|
||||||
|
,(tp1,tp3)]
|
||||||
|
|
||||||
|
pathBetween :: Point2 -> Point2 -> World -> Maybe [Point2]
|
||||||
|
pathBetween a b w = makePath' <$> return (\p -> _pathInc w M.! p) <*> a' <*> b'
|
||||||
|
where
|
||||||
|
nsa :: [Point2]
|
||||||
|
nsa = map snd $ concat $ lookLookups (zoneAroundPoint a) (_pathPoints w)
|
||||||
|
nsb = map snd $ concat $ lookLookups (zoneAroundPoint b) (_pathPoints w)
|
||||||
|
--a' = listToMaybe $ sortBy (compare `on` dist a) $ ns
|
||||||
|
--b' = listToMaybe $ sortBy (compare `on` dist b) $ ns
|
||||||
|
a' = listToMaybe $ filter (flip (isWalkable a) w) nsa
|
||||||
|
b' = listToMaybe $ filter (flip (isWalkable b) w) nsb
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
makePathBetween :: Point2 -> Point2 -> World -> Maybe [Int]
|
||||||
|
makePathBetween a b w = join $ sp <$> fmap fst a' <*> fmap fst b' <*> return g
|
||||||
|
where g = _pathGraph w
|
||||||
|
nsa = concat $ lookLookups (zoneAroundPoint a) (_pathPoints w)
|
||||||
|
nsb = concat $ lookLookups (zoneAroundPoint b) (_pathPoints w)
|
||||||
|
-- a' = listToMaybe $ sortBy (compare `on` dist a . snd) $ filter (flip (isWalkable a) w . snd) ns
|
||||||
|
-- b' = listToMaybe $ sortBy (compare `on` dist b . snd) $ filter (flip (isWalkable b) w . snd) ns
|
||||||
|
a' = listToMaybe $ filter (flip (isWalkable a) w . snd) nsa
|
||||||
|
b' = listToMaybe $ filter (flip (isWalkable b) w . snd) nsb
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ezipWith :: Monoid a => (b -> c -> d) -> Either a b -> Either a c -> Either a d
|
||||||
|
ezipWith f (Right x) (Right y) = Right (f x y)
|
||||||
|
ezipWith f (Left x) (Right _) = Left x
|
||||||
|
ezipWith f (Right _) (Left y) = Left y
|
||||||
|
ezipWith f (Left x) (Left y) = Left (mappend x y)
|
||||||
|
|
||||||
|
|
||||||
|
makePathBetween' :: Point2 -> Point2 -> World -> Either String [Int]
|
||||||
|
makePathBetween' a b w = let g = _pathGraph w
|
||||||
|
ns = labNodes g
|
||||||
|
nsa = (_pathPoints w) `ixNZ` a
|
||||||
|
nsb = (_pathPoints w) `ixNZ` b
|
||||||
|
a' = case listToMaybe $ sortBy (compare `on` dist a . snd)
|
||||||
|
-- a' = case listToMaybe
|
||||||
|
$ filter (flip (isWalkable a) w . snd) ns of
|
||||||
|
Just p -> Right $ fst p
|
||||||
|
_ -> Left "FIRST POINT UNSEEN"
|
||||||
|
b' = case listToMaybe $ sortBy (compare `on` dist b . snd)
|
||||||
|
-- b' = case listToMaybe
|
||||||
|
$ filter (flip (isWalkable b) w . snd) ns of
|
||||||
|
Just p -> Right $ fst p
|
||||||
|
_ -> Left $ "SECOND POINT UNSEEN" ++ show b
|
||||||
|
in case ezipWith (\x y -> sp x y g) a' b' of
|
||||||
|
Right (Just xs) -> Right xs
|
||||||
|
Right (Nothing) -> Left $ "NO PATH" ++ show a ++ show b ++ show a' ++ show b'
|
||||||
|
Left m -> Left m
|
||||||
|
|
||||||
|
makePathBetweenPs :: Point2 -> Point2 -> World -> Maybe [Point2]
|
||||||
|
--makePathBetweenPs a b = pathBetween a b
|
||||||
|
makePathBetweenPs a b w = fmap (mapMaybe (lab g)) $ makePathBetween b a w
|
||||||
|
where g = _pathGraph w
|
||||||
|
|
||||||
|
makePathBetweenPs' :: Point2 -> Point2 -> World -> Either String [Point2]
|
||||||
|
makePathBetweenPs' a b w = fmap (mapMaybe (lab g)) $ makePathBetween' a b w
|
||||||
|
where g = _pathGraph w
|
||||||
|
|
||||||
|
|
||||||
|
pointTowardsGoal :: Point2 -> Point2 -> World -> Maybe Point2
|
||||||
|
pointTowardsGoal a b w = join $ fmap (listToMaybe . filter (flip (isWalkable a) w))
|
||||||
|
-- $ pathBetween a b w
|
||||||
|
$ makePathBetweenPs a b w
|
||||||
|
--
|
||||||
|
|
||||||
|
pointTowardsGoal' :: Point2 -> Point2 -> World -> Either String Point2
|
||||||
|
pointTowardsGoal' a b w = join $ fmap (maybeToEither "NOSEEPATH" . listToMaybe . filter (flip (isWalkable a) w))
|
||||||
|
$ makePathBetweenPs' b a w
|
||||||
|
|
||||||
|
maybeToEither :: a -> Maybe b -> Either a b
|
||||||
|
maybeToEither _ (Just x) = Right x
|
||||||
|
maybeToEither y Nothing = Left y
|
||||||
|
|
||||||
|
pairsToIncidence :: (Eq a,Ord a) => [(a,a)] -> [(a,[a])]
|
||||||
|
pairsToIncidence = map ((\(xs,ys) -> (head xs,ys)) . unzip)
|
||||||
|
. groupBy ( (==) `on` fst)
|
||||||
|
. sort
|
||||||
|
|
||||||
|
incidenceToFunction :: Eq a => [(a,[a])] -> a -> [a]
|
||||||
|
incidenceToFunction xs a = case lookup a xs of Just ys -> ys
|
||||||
|
Nothing -> []
|
||||||
|
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
module Dodge.Prototypes where
|
||||||
|
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
import Dodge.Base
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import Data.Graph.Inductive.Graph hiding ((&))
|
||||||
|
import Data.List
|
||||||
|
|
||||||
|
--import Graphics.Gloss
|
||||||
|
--import Graphics.Gloss.Data.Vector
|
||||||
|
|
||||||
|
-- defalt datatypes / prototypes {{{
|
||||||
|
basicWall = Wall { _wlLine = [(0,0),(50,0)]
|
||||||
|
, _wlID = 0
|
||||||
|
, _wlColor = greyN 0.6
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _wlIsSeeThrough = False
|
||||||
|
}
|
||||||
|
basicAutoDoor = AutoDoor { _wlLine = [(0,0),(50,0)]
|
||||||
|
, _wlID = 0
|
||||||
|
, _doorMech = id
|
||||||
|
, _wlColor = light $ dim $ dim $ dim $ yellow
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _wlIsSeeThrough = False
|
||||||
|
}
|
||||||
|
basicDoor = Door { _wlLine = [(0,0),(50,0)]
|
||||||
|
, _wlID = 0
|
||||||
|
, _doorMech = id
|
||||||
|
, _wlColor = light $ dim $ dim $ dim $ yellow
|
||||||
|
, _wlDraw = Nothing
|
||||||
|
, _wlSeen = False
|
||||||
|
, _wlIsSeeThrough = False
|
||||||
|
}
|
||||||
|
basicCreature :: Creature
|
||||||
|
basicCreature = Creature
|
||||||
|
{ _crPos = (0,0)
|
||||||
|
, _crOldPos = (0,0)
|
||||||
|
, _crDir = 0
|
||||||
|
, _crID = 1
|
||||||
|
, _crPict = const $ onLayer CrLayer $ circleSolid 10
|
||||||
|
, _crUpdate = \ w f cr -> (f , Just cr)
|
||||||
|
, _crRad = 10
|
||||||
|
, _crMass = 10
|
||||||
|
, _crHP = 100
|
||||||
|
, _crMaxHP = 150
|
||||||
|
, _crInv = IM.empty
|
||||||
|
, _crInvSel = 0
|
||||||
|
, _crAmmo = M.empty
|
||||||
|
, _crState = basicState
|
||||||
|
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 10
|
||||||
|
}
|
||||||
|
basicState = CrSt { _goals = []
|
||||||
|
, _stance = Stance {_carriage=Walking 0 0,_posture=AtEase}
|
||||||
|
, _faction = NoFaction
|
||||||
|
, _crDamage = []
|
||||||
|
, _crPastDamage = 0
|
||||||
|
, _crSpState = GenCr
|
||||||
|
, _crApplyDamage = basicApplyDamage'
|
||||||
|
}
|
||||||
|
basicEquipment = Equipment
|
||||||
|
{ _itIdentity = Generic
|
||||||
|
, _itName = "genericEquipment"
|
||||||
|
, _itMaxStack = 1
|
||||||
|
, _itAmount = 1
|
||||||
|
, _itFloorPict = onLayer FlItLayer $ polygon [(-3,-3),(-3,3),(3,3),(3,-3)]
|
||||||
|
, _itEquipPict = \cr _ -> blank
|
||||||
|
, _itEffect = NoItEffect
|
||||||
|
, _itHammer = HammerUp
|
||||||
|
, _itID = Nothing
|
||||||
|
, _itAimingSpeed = 1
|
||||||
|
, _itAimingRange = 0
|
||||||
|
, _itZoom = basicItZoom
|
||||||
|
, _itInvColor = yellow
|
||||||
|
, _itInvDisplay = _itName
|
||||||
|
}
|
||||||
|
basicItZoom = ItZoom 20 0.2 1 20 0.2 1
|
||||||
|
basicConsumable :: Item
|
||||||
|
basicConsumable = Consumable
|
||||||
|
{ _itIdentity = Generic
|
||||||
|
, _itName = "genericConsumable"
|
||||||
|
, _itMaxStack = 9
|
||||||
|
, _itAmount = 1
|
||||||
|
, _cnEffect = \_ _ -> Nothing
|
||||||
|
, _itFloorPict = onLayer FlItLayer $ color blue $ circleSolid 3
|
||||||
|
, _itEquipPict = \_ _ -> blank
|
||||||
|
, _itID = Nothing
|
||||||
|
, _itInvColor = blue
|
||||||
|
, _itInvDisplay = \it -> _itName it ++ " x" ++ show (_itAmount it)
|
||||||
|
, _itEffect = wpRecock
|
||||||
|
, _itHammer = HammerUp
|
||||||
|
}
|
||||||
|
basicApplyDamage' :: [DamageType] -> Creature -> (World -> World, Creature)
|
||||||
|
basicApplyDamage' ds cr = (id, doPoisonDam $ foldr (\d c -> snd $ basicApplyDamage d c) cr ds')
|
||||||
|
where (ps,ds') = partition isPoison ds
|
||||||
|
isPoison (PoisonDam {}) = True
|
||||||
|
isPoison _ = False
|
||||||
|
poisonDam = quot (max 0 (sum (map _dmAmount ps) - 0)) 10
|
||||||
|
doPoisonDam = over crHP (\hp -> hp - poisonDam)
|
||||||
|
|
||||||
|
basicApplyDamage :: DamageType -> Creature -> (World -> World, Creature)
|
||||||
|
basicApplyDamage (Concussive amount from push pushexp pushRad) cr
|
||||||
|
= ( id
|
||||||
|
, over crHP (\hp -> hp - amount)
|
||||||
|
$ over crPos (+.+ (pushAmount *.* safeNormalizeV (_crPos cr -.- from)))
|
||||||
|
cr
|
||||||
|
)
|
||||||
|
where pushAmount | dist (_crPos cr) from == 0
|
||||||
|
= 0
|
||||||
|
| otherwise = min 5 $
|
||||||
|
(push*5*pushRad / (dist (_crPos cr) from * _crMass cr))**pushexp
|
||||||
|
basicApplyDamage (TorqueDam amount rot) cr
|
||||||
|
= ( id
|
||||||
|
, over crHP (\hp -> hp - amount) $ over crDir (+ rot) cr)
|
||||||
|
basicApplyDamage (PushDam amount pback) cr
|
||||||
|
= ( id
|
||||||
|
, over crHP (\hp -> hp - amount) $ over crPos (+.+ ((1/_crMass cr) *.* pback )) cr
|
||||||
|
)
|
||||||
|
basicApplyDamage dt cr
|
||||||
|
= ( id , over crHP (\hp -> hp - _dmAmount dt) cr )
|
||||||
|
basicFlIt = FlIt {_flItRot=0,_flIt = basicIt, _flItPos = (0,0), _flItID = 0}
|
||||||
|
basicIt = Consumable
|
||||||
|
{ _itIdentity = Medkit25
|
||||||
|
, _itName = "basicIt"
|
||||||
|
, _itMaxStack = 3
|
||||||
|
, _itAmount = 2
|
||||||
|
, _cnEffect = \ _ -> return
|
||||||
|
, _itFloorPict = onLayer FlItLayer $ polygon [(-3,-3),(-3,3),(3,3),(3,-3)]
|
||||||
|
, _itEquipPict = \cr _ -> blank
|
||||||
|
, _itID = Nothing
|
||||||
|
, _itInvDisplay = _itName
|
||||||
|
, _itInvColor = blue
|
||||||
|
, _itEffect = NoItEffect
|
||||||
|
, _itHammer = HammerUp
|
||||||
|
}
|
||||||
|
basicButton = Button
|
||||||
|
{ _btPict = onLayer WlLayer $ color red $ polygon $ rectNSEW 5 (-5) 10 (-10)
|
||||||
|
, _btPos = (0,0)
|
||||||
|
, _btRot = 0
|
||||||
|
, _btEvent = \b w -> set (buttons . ix (_btID b) . btPict)
|
||||||
|
(onLayer WlLayer $ color red $ polygon $ rectNSEW (-4) (-5) 10 (-10))
|
||||||
|
. set (buttons . ix (_btID b) . btState) BtNoLabel
|
||||||
|
. soundOnce 1 $ w
|
||||||
|
, _btID = 0
|
||||||
|
, _btText = "Button"
|
||||||
|
, _btState = BtOff
|
||||||
|
}
|
||||||
|
basicPT = Particle
|
||||||
|
{ _ptPos = (0,0)
|
||||||
|
, _ptStartPos = (0,0)
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = blank
|
||||||
|
, _ptID = 0
|
||||||
|
, _ptUpdate = id
|
||||||
|
}
|
||||||
|
basicPP = PressPlate
|
||||||
|
{ _ppPict = onLayer PressPlateLayer $ color (dim $ dim $ bright $ blue) $ circleSolid 5
|
||||||
|
, _ppPos = (0,0)
|
||||||
|
, _ppRot = 0
|
||||||
|
, _ppEvent = \pp -> id
|
||||||
|
, _ppID = -1
|
||||||
|
, _ppText = "Pressure plate"
|
||||||
|
}
|
||||||
|
-- }}}
|
||||||
|
basicWorld = World
|
||||||
|
{ _keys = S.empty
|
||||||
|
, _mouseButtons = S.empty
|
||||||
|
, _cameraPos = (0,0)
|
||||||
|
, _cameraAimTime = 0
|
||||||
|
, _cameraRot = 0
|
||||||
|
, _cameraZoom = 1
|
||||||
|
, _cameraCenter = (0,0)
|
||||||
|
, _creatures = IM.empty
|
||||||
|
, _creaturesZone = IM.empty
|
||||||
|
, _clouds = IM.empty
|
||||||
|
, _cloudsZone = IM.empty
|
||||||
|
, _itemPositions = IM.empty
|
||||||
|
, _particles = IM.empty
|
||||||
|
, _particles' = []
|
||||||
|
, _afterParticles' = []
|
||||||
|
, _walls = IM.empty
|
||||||
|
, _wallsZone = IM.empty
|
||||||
|
, _forceFields = IM.empty
|
||||||
|
, _floorItems = IM.empty
|
||||||
|
, _randGen = mkStdGen 2
|
||||||
|
, _mousePos = (0,0)
|
||||||
|
, _testString = []
|
||||||
|
, _yourID = 0
|
||||||
|
, _worldEvents = id
|
||||||
|
, _pressPlates = IM.empty
|
||||||
|
, _buttons = IM.empty
|
||||||
|
, _soundQueue = []
|
||||||
|
-- , _loadedSounds = IM.empty
|
||||||
|
, _sounds = M.empty
|
||||||
|
, _corpses = IM.empty
|
||||||
|
, _decorations = IM.empty
|
||||||
|
, _unlimitedAmmo = True
|
||||||
|
, _storedLevel = Nothing
|
||||||
|
, _menuState = LevelMenu 1
|
||||||
|
, _worldState = M.empty
|
||||||
|
, _lbClickMousePos = (0,0)
|
||||||
|
, _lbRotation = 0
|
||||||
|
, _pathGraph = Data.Graph.Inductive.Graph.empty
|
||||||
|
, _pathGraph' = []
|
||||||
|
, _pathPoints = IM.empty
|
||||||
|
, _pathInc = M.empty
|
||||||
|
, _windowX = 800
|
||||||
|
, _windowY = 840
|
||||||
|
, _smoke = []
|
||||||
|
, _mapDisplay = (False, 0.2)
|
||||||
|
, _lightSources = IM.empty
|
||||||
|
, _tempLightSources = [youLight]
|
||||||
|
}
|
||||||
|
youLight =
|
||||||
|
-- LS {_lsEff = \w _ p -> (logistic 1 1 1 (d p w * 0.01) )
|
||||||
|
TLS { _tlsPos = (0,0)
|
||||||
|
,_tlsRad = 300
|
||||||
|
,_tlsIntensity = 0.1
|
||||||
|
,_tlsUpdate = \w _ -> (w, Just (youLight {_tlsPos = _crPos (you w)}))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wpRecock :: ItEffect
|
||||||
|
--wpRecock = NoItEffect
|
||||||
|
wpRecock = ItInvEffect {_itInvEffect = f
|
||||||
|
,_itEffectCounter = 0
|
||||||
|
}
|
||||||
|
where f cr i = creatures . ix (_crID cr) . crInv
|
||||||
|
-- . ix i . itHammer %~ ($!) (fmap $! (moveHammerUp `seq` moveHammerUp))
|
||||||
|
-- . ix i . itHammer %~ ($!) (fmap $! moveHammerUp)
|
||||||
|
-- . ix i . itHammer . _Just %~ ($!) moveHammerUp
|
||||||
|
%~ IM.adjust fOnIt i
|
||||||
|
moveHammerUp !HammerDown = HammerReleased
|
||||||
|
moveHammerUp !HammerReleased = HammerUp
|
||||||
|
moveHammerUp !HammerUp = HammerUp
|
||||||
|
fOnIt it = it & itHammer %~ moveHammerUp
|
||||||
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
module Dodge.RandomHelp where
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Geometry.Data
|
||||||
|
|
||||||
|
import System.Random
|
||||||
|
import Control.Monad.State
|
||||||
|
import Data.List
|
||||||
|
|
||||||
|
randomRanges :: (Random a,RandomGen g) => [a] -> State g a
|
||||||
|
randomRanges xs = join $ takeOne $ f xs
|
||||||
|
where f (x:y:ys) = (state (randomR (x,y))) : (f ys)
|
||||||
|
f _ = []
|
||||||
|
|
||||||
|
takeOne :: RandomGen g => [a] -> State g a
|
||||||
|
takeOne xs = state (randomR (0,length xs - 1)) >>= (\i -> return (xs !! i))
|
||||||
|
|
||||||
|
takeOneWeighted :: (RandomGen g, Random b, Ord b, Num b) => [b] -> [a] -> State g a
|
||||||
|
takeOneWeighted ws xs = state (randomR (0, sum ws))
|
||||||
|
>>= (\w -> return (xs !! (i w ws)))
|
||||||
|
where i y (z:zs) | y <= z = 0
|
||||||
|
| otherwise = 1 + i (y-z) zs
|
||||||
|
i y _ = 0
|
||||||
|
|
||||||
|
takeOneMore :: RandomGen g => ([a],[a]) -> State g ([a],[a])
|
||||||
|
takeOneMore (xs,ys) = do
|
||||||
|
i <- state $ randomR (0,length ys - 1)
|
||||||
|
let (zs, w:ws) = splitAt i ys
|
||||||
|
return (w:xs, zs ++ ws)
|
||||||
|
|
||||||
|
takeNMore :: RandomGen g => Int -> ([a],[a]) -> State g ([a],[a])
|
||||||
|
takeNMore n p = foldr (const ((=<<) takeOneMore)) (return p) [1..n]
|
||||||
|
|
||||||
|
takeN :: RandomGen g => Int -> [a] -> State g [a]
|
||||||
|
takeN i xs = fmap fst $ takeNMore i ([],xs)
|
||||||
|
|
||||||
|
-- to randomly shuffle a list
|
||||||
|
shuffle :: RandomGen g => [a] -> State g [a]
|
||||||
|
shuffle xs = do
|
||||||
|
let l = length xs
|
||||||
|
rands <- forM [0..l-1] $ \i -> state $ randomR (0,i)
|
||||||
|
let f ys rand = let (as,b:bs) = splitAt rand ys
|
||||||
|
in (as ++ bs, b)
|
||||||
|
let (_,zs) = mapAccumR f xs rands
|
||||||
|
return zs
|
||||||
|
|
||||||
|
randomSelectionFromList :: RandomGen g => Float -> [a] -> State g [a]
|
||||||
|
randomSelectionFromList p = filterM $ const $ randProb p
|
||||||
|
|
||||||
|
randProb :: RandomGen g => Float -> State g Bool
|
||||||
|
randProb p = do p1 <- state $ randomR (0,1)
|
||||||
|
return (p1 < p)
|
||||||
|
|
||||||
|
randInCirc :: RandomGen g => Float -> State g Point2
|
||||||
|
randInCirc maxRad = do rad <- state $ randomR (0,maxRad)
|
||||||
|
ang <- state $ randomR (0,2*pi)
|
||||||
|
return $ rad *.* unitVectorAtAngle ang
|
||||||
|
|
||||||
|
randInRect :: RandomGen g => Float -> Float -> State g Point2
|
||||||
|
randInRect w h = do x <- state $ randomR (0,w)
|
||||||
|
y <- state $ randomR (0,h)
|
||||||
|
return (x,y)
|
||||||
|
|
||||||
|
maybeTakeOne :: RandomGen g => [a] -> State g (Maybe a)
|
||||||
|
maybeTakeOne [] = return Nothing
|
||||||
|
maybeTakeOne xs = state (randomR (0,length xs - 1)) >>= (\i -> return (Just (xs !! i)))
|
||||||
@@ -0,0 +1,512 @@
|
|||||||
|
module Dodge.Rendering where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Data.Graph.Inductive.Query.DFS
|
||||||
|
import Data.Graph.Inductive.Graph
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
|
import Data.List
|
||||||
|
import Data.Bifunctor
|
||||||
|
|
||||||
|
|
||||||
|
import Data.Function
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Lens
|
||||||
|
import Data.Maybe
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import qualified Data.Set as S
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
--
|
||||||
|
drawTest :: World -> Picture
|
||||||
|
drawTest w = screenT $ color red $ circle 200
|
||||||
|
where (x,y) = (_windowX w,_windowY w)
|
||||||
|
screenT = scale (1/x) (1/y)
|
||||||
|
|
||||||
|
|
||||||
|
draw' :: Picture -> World -> ([((Float,Float),(Float,Float))]
|
||||||
|
,[((Float,Float),Float,Float)]
|
||||||
|
,Picture,Picture)
|
||||||
|
draw' p w = (\(x,y) -> (wallsForGloom w,lightsForGloom w,x,y)) (draw'' p w)
|
||||||
|
|
||||||
|
draw :: Picture -> World -> Picture
|
||||||
|
draw p w = (\(x,y) -> f $ pictures [x,y]) $ draw'' p w
|
||||||
|
where f = scale (2/_windowX w) (2/_windowY w)
|
||||||
|
|
||||||
|
draw'' :: Picture -> World -> (Picture , Picture)
|
||||||
|
draw'' b w-- | (Char 'm') `S.member` _keys w
|
||||||
|
-- = let a = (-500,30)
|
||||||
|
-- b = (200,0)
|
||||||
|
-- in pictures [ color white $ polygon screenBox
|
||||||
|
-- , color blue $ circleSolid 40
|
||||||
|
-- , line [a,b]
|
||||||
|
-- , uncurry translate (ssaTriPoint a (0,0) b 40)
|
||||||
|
-- $ color red $ circleSolid 5
|
||||||
|
-- ]
|
||||||
|
= case _mapDisplay w of
|
||||||
|
(True, z) -> (blank
|
||||||
|
,pictures [color white $ circleSolid 3
|
||||||
|
,scale z z $ rotate (radToDeg (_cameraRot w))
|
||||||
|
$ uncurry translate ((0,0) -.- _cameraCenter w)
|
||||||
|
$ pictures $ mapMaybe mapWall $ IM.elems $ _walls w]
|
||||||
|
)
|
||||||
|
_ -> ( blank
|
||||||
|
--,pictures $ map fst pics
|
||||||
|
,collectDrawings w
|
||||||
|
)
|
||||||
|
-- ++ map screenShift (pathsTest ++ map drawNode (labNodes $ _pathGraph w))
|
||||||
|
where yourPos = _crPos $ you w
|
||||||
|
backgroundTile = [screenShift $ translate (5*512*x) (5*512*y) $ scale 5 5 b | x <- [x1..x2] , y <- [y1..y2] ]
|
||||||
|
where (x',y') = yourPos
|
||||||
|
(x'',y'') = (fromIntegral $ round (x'/(512*5)), fromIntegral $ round (y'/(512*5)))
|
||||||
|
x1 = (x''-1)
|
||||||
|
x2 = (x''+1)
|
||||||
|
y1 = (y''-1)
|
||||||
|
y2 = (y''+1)
|
||||||
|
screenShift = scale zoom zoom . rotate (radToDeg (_cameraRot w) )
|
||||||
|
. uncurry translate ((0,0) -.- _cameraPos w)
|
||||||
|
zoom = _cameraZoom w
|
||||||
|
pathsTest = map drawPair $ _pathGraph' w
|
||||||
|
drawPair (x,y) = color cyan $ pictures [line [x,y]
|
||||||
|
-- , uncurry translate x $ scale 0.05 0.05 $ text $ show x
|
||||||
|
]
|
||||||
|
drawNode (i,x) = color yellow $ uncurry translate x $ scale 0.05 0.05 $ text $ show i
|
||||||
|
|
||||||
|
collectDrawings :: World -> Picture
|
||||||
|
collectDrawings w = screenShift (decPicts <> ppPicts <> itFloorPicts
|
||||||
|
<> crPicts
|
||||||
|
<> clPicts
|
||||||
|
<> buttonPicts <> ptPicts
|
||||||
|
<> ptPicts'
|
||||||
|
<> afterPtPicts'
|
||||||
|
<> wlPicts
|
||||||
|
<> wallShadows
|
||||||
|
<> smokeShadows
|
||||||
|
)
|
||||||
|
<> onLayer LabelLayer
|
||||||
|
(itLabels <> ppLabels <> btLabels)
|
||||||
|
<> hudDrawings w
|
||||||
|
<> onLayer MenuLayer menuScreen
|
||||||
|
-- <> [onLayer GloomLayer $ theLighting w]
|
||||||
|
where
|
||||||
|
screenShift = scale zoom zoom . rotate (radToDeg (_cameraRot w) )
|
||||||
|
. uncurry translate ((0,0) -.- _cameraPos w)
|
||||||
|
rotPicts = map (scale zoom zoom . rotate (radToDeg (_cameraRot w)))
|
||||||
|
zoom = _cameraZoom w
|
||||||
|
decPicts :: Picture
|
||||||
|
decPicts = mconcat $ IM.elems $ _decorations w
|
||||||
|
ptPicts = mconcat $ map _ptPict (IM.elems (_particles w))
|
||||||
|
ptPicts' = mconcat $ map _ptPict' $ _particles' w
|
||||||
|
afterPtPicts' = mconcat $ map _ptPict' $ _afterParticles' w
|
||||||
|
buttonPicts = mconcat $ map btDraw (IM.elems (_buttons w))
|
||||||
|
ppPicts = mconcat $ map ppDraw (IM.elems (_pressPlates w))
|
||||||
|
crPicts :: Picture
|
||||||
|
crPicts = mconcat $ map crDraw $ IM.elems $ _creatures w
|
||||||
|
clPicts = mconcat $ map clDraw $ IM.elems $ _clouds w
|
||||||
|
wallShadows = mconcat $ map (drawWallShadow w) $ wallShadowsToDraw w
|
||||||
|
smokeShadows = mconcat $ map (drawSmokeShadow w) $ _smoke w
|
||||||
|
wlPicts = mconcat $ map drawWall (wallsToDraw w)
|
||||||
|
yourPos = _crPos $ you w
|
||||||
|
yourRot = _crDir $ you w
|
||||||
|
yourRad = _crRad $ you w
|
||||||
|
-- itFloorPicts = zipWith (uncurry translate) (map _flItPos (IM.elems (_floorItems w)))
|
||||||
|
-- (map (_itFloorPict . _flIt) (IM.elems (_floorItems w)))
|
||||||
|
itFloorPicts = mconcat $ map (drawItem) (IM.elems (_floorItems w))
|
||||||
|
itLabels = mconcat $ map (drawItemName w) (IM.elems (_floorItems w))
|
||||||
|
ppLabels = mconcat $ map (drawPPText w) (IM.elems (_pressPlates w))
|
||||||
|
btLabels = mconcat $ map (drawButText w) (IM.elems (_buttons w))
|
||||||
|
menuScreen :: Picture
|
||||||
|
menuScreen = case _menuState w of
|
||||||
|
InGame -> blank
|
||||||
|
LevelMenu x ->
|
||||||
|
mconcat [color (withAlpha 0.5 black) $ polygon $ screenBox w
|
||||||
|
,tst (-100) 100 0.4 ("LEVEL "++show x)
|
||||||
|
] <> controlsList
|
||||||
|
PauseMenu -> mconcat [color (withAlpha 0.5 black) $ polygon $ screenBox w
|
||||||
|
,tst (-100) 100 0.4 "PAUSED"
|
||||||
|
,tst (-100) 50 0.2 "n - new level"
|
||||||
|
,tst (-100) 0 0.2 "r - restart"
|
||||||
|
] <> controlsList
|
||||||
|
GameOverMenu -> mconcat [color (withAlpha 0.5 black) $ polygon $ screenBox w
|
||||||
|
,tst (-100) 100 0.4 "GAME OVER"
|
||||||
|
,tst (-100) 50 0.2 "n - new level"
|
||||||
|
,tst (-100) 0 0.2 "r - restart"
|
||||||
|
]
|
||||||
|
<> controlsList
|
||||||
|
where tst x y sc t = translate x y $ scale sc sc $ color white $ text t
|
||||||
|
|
||||||
|
hudDrawings :: World -> Picture
|
||||||
|
hudDrawings w = (onLayer InvLayer)
|
||||||
|
$ displayInv 0 w
|
||||||
|
<> mconcat
|
||||||
|
[ dShadCol white $ displayHP 0 w
|
||||||
|
, dShadCol (itCol (yourItem w))
|
||||||
|
$ drawCursor w, translate (-390) 20
|
||||||
|
$ scale 0.05 0.05 $ dShadCol white $ text (_testString w)
|
||||||
|
]
|
||||||
|
where itCol = fromMaybe (greyN 0.5) . (^? itInvColor)
|
||||||
|
|
||||||
|
crDraw :: Creature -> Drawing
|
||||||
|
crDraw c = uncurry translateDrawing (_crPos c) $ rotateDrawing (- radToDeg (_crDir c)) (_crPict c c)
|
||||||
|
ppDraw :: PressPlate -> Drawing
|
||||||
|
ppDraw c = uncurry translateDrawing (_ppPos c) $ rotateDrawing (- radToDeg (_ppRot c)) (_ppPict c)
|
||||||
|
btDraw :: Button -> Drawing
|
||||||
|
btDraw c = uncurry translateDrawing (_btPos c) $ rotateDrawing (- radToDeg (_btRot c)) (_btPict c)
|
||||||
|
|
||||||
|
clDraw :: Cloud -> Drawing
|
||||||
|
clDraw c = uncurry translateDrawing (_clPos c) $ (_clPict c c)
|
||||||
|
|
||||||
|
drawCursor :: World -> Picture
|
||||||
|
drawCursor w = translate (105-halfWidth w)
|
||||||
|
(halfHeight w - (25* (fromIntegral iPos)) - 20
|
||||||
|
)
|
||||||
|
-- $ rectangleWire 200 25
|
||||||
|
$ line [(200,12.5),(-100,12.5),(-100,-12.5),(200,-12.5)]
|
||||||
|
where iPos = _crInvSel $ _creatures w IM.! _yourID w
|
||||||
|
|
||||||
|
|
||||||
|
controlsList = mconcat [tst (-250) (-130) 0.15 "controls:"
|
||||||
|
,tst (-150) (-130) 0.15 "wasd"
|
||||||
|
,tst 0 (-130) 0.15 "movement"
|
||||||
|
,tst (-150) (-160) 0.15 "[rmb]"
|
||||||
|
,tst 0 (-160) 0.15 "aim"
|
||||||
|
,tst (-150) (-190) 0.15 "[rmb+lmb]"
|
||||||
|
,tst 0 (-190) 0.15 "shoot or use item"
|
||||||
|
,tst (-150) (-220) 0.15 "[wheelscroll]"
|
||||||
|
,tst 0 (-220) 0.15 "select item"
|
||||||
|
,tst (-150) (-250) 0.15 "[space]"
|
||||||
|
,tst 0 (-250) 0.15 "pickup item"
|
||||||
|
,tst (-150) (-280) 0.15 "f"
|
||||||
|
,tst 0 (-280) 0.15 "drop item"
|
||||||
|
,tst (-150) (-310) 0.15 "cp[esc]"
|
||||||
|
,tst 0 (-310) 0.15 "pause"
|
||||||
|
,tst (-150) (-340) 0.15 "qe[lmb]"
|
||||||
|
,tst 0 (-340) 0.15 "rotate camera"
|
||||||
|
]
|
||||||
|
where tst x y sc t = translate x y $ scale sc sc $ color white $ text t
|
||||||
|
|
||||||
|
screenBox w = [ (halfWidth w, halfHeight w)
|
||||||
|
, (-halfWidth w, halfHeight w)
|
||||||
|
, (-halfWidth w,-halfHeight w)
|
||||||
|
, ( halfWidth w,-halfHeight w)
|
||||||
|
]
|
||||||
|
|
||||||
|
mapWall :: Wall -> Maybe Picture
|
||||||
|
mapWall wl =
|
||||||
|
case _wlSeen wl of
|
||||||
|
False -> Nothing
|
||||||
|
True -> Just $ color c $ polygon [x,x +.+ n2,y +.+ n2, y]
|
||||||
|
where t = 5 *.* errorNormalizeV 68 (y -.- x)
|
||||||
|
n = vNormal t
|
||||||
|
n2 = 4 *.* n
|
||||||
|
(x:y:_) = _wlLine wl
|
||||||
|
c = _wlColor wl
|
||||||
|
|
||||||
|
wallsToDraw :: World -> [Wall]
|
||||||
|
wallsToDraw w = filter isVisible $ IM.elems $ wallsOnScreen w
|
||||||
|
where onScreen wall = lineOnScreen w (_wlLine wall)
|
||||||
|
isVisible wl | wl ^? blVisible == Just False = False
|
||||||
|
| otherwise = onScreen wl
|
||||||
|
|
||||||
|
wallsOnScreen :: World -> IM.IntMap Wall
|
||||||
|
wallsOnScreen w = wallsNearZones (zoneOfScreen w) w
|
||||||
|
|
||||||
|
drawSmokeShadow :: World -> Smoke -> Drawing
|
||||||
|
drawSmokeShadow w sm@(Smoke {_smPos = p, _smRad = r', _smColor = c, _smPs = ps, _smTime = t})
|
||||||
|
= (
|
||||||
|
onLayerL [74,0] $ color col $
|
||||||
|
pictures [uncurry translate p $ circle r'
|
||||||
|
,line $ (\(x,y) -> [x,y]) $ smokePerpLine (_cameraPos w) p sm
|
||||||
|
,line [_cameraPos w, mouseWorldPos w]
|
||||||
|
,trap' p
|
||||||
|
]
|
||||||
|
)
|
||||||
|
<>
|
||||||
|
(onLayerL [74] $ color (withAlpha 0.1 c) $ pictures
|
||||||
|
$ concatMap (f . (+.+ p)) p's
|
||||||
|
)
|
||||||
|
where
|
||||||
|
r = r'/2
|
||||||
|
col | smokeLOS (_cameraPos w) (mouseWorldPos w) w = red
|
||||||
|
| otherwise = green
|
||||||
|
orth p' = r *.* (safeNormalizeV $ vNormal $ p' -.- _cameraCenter w)
|
||||||
|
pa p' = p' +.+ orth p'
|
||||||
|
pb p' = p' -.- orth p'
|
||||||
|
pao p' = pa p' +.+ l *.* safeNormalizeV (pa p' -.- _cameraCenter w)
|
||||||
|
pbo p' = pb p' +.+ l *.* safeNormalizeV (pb p' -.- _cameraCenter w)
|
||||||
|
semiCirc p' = uncurry translate p' $ rotate (radToDeg (a p')) $ arcSolid 0 180 r
|
||||||
|
a p' = pi - argV (orth p')
|
||||||
|
trap p' = polygon [pa p', pb p', pbo p', pao p']
|
||||||
|
f p' = [semiCirc p', trap p']
|
||||||
|
p's = zipWith (\x p -> rotateV (x * fromIntegral t / 60) p) xs ps
|
||||||
|
xs = concat $ repeat [-1,-0.5,0,0.5,1]
|
||||||
|
l = _windowX w + _windowY w + magV (_cameraPos w -.- _cameraCenter w)
|
||||||
|
-- cenpic = color (withAlpha 0.5 c) $ pictures $ f p
|
||||||
|
orth' p' = r' *.* (safeNormalizeV $ vNormal $ p' -.- _cameraCenter w)
|
||||||
|
pa' p' = p' +.+ orth' p'
|
||||||
|
pb' p' = p' -.- orth' p'
|
||||||
|
pao' p' = pa' p' +.+ l *.* safeNormalizeV (pa' p' -.- _cameraCenter w)
|
||||||
|
pbo' p' = pb' p' +.+ l *.* safeNormalizeV (pb' p' -.- _cameraCenter w)
|
||||||
|
trap' p' = line [pa' p', pb' p', pbo' p', pao' p',pa' p']
|
||||||
|
semiCirc' p' = uncurry translate p' $ rotate (radToDeg (a p')) $ arcSolid 0 180 r'
|
||||||
|
|
||||||
|
|
||||||
|
drawWall :: Wall -> Drawing
|
||||||
|
drawWall wl = case _wlDraw wl of
|
||||||
|
Nothing -> onLayerL [levLayer WlLayer, layer2] $ color c $ polygon [x,x +.+ n2,y +.+ n2, y]
|
||||||
|
Just d -> d wl
|
||||||
|
where
|
||||||
|
(x:y:_) = _wlLine wl
|
||||||
|
c = _wlColor wl
|
||||||
|
t = 5 *.* errorNormalizeVDR (y -.- x)
|
||||||
|
n = vNormal t
|
||||||
|
n2 = 3 *.* n
|
||||||
|
layer2 | _wlIsSeeThrough wl = 0
|
||||||
|
| isJust $ wl ^? doorMech = 1
|
||||||
|
| otherwise = 2
|
||||||
|
-- wallOrdering wl = (not $ _wlIsSeeThrough wl, not $ isJust $ wl ^? doorMech)
|
||||||
|
|
||||||
|
errorNormalizeVDR :: Point2 -> Point2
|
||||||
|
errorNormalizeVDR (0,0) = error $ "problem with function: errorNormalizeVDR in DodgeRendering"
|
||||||
|
errorNormalizeVDR p = normalizeV p
|
||||||
|
|
||||||
|
printPoint :: Point2 -> Picture
|
||||||
|
printPoint p = color white $ uncurry translate p $ pictures [circle 3 ,scale 0.05 0.05 $ text (show p)]
|
||||||
|
|
||||||
|
printRotPoint :: Float -> Point2 -> Picture
|
||||||
|
printRotPoint r p = color white $ uncurry translate p $ pictures [circle 3
|
||||||
|
, rotate r $ scale 0.1 0.1 $ text (show p)]
|
||||||
|
|
||||||
|
outsideScreenPolygon :: World -> [Point2]
|
||||||
|
outsideScreenPolygon w = [tr,tl,bl,br]
|
||||||
|
where scRot = rotateV (_cameraRot w)
|
||||||
|
scZoom p | _cameraZoom w /= 0 = (1/_cameraZoom w) *.* p
|
||||||
|
scTran p = p +.+ _cameraPos w
|
||||||
|
tr = scTran $ scRot $ scZoom ( 3*halfWidth w , 3* halfHeight w)
|
||||||
|
tl = scTran $ scRot $ scZoom (- (3*halfWidth w), 3* halfHeight w)
|
||||||
|
br = scTran $ scRot $ scZoom ( 3*halfWidth w ,- (3* halfHeight w))
|
||||||
|
bl = scTran $ scRot $ scZoom (- (3*halfWidth w),- (3* halfHeight w))
|
||||||
|
x = halfWidth w + halfHeight w
|
||||||
|
|
||||||
|
-- wallShadowsToDrawOnScreen :: World -> [Wall]
|
||||||
|
-- wallShadowsToDrawOnScreen w = --IM.elems $ _walls w
|
||||||
|
-- sortBy (compare `on` not . _wlIsSeeThrough) $ filter isVisible $ IM.elems
|
||||||
|
-- $ _walls w
|
||||||
|
-- -- $ wallsOnScreen w
|
||||||
|
-- -- should really sort this out
|
||||||
|
-- where onScreen wall = lineOnScreen w (_wlLine wall)
|
||||||
|
-- isVisible wl | wl ^? blVisible == Just False = False
|
||||||
|
-- | otherwise = onScreen wl
|
||||||
|
|
||||||
|
wallShadowsToDraw :: World -> [Wall]
|
||||||
|
wallShadowsToDraw w = --IM.elems $ _walls w
|
||||||
|
filter isVisible $ IM.elems -- $ _walls w
|
||||||
|
$ wallsNearZones (zoneOfSight w) w
|
||||||
|
-- should really sort this out
|
||||||
|
where onScreen wall = True -- lineOnScreenCone w (_wlLine wall)
|
||||||
|
isVisible wl | wl ^? blVisible == Just False = False
|
||||||
|
| otherwise = onScreen wl
|
||||||
|
|
||||||
|
-- cannot only test if walls are on screen, but also if they are on the cone
|
||||||
|
-- towards the center of sight
|
||||||
|
lineOnScreenCone :: World -> [Point2] -> Bool
|
||||||
|
lineOnScreenCone w (p1:p2:_) = errorPointInPolygon 8 p1 sp || errorPointInPolygon 9 p2 sp
|
||||||
|
|| any (isJust . uncurry (intersectSegSeg' p1 p2)) sps
|
||||||
|
where sp' = screenPolygon w
|
||||||
|
vp = _cameraCenter w
|
||||||
|
sp | pointInPolygon vp sp' = sp'
|
||||||
|
| otherwise = orderPolygon $ (_cameraCenter w : sp')
|
||||||
|
sps = zip sp (tail sp ++ [head sp])
|
||||||
|
|
||||||
|
|
||||||
|
lineOnScreen :: World -> [Point2] -> Bool
|
||||||
|
lineOnScreen w (p1:p2:_) = errorPointInPolygon 8 p1 sp || errorPointInPolygon 9 p2 sp
|
||||||
|
|| any (isJust . uncurry (intersectSegSeg' p1 p2)) sps
|
||||||
|
where sp = screenPolygon w
|
||||||
|
sps = zip sp (tail sp ++ [head sp])
|
||||||
|
|
||||||
|
|
||||||
|
drawWallShadow :: World -> Wall -> Drawing
|
||||||
|
drawWallShadow w wall
|
||||||
|
| isRHS sightFrom x y = blank
|
||||||
|
| otherwise = onLayerL l $ color shadCol $ polygon $ points
|
||||||
|
where
|
||||||
|
l | _wlIsSeeThrough wall = [levLayer ShadowLayer]
|
||||||
|
| otherwise = [levLayer ShadowLayer, 0]
|
||||||
|
(x:y:_) = _wlLine wall
|
||||||
|
ps = linePointsBetween x y
|
||||||
|
ds = map (\p -> safeNormalizeV (p -.- sightFrom)) ps
|
||||||
|
p's = zipWith (\d x -> (15 *.* d) +.+ x) ds ps
|
||||||
|
sightFrom = _cameraCenter w
|
||||||
|
shadCol = if _wlIsSeeThrough wall then withAlpha 0.2 $ _wlColor wall
|
||||||
|
else black
|
||||||
|
corns = screenPolygon w
|
||||||
|
borders = filter g $ zip corns (tail corns ++ [head corns])
|
||||||
|
g (a,b) = isLHS a b sightFrom
|
||||||
|
borderps = mapMaybe f borders ++ mapMaybe f' borders ++ shadowedCorners
|
||||||
|
coneTop = nub $ concatMap (\(a,b) -> [a,b]) $ borders
|
||||||
|
shadowedCorners = filter isShadowed coneTop
|
||||||
|
isShadowed p = isLHS sightFrom x p && isRHS sightFrom y p
|
||||||
|
f (bpa,bpb) = intersectSegLineFrom' bpa bpb x (head p's)
|
||||||
|
f' (bpa,bpb) = intersectSegLineFrom' bpa bpb y (last p's)
|
||||||
|
points = orderPolygon (borderps ++ p's)
|
||||||
|
|
||||||
|
linePointsBetween :: Point2 -> Point2 -> [Point2]
|
||||||
|
linePointsBetween p p' | d > 99 = map (\m -> p +.+ fromIntegral m *.* p'') [0..n-1] ++ [p']
|
||||||
|
| otherwise = [p,p']
|
||||||
|
where d = dist p p'
|
||||||
|
n = ceiling $ d / 50
|
||||||
|
p'' = (1/fromIntegral n) *.* (p' -.- p)
|
||||||
|
|
||||||
|
displayInv :: Int -> World -> Picture
|
||||||
|
displayInv n w = mconcat $ zipWith (translate (10-halfWidth w))
|
||||||
|
(map (\x-> halfHeight w-(25*(fromIntegral x+1))) ns) $ map dItem' is
|
||||||
|
where (ns,is) = unzip $ IM.toList $ _crInv $ _creatures w IM.! n
|
||||||
|
|
||||||
|
dItem' NoItem = scale 0.15 0.15 $ dShadCol (greyN 0.5) $ text "----"
|
||||||
|
dItem' i = scale 0.15 0.15 $ pictures [dropShadow t, color (_itInvColor i) t]
|
||||||
|
where t = text $ _itInvDisplay i i
|
||||||
|
|
||||||
|
displayAmount :: Int -> String
|
||||||
|
displayAmount n | n > 1 = "-x" ++show n
|
||||||
|
| otherwise = []
|
||||||
|
|
||||||
|
rectangleSolid x y = polygon [(x,y),(x,-y),(-x,-y),(-x,y)]
|
||||||
|
|
||||||
|
drawItem :: FloorItem -> Drawing
|
||||||
|
drawItem (FlAm {_flItPos = p}) = uncurry translateDrawing p $ onLayer FlItLayer $ rectangleSolid 5 5
|
||||||
|
drawItem flIt = uncurry translateDrawing (_flItPos flIt)
|
||||||
|
$ rotateDrawing (radToDeg $ _flItRot flIt) (_itFloorPict (_flIt flIt))
|
||||||
|
|
||||||
|
drawButText :: World -> Button -> Picture
|
||||||
|
drawButText w bt | magV (_crPos (you w) -.- _btPos bt) < 100
|
||||||
|
&& hasLOS (_btPos bt) (_crPos (you w)) w
|
||||||
|
&& _btState bt /= BtNoLabel
|
||||||
|
= t $ rotate (radToDeg (-_cameraRot w))
|
||||||
|
$ pictures $ [ scLine [(-8,10),(-15,10),(-15,-10),(-8,-10)]
|
||||||
|
, scLine [( 8,10),( 15,10),( 15,-10),( 8,-10)]
|
||||||
|
,translate (-15) (-10*sqrt zoom - 5) $ dShadCol white
|
||||||
|
$ scale 0.1 0.1 $ text $ _btText bt
|
||||||
|
]
|
||||||
|
| otherwise = blank
|
||||||
|
where t = rotate (radToDeg (_cameraRot w)) . uncurry translate (zoom *.* (_btPos bt -.- _cameraPos w))
|
||||||
|
zoom = _cameraZoom w
|
||||||
|
scLine = dShadCol white . line . fmap (sqrt zoom *.*)
|
||||||
|
|
||||||
|
drawPPText :: World -> PressPlate -> Picture
|
||||||
|
drawPPText w pp | magV (_crPos (you w) -.- _ppPos pp) < 100
|
||||||
|
&& hasLOS (_ppPos pp) (_crPos (you w)) w
|
||||||
|
= t $ rotate (radToDeg (-_cameraRot w))
|
||||||
|
$ pictures $ [ scLine [(-8,10),(-15,10),(-15,-10),(-8,-10)]
|
||||||
|
, scLine [( 8,10),( 15,10),( 15,-10),( 8,-10)]
|
||||||
|
,translate (-15) (-10*sqrt zoom - 5) $ dShadCol white
|
||||||
|
$ scale 0.1 0.1 $ text $ _ppText pp
|
||||||
|
]
|
||||||
|
| otherwise = blank
|
||||||
|
where t = rotate (radToDeg (_cameraRot w)) . uncurry translate (zoom *.* (_ppPos pp -.- _cameraPos w))
|
||||||
|
zoom = _cameraZoom w
|
||||||
|
scLine = dShadCol white . line . fmap (sqrt zoom *.*)
|
||||||
|
|
||||||
|
drawItemName :: World -> FloorItem -> Picture
|
||||||
|
drawItemName w flIt | magV (_crPos (you w) -.- _flItPos flIt) < 100
|
||||||
|
&& hasLOS (_flItPos flIt) (_crPos (you w)) w
|
||||||
|
= t $ rotate (radToDeg (-_cameraRot w))
|
||||||
|
$ pictures $ [ scLine [(-8,10),(-15,10),(-15,-10),(-8,-10)]
|
||||||
|
, scLine [( 8,10),( 15,10),( 15,-10),( 8,-10)]
|
||||||
|
,translate (-15) (-10*sqrt zoom - 5) $ dShadCol white
|
||||||
|
$ scale 0.1 0.1 $ text $ nameOfItem
|
||||||
|
]
|
||||||
|
| otherwise = blank
|
||||||
|
where t = rotate (radToDeg (_cameraRot w)) . uncurry translate (zoom *.* (_flItPos flIt -.- _cameraPos w))
|
||||||
|
nameOfItem = case flIt of FlIt {} -> _itName $ _flIt flIt
|
||||||
|
FlAm {_flAm = PistolBullet} -> "Bullets"
|
||||||
|
FlAm {_flAm = LiquidFuel} -> "Liquid Fuel"
|
||||||
|
zoom = _cameraZoom w
|
||||||
|
scLine = dShadCol white . line . fmap (sqrt zoom *.*)
|
||||||
|
|
||||||
|
ringPict :: Drawing
|
||||||
|
ringPict = onLayer LabelLayer $ dShadCol white $ pictures [line [(-8,10),(-15,10),(-15,-10),(-8,-10)]
|
||||||
|
,line [( 8,10),( 15,10),( 15,-10),( 8,-10)]
|
||||||
|
]
|
||||||
|
|
||||||
|
dShadCol :: Color -> Picture -> Picture
|
||||||
|
dShadCol c p = pictures $
|
||||||
|
map (flip (uncurry translate) p) [(0.2,-0.2)
|
||||||
|
,(0.4,-0.4)
|
||||||
|
,(0.6,-0.6)
|
||||||
|
,(0.8,-0.8)
|
||||||
|
,( 1,- 1)
|
||||||
|
,(1.2,-1.2)
|
||||||
|
] ++ [color c p]
|
||||||
|
|
||||||
|
dropShadow :: Picture -> Picture
|
||||||
|
dropShadow p = pictures [ translate 5 (-5) p
|
||||||
|
, translate 1 (-1) p
|
||||||
|
, translate 2 (-2) p
|
||||||
|
, translate 3 (-3) p
|
||||||
|
, translate 4 (-4) p
|
||||||
|
, translate 6 (-6) p
|
||||||
|
, translate 7 (-7) p
|
||||||
|
, translate 8 (-8) p
|
||||||
|
, translate 9 (-9) p
|
||||||
|
]
|
||||||
|
|
||||||
|
ffToDraw :: World -> [ForceField]
|
||||||
|
ffToDraw w = filter (lineOnScreen w . _ffLine) $
|
||||||
|
IM.elems $ fmap (over ffLine (map (\p->p -.- _cameraPos w))) $
|
||||||
|
_forceFields w
|
||||||
|
|
||||||
|
drawFF :: ForceField -> Picture
|
||||||
|
drawFF (FF {_ffLine = l, _ffColor = col}) = pictures [color white $ line l, color col
|
||||||
|
$ lineOfThickness 6 l ]
|
||||||
|
|
||||||
|
|
||||||
|
drawFFShadow :: World -> ForceField -> [Picture]
|
||||||
|
drawFFShadow w ff
|
||||||
|
| youOnFF = []
|
||||||
|
| otherwise = map (rotate (radToDeg (- _cameraRot w)) . pane)
|
||||||
|
[0,0.05..0.25]
|
||||||
|
where p = rotateV (-_cameraRot w) $ ypShift
|
||||||
|
x = rotateV (-_cameraRot w) x'
|
||||||
|
y = rotateV (-_cameraRot w) y'
|
||||||
|
yp = _crPos $ you w
|
||||||
|
(x1:y1:_) = _ffLine ff
|
||||||
|
(x':y':_) | isRHS x1 y1 yp = (y1:x1:[])
|
||||||
|
| otherwise = (x1:y1:[])
|
||||||
|
fCol = color (_ffColor ff)
|
||||||
|
col = _ffColor ff
|
||||||
|
ypShift = yp -.- _cameraPos w
|
||||||
|
youOnFF = circOnLine' x' y' ypShift (_crRad $ you w)
|
||||||
|
pane j = color (withAlpha 0.1 col)
|
||||||
|
$ polygon
|
||||||
|
$ [ x
|
||||||
|
, x +.+ ((0.1 + j) *.* (x -.- ypShift))
|
||||||
|
, y +.+ ((0.1 + j) *.* (y -.- ypShift))
|
||||||
|
, y]
|
||||||
|
|
||||||
|
displayHP :: Int -> World -> Picture
|
||||||
|
displayHP n w = translate (halfWidth w-70) (halfHeight w-40) $
|
||||||
|
scale 0.2 0.2 $ text $ show
|
||||||
|
$ _crHP $ _creatures w IM.! n
|
||||||
|
|
||||||
|
testPic w = blank
|
||||||
|
|
||||||
|
wallsForGloom :: World -> [(Point2,Point2)]
|
||||||
|
wallsForGloom w = map (\(x:y:_) -> (screenShift x,screenShift y)) $
|
||||||
|
map _wlLine $ filter (not . _wlIsSeeThrough)
|
||||||
|
$ IM.elems $ wallsNearZones (zoneOfDoubleScreen w) w
|
||||||
|
where screenShift x = zoom *.* rotateV (0 - _cameraRot w) (x -.- _cameraPos w)
|
||||||
|
zoom = _cameraZoom w
|
||||||
|
|
||||||
|
|
||||||
|
lightsForGloom :: World -> [(Point2,Float,Float)]
|
||||||
|
lightsForGloom w = map getLS (IM.elems $ _lightSources w) ++ map getTLS (_tempLightSources w)
|
||||||
|
where getLS ls = (screenShift $ _lsPos ls, _lsRad ls * zoom, _lsIntensity ls)
|
||||||
|
getTLS ls = (screenShift $ _tlsPos ls, _tlsRad ls * zoom, _tlsIntensity ls)
|
||||||
|
screenShift x = zoom *.* rotateV (0 - _cameraRot w) (x -.- _cameraPos w)
|
||||||
|
zoom = _cameraZoom w
|
||||||
@@ -0,0 +1,874 @@
|
|||||||
|
module Dodge.Rooms where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Weapons
|
||||||
|
import Dodge.Critters
|
||||||
|
import Dodge.LevelGen
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.RandomHelp
|
||||||
|
import Dodge.Prototypes
|
||||||
|
import Dodge.Path
|
||||||
|
import Dodge.Layout
|
||||||
|
import Dodge.LightSources
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import qualified SDL.Mixer as Mix
|
||||||
|
|
||||||
|
import Control.Monad.State
|
||||||
|
import Control.Monad.Loops
|
||||||
|
import Control.Lens
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Tree
|
||||||
|
import Data.Either
|
||||||
|
import Data.Function
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Data.Graph.Inductive.Graph hiding ((&))
|
||||||
|
import Data.Graph.Inductive.Basic
|
||||||
|
import Data.Graph.Inductive.PatriciaTree
|
||||||
|
import Data.Graph.Inductive.NodeMap
|
||||||
|
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lev1 :: RandomGen g => State g (Tree Room)
|
||||||
|
lev1 = do
|
||||||
|
firstWeapon <- takeOne $ [[branchRectWith weaponRoom,blockedCorridor]] ++ replicate 5 [weaponRoom]
|
||||||
|
iterateWhile boundClip $ fmap (shiftRoomTree . expandTreeBy id)
|
||||||
|
$ sequence $ treePost
|
||||||
|
(
|
||||||
|
-- [randomiseLinks =<< pistolerRoom]
|
||||||
|
[return $ return $ Right deadEndRoom
|
||||||
|
]
|
||||||
|
++ [return $ connectRoom corridor
|
||||||
|
,return $ connectRoom door]
|
||||||
|
-- ++ [randomiseLinks =<< longRoom]
|
||||||
|
++ [randomiseLinks =<< pistolerRoom]
|
||||||
|
-- ++ [randomiseLinks =<< shooterRoom]
|
||||||
|
++ [return $ connectRoom door]
|
||||||
|
-- ++ [return $ connectRoom $ set rmPS [PS (100,100) 0 $ PutCrit explosiveBarrel
|
||||||
|
-- ,PS (105,110) 0 $ PutCrit explosiveBarrel
|
||||||
|
-- ,PS (115,110) 0 $ PutCrit autoCrit
|
||||||
|
-- ,PS (135,110) 0 $ PutCrit explosiveBarrel
|
||||||
|
-- ,PS (105,140) 0 $ PutCrit explosiveBarrel
|
||||||
|
-- ]
|
||||||
|
-- $ roomRect 300 300]
|
||||||
|
++ [randomiseLinks =<< shooterRoom]
|
||||||
|
++ (replicate 3 $ randomiseLinks corridor)
|
||||||
|
++ [return $ connectRoom corridor]
|
||||||
|
-- ++ [randomiseLinks =<< longRoom]
|
||||||
|
++ [return $ connectRoom corridor]
|
||||||
|
++ firstWeapon
|
||||||
|
++ (replicate 3 $ randomiseLinks corridor)
|
||||||
|
++ [return $ connectRoom corridor]
|
||||||
|
++[return $ connectRoom corridor,return $ connectRoom door]
|
||||||
|
++ [shootingRange]
|
||||||
|
++ (replicate 3 $ randomiseLinks corridor)
|
||||||
|
++[roomMiniIntro]
|
||||||
|
++ (replicate 3 $ randomiseLinks corridor)
|
||||||
|
++ [return $ connectRoom corridor
|
||||||
|
,return $ connectRoom door
|
||||||
|
,slowDoorRoom]
|
||||||
|
++ [return $ connectRoom corridor
|
||||||
|
,spawnerRoom
|
||||||
|
,return $ connectRoom corridor
|
||||||
|
]
|
||||||
|
)
|
||||||
|
$ randomiseLinks corridor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
generateLevel :: Int -> World -> World
|
||||||
|
generateLevel i w =
|
||||||
|
do haltSound $ ((generateFromTree $ levelTree i) w)
|
||||||
|
|
||||||
|
levelTree :: Int -> State StdGen (Tree Room)
|
||||||
|
levelTree 1 = lev1
|
||||||
|
|
||||||
|
levelPortalAt :: Point2 -> Int -> PressPlate
|
||||||
|
levelPortalAt p x
|
||||||
|
= PressPlate
|
||||||
|
{ _ppPict = onLayer PressPlateLayer $ color blue $ circle 10
|
||||||
|
, _ppPos = p
|
||||||
|
, _ppRot = 0
|
||||||
|
, _ppEvent = \pp w -> if dist (_crPos (you w)) (_ppPos pp) < 10
|
||||||
|
then generateLevel x w
|
||||||
|
else w
|
||||||
|
, _ppID = -1
|
||||||
|
, _ppText = "Portal to level "++ show x
|
||||||
|
}
|
||||||
|
|
||||||
|
airlockOneWay :: Int -> Room
|
||||||
|
airlockOneWay n = Room
|
||||||
|
{ _rmPolys = [rectNSWE 90 0 0 40]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = []
|
||||||
|
, _rmPS = [PS (0,15) 0 $ PutTriggerDoor col (not . cond) (0,0) (0,40)
|
||||||
|
,PS (0,75) 0 $ PutTriggerDoor col (cond) (0,0) (0,40)
|
||||||
|
,PS (35,45) (pi/2) $ PutButton $ makeButton col (over worldState
|
||||||
|
(M.insert (DoorNumOpen n) True))
|
||||||
|
]
|
||||||
|
--, _rmBound = rectNSWE 90 30 (-30) 30
|
||||||
|
, _rmBound = rectNSWE 75 15 0 40
|
||||||
|
}
|
||||||
|
where lnks = [((0,85),0)
|
||||||
|
,((0, 5),pi)
|
||||||
|
]
|
||||||
|
cond w = or $ M.lookup (DoorNumOpen n) (_worldState w)
|
||||||
|
col = dim $ dim $ bright red
|
||||||
|
airlock :: Int -> Room
|
||||||
|
airlock n = Room
|
||||||
|
{ _rmPolys = [rectNSWE 90 0 0 40]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = [((20,85),(20,45))
|
||||||
|
,((20,45),(20, 5))
|
||||||
|
]
|
||||||
|
, _rmPS = [PS (0,15) 0 $ PutTriggerDoor col (not . cond) (0,0) (40,0)
|
||||||
|
,PS (0,75) 0 $ PutTriggerDoor col (cond) (0,0) (40,0)
|
||||||
|
,PS (35,45) (pi/2) $ PutButton $ makeSwitch col
|
||||||
|
(over worldState (M.insert (DoorNumOpen n) True))
|
||||||
|
(over worldState (M.insert (DoorNumOpen n) False))
|
||||||
|
]
|
||||||
|
, _rmBound = rectNSWE 75 15 0 40
|
||||||
|
}
|
||||||
|
where lnks = [((20,85),0)
|
||||||
|
,((20, 5),pi)
|
||||||
|
]
|
||||||
|
cond w = or $ M.lookup (DoorNumOpen n) (_worldState w)
|
||||||
|
col = dim $ dim $ bright red
|
||||||
|
corridor :: Room
|
||||||
|
corridor = Room
|
||||||
|
{ _rmPolys = [rectNSWE 80 0 0 40
|
||||||
|
,[(0,80), (0,80) +.+ rotateV (pi/3) (40,0),(40,80)]
|
||||||
|
,[(40,0), (0,0) +.+ rotateV (0-pi/3) (40,0),( 0, 0)]
|
||||||
|
]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = concatMap (doublePair . (,) (20,60)) $ map fst lnks
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = rectNSWE 50 30 0 40
|
||||||
|
}
|
||||||
|
where lnks = [((20,70) ,0)
|
||||||
|
,((20,80) +.+ rotateV (0-pi/3) (-20,0), pi/6)
|
||||||
|
,((20,80) +.+ rotateV ( pi/3) ( 20,0),0-pi/6)
|
||||||
|
-- ,((40,0) +.+ rotateV ( pi/3) (-20,0), 2*pi/3)
|
||||||
|
-- ,(( 0,0) +.+ rotateV (0-pi/3) ( 20,0),0-2*pi/3)
|
||||||
|
,((20,10) ,pi)
|
||||||
|
]
|
||||||
|
corridorN :: Room
|
||||||
|
corridorN = Room
|
||||||
|
{ _rmPolys = [rectNSWE 80 0 0 40
|
||||||
|
]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = pth
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = rectNSWE 50 30 0 40
|
||||||
|
}
|
||||||
|
where lnks = [((20,70) ,0)
|
||||||
|
,((20,10) ,pi)
|
||||||
|
]
|
||||||
|
pth = doublePair ((20,70),(20,10))
|
||||||
|
tEast :: Room
|
||||||
|
tEast = Room
|
||||||
|
{ _rmPolys = [rectNSWE 80 0 (-20) (20)
|
||||||
|
,rectNSWE 80 40 (-40) 40
|
||||||
|
]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = concatMap (doublePair . (,) (0,60)) $ map fst lnks
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = rectNSWE 70 10 0 40
|
||||||
|
}
|
||||||
|
where lnks = [(( 30,60),-pi/2)
|
||||||
|
,((-30,60),pi/2)
|
||||||
|
,((0,10),pi)
|
||||||
|
]
|
||||||
|
tWest :: Room
|
||||||
|
tWest = Room
|
||||||
|
{ _rmPolys = [rectNSWE 80 0 (-20) (20)
|
||||||
|
,rectNSWE 80 40 (-40) 40
|
||||||
|
]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = concatMap (doublePair . (,) (0,60)) $ map fst lnks
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = rectNSWE 70 10 0 40
|
||||||
|
}
|
||||||
|
where lnks = [((-30,60),pi/2)
|
||||||
|
,(( 30,60),-pi/2)
|
||||||
|
,((0,10),pi)
|
||||||
|
]
|
||||||
|
roomC :: Float -> Float -> Room
|
||||||
|
roomC x y = Room
|
||||||
|
{ _rmPolys = [rectNSWE y 0 0 x]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = []
|
||||||
|
, _rmPS = [PS (0,0) 0 $ PutWindowBlock (x/2,0) (x/2,y-60)
|
||||||
|
]
|
||||||
|
--, _rmBound = rectNSWE y 0 0 x
|
||||||
|
, _rmBound = []
|
||||||
|
}
|
||||||
|
where lnks = [( (x-20, 0),pi)
|
||||||
|
,( ( 20, 0),pi)
|
||||||
|
,( ( 0, 20),pi/2)
|
||||||
|
]
|
||||||
|
|
||||||
|
makeRect :: Float -> Float -> [(Point2,Point2)]
|
||||||
|
makeRect x y = [((0,0),(x,0))
|
||||||
|
,((0,0),(0,y))
|
||||||
|
,((x,y),(x,0))
|
||||||
|
,((x,y),(0,y))
|
||||||
|
]
|
||||||
|
|
||||||
|
gridPoints :: Float -> Int -> Float -> Int -> [Point2]
|
||||||
|
gridPoints x nx y ny = [(a,b) | a <- take nx $ scanl (+) 0 $ repeat x
|
||||||
|
, b <- take ny $ scanl (+) 0 $ repeat y
|
||||||
|
]
|
||||||
|
|
||||||
|
makeGrid :: Float -> Int -> Float -> Int -> [(Point2,Point2)]
|
||||||
|
makeGrid x nx y ny = nub $ concatMap doublePair
|
||||||
|
$ concatMap (\p -> map (\(a,b) -> (p +.+ a,p +.+ b)) $ makeRect x y)
|
||||||
|
$ gridPoints x nx y ny
|
||||||
|
|
||||||
|
roomPadCut :: [Point2] -> Point2 -> Room
|
||||||
|
roomPadCut ps p = Room
|
||||||
|
{ _rmPolys = [ps]
|
||||||
|
, _rmLinks = [(p,0),((0,0),pi)]
|
||||||
|
, _rmPath = [((0,0),p)]
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = []
|
||||||
|
}
|
||||||
|
|
||||||
|
roomRect' :: Float -> Float -> Int -> Int -> Room
|
||||||
|
roomRect' x y a b = Room
|
||||||
|
{ _rmPolys = [rectNSWE y 0 0 x ]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = concatMap doublePair pth
|
||||||
|
, _rmPS = [PS (x/2,y/2) 0 $ basicLS]
|
||||||
|
, _rmBound = rectNSWE (y+5) (-5) (-5) (x+5)
|
||||||
|
}
|
||||||
|
where yn,xn :: Int
|
||||||
|
yn = b
|
||||||
|
yd = (y - 40) / fromIntegral yn
|
||||||
|
xn = a
|
||||||
|
xd = (x - 40) / fromIntegral xn
|
||||||
|
elnks = flip zip (repeat ( pi/2)) $ translateS (0,20) $ gridPoints 0 1 yd (yn+1)
|
||||||
|
wlnks = flip zip (repeat (-pi/2)) $ translateS (x,20) $ gridPoints 0 1 yd (yn+1)
|
||||||
|
nlnks = flip zip (repeat ( 0)) $ translateS (20,y) $ gridPoints xd (xn+1) 0 1
|
||||||
|
slnks = flip zip (repeat ( pi)) $ translateS (20,0) $ gridPoints xd (xn+1) 0 1
|
||||||
|
lnks = nlnks ++ elnks ++ wlnks ++ slnks
|
||||||
|
pth = linksAndPath lnks $ translateS (20,20) (makeGrid xd xn yd yn)
|
||||||
|
|
||||||
|
roomRect :: Float -> Float -> Room
|
||||||
|
roomRect x y = roomRect' x y ((ceiling x - 40) `div` 60) ((ceiling y - 40) `div` 60)
|
||||||
|
|
||||||
|
linksAndPath :: [(Point2,Float)] -> [(Point2,Point2)] -> [(Point2,Point2)]
|
||||||
|
linksAndPath lnks subpth = subpth ++ concatMap linkClosest lnks
|
||||||
|
where linkClosest (p,_) = doublePair (p, head $ sortBy (compare `on` dist p) $ map fst subpth)
|
||||||
|
|
||||||
|
door :: Room
|
||||||
|
door = Room
|
||||||
|
{ _rmPolys = [rectNSWE 30 0 0 40]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = [((20,25),(20,5))]
|
||||||
|
, _rmPS = [PS (0,15) 0 $ PutAutoDoor (0,0) (40,0)]
|
||||||
|
, _rmBound = []
|
||||||
|
}
|
||||||
|
where lnks = [((20,25),0)
|
||||||
|
,((20, 5),pi)
|
||||||
|
]
|
||||||
|
roomPillars :: Room
|
||||||
|
roomPillars = over rmLinks init $ set rmPS plmnts $ roomRect' 240 240 2 2
|
||||||
|
where plmnts = g 180 150 90 60
|
||||||
|
f a x b y = putBlockRect a x b y
|
||||||
|
-- putWallBlocks (a,b) (a,y)
|
||||||
|
-- ++ putWallBlocks (a,y) (x,y)
|
||||||
|
-- ++ putWallBlocks (x,y) (x,b)
|
||||||
|
-- ++ putWallBlocks (x,b) (a,b)
|
||||||
|
g a b c d = f a b a b ++ f a b c d ++ f c d a b ++ f c d c d
|
||||||
|
|
||||||
|
|
||||||
|
putBlockRect a x b y = putWallBlocks (a,b) (a,y)
|
||||||
|
++ putWallBlocks (a,y) (x,y)
|
||||||
|
++ putWallBlocks (x,y) (x,b)
|
||||||
|
++ putWallBlocks (x,b) (a,b)
|
||||||
|
putBlockV a x b y = putWallBlocks (a,b) (a,y)
|
||||||
|
++ putWallBlocks (x,b) (a,b)
|
||||||
|
putBlockC a x b y = putWallBlocks (a,b) (a,y)
|
||||||
|
++ putWallBlocks (x,b) (a,b)
|
||||||
|
++ putWallBlocks (a,y) (x,y)
|
||||||
|
putBlockN a x b y = putWallBlocks (a,b) (a,y)
|
||||||
|
++ putWallBlocks (x,b) (a,b)
|
||||||
|
++ putWallBlocks (x,y) (x,b)
|
||||||
|
|
||||||
|
branchWith :: Room -> [Tree Room] -> Tree (Either Room Room)
|
||||||
|
branchWith r ts = Node (Left r) $ [return $ Right door] ++ fmap (fmap Left) ts
|
||||||
|
|
||||||
|
glassSwitchBack :: RandomGen g => State g Room
|
||||||
|
glassSwitchBack = do
|
||||||
|
wth <- state $ randomR (200,400)
|
||||||
|
hgt <- state $ randomR (400,600)
|
||||||
|
wllen <- state $ randomR (60,wth/2-40)
|
||||||
|
let hf = hgt/5
|
||||||
|
let plmnts = [PS (0,0) 0 $ PutWindowBlock (wth-60, hf) (wllen,hf)
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (wth-wllen,2*hf) (60, 2*hf)
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (wth-60, 3*hf) (wllen,3*hf)
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (wth-wllen,4*hf) (60, 4*hf)
|
||||||
|
]
|
||||||
|
++ putWallBlocks ( 0, 1*hf) (wllen,1*hf)
|
||||||
|
++ putWallBlocks (wth-wllen, 2*hf) ( wth,2*hf)
|
||||||
|
++ putWallBlocks ( 0, 3*hf) (wllen,3*hf)
|
||||||
|
++ putWallBlocks (wth-wllen, 4*hf) ( wth,4*hf)
|
||||||
|
return $ set rmPS plmnts $ roomRect' wth hgt 2 6
|
||||||
|
|
||||||
|
manyDoors :: Int -> Tree (Either Room Room)
|
||||||
|
manyDoors i = treePost (replicate i (Left door)) $ Right door
|
||||||
|
|
||||||
|
glassLesson :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
glassLesson = do
|
||||||
|
corridors <- sequence $ replicate 3 $ fmap Left $ randLinks corridor
|
||||||
|
return $ Node (Left $ botRoom) [deadRoom door,uppers, treePost (Left door : corridors) $ Right door]
|
||||||
|
where uppers = Node (Left door) [deadRoom topRoom]
|
||||||
|
botRoom = set rmPS botplmnts
|
||||||
|
$ roomRect' 200 200 1 1
|
||||||
|
topRoom = set rmPS topplmnts
|
||||||
|
$ roomRect' 200 200 1 1
|
||||||
|
botplmnts = [PS (0,0) 0 $ PutWindow (rectNSWE (200) 0 (90) (110))
|
||||||
|
$ withAlpha 0.5 aquamarine
|
||||||
|
,PS (50,100) 0 $ PutCrit miniGunCrit
|
||||||
|
]
|
||||||
|
topplmnts = [PS (0,0) 0 $ PutWindowBlock (100,200) (100,0)
|
||||||
|
,PS (50,100) 0 $ PutCrit miniGunCrit
|
||||||
|
]
|
||||||
|
|
||||||
|
miniRoom1 :: RandomGen g => State g Room
|
||||||
|
miniRoom1 = do
|
||||||
|
wth <- state $ randomR (200,400)
|
||||||
|
hgt <- state $ randomR (400,600)
|
||||||
|
wllen <- state $ randomR (60,wth/2-40)
|
||||||
|
let hf = hgt/5
|
||||||
|
cry <- randomRanges [--50+2*hf,30+3*hf
|
||||||
|
50+3*hf,30+4*hf
|
||||||
|
,50+4*hf,30+5*hf
|
||||||
|
]
|
||||||
|
crx <- state $ randomR (wllen,wth-(wllen+40))
|
||||||
|
let plmnts = [PS (0,0) 0 $ PutWindowBlock (wth-60, 40+hf) (wllen,40+hf)
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (wth-wllen,40+2*hf) (60,40+2*hf)
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (wth-60, 40+3*hf) (wllen,40+3*hf)
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (wth-wllen,40+4*hf) (60,40+4*hf)
|
||||||
|
,PS (crx,cry) 0 $ PutCrit miniGunCrit
|
||||||
|
,PS (wth-20,hgt/2+40) 0 $ randC
|
||||||
|
]
|
||||||
|
++ putWallBlocks ( 0, 40+1*hf) (wllen,40+1*hf)
|
||||||
|
++ putWallBlocks (wth-wllen, 40+2*hf) ( wth,40+2*hf)
|
||||||
|
++ putWallBlocks ( 0, 40+3*hf) (wllen,40+3*hf)
|
||||||
|
++ putWallBlocks (wth-wllen, 40+4*hf) ( wth,40+4*hf)
|
||||||
|
return $ set rmPS plmnts $ shiftRoomBy ((0,40),0) $ roomRect' wth hgt 2 4
|
||||||
|
|
||||||
|
miniTree2 :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
miniTree2 = miniRoom1 >>= randLinks >>= changeLinkTo (\p -> (snd . fst) p < 70)
|
||||||
|
>>= return . flip branchWith (replicate 3 $ treePost [door,corridor]
|
||||||
|
critInDeadEnd )
|
||||||
|
|
||||||
|
miniRoom3 :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
miniRoom3 = do
|
||||||
|
w <- state $ randomR (300,400)
|
||||||
|
h <- state $ randomR (300,400)
|
||||||
|
let l = -70
|
||||||
|
let r = 70
|
||||||
|
let cp = (0,h/2+40)
|
||||||
|
let f = rot90Around cp
|
||||||
|
let f2 = f . f
|
||||||
|
let f3 = f . f .f
|
||||||
|
let cpa p = p +.+ cp
|
||||||
|
let bl = putWallBlocks (cpa (-20,-40)) (cpa (-20,-80))
|
||||||
|
let br = putWallBlocks (cpa (20,-40)) (cpa (20,-80))
|
||||||
|
let bm = putWallBlocks (cpa (25,-35)) (cpa (35,-25))
|
||||||
|
let b = PutBlock [5,20,20] (greyN 0.5) [(-10,-60)
|
||||||
|
,( 10,-60)
|
||||||
|
,( 10,-80)
|
||||||
|
,(-10,-80)
|
||||||
|
]
|
||||||
|
let plmnts = [PS cp 0 $ PutCrit miniGunCrit
|
||||||
|
,PS cp 0 $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (1*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (2*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (3*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (4*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (5*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (6*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (7*pi/4) $ PutWindowBlock (0,-40) (0,-80)
|
||||||
|
,PS cp (pi/8) b
|
||||||
|
,PS cp (pi/8+1*pi/4) b
|
||||||
|
,PS cp (pi/8+2*pi/4) b
|
||||||
|
,PS cp (pi/8+3*pi/4) b
|
||||||
|
,PS cp (pi/8+4*pi/4) b
|
||||||
|
,PS cp (pi/8+5*pi/4) b
|
||||||
|
,PS cp (pi/8+6*pi/4) b
|
||||||
|
,PS cp (pi/8+7*pi/4) b
|
||||||
|
]
|
||||||
|
randomiseLinks $ set rmPS plmnts $ roomRect w h
|
||||||
|
|
||||||
|
rot90Around :: Point2 -> Point2 -> Point2
|
||||||
|
rot90Around cen p = cen +.+ vNormal (p -.- cen)
|
||||||
|
|
||||||
|
-- So, the idea is to attach outer children to the bottommost right nodes
|
||||||
|
-- inside an inner tree
|
||||||
|
roomMiniIntro :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
roomMiniIntro = fmap (g . expandTreeBy f) $ sequence $ treePost
|
||||||
|
[return $ connectRoom door
|
||||||
|
,join $ takeOne [miniTree2,glassLesson]
|
||||||
|
-- ,join $ takeOne [miniRoom1]
|
||||||
|
]
|
||||||
|
$ randomiseLinks corridor
|
||||||
|
where f (Node (Right x) xs) = Node (Right (Right x)) $ map f xs
|
||||||
|
f (Node (Left x) xs) = Node (Left (Left x)) $ map f xs
|
||||||
|
g (Node (Right x) []) = Node (Right x) []
|
||||||
|
g (Node (Right x) xs) = Node (Left x) $ map g xs
|
||||||
|
g (Node y ys) = Node y $ map g ys
|
||||||
|
|
||||||
|
|
||||||
|
putWallBlocks :: Point2 -> Point2 -> [PlacementSpot]
|
||||||
|
putWallBlocks a b = map (\p -> PS p rot $ PutBlock [5,20,20] (greyN 0.5)
|
||||||
|
$ reverse $ rectNSWE 10 (-10) (-10) 10)
|
||||||
|
ps
|
||||||
|
where d = dist a b
|
||||||
|
rot = argV (b -.- a)
|
||||||
|
numPoints' = floor (d / 20)
|
||||||
|
numPoints = numPoints'*2 + 1
|
||||||
|
ns = take (numPoints + 1) [0..]
|
||||||
|
ps = map (\i -> a +.+ i/ (fromIntegral numPoints) *.* (b -.- a))
|
||||||
|
$ map fromIntegral ns
|
||||||
|
|
||||||
|
roomCenterPillar :: RandomGen g => State g Room
|
||||||
|
roomCenterPillar = changeLinkTo ((\p -> dist p (120,0) < 10) . fst)
|
||||||
|
$ set rmPS plmnts $ roomRect' 240 240 2 2
|
||||||
|
where plmnts = putWallBlocks (110,110) (110,130)
|
||||||
|
++ putWallBlocks (130,110) (130,130)
|
||||||
|
|
||||||
|
roomOctogon :: Room
|
||||||
|
roomOctogon = Room
|
||||||
|
{ _rmPolys = [[(-20,40),(20,40),(50,70),(50,110),(20,140),(-20,140),(-50,110),(-50,70)]
|
||||||
|
]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = allPairs $ map fst lnks
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = [(-20,30),(20,30),(60,70),(60,110),(20,150),(-20,150),(-60,110),(-60,70)]
|
||||||
|
}
|
||||||
|
where lnks = [((0,140),0)
|
||||||
|
,((35,125),0-pi/4)
|
||||||
|
,((-35,125),pi/4)
|
||||||
|
,( (50,90),0-pi/2)
|
||||||
|
,( (-50,90),pi/2)
|
||||||
|
,((35,55),0-3*pi/4)
|
||||||
|
,((-35,55),3*pi/4)
|
||||||
|
,( (0,40),pi)
|
||||||
|
]
|
||||||
|
|
||||||
|
randomCorridorFrom :: RandomGen g => [a] -> State g (Tree (Either a a))
|
||||||
|
randomCorridorFrom xs = do
|
||||||
|
rooms <- sequence $ replicate 5 $ takeOne xs
|
||||||
|
return $ treeTrunk (map Left $ init rooms) (Node (Right (last rooms)) [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--splitWall :: Float -> Wall -> [[Point]]
|
||||||
|
--splitWall x wl =
|
||||||
|
|
||||||
|
sWalls g = wallsFromTree $ evalState (iterateWhile boundClip lev1) $ g
|
||||||
|
|
||||||
|
|
||||||
|
randFirstWeapon :: State StdGen PSType
|
||||||
|
randFirstWeapon = do
|
||||||
|
r <- state $ randomR (-pi,pi)
|
||||||
|
takeOne $ map PutFlIt
|
||||||
|
[ basicFlIt {_flItRot=r,_flIt = pistol}
|
||||||
|
, basicFlIt {_flItRot=r,_flIt = ltAutoGun}
|
||||||
|
-- , basicFlIt {_flItRot=r,_flIt = autoGun}
|
||||||
|
, basicFlIt {_flItRot=r,_flIt = spreadGun}
|
||||||
|
, basicFlIt {_flItRot=r,_flIt = multGun}
|
||||||
|
, basicFlIt {_flItRot=r,_flIt = launcher}
|
||||||
|
-- , basicFlIt {_flItRot=r,_flIt = lasGun}
|
||||||
|
-- , basicFlIt {_flItRot=r,_flIt = flamer}
|
||||||
|
]
|
||||||
|
|
||||||
|
--randC1 :: State StdGen PSType
|
||||||
|
randC1 = RandPS $ takeOne $ map PutCrit $ (armourChaseCrit : replicate 50 chaseCrit)
|
||||||
|
|
||||||
|
randC = randC1
|
||||||
|
|
||||||
|
-- randSwarmCrit = RandPS $ takeOne $ map PutCrit $ (armouredSwarmCrit : replicate 100 swarmCrit)
|
||||||
|
|
||||||
|
weaponEmptyRoom :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
weaponEmptyRoom = do
|
||||||
|
w <- state $ randomR (220,300)
|
||||||
|
h <- state $ randomR (220,300)
|
||||||
|
let plmnts = [PS (w/2,h-40) 0 $ RandPS randFirstWeapon
|
||||||
|
,PS (20,20) (pi/2) $ randC1
|
||||||
|
,PS (w-20,20) (pi/2) $ randC1
|
||||||
|
]
|
||||||
|
randomiseLinks =<< (changeLinkTo ((\p -> dist p (w/2,0) < 10) . fst)
|
||||||
|
$ set rmPS plmnts $ roomRect' w h 2 2)
|
||||||
|
|
||||||
|
|
||||||
|
--weaponUnderCrits :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
--weaponUnderCrits = do
|
||||||
|
-- let plmnts = [PS (0,0) 0 $ RandPS randFirstWeapon
|
||||||
|
-- ,PS (-5,0) (0-pi/2) $ randC1
|
||||||
|
-- ,PS (5,20) (0-pi/2) $ randC1
|
||||||
|
-- ]
|
||||||
|
-- let continuationRoom = treeTrunk [Left corridorN,Left corridorN]
|
||||||
|
-- (connectRoom (set rmPS plmnts $ corridorN))
|
||||||
|
-- deadEndRoom <- takeOne [roomSquare,roomPillars,roomVs,roomCenterPillar]
|
||||||
|
-- junctionRoom <- takeOne [Left corridorE,Left corridorW]
|
||||||
|
-- return $ treeTrunk [Left corridorN,Left corridorN]
|
||||||
|
-- $ Node junctionRoom
|
||||||
|
-- [continuationRoom
|
||||||
|
-- ,deadRoom deadEndRoom
|
||||||
|
-- ]
|
||||||
|
|
||||||
|
|
||||||
|
weaponBehindPillar :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
weaponBehindPillar = do
|
||||||
|
crPos <- takeOne $ [(x,y) | x <- [20,220], y <- [20,220]] ++ [(120,160),(120,200)]
|
||||||
|
let d p = argV $ (120,80) -.- p
|
||||||
|
let plmnts1 = [PS (120,160) 0 $ RandPS randFirstWeapon
|
||||||
|
,PS crPos (d crPos) $ randC1
|
||||||
|
]
|
||||||
|
rcp <- roomCenterPillar
|
||||||
|
return $ treeTrunk [Left door
|
||||||
|
,Left $ over rmLinks tail $ over rmPS (++ plmnts1) rcp]
|
||||||
|
(connectRoom $ set rmPS [PS (20,60) (0-pi/2) $ randC1]
|
||||||
|
$ corridorN)
|
||||||
|
|
||||||
|
weaponBetweenPillars :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
weaponBetweenPillars = do
|
||||||
|
wpPos <- takeOne [(x,y) | x <- [20,120,220], y <- [20,120,220]]
|
||||||
|
(ps,_) <- takeNMore 2 ([], [(x,y) | x <- [20,220], y <- [20,120,220]])
|
||||||
|
let crPos1 = ps !! 0
|
||||||
|
let crPos2 = ps !! 1
|
||||||
|
let d p = argV $ (120,120) -.- p
|
||||||
|
let plmnts = [PS wpPos 0 $ RandPS randFirstWeapon
|
||||||
|
,PS crPos1 (d crPos1) $ randC1
|
||||||
|
,PS crPos2 (d crPos2) $ randC1
|
||||||
|
]
|
||||||
|
randomiseLinks =<< (filterLinks f $ over rmPS (++plmnts) $ roomPillars)
|
||||||
|
where f (_,a) = a == 0
|
||||||
|
|
||||||
|
blockedCorridor :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
blockedCorridor = do
|
||||||
|
r <- state $ randomR (0,pi)
|
||||||
|
n <- state $ randomR (0,3)
|
||||||
|
let plmnts = [PS (20,40) r $ PutBlock [5,5,5] (150/256, 75/256, 0, 250/256)
|
||||||
|
$ reverse $ rectNSWE 10 (-10) (-10) 10
|
||||||
|
]
|
||||||
|
sequence $ treePost (replicate n $ fmap Left $ randLinks corridor)
|
||||||
|
$ fmap Right $ return $ set rmPS plmnts corridor
|
||||||
|
|
||||||
|
-- weaponBehindV :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
-- weaponBehindV = do
|
||||||
|
-- wpPos <- takeOne [(x,y) | x <- [70,-70], y <- [90,230]]
|
||||||
|
-- (ps,_) <- takeNMore 2 ([], [(x,y) | x <- [60,-60], y <- [100,220]])
|
||||||
|
-- r <- state $ randomR (0,pi)
|
||||||
|
-- let crPos1 = ps !! 0
|
||||||
|
-- let crPos2 = ps !! 1
|
||||||
|
-- let d p = argV $ (0,160) -.- p
|
||||||
|
-- let plmnts = [PS wpPos 0 $ RandPS randFirstWeapon
|
||||||
|
-- ,PS crPos1 (d crPos1) $ randC1
|
||||||
|
-- ,PS crPos2 (d crPos2) $ randC1
|
||||||
|
-- ,PS (0,295) r $ PutBlock [5,5,5] (makeColorI 150 75 0 250)
|
||||||
|
-- $ reverse $ rectNSWE 10 (-10) (-10) 10
|
||||||
|
-- ]
|
||||||
|
-- return $ connectRoom $ set rmPS plmnts $ roomVs
|
||||||
|
|
||||||
|
weaponLongCorridor :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
weaponLongCorridor = do
|
||||||
|
root <- takeOne $ [tEast, tWest]
|
||||||
|
connectingRoom <- takeOne [tEast,tWest]
|
||||||
|
i1 <- state $ randomR (2,5)
|
||||||
|
i2 <- state $ randomR (2,5)
|
||||||
|
let branch1 = treeTrunk (replicate i1 $ Left corridor) (connectRoom $ putCrs connectingRoom)
|
||||||
|
let branch2 = treeTrunk (replicate i2 $ Left corridor) (deadRoom $ putWp corridorN)
|
||||||
|
return $ Node (Left root) [branch1,branch2]
|
||||||
|
where putCrs = set rmPS [PS (10,40) (-pi/2) $ randC
|
||||||
|
,PS (-10,40) (-pi/2) $ randC
|
||||||
|
]
|
||||||
|
putWp = set rmPS [PS (20,40) 0 $ RandPS randFirstWeapon]
|
||||||
|
|
||||||
|
critInDeadEnd :: Room
|
||||||
|
critInDeadEnd = set rmPS [PS (0,0) 0 $ randC] deadEndRoom
|
||||||
|
|
||||||
|
deadEndRoom :: Room
|
||||||
|
deadEndRoom = Room
|
||||||
|
{ _rmPolys = [rectNSWE 40 (-20) (-20) 20
|
||||||
|
]
|
||||||
|
, _rmLinks = lnks
|
||||||
|
, _rmPath = []
|
||||||
|
, _rmPS = []
|
||||||
|
, _rmBound = rectNSWE 20 (-20) (-30) 30
|
||||||
|
}
|
||||||
|
where lnks = [((0,30) ,0)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
weaponRoom :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
weaponRoom = do
|
||||||
|
x <- takeOne [weaponEmptyRoom
|
||||||
|
--, weaponUnderCrits
|
||||||
|
, weaponBehindPillar
|
||||||
|
, weaponBetweenPillars
|
||||||
|
, weaponLongCorridor
|
||||||
|
]
|
||||||
|
x
|
||||||
|
|
||||||
|
roomCCrits :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
roomCCrits = do
|
||||||
|
ps <- sequence $ replicate 25 $ randInCirc 9
|
||||||
|
let plmnts = map (\p -> PS p 0 $ randC)
|
||||||
|
$ zipWith (+.+) [(x,y) | x<-[110,130,150,170,190], y<- [70,90,110,130,150]] ps
|
||||||
|
return $ connectRoom $ over rmPS (++plmnts) $ roomC 200 200
|
||||||
|
|
||||||
|
--
|
||||||
|
-- roomSwarm :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
-- roomSwarm = do
|
||||||
|
-- ps <- sequence $ replicate 25 $ randInCirc 200
|
||||||
|
-- let plmnts = map (\p -> PS ((500,500) +.+ p) 0 $ PutCrit swarmCrit) ps
|
||||||
|
-- return $ connectRoom $ set rmPS plmnts $ roomRect 1000 1000
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
roomToLevel2 :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
roomToLevel2 = join $ takeOne
|
||||||
|
[ portalRoom1
|
||||||
|
]
|
||||||
|
|
||||||
|
portalRoom1 :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
portalRoom1 = return $ connectRoom $ (roomRect 300 300) { _rmPS = plmnts}
|
||||||
|
where plmnts = [PS (0,0) (0-pi/2) $ PutPressPlate (levelPortalAt (200,120) 1)
|
||||||
|
,PS (60,100) pi $ PutCrit autoCrit
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
branchRectWith :: RandomGen g => State g (Tree (Either Room Room)) -> State g (Tree (Either Room Room))
|
||||||
|
branchRectWith t = do
|
||||||
|
x <- state $ randomR (100,200)
|
||||||
|
y <- state $ randomR (100,200)
|
||||||
|
b <- t
|
||||||
|
root <- randLinks $ roomRect x y
|
||||||
|
return $ Node (Left root) [Node (Right door) [], fmap rToL $ treeTrunk [Left door] b]
|
||||||
|
where rToL :: Either a a -> Either a a
|
||||||
|
rToL (Right r) = Left r
|
||||||
|
rToL (Left r) = Left r
|
||||||
|
|
||||||
|
slowDoorRoom :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
slowDoorRoom = do
|
||||||
|
x <- state $ randomR (400,800)
|
||||||
|
y <- state $ randomR (400,800)
|
||||||
|
h <- state $ randomR (200,min (y-100) 500)
|
||||||
|
(butPos,butRot) <- takeOne [( (x/2-50,5),0)
|
||||||
|
,( (x/2+50,5),0)
|
||||||
|
]
|
||||||
|
let n = 25
|
||||||
|
xs <- sequence $ replicate n $ state $ randomR (10,x-10)
|
||||||
|
ys <- sequence $ replicate n $ state $ randomR (h+20,y)
|
||||||
|
rs <- sequence $ replicate n $ state $ randomR (0,2*pi)
|
||||||
|
let ps = zip xs ys
|
||||||
|
xs' <- sequence $ replicate 5 $ state $ randomR (10,x-10)
|
||||||
|
ys' <- sequence $ replicate 5 $ state $ randomR (h+20,y)
|
||||||
|
let crits = zipWith (\p r -> PS p r randC1) ps rs
|
||||||
|
let barrels = zipWith (\x y -> PS (x,y) 0 $ PutCrit explosiveBarrel) xs' ys'
|
||||||
|
let pillarsa = []
|
||||||
|
let pillarsb = putBlockRect (x/5-20) (x/5+20) (h/2-20) (h/2+20)
|
||||||
|
++ putBlockRect (2*x/5-20) (2*x/5+20) (h/2-20) (h/2+20)
|
||||||
|
++ putBlockRect (3*x/5-20) (3*x/5+20) (h/2-20) (h/2+20)
|
||||||
|
++ putBlockRect (4*x/5-20) (4*x/5+20) (h/2-20) (h/2+20)
|
||||||
|
let pillarsc = putBlockRect (x/3-20) (x/3+20) (h/2-20) (h/2+20)
|
||||||
|
++ putBlockRect (2*x/3-20) (2*x/3+20) (h/2-20) (h/2+20)
|
||||||
|
pillars <- takeOne [pillarsa, pillarsb, pillarsc]
|
||||||
|
let cond x = (snd . fst) x > h + 40
|
||||||
|
let cond2 x = (snd . fst) x < h - 40
|
||||||
|
but <- takeOne [PutBtDoor (dim $ light red) butPos butRot (0,h) (x,h)
|
||||||
|
-- ,PutSwitchDoor (dim $ light red) butPos butRot (0,h) (x,h)
|
||||||
|
]
|
||||||
|
fmap connectRoom (filterLinks cond =<< (changeLinkTo cond2
|
||||||
|
$ set rmPS ([PS (0,0) 0 but] ++ crits ++ pillars ++ barrels)
|
||||||
|
$ roomRect x y
|
||||||
|
))
|
||||||
|
longRoom :: RandomGen g => State g Room
|
||||||
|
longRoom = do
|
||||||
|
h <- state $ randomR (1500,1500)
|
||||||
|
let w = 75
|
||||||
|
let cond x = (snd . fst) x < h - 40
|
||||||
|
-- let solidBlocks = putWallBlocks (12.5,h-25) (12.5,h-265)
|
||||||
|
-- ++putWallBlocks (37.5,h-25) (37.5,h-265)
|
||||||
|
-- ++putWallBlocks (62.5,h-25) (62.5,h-265)
|
||||||
|
-- ++putWallBlocks (87.5,h-25) (87.5,h-265)
|
||||||
|
-- let wBlocks = [PS (0,0) 0 $ PutWindowBlock (12.5,h-50) (12.5,h-265)
|
||||||
|
-- ,PS (0,0) 0 $ PutWindowBlock (37.5,h-50) (37.5,h-265)
|
||||||
|
-- ,PS (0,0) 0 $ PutWindowBlock (62.5,h-50) (62.5,h-265)
|
||||||
|
-- ,PS (0,0) 0 $ PutWindowBlock (87.5,h-50) (87.5,h-265)
|
||||||
|
-- ]
|
||||||
|
let ws = map (\ps -> PS (0,0) 0 $ PutWindow ps $ withAlpha 0.5 aquamarine)
|
||||||
|
[rectNSWE (h-35) (h-135) (-10) 10
|
||||||
|
,rectNSWE (h-35) (h-135) 15 35
|
||||||
|
,rectNSWE (h-35) (h-135) 40 60
|
||||||
|
,rectNSWE (h-35) (h-135) 65 85
|
||||||
|
]
|
||||||
|
let wsDefense = map (\ps -> PS (0,0) 0 $ PutWindow ps $ withAlpha 0.5 aquamarine)
|
||||||
|
[rectNSWE (95) (70) 0 25
|
||||||
|
,rectNSWE (95) (70) 50 75
|
||||||
|
]
|
||||||
|
brls <- fmap (map (\p -> PS (p +.+ (10,200)) 0 $ PutCrit explosiveBarrel) )
|
||||||
|
$ sequence $ replicate 5 $ randInRect (w-20) 900
|
||||||
|
let rm = roomRect' w (h+70) 1 1 & rmPolys %~ ( (++)
|
||||||
|
[rectNSWE h (h-165) (-45) (w+45)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
changeLinkTo cond $ set rmPS (ws ++ brls ++ wsDefense ++
|
||||||
|
[PS ( 12.5,h-25) 0 $ PutCrit longCrit
|
||||||
|
,PS ( 37.5,h-25) 0 $ PutCrit longCrit
|
||||||
|
,PS ( 62.5,h-25) 0 $ PutCrit longCrit
|
||||||
|
]
|
||||||
|
)
|
||||||
|
$ rm
|
||||||
|
|
||||||
|
|
||||||
|
shooterRoom :: RandomGen g => State g Room
|
||||||
|
shooterRoom = do
|
||||||
|
h <- state $ randomR (200,300)
|
||||||
|
let cond x = (snd . fst) x < h - 40
|
||||||
|
changeLinkTo cond $ set rmPS ( putWallBlocks (50,50) (50,h) ++
|
||||||
|
[PS ( 25,h-25) 0 $ PutCrit $ addArmour autoCrit
|
||||||
|
,PS ( 75,h-30) 0 $ PutCrit explosiveBarrel
|
||||||
|
,PS ( 75,h-60) 0 $ PutCrit explosiveBarrel
|
||||||
|
,PS ( 85,h-10) 0 $ PutCrit explosiveBarrel
|
||||||
|
,PS ( 85,h-45) 0 $ PutCrit explosiveBarrel
|
||||||
|
]
|
||||||
|
)
|
||||||
|
$ roomRect' 100 h 1 1
|
||||||
|
|
||||||
|
|
||||||
|
shootersRoom1 :: RandomGen g => State g Room
|
||||||
|
shootersRoom1 = do
|
||||||
|
w <- state $ randomR (200,300)
|
||||||
|
x1 <- state $ randomR (20,w/3-20)
|
||||||
|
x2 <- state $ randomR (w/3+20,2*w/3-20)
|
||||||
|
x3 <- state $ randomR (2*w/3+20,w-20)
|
||||||
|
y1 <- state $ randomR (250,560)
|
||||||
|
y2 <- state $ randomR (250,560)
|
||||||
|
y3 <- iterateWhile (\y' -> abs (y1 - y2) < 60 && abs (y2 - y') < 60) $ state $ randomR (250,560)
|
||||||
|
x4 <- state $ randomR (60,w-60)
|
||||||
|
y4 <- state $ randomR (40,180)
|
||||||
|
p <- takeOne [(x1,y1-10),(x2,y2-10),(x3,y3-10)]
|
||||||
|
let bln x y = putBlockN (x+25) (x-25) (y+10) (y)
|
||||||
|
let blv x y = putBlockV (x+25) (x-25) (y+10) (y)
|
||||||
|
let plmnts = bln x1 y1 ++ bln x2 y2 ++ bln x3 y3 ++ blv x4 y4
|
||||||
|
++ [PS p (-pi/2) $ PutCrit autoCrit
|
||||||
|
]
|
||||||
|
return $ set rmPS plmnts $ roomRect w 600
|
||||||
|
|
||||||
|
shootersRoom :: RandomGen g => State g Room
|
||||||
|
shootersRoom = do
|
||||||
|
w <- state $ randomR (200,300)
|
||||||
|
x1 <- state $ randomR (20,w/3-20)
|
||||||
|
x2 <- state $ randomR (w/3+20,2*w/3-20)
|
||||||
|
x3 <- state $ randomR (2*w/3+20,w-20)
|
||||||
|
y1 <- state $ randomR (250,560)
|
||||||
|
y2 <- state $ randomR (250,560)
|
||||||
|
y3 <- iterateWhile (\y' -> abs (y1 - y2) < 60 && abs (y2 - y') < 60) $ state $ randomR (250,560)
|
||||||
|
x4 <- state $ randomR (60,w-60)
|
||||||
|
y4 <- state $ randomR (40,180)
|
||||||
|
let bln x y = putBlockN (x+25) (x-25) (y+10) (y)
|
||||||
|
let blv x y = putBlockV (x+25) (x-25) (y+10) (y)
|
||||||
|
let plmnts = bln x1 y1 ++ bln x2 y2 ++ bln x3 y3 ++ blv x4 y4
|
||||||
|
++ [PS (x1,y1-10) (-pi/2) $ PutCrit autoCrit
|
||||||
|
,PS (x2,y2-10) (-pi/2) $ PutCrit autoCrit
|
||||||
|
,PS (x3,y3-10) (-pi/2) $ PutCrit autoCrit
|
||||||
|
]
|
||||||
|
return $ set rmPS plmnts $ roomRect w 600
|
||||||
|
|
||||||
|
pistolerRoom :: RandomGen g => State g Room
|
||||||
|
pistolerRoom = do
|
||||||
|
let f2 x y = putWallBlocks (x,y) (x,y)
|
||||||
|
-- let f2 x y = [PS (x,y) 0 $ PutWindow (rectNSEW 40 0 0 40) $ withAlpha 0.5 aquamarine]
|
||||||
|
f3 x y = putWallBlocks (x-20,y) (x+20,y)
|
||||||
|
++ putWallBlocks (x,y-20) (x,y+20)
|
||||||
|
f4 x y = putWallBlocks (x-20,y-20) (x+20,y+20)
|
||||||
|
++ putWallBlocks (x+20,y-20) (x-20,y+20)
|
||||||
|
--f <- takeOne [f2,f3,f4]
|
||||||
|
f <- takeOne [f2]
|
||||||
|
h <- state $ randomR (400,800)
|
||||||
|
let w = h
|
||||||
|
i <- takeOne [2,4,6,8]
|
||||||
|
let j = fromIntegral (i+1)
|
||||||
|
-- the 20+etc here is to correct for the pathfinding grid, which matches up to
|
||||||
|
-- edges based on the size of doors (i.e. the line next to the wall is 20
|
||||||
|
-- away from it)
|
||||||
|
xs = take i [20+0.5*(w-40)/(j-1),20+1.5*(w-40)/(j-1)..]
|
||||||
|
ys = take i [20+0.5*(h-40)/(j-1),20+1.5*(h-40)/(j-1)..]
|
||||||
|
gap = 0.5 * h / j
|
||||||
|
ps <- takeN 3 $ zip (map (+gap) xs) ys
|
||||||
|
aa <- state $ randomR (0,2*pi)
|
||||||
|
ab <- state $ randomR (0,2*pi)
|
||||||
|
ac <- state $ randomR (0,2*pi)
|
||||||
|
let plmnts = [PS (ps !! 0) aa $ PutCrit pistolCrit
|
||||||
|
,PS (ps !! 1) ab $ PutCrit pistolCrit
|
||||||
|
,PS (ps !! 2) ac $ PutCrit pistolCrit
|
||||||
|
-- ,PS (w-5,h/2) 0 $ basicLS
|
||||||
|
-- ,PS (5,h/2) 0 $ basicLS
|
||||||
|
-- ,PS (w/2,h-5) 0 $ basicLS
|
||||||
|
-- ,PS (w/2,5) 0 $ basicLS
|
||||||
|
-- ,PS (w-50,h/2) 0 $ basicLS
|
||||||
|
-- ,PS (50,h/2) 0 $ basicLS
|
||||||
|
,PS (w/2,h-50) 0 $ basicLS
|
||||||
|
,PS (w/2,50) 0 $ basicLS
|
||||||
|
,PS (w-5,h-5) 0 $ basicLS
|
||||||
|
,PS (5,h-5) 0 $ basicLS
|
||||||
|
,PS (w-5,5) 0 $ basicLS
|
||||||
|
,PS (5,5) 0 $ basicLS
|
||||||
|
,PS (w/2,h/2) 0 $ basicLS
|
||||||
|
]
|
||||||
|
++
|
||||||
|
-- f (w*0.25) (h*0.5)
|
||||||
|
concat [f x y | x<-xs,y<-ys]
|
||||||
|
return
|
||||||
|
$ set rmPS plmnts $ roomRect' w h (max i 2) (max i 2)
|
||||||
|
|
||||||
|
shootingRange :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
shootingRange = do
|
||||||
|
rm1 <- shootersRoom1 >>= changeLinkTo (\((_,y),_) -> y < 40)
|
||||||
|
>>= filterLinks (\((_,y),r) -> y > 200 && r /= 0)
|
||||||
|
rm2 <- shootersRoom >>= changeLinkTo (\((x,y),_) -> y < 10 && x > 20 && x < 180)
|
||||||
|
>>= filterLinks (\((_,y),r) -> y > 200 && r /= 0)
|
||||||
|
rm3 <- shootersRoom >>= changeLinkTo (\((x,y),_) -> y < 10 && x > 20 && x < 180)
|
||||||
|
>>= filterLinks (\(_,r) -> r == 0)
|
||||||
|
return $ treePost [Left rm1
|
||||||
|
,Left $ roomPadCut (rectNSWE 20 (-20) (-80) 80) (0,20)
|
||||||
|
,Left rm2
|
||||||
|
,Left $ roomPadCut (rectNSWE 20 (-20) (-80) 80) (0,20)
|
||||||
|
]
|
||||||
|
(Right rm3)
|
||||||
|
|
||||||
|
|
||||||
|
spawnerRoom :: RandomGen g => State g (Tree (Either Room Room))
|
||||||
|
spawnerRoom = do
|
||||||
|
x <- state $ randomR (250,300)
|
||||||
|
y <- state $ randomR (300,400)
|
||||||
|
wl <- takeOne [PS (0,0) 0 $ PutWindow (rectNSWE (y-60) 0 (x/2-10) (x/2+10))
|
||||||
|
$ withAlpha 0.5 aquamarine
|
||||||
|
,PS (0,0) 0 $ PutWindowBlock (x/2,0) (x/2,y-60)
|
||||||
|
]
|
||||||
|
let plmnts = [PS (x/4, y/4) (pi/2) $ PutCrit spawnerCrit
|
||||||
|
-- ,PS (0,0) 0 $ PutWindowBlock (x/2,0) (x/2,y-60)
|
||||||
|
,wl
|
||||||
|
]
|
||||||
|
let f ((lx,_),_) = lx < x/2-5
|
||||||
|
roomWithSpawner <- randomiseLinks =<< (filterLinks f $ set rmPS plmnts $ roomRect' x y 2 2)
|
||||||
|
return $ treeTrunk [Left (airlock 0)] roomWithSpawner
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
module Dodge.Smoke where
|
||||||
|
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.RandomHelp
|
||||||
|
import Geometry.Data
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad.State
|
||||||
|
|
||||||
|
spawnSmokeAt :: Point2 -> World -> World
|
||||||
|
spawnSmokeAt p w =
|
||||||
|
w & smoke %~ (:) thesmoke
|
||||||
|
& randGen .~ g
|
||||||
|
where
|
||||||
|
thesmoke = Smoke
|
||||||
|
{_smPos = p
|
||||||
|
,_smRad = 20
|
||||||
|
,_smColor = white
|
||||||
|
,_smUpdate = f
|
||||||
|
,_smPs = ps
|
||||||
|
,_smTime = 500
|
||||||
|
}
|
||||||
|
f s = case s ^. smPs of
|
||||||
|
[] -> Nothing
|
||||||
|
_ -> case s ^. smTime of
|
||||||
|
0 -> Just $ s & smPs %~ init
|
||||||
|
t -> Just $ s & smTime .~ t-1
|
||||||
|
(ps,g) = runState (sequence $ replicate 60 $ randInCirc 20) $ _randGen w
|
||||||
|
|
||||||
|
spawnSmokeAtCursor :: World -> World
|
||||||
|
spawnSmokeAtCursor w = spawnSmokeAt (mouseWorldPos w) w
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
module Dodge.SoundLogic where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import qualified Data.Map as M
|
||||||
|
-- }}}
|
||||||
|
--
|
||||||
|
-- PLACEHOLDERS:
|
||||||
|
|
||||||
|
haltSound :: World -> World
|
||||||
|
haltSound w = w
|
||||||
|
|
||||||
|
pauseSound :: World -> World
|
||||||
|
pauseSound w = w
|
||||||
|
|
||||||
|
resumeSound :: World -> World
|
||||||
|
resumeSound w = w
|
||||||
|
|
||||||
|
soundOnce :: Int -> World -> World
|
||||||
|
soundOnce i = over soundQueue ((:) i)
|
||||||
|
|
||||||
|
continueSoundFrom :: SoundOrigin -> Int -> Int -> Int -> World -> World
|
||||||
|
continueSoundFrom so sType time fadeTime w
|
||||||
|
= over sounds (M.insertWith f so sound) w
|
||||||
|
where sound = Sound { _soundType = sType
|
||||||
|
, _soundTime = time
|
||||||
|
, _soundFadeTime = fadeTime
|
||||||
|
, _soundChannel = Nothing
|
||||||
|
, _soundPos = Nothing
|
||||||
|
}
|
||||||
|
f _ s = s {_soundTime = time, _soundFadeTime = fadeTime}
|
||||||
|
|
||||||
|
soundFrom :: SoundOrigin -> Int -> Int -> Int -> World -> World
|
||||||
|
soundFrom so sType time fadeTime w
|
||||||
|
= over sounds (M.insertWith f so sound) w
|
||||||
|
where sound = Sound { _soundType = sType
|
||||||
|
, _soundTime = time
|
||||||
|
, _soundFadeTime = fadeTime
|
||||||
|
, _soundChannel = Nothing
|
||||||
|
, _soundPos = Nothing
|
||||||
|
}
|
||||||
|
f _ s = s {_soundTime = time, _soundFadeTime = fadeTime}
|
||||||
|
|
||||||
|
soundMultiFrom :: [SoundOrigin] -> Int -> Int -> Int -> World -> World
|
||||||
|
soundMultiFrom [] _ _ _ w = w
|
||||||
|
soundMultiFrom (so:sos) sType time fadeTime w
|
||||||
|
| so `M.member` _sounds w = soundMultiFrom sos sType time fadeTime w
|
||||||
|
| otherwise = over sounds (M.insert so sound) w
|
||||||
|
where sound = Sound { _soundType = sType
|
||||||
|
, _soundTime = time
|
||||||
|
, _soundFadeTime = fadeTime
|
||||||
|
, _soundChannel = Nothing
|
||||||
|
, _soundPos = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
stopSoundFrom :: SoundOrigin -> World -> World
|
||||||
|
stopSoundFrom so = over (sounds . ix so . soundTime) (min 0)
|
||||||
|
|
||||||
|
|
||||||
|
-- idea: pass the id of the sound to the world and send it to some queue for
|
||||||
|
-- playback
|
||||||
|
|
||||||
|
reloadSound,putDownSound,pickUpSound,fireSound,grenadeBang,healSound,teleSound,twoStepSlowSound :: Int
|
||||||
|
clickSound = 1
|
||||||
|
reloadSound = 2
|
||||||
|
pickUpSound = 4
|
||||||
|
putDownSound = 5
|
||||||
|
fireSound = 6
|
||||||
|
grenadeBang = 7
|
||||||
|
tapQuiet = 8
|
||||||
|
twoStepSound = 9
|
||||||
|
healSound = 10
|
||||||
|
doorSound = 11
|
||||||
|
twoStepSlowSound = 12
|
||||||
|
knifeSound = 13
|
||||||
|
buzzSound = 14
|
||||||
|
hitSound = 15
|
||||||
|
autoGunSound = 16
|
||||||
|
shotgunSound = 17
|
||||||
|
teleSound = 18
|
||||||
|
longGunSound = 19
|
||||||
|
launcherSound = 20
|
||||||
|
smokeTrailSound = 21
|
||||||
|
foot1Sound = 22
|
||||||
|
foot2Sound = 23
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
module Dodge.Update where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.WallCreatureCollisions
|
||||||
|
import Dodge.Block
|
||||||
|
import Dodge.Camera
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import qualified Data.Set as S
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
update :: World -> World
|
||||||
|
update w = let w1 = updateParticles' $ updateParticles
|
||||||
|
$ updateLightSources
|
||||||
|
$ zoneClouds
|
||||||
|
$ updateClouds
|
||||||
|
$ updateSmoke
|
||||||
|
$ updateCreatures
|
||||||
|
$ updateBlocks -- $ zoning
|
||||||
|
$ updateSeenWalls
|
||||||
|
w
|
||||||
|
w2 = -- updateWeaponCounters $
|
||||||
|
simpleCrSprings
|
||||||
|
$ zoneCreatures $ wallEvents
|
||||||
|
$ set worldEvents id $ _worldEvents w1
|
||||||
|
w1
|
||||||
|
w3 = updateCamera
|
||||||
|
$ updateAfterParticles'
|
||||||
|
$ colCrsWalls
|
||||||
|
w2
|
||||||
|
in checkEndGame $ ppEvents w3
|
||||||
|
where -- zoning w = set wallsZone (IM.foldr wallInZone IM.empty (_walls w))
|
||||||
|
-- w
|
||||||
|
-- wallInZone wl | dist (_wlLine wl !! 0) (_wlLine wl !! 1) <= 2*zoneSize
|
||||||
|
-- = insertIMInZone x y wlid wl
|
||||||
|
-- | otherwise = flip (foldr (\(a,b) -> insertIMInZone a b wlid wl)) ips
|
||||||
|
-- where (x,y) = zoneOfPoint $ (pHalf (_wlLine wl !! 0) (_wlLine wl !! 1))
|
||||||
|
-- wlid = _wlID wl
|
||||||
|
-- ips = map zoneOfPoint $ divideLine (2*zoneSize) (_wlLine wl !! 0) (_wlLine wl !! 1)
|
||||||
|
zoneCreatures = set creaturesZone (IM.foldr creatureInZone IM.empty (_creatures w))
|
||||||
|
creatureInZone cr = insertIMInZone x y cid cr
|
||||||
|
where (x,y) = zoneOfPoint $ _crPos cr
|
||||||
|
cid = _crID cr
|
||||||
|
zoneClouds = set cloudsZone (IM.foldr cloudInZone IM.empty (_clouds w))
|
||||||
|
cloudInZone cr = insertIMInZone x y cid cr
|
||||||
|
where (x,y) = zoneOfPoint $ _clPos cr
|
||||||
|
cid = _clID cr
|
||||||
|
|
||||||
|
updateLightSources w = set tempLightSources (catMaybes tlss) w'
|
||||||
|
where (w',tlss) = mapAccumR (\a b -> _tlsUpdate b a b) w $ _tempLightSources w
|
||||||
|
|
||||||
|
updateParticles w = IM.foldr' _ptUpdate w $ _particles w
|
||||||
|
|
||||||
|
updateSmoke :: World -> World
|
||||||
|
updateSmoke w = w & smoke %~ mapMaybe (\s -> _smUpdate s s)
|
||||||
|
|
||||||
|
updateParticles' :: World -> World
|
||||||
|
updateParticles' w = set particles' (catMaybes ps) w'
|
||||||
|
where (w',ps) = mapAccumR (\a b -> _ptUpdate' b a b) w $ _particles' w
|
||||||
|
|
||||||
|
updateAfterParticles' :: World -> World
|
||||||
|
updateAfterParticles' w = set afterParticles' (catMaybes ps) w'
|
||||||
|
where (w',ps) = mapAccumR (\a b -> _ptUpdate' b a b) w $ _afterParticles' w
|
||||||
|
|
||||||
|
updateCreatures :: World -> World
|
||||||
|
updateCreatures w = f $ set randGen newG $ set creatures (IM.mapMaybe id crs) w
|
||||||
|
where ((f,newG),crs) = IM.mapAccum (\g' cr -> _crUpdate cr w g' cr) (id,_randGen w) $ _creatures w
|
||||||
|
|
||||||
|
|
||||||
|
wallEvents :: World -> World
|
||||||
|
wallEvents w = IM.foldr (_doorMech) w ( IM.filter (\d -> case d of
|
||||||
|
Door {} -> True
|
||||||
|
AutoDoor {} -> True
|
||||||
|
BlockAutoDoor {} -> True
|
||||||
|
_ -> False) ( _walls w))
|
||||||
|
|
||||||
|
ppEvents :: World -> World
|
||||||
|
ppEvents w = IM.foldr' (\pp w -> _ppEvent pp pp w) w $ _pressPlates w
|
||||||
|
|
||||||
|
updateSeenWalls :: World -> World
|
||||||
|
updateSeenWalls w = foldr markSeen w wallsToUpdate
|
||||||
|
where yPos = _crPos $ _creatures w IM.! 0
|
||||||
|
wallsToUpdate = map _wlID $ IM.elems $ wallsNearPoint yPos w
|
||||||
|
-- wallsToUpdate = concatMap (\p -> collidePointFindWalls yPos (yPos +.+p) $ wallsAlongLine yPos (yPos +.+ p) w)
|
||||||
|
-- $ nRays 60
|
||||||
|
markSeen i = set (walls . ix i . wlSeen) True
|
||||||
|
|
||||||
|
setTestStringIO :: IO World -> IO World
|
||||||
|
setTestStringIO = fmap (\ w -> set testString (show $ s w) w)
|
||||||
|
where s w = (-.-) <$> (w ^? creatures . ix 0 . crPos) <*> (w ^? creatures . ix 0 . crOldPos)
|
||||||
|
-- w --do numC <- Mix.numChannelsPlaying
|
||||||
|
-- w' <- w
|
||||||
|
-- let ycr = _creatures w' IM.! 0
|
||||||
|
-- let st = _stance $ _crState ycr
|
||||||
|
-- let vel = magV $ _crPos ycr -.- _crOldPos ycr
|
||||||
|
-- let (x,y) = zoneOfPoint $ _crPos ycr
|
||||||
|
-- --fmap (set testString $ show (map _wlLine $ IM.elems $ _wallsZone w' IM.! x IM.! y)) w
|
||||||
|
-- fmap (set testString $ show $ last $ show $ pairsToIncidence' $ _pathGraph' w') w
|
||||||
|
|
||||||
|
-- update logic: creature old position set, creatures perform actions including movement,
|
||||||
|
-- creatures collided with each other, camera updated, particles updated, creatures
|
||||||
|
-- collided with walls
|
||||||
|
|
||||||
|
|
||||||
|
checkEndGame :: World -> World
|
||||||
|
checkEndGame w | _crHP (you w) < 1 = haltSound $ w {_menuState = GameOverMenu}
|
||||||
|
| otherwise = w
|
||||||
|
|
||||||
|
updateClouds :: World -> World
|
||||||
|
updateClouds w = IM.foldr' updateCloud w $ _clouds w
|
||||||
|
|
||||||
|
updateCloud :: Cloud -> World -> World
|
||||||
|
updateCloud c w | _clTimer c < 1 = w & clouds %~ IM.delete (_clID c)
|
||||||
|
| otherwise = moveCloud c w
|
||||||
|
|
||||||
|
moveCloud :: Cloud -> World -> World
|
||||||
|
moveCloud c w = _clEffect c c . theUpdate $ w
|
||||||
|
where newVel = 0.99 *.* springVels
|
||||||
|
springVels = IM.foldr' (clClSpringVel c w) (_clVel c) (cloudsNearPoint oldPos w)
|
||||||
|
oldPos = _clPos c
|
||||||
|
newPos = oldPos +.+ newVel
|
||||||
|
hitWl = collideCircWalls' oldPos newPos 5 $ wallsNearPoint newPos w
|
||||||
|
finalPos = fromMaybe newPos (fmap fst hitWl)
|
||||||
|
finalVel = fromMaybe newVel (fmap snd hitWl)
|
||||||
|
theUpdate w' = w' & clouds . ix (_clID c) . clTimer %~ (\t -> t - 1)
|
||||||
|
& clouds . ix (_clID c) . clVel .~ finalVel
|
||||||
|
& clouds . ix (_clID c) . clPos .~ finalPos
|
||||||
|
|
||||||
|
clClSpringVel :: Cloud -> World -> Cloud -> Point2 -> Point2
|
||||||
|
clClSpringVel a w b v
|
||||||
|
| ida == idb = v
|
||||||
|
| dist pa pb < 5 = v +.+ 0.1 *.* (safeNormalizeV (pa -.- pb))
|
||||||
|
| otherwise = v
|
||||||
|
where ida = _clID a
|
||||||
|
idb = _clID b
|
||||||
|
pa = _clPos a
|
||||||
|
pb = _clPos b
|
||||||
|
|
||||||
|
simpleCrSprings :: World -> World
|
||||||
|
simpleCrSprings w = IM.foldr' crSpring w $ _creatures w
|
||||||
|
|
||||||
|
crSpring :: Creature -> World -> World
|
||||||
|
crSpring c w = IM.foldr' (crCrSpring c) w $ cs
|
||||||
|
where cs = creaturesNearPoint (_crPos c) w
|
||||||
|
|
||||||
|
crCrSpring :: Creature -> Creature -> World -> World
|
||||||
|
crCrSpring c1 c2 w
|
||||||
|
| id1 == id2 = w
|
||||||
|
| vec == (0,0) = w
|
||||||
|
| diff < comRad = over (creatures . ix id1 . crPos) (+.+ overlap1)
|
||||||
|
$ over (creatures . ix id2 . crPos) (-.- overlap2) w
|
||||||
|
| otherwise = w
|
||||||
|
where id1 = _crID c1
|
||||||
|
id2 = _crID c2
|
||||||
|
vec = _crPos c1 -.- _crPos c2
|
||||||
|
diff = magV $ vec
|
||||||
|
comRad = _crRad c1 + _crRad c2
|
||||||
|
overlap1 = ((comRad - diff) * _crMass c2 * 0.5 / massT) *.* errorNormalizeV 55 vec
|
||||||
|
overlap2 = ((comRad - diff) * _crMass c1 * 0.5 / massT) *.* errorNormalizeV 56 vec
|
||||||
|
massT = _crMass c1 + _crMass c2
|
||||||
|
|
||||||
|
collidePointFindWall :: Point2 -> Point2 -> IM.IntMap Wall -> Maybe Int
|
||||||
|
collidePointFindWall p1 p2 ws = fmap fst $ listToMaybe $ sortBy f
|
||||||
|
$ IM.toList
|
||||||
|
$ IM.mapMaybe ((\(x:y:_) -> intersectSegSeg' p1 p2 x y) . _wlLine) ws
|
||||||
|
where f (_,a) (_,b) = compare (magV (p1 -.- a)) (magV (p1 -.- b))
|
||||||
|
|
||||||
|
collidePointFindWalls :: Point2 -> Point2 -> IM.IntMap Wall -> [Int]
|
||||||
|
collidePointFindWalls p1 p2 ws
|
||||||
|
-- = map fst . takeWhileAnd g . map snd . sortBy (compare `on` (dist p1 . fst . fromJust)) . filter ((/=) Nothing . fst) . map f $ IM.toList ws
|
||||||
|
= map fst . takeWhileAnd g . map snd . sortBy (compare `on` (dist p1 . fromJust . fst)) . filter ((/=) Nothing . fst) . map f $ IM.toList ws
|
||||||
|
where f (i,wl) = (intersectSegSeg' (_wlLine wl !! 0) (_wlLine wl !! 1) p1 p2, (i,wl))
|
||||||
|
g (_,wl) = _wlIsSeeThrough wl
|
||||||
|
takeWhileAnd h xs = let (ys,zs) = span h xs
|
||||||
|
in ys ++ tf zs
|
||||||
|
where tf (x:_) = [x]
|
||||||
|
tf _ = []
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
module Dodge.WallCreatureCollisions where
|
||||||
|
-- imports {{{
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import Control.Lens
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
|
||||||
|
colCrsWalls :: World -> World
|
||||||
|
colCrsWalls w = over creatures (fmap (colCrWall w)) w
|
||||||
|
|
||||||
|
colCrWall :: World -> Creature -> Creature
|
||||||
|
colCrWall w c = pushOutFromWall w c
|
||||||
|
|
||||||
|
pushOutFromWall :: World -> Creature -> Creature
|
||||||
|
pushOutFromWall w c
|
||||||
|
| p1 == p2 = c
|
||||||
|
| otherwise = over crPos (
|
||||||
|
collideCorners rad p1 wallPoints
|
||||||
|
.
|
||||||
|
collideWalls rad p1 ls
|
||||||
|
. checkPushThroughs rad p1 ls
|
||||||
|
)
|
||||||
|
c
|
||||||
|
where rad = _crRad c + wallBuffer
|
||||||
|
p1 = _crOldPos c
|
||||||
|
p2 = _crPos c
|
||||||
|
ls = IM.elems $ fmap _wlLine $ wallsNearPoint p2 w
|
||||||
|
wallPoints = nub $ concat ls
|
||||||
|
|
||||||
|
-- colCrPushThrough :: World -> Creature -> Creature
|
||||||
|
-- colCrPushThrough w cr = set crPos (checkPushThroughs rad p1 p2 ls) cr
|
||||||
|
-- where rad = _crRad cr + wallBuffer
|
||||||
|
-- p1 = _crOldPos cr
|
||||||
|
-- p2 = _crPos cr
|
||||||
|
-- ls = IM.elems $ fmap _wlLine $ wallsNearPoint p2 w
|
||||||
|
-- -- probably best to push check pushing through walls before creature springs
|
||||||
|
|
||||||
|
-- the amount to push creatures out from walls, extra to their radius
|
||||||
|
wallBuffer = 3
|
||||||
|
|
||||||
|
-- the following tests whether a moving circle crosses a list of walls, and
|
||||||
|
-- places the circle accordingly.
|
||||||
|
-- It supposes that the circle will only interact with at most two walls.
|
||||||
|
-- the reverse prevents the collision from happening again with the first wall,
|
||||||
|
-- when two walls are collided with
|
||||||
|
collideWalls :: Float -> Point2 -> [[Point2]] -> Point2 -> Point2
|
||||||
|
collideWalls rad cp1 walls cp2
|
||||||
|
= case (listToMaybe.mapMaybe (collideWall rad cp1 cp2)) walls of
|
||||||
|
Nothing -> cp2
|
||||||
|
Just cp3 -> case (listToMaybe.reverse.mapMaybe (collideWall rad cp1 cp3)) walls of
|
||||||
|
Nothing -> cp3
|
||||||
|
Just cp4 -> cp4
|
||||||
|
|
||||||
|
-- assumes that the wall is orientated
|
||||||
|
-- assumes wall points are different
|
||||||
|
collideWall :: Float -> Point2 -> Point2 -> [Point2] -> Maybe (Point2)
|
||||||
|
collideWall rad cp1 cp2 (wp1:wp2:_)
|
||||||
|
| isOnWall = Just newP -- +.+ (1 *.* norm))
|
||||||
|
| otherwise = Nothing
|
||||||
|
where norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
|
||||||
|
wp1' = (rad *.* norm) +.+ wp1
|
||||||
|
wp2' = (rad *.* norm) +.+ wp2
|
||||||
|
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
|
||||||
|
isOnWall = circOnLine' wp1 wp2 cp2 rad
|
||||||
|
isJust Nothing = False
|
||||||
|
isJust _ = True
|
||||||
|
|
||||||
|
collideCorners :: Float -> Point2 -> [Point2] -> Point2 -> Point2
|
||||||
|
collideCorners rad p1 ps p2 = foldr (intersectCirclePoint rad) p2 ps
|
||||||
|
|
||||||
|
-- collide circles with points (outer corners)
|
||||||
|
intersectCirclePoint :: Float -> Point2 -> Point2 -> Point2
|
||||||
|
intersectCirclePoint rad p cCen | dist cCen p > rad = cCen
|
||||||
|
| otherwise = p +.+ (rad *.* errorNormalizeV 65 (cCen -.- p))
|
||||||
|
|
||||||
|
checkPushThroughs :: Float -> Point2 -> [[Point2]] -> Point2 -> Point2
|
||||||
|
checkPushThroughs rad cp1 walls cp2
|
||||||
|
= fromMaybe cp2 $ (listToMaybe.mapMaybe (checkPushThrough rad cp1 cp2)) walls
|
||||||
|
|
||||||
|
checkPushThrough :: Float -> Point2 -> Point2 -> [Point2] -> Maybe (Point2)
|
||||||
|
checkPushThrough rad cp1 cp2 (wp1:wp2:_)
|
||||||
|
| isPushedThrough = intersectSegSeg' cp1 cp2 wp1 wp2
|
||||||
|
| otherwise = Nothing
|
||||||
|
where norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
|
||||||
|
wp1' = (rad *.* norm) +.+ wp1
|
||||||
|
wp2' = (rad *.* norm) +.+ wp2
|
||||||
|
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
|
||||||
|
isPushedThrough = isRHS wp1 wp2 cp2 && isJust (intersectSegSeg' cp1 cp2 wp1 wp2)
|
||||||
|
isJust Nothing = False
|
||||||
|
isJust _ = True
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,901 @@
|
|||||||
|
module Dodge.WorldActions where
|
||||||
|
|
||||||
|
import Dodge.Data
|
||||||
|
import Dodge.Base
|
||||||
|
import Dodge.SoundLogic
|
||||||
|
import Dodge.RandomHelp
|
||||||
|
import Geometry
|
||||||
|
import Picture
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Monad.State
|
||||||
|
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Function
|
||||||
|
import Data.List
|
||||||
|
import qualified Data.IntMap.Strict as IM
|
||||||
|
|
||||||
|
makeExplosionAt :: Point2 -> World -> World
|
||||||
|
makeExplosionAt p w = soundOnce grenadeBang
|
||||||
|
$ over tempLightSources ((:) $ tLightFade 20 150 intensityF p)
|
||||||
|
$ fs
|
||||||
|
$ over particles (IM.insert n exp)
|
||||||
|
w
|
||||||
|
where n = newParticleKey w
|
||||||
|
exp = Particle
|
||||||
|
{ _ptPos = p
|
||||||
|
, _ptStartPos = p
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = blank
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = explosionWaveDamage 6 p n
|
||||||
|
}
|
||||||
|
fVs = fst $ runState ((sequence . take 75 . repeat . randInCirc) 1) $ _randGen w
|
||||||
|
fPs' = evalState ((sequence . take 75 . repeat . randInCirc) 15) $ _randGen w
|
||||||
|
fPs = map (pushAgainstWalls . (+.+) p . (*.*) 0.5)
|
||||||
|
--fPs = map (pushAgainstWalls . (+.+) p . (*.*) 5)
|
||||||
|
fPs'
|
||||||
|
inversePushOut v = (15 - magV v) * 0.01 *.* v
|
||||||
|
fVs' = zipWith (+.+) fVs $ map inversePushOut fPs'
|
||||||
|
sizes = randomRs (2,6) $ _randGen w
|
||||||
|
times = randomRs (20,25) $ _randGen w
|
||||||
|
mF q v size time = makeFlameletTimed q v (levLayer GloomLayer - 1) Nothing size time
|
||||||
|
newFs = zipWith4 mF fPs (fmap (3 *.*) fVs') sizes times
|
||||||
|
fs w = foldr ($) w newFs
|
||||||
|
pushAgainstWalls q = fromMaybe q $ fmap (\(x,y)-> x +.+ y)
|
||||||
|
$ collidePointWalls p q $ wallsNearPoint q w
|
||||||
|
fadeFac x | x > 10 = 0
|
||||||
|
| otherwise = 1 - fromIntegral x / 10
|
||||||
|
intensityF x | x < 10 = 1 / (10 - fromIntegral x)
|
||||||
|
| otherwise = 1
|
||||||
|
|
||||||
|
explosionWaveDamage :: Int -> Point2 -> Int -> World -> World
|
||||||
|
explosionWaveDamage 0 p ptid = over particles (IM.delete ptid)
|
||||||
|
explosionWaveDamage time p ptid
|
||||||
|
= set (particles . ix ptid . ptUpdate) (explosionWaveDamage (time-1) p ptid)
|
||||||
|
. shockWaveDamage p rad 20
|
||||||
|
. set (particles . ix ptid . ptPict) shockwavePic
|
||||||
|
. lowLightRadAt white 0.3 75 150 p
|
||||||
|
where shockwavePic = onLayer PtLayer $ uncurry translate p $ color (withAlpha 0.9 white)
|
||||||
|
$ thickCircle rad (thickness*2)
|
||||||
|
thickness = max 0 $ sqrt $ (fromIntegral time - 2) * 8
|
||||||
|
rad = 40 - thickness
|
||||||
|
|
||||||
|
shockWaveDamage :: Point2 -> Float -> Int -> World -> World
|
||||||
|
shockWaveDamage p rad amount w = flip (foldr damageBlocks) hitBlocks $ over creatures (IM.map f) w
|
||||||
|
where f cr | dist (_crPos cr) p < rad + _crRad cr = over (crState . crDamage)
|
||||||
|
((:) $ Concussive amount p 2 0.5 rad) cr
|
||||||
|
| otherwise = cr
|
||||||
|
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 - (div amount 2)))
|
||||||
|
w
|
||||||
|
(_blIDs wall)
|
||||||
|
_ -> w
|
||||||
|
|
||||||
|
makeShockwaveAt :: Point2 -> Float -> Int -> Float -> Color
|
||||||
|
-> World -> World
|
||||||
|
makeShockwaveAt p rad dam push col = over particles' ((:) theShockwave)
|
||||||
|
where theShockwave = shockwaveAt p rad dam push col 10
|
||||||
|
|
||||||
|
shockwaveAt :: Point2 -> Float -> Int -> Float -> Color -> Int -> Particle'
|
||||||
|
shockwaveAt p rad dam push col maxtime
|
||||||
|
= Shockwave'
|
||||||
|
{ _ptPict' = blank
|
||||||
|
, _ptUpdate' = mvShockwave'
|
||||||
|
, _btColor' = col
|
||||||
|
, _btPos' = p
|
||||||
|
, _btRad' = rad
|
||||||
|
, _btDam' = dam
|
||||||
|
, _btPush' = push
|
||||||
|
, _btMaxTime' = maxtime
|
||||||
|
, _btTimer' = maxtime
|
||||||
|
}
|
||||||
|
|
||||||
|
mvShockwave' :: World -> Particle' -> (World, Maybe Particle')
|
||||||
|
mvShockwave' w pt
|
||||||
|
| _btTimer' pt <= 0 = (w, Nothing)
|
||||||
|
| otherwise
|
||||||
|
= (dams w , Just $ set btTimer' (t - 1) $ set ptPict' pic pt)
|
||||||
|
where r = _btRad' pt
|
||||||
|
p = _btPos' pt
|
||||||
|
push = _btPush' pt
|
||||||
|
dam = _btDam' pt
|
||||||
|
t = _btTimer' pt
|
||||||
|
tFraction = fromIntegral t / fromIntegral (_btMaxTime' pt)
|
||||||
|
pic = onLayer PtLayer $ uncurry translate p
|
||||||
|
$ color (_btColor' pt) $ thickCircle rad thickness
|
||||||
|
rad = r - (3/4) * r * tFraction
|
||||||
|
thickness = tFraction**2 * r
|
||||||
|
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 dam (25 * push *.* safeNormalizeV (_crPos cr -.- p)))
|
||||||
|
cr
|
||||||
|
| otherwise = cr
|
||||||
|
moveShockWave :: Int -> Point2 -> Float -> Float -> Float -> World -> Particle' -> (World, Maybe Particle')
|
||||||
|
moveShockWave 0 _ _ _ _ w _ = (w, Nothing)
|
||||||
|
moveShockWave t p r push pushexp w pt
|
||||||
|
= (dams w, Just $ newupdate $ newpic pt )
|
||||||
|
where newupdate = set ptUpdate' $ moveShockWave (t-1) p r push pushexp
|
||||||
|
newpic = set ptPict' (onLayer PtLayer $ uncurry translate p
|
||||||
|
$ color cyan $ thickCircle rad thickness)
|
||||||
|
rad = r - (3/40) * r * fromIntegral t
|
||||||
|
thickness = (fromIntegral 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 (_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'
|
||||||
|
{ _ptPict' = 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 ptPict' (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
|
||||||
|
|
||||||
|
|
||||||
|
-- makeTremorAt :: Int -> Point -> Float -> Particle
|
||||||
|
-- makeTremorAt i pos d = Particle
|
||||||
|
-- { _ptPos = (0,0)
|
||||||
|
-- , _ptStartPos = (0,0)
|
||||||
|
-- , _ptVel = (0,0)
|
||||||
|
-- , _ptPict = Line [(0,0),(0,0)]
|
||||||
|
-- , _ptID = i
|
||||||
|
-- , _ptUpdate = moveTremor [pos] 200 pos d i
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- moveTremor :: [Point] -> Int -> Point -> Float -> Int -> World -> World
|
||||||
|
-- moveTremor ps t p1 d i w
|
||||||
|
-- | t <= -30 = over particles (IM.delete i) . over partUnder (IM.delete i) $ w
|
||||||
|
-- | t <= 0 = set (particles . ix i . ptUpdate) (moveTremor ps (t-1) p1 d i)
|
||||||
|
-- . over partUnder
|
||||||
|
-- (IM.insert i (lineOfThickness (0.1*(fromIntegral t + 30)) ps)) $ w
|
||||||
|
-- | otherwise = case hitWall of
|
||||||
|
-- Nothing -> upUndPict . set randGen g . shakeCrs .
|
||||||
|
-- shatterAt p1 pl $ shatterAt p1 pr
|
||||||
|
-- $ set (particles . ix i . ptUpdate) (moveTremor (p3:ps) (t-1) p3 d i)
|
||||||
|
-- $ damCrsOnLine 10 p1 p3 w
|
||||||
|
-- Just (pw,pRef) -> set (particles . ix i . ptUpdate) (moveTremor ps (t-1) p1 (argV pRef) i) w
|
||||||
|
-- where p2 = p1 +.+ 5 *.* unitVectorAtAngle d
|
||||||
|
-- pl = p1 +.+ rotateV (d + pi/4) (75,0)
|
||||||
|
-- pr = p1 +.+ rotateV (d - pi/4) (75,0)
|
||||||
|
-- hitWall = collidePointWalls p1 p3 $ wallsAlongLine p1 p3 w
|
||||||
|
-- (d1,g) = randomR (0,2*pi) $ _randGen w
|
||||||
|
-- v = unitVectorAtAngle d1
|
||||||
|
-- p3 = p2 +.+ 5 *.* v
|
||||||
|
-- shakeCrs = over creatures (IM.map shCr)
|
||||||
|
-- shCr cr | dist p1 (_crPos cr) < 75 = over crPos (+.+ v) cr
|
||||||
|
-- | otherwise = cr
|
||||||
|
-- upUndPict = over partUnder (IM.insert i pic)
|
||||||
|
-- pic = pictures [color white $ lineOfThickness 3 [p3,head ps]
|
||||||
|
-- ,lineOfThickness 3 ps]
|
||||||
|
--
|
||||||
|
-- makeTremorAt' :: Int -> Point -> Particle
|
||||||
|
-- makeTremorAt' i pos = Particle
|
||||||
|
-- { _ptPos = (0,0)
|
||||||
|
-- , _ptStartPos = (0,0)
|
||||||
|
-- , _ptVel = (0,0)
|
||||||
|
-- , _ptPict = Line [(0,0),(0,0)]
|
||||||
|
-- , _ptID = i
|
||||||
|
-- , _ptUpdate = rotateTremorAround 10 pos i
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- rotateTremorAround :: Int -> Point -> Int -> World -> World
|
||||||
|
-- rotateTremorAround 0 p i w = over particles (IM.delete i) w
|
||||||
|
-- rotateTremorAround t p i w = set (particles . ix i . ptUpdate) (rotateTremorAround (t-1) p i)
|
||||||
|
-- $ foldr ($) w shatters
|
||||||
|
-- where rot d = rotateV $ d + fromIntegral t * pi / 7
|
||||||
|
-- ds = fmap (\x -> fromIntegral x * pi / 2.5) [0..4]
|
||||||
|
-- shatters = map (\d -> shatterAt p (p +.+ rot d (300,0))) ds
|
||||||
|
--
|
||||||
|
-- shatterAt :: Point -> Point -> World -> World
|
||||||
|
-- shatterAt p1 p2 w = case wlHit of
|
||||||
|
-- Just (p,v,col) -> makeFallingBlock col (f1 p) (f2 v)
|
||||||
|
-- $ set randGen g w
|
||||||
|
-- _ -> w
|
||||||
|
-- where wlHit = collidePointWallsNormCol p1 p2 $ wallsNearPoint p2 w
|
||||||
|
-- f1 q = q +.+ 5 *.* errorNormalizeV 10 (p2 -.- p1)
|
||||||
|
-- (d,g) = randomR (-0.5,0.5) $ _randGen w
|
||||||
|
-- f2 v' = rotateV d $ 3 *.* errorNormalizeV 11 v'
|
||||||
|
--
|
||||||
|
-- fallingWall :: Int -> Color -> Point -> Float -> Particle
|
||||||
|
-- fallingWall pid col p d
|
||||||
|
-- = Particle
|
||||||
|
-- { _ptPos = p
|
||||||
|
-- , _ptStartPos = p
|
||||||
|
-- , _ptVel = rotateV d (3,0)
|
||||||
|
-- , _ptPict = Line [(0,0),(0,0)]
|
||||||
|
-- , _ptID = pid
|
||||||
|
-- , _ptUpdate = moveFallingWall 10 p d col pid
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- moveFallingWall :: Int -> Point -> Float -> Color -> Int -> World -> World
|
||||||
|
-- moveFallingWall 0 p d col i w = over particles (IM.delete i) $ b1 $ b2 w
|
||||||
|
-- where b1 = makeFallingBlock col (p +.+ ( 3, 3)) (rotateV d (0,5))
|
||||||
|
-- b2 = makeFallingBlock col (p +.+ (-3,-3)) (rotateV d (0,1))
|
||||||
|
-- moveFallingWall t p d col i w
|
||||||
|
-- = set (particles . ix i . ptUpdate) (moveFallingWall (t-1) np d col i)
|
||||||
|
-- $ set (particles . ix i . ptPict) pic
|
||||||
|
-- w
|
||||||
|
-- where np = p +.+ 1 *.* unitVectorAtAngle d
|
||||||
|
-- pic = uncurry translate p $ rotate (-radToDeg d)
|
||||||
|
-- $ color col $ polygon [(-6,-6)
|
||||||
|
-- ,(-6, 6)
|
||||||
|
-- ,( 6, 6)
|
||||||
|
-- ,( 6,-6)
|
||||||
|
-- ]
|
||||||
|
--
|
||||||
|
-- makeFallingBlock :: Color -> Point -> Point -> World -> World
|
||||||
|
-- makeFallingBlock col p v w
|
||||||
|
-- = over particles (IM.insert i $ fallingBlock i col p v) w
|
||||||
|
-- where i = newParticleKey w
|
||||||
|
--
|
||||||
|
-- fallingBlock :: Int -> Color -> Point -> Point -> Particle
|
||||||
|
-- fallingBlock pid col p v
|
||||||
|
-- = Particle
|
||||||
|
-- { _ptPos = p
|
||||||
|
-- , _ptStartPos = p
|
||||||
|
-- , _ptVel = v
|
||||||
|
-- , _ptPict = Line [(0,0),(0,0)]
|
||||||
|
-- , _ptID = pid
|
||||||
|
-- , _ptUpdate = moveFallingBlock 10 p v col pid
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- moveFallingBlock :: Int -> Point -> Point -> Color -> Int -> World -> World
|
||||||
|
-- moveFallingBlock 0 p v col i w = over particles (IM.delete i) w
|
||||||
|
-- moveFallingBlock t p v col i w
|
||||||
|
-- = case hitCr of
|
||||||
|
-- Nothing -> set (particles . ix i . ptUpdate) (moveFallingBlock (t-1) np v col i)
|
||||||
|
-- $ set (particles . ix i . ptPict) pic
|
||||||
|
-- w
|
||||||
|
-- Just (p1,v1,crID) -> over particles (IM.delete i)
|
||||||
|
-- $ over (creatures . ix crID . crHP) (\hp -> hp - 1) w
|
||||||
|
-- where np = p +.+ v
|
||||||
|
-- pic = uncurry translate p $ rotate (-radToDeg (argV v))
|
||||||
|
-- $ color col $ polygon [(-3,-3)
|
||||||
|
-- ,(-3, 3)
|
||||||
|
-- ,( 3, 3)
|
||||||
|
-- ,( 3,-3)
|
||||||
|
-- ]
|
||||||
|
-- hitCr = reflectCircCreatures 4 p np $ _creatures w
|
||||||
|
|
||||||
|
|
||||||
|
-- if the spark is created by another Particle', it cannot be directly added to
|
||||||
|
-- the list, hence the redirect through worldEvents
|
||||||
|
createSpark :: Int -> Int -> Point2 -> Float -> Maybe Int -> World -> World
|
||||||
|
createSpark time colid pos dir maycid w = over worldEvents
|
||||||
|
((.) $ ( over particles' ((:) spark)
|
||||||
|
. flareAt' white 0.02 0.05 pos')
|
||||||
|
-- . lowLightAt' pos)
|
||||||
|
) w
|
||||||
|
where spark = Bul' { _ptPict' = blank
|
||||||
|
, _ptUpdate' = mvGenBullet'
|
||||||
|
, _btVel' = rotateV dir (5,0)
|
||||||
|
, _btColor' = numColor colid
|
||||||
|
, _btTrail' = [pos]
|
||||||
|
, _btPassThrough' = maycid
|
||||||
|
, _btWidth' = 1
|
||||||
|
, _btTimer' = time
|
||||||
|
, _btHitEffect' = threeEff' sparkEff noEff noEff
|
||||||
|
}
|
||||||
|
x = fst $ randomR (0,20) $ _randGen w
|
||||||
|
pos' = pos +.+ rotateV dir (x,0)
|
||||||
|
sparkEff bt p cr = over (creatures . ix (_crID cr) . crState . crDamage)
|
||||||
|
((:) $ SparkDam 1 sp p ep)
|
||||||
|
where sp = head (_btTrail' bt)
|
||||||
|
ep = sp +.+ _btVel' bt
|
||||||
|
|
||||||
|
createBarrelSpark :: Int -> Int -> Point2 -> Float -> Maybe Int -> World -> World
|
||||||
|
createBarrelSpark time colid pos dir maycid w = over worldEvents
|
||||||
|
((.) $ ( over particles' ((:) spark)
|
||||||
|
. flareAt' white 0.005 0.03 pos')
|
||||||
|
-- . lowLightAt' pos)
|
||||||
|
) w
|
||||||
|
where spark = Bul' { _ptPict' = blank
|
||||||
|
, _ptUpdate' = mvGenBullet'
|
||||||
|
, _btVel' = rotateV dir (5,0)
|
||||||
|
, _btColor' = numColor colid
|
||||||
|
, _btTrail' = [pos]
|
||||||
|
, _btPassThrough' = maycid
|
||||||
|
, _btWidth' = 1
|
||||||
|
, _btTimer' = time
|
||||||
|
, _btHitEffect' = threeEff' sparkEff noEff noEff
|
||||||
|
}
|
||||||
|
x = fst $ randomR (0,20) $ _randGen w
|
||||||
|
pos' = pos +.+ rotateV dir (x,0)
|
||||||
|
sparkEff bt p cr = over (creatures . ix (_crID cr) . crState . crDamage)
|
||||||
|
((:) $ SparkDam 1 sp p ep)
|
||||||
|
where sp = head (_btTrail' bt)
|
||||||
|
ep = sp +.+ _btVel' bt
|
||||||
|
|
||||||
|
flareAt :: Color -> Point2 -> World -> World
|
||||||
|
flareAt c p = flareAt' c 0.02 0.5 p
|
||||||
|
|
||||||
|
flareAt' :: Color -> Float -> Float -> Point2 -> World -> World
|
||||||
|
flareAt' col alphax alphay p
|
||||||
|
= over particles' ((:) (flashColAt col alphax p))
|
||||||
|
. lowLightColAt col alphay p
|
||||||
|
|
||||||
|
|
||||||
|
tLightFade :: Int -> Float -> (Int -> Float) -> Point2 -> TempLightSource
|
||||||
|
tLightFade 0 rmax intensityF p =
|
||||||
|
TLS { _tlsPos = p
|
||||||
|
, _tlsRad = rmax
|
||||||
|
, _tlsIntensity = intensityF 0
|
||||||
|
, _tlsUpdate = \w _ -> (w, Nothing)
|
||||||
|
}
|
||||||
|
tLightFade i rmax intensityF p =
|
||||||
|
TLS { _tlsPos = p
|
||||||
|
, _tlsRad = rmax
|
||||||
|
, _tlsIntensity = intensityF i
|
||||||
|
, _tlsUpdate = \w _ -> (w, Just $ tLightFade (i-1) rmax intensityF p)
|
||||||
|
}
|
||||||
|
|
||||||
|
tLightRad :: Int -> Float -> Float -> Point2 -> TempLightSource
|
||||||
|
tLightRad 0 rmax rmin p =
|
||||||
|
TLS { _tlsPos = p
|
||||||
|
, _tlsRad = rmax
|
||||||
|
, _tlsIntensity = 0.5
|
||||||
|
, _tlsUpdate = \w _ -> (w, Nothing)
|
||||||
|
}
|
||||||
|
tLightRad i rmax rmin p =
|
||||||
|
TLS { _tlsPos = p
|
||||||
|
, _tlsRad = rmax
|
||||||
|
, _tlsIntensity = 0.5
|
||||||
|
, _tlsUpdate = \w _ -> (w, Just $ tLightRad (i-1) rmax rmin p)
|
||||||
|
}
|
||||||
|
|
||||||
|
tLightAt i p = tLightRad i 100 0 p
|
||||||
|
|
||||||
|
flareWidth :: Float -> Float -> Float -> Int -> Color -> Float -> Float -> Point2 -> World -> World
|
||||||
|
flareWidth len wdth rad rays col ax ay p
|
||||||
|
= over particles' ((:) (flashRadAt rad col ax p))
|
||||||
|
. lowLightWidthAt len wdth col ay rays rad p
|
||||||
|
|
||||||
|
flareRad :: Float -> Int -> Color -> Float -> Float -> Point2 -> World -> World
|
||||||
|
flareRad rad rays col ax ay p
|
||||||
|
= over particles' ((:) (flashRadAt rad col ax p))
|
||||||
|
. lowLightRadAt col ay rays rad p
|
||||||
|
|
||||||
|
flashRadAt :: Float -> Color -> Float -> Point2 -> Particle'
|
||||||
|
flashRadAt rad col alphax (x,y) =
|
||||||
|
Particle'
|
||||||
|
{ _ptPict' = mconcat $
|
||||||
|
map (\i -> onLayerL [levLayer PtLayer]
|
||||||
|
$ color (withAlpha alphax col) $ translate x y $ circleSolid i
|
||||||
|
)
|
||||||
|
[rad/5,2*rad/5,3*rad/5,4*rad/5,rad]
|
||||||
|
, _ptUpdate' = ptTimer' 1
|
||||||
|
}
|
||||||
|
|
||||||
|
flashColAt :: Color -> Float -> Point2 -> Particle'
|
||||||
|
flashColAt col alphax (x,y) =
|
||||||
|
Particle'
|
||||||
|
{ _ptPict' = mconcat $
|
||||||
|
map (\i -> onLayerL [levLayer PtLayer]
|
||||||
|
$ color (withAlpha alphax col) $ translate x y $ circleSolid i
|
||||||
|
)
|
||||||
|
[20,25,30,35,40,45,50]
|
||||||
|
, _ptUpdate' = ptTimer' 1
|
||||||
|
}
|
||||||
|
lowLightWidthAt :: Float -> Float -> Color -> Float -> Int -> Float -> Point2 -> World -> World
|
||||||
|
lowLightWidthAt len wdth col alphay rays rad p w = foldr (lowLightWidthHit len wdth col alphay p) w ps
|
||||||
|
where ps = map ((+.+) p) $ nRaysRad rays rad
|
||||||
|
|
||||||
|
lowLightRadAt :: Color -> Float -> Int -> Float -> Point2 -> World -> World
|
||||||
|
lowLightRadAt col alphay rays rad p w = foldr (lowLightColHit col alphay p) w ps
|
||||||
|
where ps = map ((+.+) p) $ nRaysRad rays rad
|
||||||
|
|
||||||
|
lowLightColAt :: Color -> Float -> Point2 -> World -> World
|
||||||
|
lowLightColAt col alphay p w = foldr (lowLightColHit col alphay p) w ps
|
||||||
|
where ps = map ((+.+) p) $ nRaysRad 20 30
|
||||||
|
|
||||||
|
lowLightDirected :: Color -> Float -> Point2 -> Point2 -> [Float] -> World -> World
|
||||||
|
lowLightDirected col alpha a b angles w
|
||||||
|
= foldr (\angle w' -> lowLightColHit col alpha a (a +.+ rotateV angle b) w') w angles
|
||||||
|
|
||||||
|
lowLightColHit :: Color -> Float -> Point2 -> Point2 -> World -> World
|
||||||
|
lowLightColHit col alphay a b w
|
||||||
|
= case thingsHitLongLine a b w of
|
||||||
|
((p, E3x2 wall):_)
|
||||||
|
-> over particles' ((:) (wallGlareCol col alphay p wall)) w
|
||||||
|
((p, E3x1 cr):_)
|
||||||
|
-> over afterParticles' ((:) (crGlareCol col alphay p cr)) w
|
||||||
|
_ -> w
|
||||||
|
|
||||||
|
wallGlareCol :: Color -> Float -> Point2 -> Wall -> Particle'
|
||||||
|
wallGlareCol col alphay p wl = wallGlareWidth 10 5 col alphay p wl
|
||||||
|
|
||||||
|
crGlareCol :: Color -> Float -> Point2 -> Creature -> Particle'
|
||||||
|
crGlareCol col alphay p cr = crGlareWidth 5 col alphay p cr
|
||||||
|
|
||||||
|
lowLightWidthHit :: Float -> Float -> Color -> Float -> Point2 -> Point2 -> World -> World
|
||||||
|
lowLightWidthHit len wdth col alphay a b w
|
||||||
|
= case thingsHitLongLine a b w of
|
||||||
|
((p, E3x2 wall):_)
|
||||||
|
-> over particles' ((:) (wallGlareWidth len wdth col alphay p wall)) w
|
||||||
|
((p, E3x1 cr):_)
|
||||||
|
-> over afterParticles' ((:) (crGlareWidth wdth col alphay p cr)) w
|
||||||
|
_ -> w
|
||||||
|
|
||||||
|
wallGlareWidth :: Float -> Float -> Color -> Float -> Point2 -> Wall -> Particle'
|
||||||
|
wallGlareWidth len wdth col alphay p wl =
|
||||||
|
Particle'
|
||||||
|
{ _ptPict' = onLayerL [levLayer GloomLayer + 2] $ l
|
||||||
|
, _ptUpdate' = ptTimer' 1
|
||||||
|
}
|
||||||
|
where l = color (withAlpha alphay col) $ lineOfThickness wdth $ [p +.+ x, p -.- x]
|
||||||
|
x = len *.* ( normalizeV $ a -.- b)
|
||||||
|
(a:b:_) = _wlLine wl
|
||||||
|
|
||||||
|
crGlareWidth :: Float -> Color -> Float -> Point2 -> Creature -> Particle'
|
||||||
|
crGlareWidth wdth col alphay p cr =
|
||||||
|
Particle'
|
||||||
|
{ _ptPict' = onLayerL [levLayer GloomLayer + 2] $ l cp
|
||||||
|
--{ _ptPict' = [(blank, [levLayer WlLayer + 2])]
|
||||||
|
, _ptUpdate' = \w pt -> (w, Just $ pt {_ptPict' = onLayerL [levLayer GloomLayer + 2] $ upp cid w
|
||||||
|
,_ptUpdate' = ptTimer' 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
where l x = uncurry translate x
|
||||||
|
$ rotate (90 - (radToDeg $ argV (p -.- x)))
|
||||||
|
$ color (withAlpha alphay col)
|
||||||
|
$ thickArc 0 180 (_crRad cr) wdth
|
||||||
|
-- where l = rotate (radToDeg $ argV (cp -.- p)) $ uncurry translate cp $ color (withAlpha 0.5 white) $ thickArc 0 25 (_crRad cr) 5
|
||||||
|
cp = _crPos cr
|
||||||
|
cid = _crID cr
|
||||||
|
upp cid' w' = case w' ^? creatures . ix cid . crPos of
|
||||||
|
Just y -> l y
|
||||||
|
--Just p -> uncurry translate p $ circleSolid 5
|
||||||
|
_ -> blank
|
||||||
|
|
||||||
|
muzFlareAt :: Point2 -> World -> World
|
||||||
|
muzFlareAt p = over particles' ((:) (muzzleFlareAt p))
|
||||||
|
. lowLightAt p
|
||||||
|
|
||||||
|
muzzleFlareAt :: Point2 -> Particle'
|
||||||
|
muzzleFlareAt (x,y) =
|
||||||
|
Particle'
|
||||||
|
{ _ptPict' = mconcat
|
||||||
|
$ map (\i -> onLayer PtLayer $ color (withAlpha 0.02 white) $ translate x y $ circleSolid i
|
||||||
|
)
|
||||||
|
[20,25,30,35,40,45,50]
|
||||||
|
, _ptUpdate' = ptTimer' 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ptTimer' :: Int -> World -> Particle' -> (World, Maybe Particle')
|
||||||
|
ptTimer' 0 w pt = (w, Nothing)
|
||||||
|
ptTimer' n w pt = (w, Just $ pt {_ptUpdate' = ptTimer' (n-1)})
|
||||||
|
|
||||||
|
lowLightAt :: Point2 -> World -> World
|
||||||
|
lowLightAt p w = lowLightWidthAt 10 5 white 0.5 20 30 p w
|
||||||
|
|
||||||
|
lowLightHit :: Point2 -> Point2 -> World -> World
|
||||||
|
lowLightHit a b w
|
||||||
|
= case thingsHitLongLine a b w of
|
||||||
|
((p, E3x2 wall):_)
|
||||||
|
-> over particles' ((:) (wallGlare p wall)) w
|
||||||
|
((p, E3x1 cr):_)
|
||||||
|
-> over afterParticles' ((:) (crGlare p cr)) w
|
||||||
|
_ -> w
|
||||||
|
|
||||||
|
wallGlare :: Point2 -> Wall -> Particle'
|
||||||
|
wallGlare p wl = wallGlareWidth 10 5 white 0.5 p wl
|
||||||
|
|
||||||
|
crGlare :: Point2 -> Creature -> Particle'
|
||||||
|
crGlare = crGlareCol white 0.5
|
||||||
|
-- crGlare p cr =
|
||||||
|
-- Particle'
|
||||||
|
-- { _ptPict' = [(l, [levLayer WlLayer + 2])]
|
||||||
|
-- , _ptUpdate' = ptTimer' 1
|
||||||
|
-- }
|
||||||
|
-- where l = uncurry translate cp
|
||||||
|
-- $ rotate (90 - (radToDeg $ argV (p -.- cp)))
|
||||||
|
-- $ color (withAlpha 0.5 white)
|
||||||
|
-- $ thickArc 0 180 (_crRad cr) 5
|
||||||
|
-- -- where l = rotate (radToDeg $ argV (cp -.- p)) $ uncurry translate cp $ color (withAlpha 0.5 white) $ thickArc 0 25 (_crRad cr) 5
|
||||||
|
-- cp = _crPos cr
|
||||||
|
|
||||||
|
|
||||||
|
lowLightAt' :: Point2 -> World -> World
|
||||||
|
lowLightAt' p w = foldr (lowLightHit' p) w ps
|
||||||
|
where ps = map ((+.+) p) $ nRaysRad 20 30
|
||||||
|
|
||||||
|
lowLightHit' :: Point2 -> Point2 -> World -> World
|
||||||
|
lowLightHit' a b w
|
||||||
|
= case thingsHitLongLine a b w of
|
||||||
|
((p, E3x2 wall):_)
|
||||||
|
-> over particles' ((:) (wallGlare' p wall)) w
|
||||||
|
_ -> w
|
||||||
|
|
||||||
|
wallGlare' :: Point2 -> Wall -> Particle'
|
||||||
|
wallGlare' p wl = wallGlareWidth 5 5 white 0.1 p wl
|
||||||
|
|
||||||
|
noEff _ _ _ = id
|
||||||
|
|
||||||
|
threeEff' ::
|
||||||
|
( Particle' -> Point2 -> Creature -> World -> World ) ->
|
||||||
|
( Particle' -> Point2 -> Wall -> World -> World ) ->
|
||||||
|
( Particle' -> Point2 -> ForceField -> World -> World ) ->
|
||||||
|
Particle' -> (Point2, Either3 Creature Wall ForceField) -> World -> World
|
||||||
|
threeEff' crEff wlEff ffEff pt thing w = case thing of
|
||||||
|
(p,E3x1 cr) -> crEff pt p cr w
|
||||||
|
(p,E3x2 wl) -> wlEff pt p wl w
|
||||||
|
(p,E3x3 ff) -> ffEff pt p ff w
|
||||||
|
|
||||||
|
mvGenBullet' :: World -> Particle' -> (World, Maybe Particle')
|
||||||
|
mvGenBullet' w bt
|
||||||
|
| t <= 0 = (w, Nothing)
|
||||||
|
| t < 4 = (w, Just $ set btPassThrough' Nothing
|
||||||
|
$ set btTrail' (p:p:ps)
|
||||||
|
$ set ptPict' (bulLine col wth (p:p:ps))
|
||||||
|
$ set btTimer' (t-1) bt
|
||||||
|
)
|
||||||
|
| otherwise = case thingsHitExceptCr mcr p (p +.+ vel) w of
|
||||||
|
[] -> (w, Just $ set btPassThrough' Nothing
|
||||||
|
$ set btTrail' (p +.+ vel :p:ps)
|
||||||
|
$ set ptPict' (bulLine col wth (p +.+ vel:p:ps))
|
||||||
|
$ set btTimer' (t-1) bt
|
||||||
|
)
|
||||||
|
((hitp,thing):_)
|
||||||
|
-> ( hiteff bt (hitp,thing) w
|
||||||
|
, Just $ set btPassThrough' Nothing
|
||||||
|
$ set btTrail' (hitp:p:ps)
|
||||||
|
$ set ptPict' (bulLine col wth (hitp:p:ps))
|
||||||
|
$ set btTimer' 3 bt
|
||||||
|
)
|
||||||
|
where mcr = _btPassThrough' bt
|
||||||
|
col = _btColor' bt
|
||||||
|
(p:ps) = _btTrail' bt
|
||||||
|
vel = _btVel' bt
|
||||||
|
hiteff = _btHitEffect' bt
|
||||||
|
wth = _btWidth' bt
|
||||||
|
t = _btTimer' bt
|
||||||
|
|
||||||
|
bulLine :: Color -> Float -> [Point2] -> Drawing
|
||||||
|
bulLine col width (a:b:[])
|
||||||
|
= onLayer HPtLayer $ pictures
|
||||||
|
[color (withAlpha 0.5 col) $ lineOfThickness (width+1.5) [a,b]
|
||||||
|
,color white $ wedgeOfThickness width a b
|
||||||
|
-- ,color white $ lineOfThickness width [a,b]
|
||||||
|
-- ,color (withAlpha 0.3 white) $ lineOfThickness width [a3,b]
|
||||||
|
-- ,color (withAlpha 0.7 white) $ lineOfThickness width [a2,a3]
|
||||||
|
]
|
||||||
|
-- where a2 = (0.7 *.* a) +.+ (0.3 *.* b)
|
||||||
|
-- a3 = (0.3 *.* a) +.+ (0.7 *.* b)
|
||||||
|
bulLine col width (a:b:c:_)
|
||||||
|
= onLayer HPtLayer $ pictures
|
||||||
|
[color (withAlpha 0.5 col) $ lineOfThickness (width+1.5) [a,c]
|
||||||
|
,color white $ wedgeOfThickness width a c
|
||||||
|
-- ,color white $ lineOfThickness width [a,b]
|
||||||
|
-- ,color (withAlpha 0.3 white) $ lineOfThickness (width-1) [a3,c]
|
||||||
|
-- ,color (withAlpha 0.7 white) $ lineOfThickness width [a2,a3]
|
||||||
|
-- ,color white $ lineOfThickness width [a,a2]
|
||||||
|
]
|
||||||
|
-- where a2 = b
|
||||||
|
-- a3 = (0.5 *.* b) +.+ (0.5 *.* c)
|
||||||
|
-- where a2 = (0.7 *.* a) +.+ (0.3 *.* b)
|
||||||
|
-- a3 = (0.3 *.* a) +.+ (0.7 *.* b)
|
||||||
|
--bulLine col width (a:b:c:d:_)
|
||||||
|
-- = onLayer HPtLayer $ pictures
|
||||||
|
-- [color (withAlpha 0.5 col) $ lineOfThickness (width+1) [a,c]
|
||||||
|
-- ,color (withAlpha 0.3 white) $ lineOfThickness width [a3,b]
|
||||||
|
-- ,color (withAlpha 0.7 white) $ lineOfThickness width [a2,a3]
|
||||||
|
-- ,color white $ lineOfThickness width [a,a2]
|
||||||
|
-- ]
|
||||||
|
-- where a2 = (0.7 *.* a) +.+ (0.3 *.* b)
|
||||||
|
-- a3 = (0.3 *.* a) +.+ (0.7 *.* b)
|
||||||
|
|
||||||
|
thingsHitExceptCr :: Maybe Int -> Point2 -> Point2 -> World
|
||||||
|
-> [(Point2, (Either3 Creature Wall ForceField))]
|
||||||
|
thingsHitExceptCr Nothing sp ep = thingsHit sp ep
|
||||||
|
thingsHitExceptCr (Just cid) sp ep = filter crNotCid . thingsHit sp ep
|
||||||
|
where crNotCid (_,(E3x1 cr)) = _crID cr /= cid
|
||||||
|
crNotCid _ = True
|
||||||
|
|
||||||
|
thingsHit :: Point2 -> Point2 -> World -> [(Point2, (Either3 Creature Wall ForceField))]
|
||||||
|
thingsHit sp ep w = sortBy (compare `on` dist sp . fst) (crs ++ walls ++ ffs)
|
||||||
|
where hitCrs = IM.elems $ IM.filter (\cr -> circOnLine sp ep (_crPos cr) (_crRad cr))
|
||||||
|
$ creaturesAlongLine sp ep w
|
||||||
|
crPs = map (\cr -> ssaTriPoint ep (_crPos cr) sp (_crRad cr)) hitCrs
|
||||||
|
crs = zip crPs (map E3x1 hitCrs)
|
||||||
|
-- hitWls = wallsOnLine sp ep (f y $ f x $ _wallsZone w)
|
||||||
|
hitWls = wallsOnLine sp ep (IM.unions [f b $ f a $ _wallsZone w | a<-[x-1,x,x+1]
|
||||||
|
, b<-[y-1,y,y+1]])
|
||||||
|
(x,y) = zoneOfPoint (0.5 *.* (sp +.+ ep))
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
walls = zip (map (fromJust . hitPoint) hitWls) (map E3x2 hitWls)
|
||||||
|
hitPoint w = intersectSegSeg' sp ep (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
hitFFs = mapMaybe (collidePointFF sp ep (_randGen w)) (IM.elems $ _forceFields w)
|
||||||
|
ffs = map (\(p,(_,i)) -> (p, E3x3 $ _forceFields w IM.! i)) hitFFs
|
||||||
|
|
||||||
|
thingsHitExceptCrLongLine :: Maybe Int -> Point2 -> Point2 -> World
|
||||||
|
-> [(Point2, (Either3 Creature Wall ForceField))]
|
||||||
|
thingsHitExceptCrLongLine Nothing sp ep = thingsHitLongLine sp ep
|
||||||
|
thingsHitExceptCrLongLine (Just cid) sp ep = filter crNotCid . thingsHitLongLine sp ep
|
||||||
|
where crNotCid (_,(E3x1 cr)) = _crID cr /= cid
|
||||||
|
crNotCid _ = True
|
||||||
|
|
||||||
|
thingsHitLongLine :: Point2 -> Point2 -> World -> [(Point2, (Either3 Creature Wall ForceField))]
|
||||||
|
--thingsHitLongLine sp ep w = crs ++ walls
|
||||||
|
thingsHitLongLine sp ep w = sortBy (compare `on` dist sp . fst) (crs ++ walls ++ ffs)
|
||||||
|
where hitCrs = IM.elems $ IM.filter (\cr -> circOnLine sp ep (_crPos cr) (_crRad cr))
|
||||||
|
-- $ _creatures w
|
||||||
|
$ creaturesAlongLine sp ep w
|
||||||
|
crPs = map (\cr -> ssaTriPoint ep (_crPos cr) sp (_crRad cr)) hitCrs
|
||||||
|
--crPs = map _crPos hitCrs
|
||||||
|
crs = zip crPs (map E3x1 hitCrs)
|
||||||
|
-- hitWls = wallsOnLine sp ep (f y $ f x $ _wallsZone w)
|
||||||
|
hitWls = wallsOnLine sp ep $ wallsAlongLine sp ep w
|
||||||
|
--hitWls = wallsOnLine sp ep $ _walls w
|
||||||
|
f i m = case IM.lookup i m of Just val -> val
|
||||||
|
_ -> IM.empty
|
||||||
|
--walls = zip (map (fromJust . hitPoint) hitWls) (map E3x2 hitWls)
|
||||||
|
walls = zip (map (fromJust . hitPoint) hitWls) (map E3x2 hitWls)
|
||||||
|
hitPoint w = intersectSegSeg' sp ep (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
-- hitPoint w = intersectSegSeg' sp ep (_wlLine w !! 0) (_wlLine w !! 1)
|
||||||
|
hitFFs = mapMaybe (collidePointFF sp ep (_randGen w)) (IM.elems $ _forceFields w)
|
||||||
|
ffs = map (\(p,(_,i)) -> (p, E3x3 $ _forceFields w IM.! i)) hitFFs
|
||||||
|
cr = _creatures w IM.! 0
|
||||||
|
|
||||||
|
makeFlameletTimed :: Point2 -> Point2 -> Int -> Maybe Int -> Float -> Int -> World -> World
|
||||||
|
makeFlameletTimed pos vel levelInt maycid size time w
|
||||||
|
= set randGen g $ over particles' ((:) theFlamelet)
|
||||||
|
$ flareAt' white 0.01 0.1 pos w
|
||||||
|
where theFlamelet =
|
||||||
|
Pt' { _ptPict' = blank
|
||||||
|
, _ptUpdate' = moveFlamelet levelInt rot
|
||||||
|
, _btVel' = vel
|
||||||
|
, _btColor' = red
|
||||||
|
, _btPos' = pos
|
||||||
|
, _btPassThrough' = maycid
|
||||||
|
, _btWidth' = size
|
||||||
|
, _btTimer' = time
|
||||||
|
, _btHitEffect' = threeEff' (doFlameDam 1) noEff noEff
|
||||||
|
}
|
||||||
|
(rot ,g) = randomR (0,3) $ _randGen w
|
||||||
|
|
||||||
|
makeFlamelet :: Point2 -> Point2 -> Int -> Maybe Int -> Float -> World -> World
|
||||||
|
makeFlamelet pos vel levelInt maycid size w = set randGen g $ over particles' ((:) theFlamelet) w
|
||||||
|
where theFlamelet =
|
||||||
|
Pt' { _ptPict' = blank
|
||||||
|
, _ptUpdate' = moveFlamelet levelInt rot
|
||||||
|
, _btVel' = vel
|
||||||
|
, _btColor' = red
|
||||||
|
, _btPos' = pos
|
||||||
|
, _btPassThrough' = maycid
|
||||||
|
, _btWidth' = size
|
||||||
|
, _btTimer' = 20
|
||||||
|
, _btHitEffect' = threeEff' (doFlameDam 1) noEff noEff
|
||||||
|
}
|
||||||
|
(rot ,g) = randomR (0.5,1.5) $ _randGen w
|
||||||
|
moveFlamelet :: Int -> Float -> World -> Particle' -> (World, Maybe Particle')
|
||||||
|
moveFlamelet levelInt rot w pt =
|
||||||
|
case _btTimer' pt of
|
||||||
|
time | time <= 0 -> (w, Nothing)
|
||||||
|
| otherwise -> (damcrs, mvPt)
|
||||||
|
where sp = _btPos' pt
|
||||||
|
vel = _btVel' pt
|
||||||
|
ep = sp +.+ vel
|
||||||
|
size = _btWidth' pt
|
||||||
|
siz2 = size + 0.2
|
||||||
|
mvPt = Just $ pt {_btTimer' = time - 1, _btPos' = ep, _ptPict' = thepicture
|
||||||
|
, _btPassThrough' = Nothing
|
||||||
|
,_btVel' = 0.8 *.* vel}
|
||||||
|
damcrs = foldr ($) w $ map dodam $ filter closeCrs $ IM.elems $ _creatures w
|
||||||
|
closeCrs cr = dist ep (_crPos cr) < _crRad cr + size
|
||||||
|
dodam cr = over (creatures . ix (_crID cr) . crState . crDamage)
|
||||||
|
((:) $ Flaming 3 sp ep ep)
|
||||||
|
pic = onLayerL [levelInt,3] $ uncurry translate ep
|
||||||
|
$ color (mixColors (fromIntegral (max 0 (time-2))) (10-fromIntegral time)
|
||||||
|
white (dark red)
|
||||||
|
)
|
||||||
|
$ rotate (radToDeg ( 0.1 * fromIntegral time + rot))
|
||||||
|
$ scale sc sc
|
||||||
|
$ polygon [(-size,-size),(size,-size),(size,size),(-size,size)]
|
||||||
|
piu = onLayerL [levelInt,1] $ uncurry translate ep
|
||||||
|
$ color (dark red) $ rotate (radToDeg (rot - 0.1 * fromIntegral time))
|
||||||
|
$ scale s1 s1
|
||||||
|
$ polygon $ rectNSWE (siz2) (-siz2) (-siz2) (siz2)
|
||||||
|
glow = onLayerL [levelInt,0] $ uncurry translate ep
|
||||||
|
$ color (withAlpha 0.01 red)
|
||||||
|
$ circleSolid 30
|
||||||
|
sc = (*) 2 $ log $ 1 + fromIntegral time / 20
|
||||||
|
s1 = (*) 2 $ log $ 2 + fromIntegral time / 40
|
||||||
|
s2 = 0.5 * (sc + s1)
|
||||||
|
thepicture = pic <> piu <> pi2 <> glow
|
||||||
|
pi2 = onLayerL [levelInt,2] $ uncurry translate ep
|
||||||
|
$ color (mixColors (fromIntegral (max 0 (time-2))) (10-fromIntegral time)
|
||||||
|
orange (dark red)
|
||||||
|
)
|
||||||
|
$ rotate (radToDeg $ rot + 0.2 * fromIntegral time)
|
||||||
|
$ scale s2 s2
|
||||||
|
$ polygon $ rectNSWE (siz2) (-siz2) (-siz2) (siz2)
|
||||||
|
|
||||||
|
|
||||||
|
doFlameDam :: Int -> Particle' -> Point2 -> Creature -> World -> World
|
||||||
|
doFlameDam amount pt p cr = over (creatures . ix (_crID cr) . crState . crDamage)
|
||||||
|
((:) $ Flaming amount sp p ep)
|
||||||
|
where sp = _btPos' pt
|
||||||
|
ep = sp +.+ _btVel' pt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
makeSmokeAt :: Point2 -> World -> World
|
||||||
|
makeSmokeAt p w = over particles (IM.insert n smP) w
|
||||||
|
where n = newParticleKey w
|
||||||
|
smP = Particle
|
||||||
|
{ _ptPos = p
|
||||||
|
, _ptStartPos = p
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = onLayer PtLayer $ uncurry translate p $ color black $ circleSolid 1
|
||||||
|
--, _ptPict = uncurry translate p $ color white $ circleSolid 30
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = moveSmoke p 20 n
|
||||||
|
}
|
||||||
|
|
||||||
|
moveSmoke :: Point2 -> Int -> Int -> World -> World
|
||||||
|
moveSmoke p time i | time > 0 =
|
||||||
|
set (particles . ix i . ptPict) pic . set (particles . ix i . ptUpdate) (moveSmoke p (time-1) i)
|
||||||
|
| time > -50 =
|
||||||
|
set (particles . ix i . ptPict) pi1 . set (particles . ix i . ptUpdate) (moveSmoke p (time-1) i)
|
||||||
|
| otherwise =
|
||||||
|
over particles (IM.delete i)
|
||||||
|
|
||||||
|
where pic = onLayer PtLayer $ uncurry translate p $ color (greyN 0.5) $ circleSolid $ 21 - fromIntegral time
|
||||||
|
pi1 = onLayer PtLayer $ uncurry translate p
|
||||||
|
$ color (withAlpha (1 + fromIntegral time /50) (greyN 0.5))
|
||||||
|
$ circleSolid $ 21
|
||||||
|
|
||||||
|
makeSmokeAt' :: Float -> Int -> Point2 -> World -> World
|
||||||
|
makeSmokeAt' scal time p w = over particles (IM.insert n smP) w
|
||||||
|
where n = newParticleKey w
|
||||||
|
smP = Particle
|
||||||
|
{ _ptPos = p
|
||||||
|
, _ptStartPos = p
|
||||||
|
, _ptVel = (0,0)
|
||||||
|
, _ptPict = onLayer PtLayer $ uncurry translate p $ color black $ circleSolid 1
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = moveSmoke' scal p time n
|
||||||
|
}
|
||||||
|
|
||||||
|
moveSmoke' :: Float -> Point2 -> Int -> Int -> World -> World
|
||||||
|
moveSmoke' scal p time i
|
||||||
|
| time > 0
|
||||||
|
= set (particles . ix i . ptPict) pic
|
||||||
|
. set (particles . ix i . ptUpdate) (moveSmoke' scal p (time-1) i)
|
||||||
|
| time > -50
|
||||||
|
= set (particles . ix i . ptPict) pi1
|
||||||
|
. set (particles . ix i . ptUpdate) (moveSmoke' scal p (time-1) i)
|
||||||
|
| otherwise
|
||||||
|
= over particles (IM.delete i)
|
||||||
|
where pic = onLayer PtLayer $ uncurry translate p $ color (greyN 0.5) $ circleSolid $ (21 - fromIntegral time) * scal
|
||||||
|
pi1 = onLayer PtLayer $ uncurry translate p
|
||||||
|
$ color (withAlpha (1 + fromIntegral time /50) (greyN 0.5))
|
||||||
|
$ circleSolid $ 21 * scal
|
||||||
|
|
||||||
|
makeSmokeAt'' :: Point2 -> Float -> Int -> Point2 -> World -> World
|
||||||
|
makeSmokeAt'' vel scal time p w = over particles' ((:) smokeP) w
|
||||||
|
where smokeP = Particle'
|
||||||
|
{ _ptPict' = onLayerL [levLayer PtLayer - 1] $ uncurry translate p $ color black $ circleSolid 1
|
||||||
|
, _ptUpdate' = moveSmoke'' scal time p vel
|
||||||
|
}
|
||||||
|
|
||||||
|
moveSmoke'' :: Float -> Int -> Point2 -> Point2 -> World -> Particle' -> (World, Maybe Particle')
|
||||||
|
moveSmoke'' scal time p vel w pt
|
||||||
|
| time > 0 = (w, Just $ pt { _ptPict' = pic
|
||||||
|
, _ptUpdate' = moveSmoke'' scal (time-1) pNew vel
|
||||||
|
})
|
||||||
|
| time > -50 = (w, Just $ pt { _ptPict' = pi1
|
||||||
|
, _ptUpdate' = moveSmoke'' scal (time-1) pNew vel
|
||||||
|
})
|
||||||
|
| otherwise = (w, Nothing)
|
||||||
|
where pNew = p +.+ vel
|
||||||
|
pic = onLayerL [levLayer PtLayer - 1]
|
||||||
|
$ uncurry translate pNew
|
||||||
|
$ color (greyN 0.5) $ circleSolid $ (21 - fromIntegral time) * scal
|
||||||
|
pi1 = (onLayerL [levLayer PtLayer]
|
||||||
|
$ uncurry translate pNew
|
||||||
|
$ color (withAlpha (0.3 * (1 + fromIntegral time /50)) (greyN 0.5))
|
||||||
|
$ circleSolid $ 21 * scal)
|
||||||
|
<> (onLayerL [levLayer BgLayer+1]
|
||||||
|
$ uncurry translate pNew
|
||||||
|
$ color (withAlpha (1 + fromIntegral time /50) (greyN 0.5))
|
||||||
|
$ circleSolid $ 21 * scal)
|
||||||
|
|
||||||
|
makeColorSmokeAt :: Color -> Point2 -> Float -> Int -> Point2 -> World -> World
|
||||||
|
makeColorSmokeAt col vel scal time p w = over particles (IM.insert n smP) w
|
||||||
|
where n = newParticleKey w
|
||||||
|
smP = Particle
|
||||||
|
{ _ptPos = p
|
||||||
|
, _ptStartPos = p
|
||||||
|
, _ptVel = vel
|
||||||
|
, _ptPict = onLayer PtLayer $ uncurry translate p $ color col $ circleSolid 1
|
||||||
|
, _ptID = n
|
||||||
|
, _ptUpdate = moveColorSmoke col scal time n
|
||||||
|
}
|
||||||
|
|
||||||
|
moveColorSmoke :: Color -> Float -> Int -> Int -> World -> World
|
||||||
|
moveColorSmoke col scal time i w
|
||||||
|
| time > 0
|
||||||
|
= set (particles . ix i . ptPict) pic
|
||||||
|
. set (particles . ix i . ptUpdate) (moveColorSmoke col scal (time-1) i)
|
||||||
|
. set (particles . ix i . ptPos) newPos
|
||||||
|
. setVel
|
||||||
|
$ w
|
||||||
|
| time > -50
|
||||||
|
= set (particles . ix i . ptPict) pi1
|
||||||
|
. set (particles . ix i . ptUpdate) (moveColorSmoke col scal (time-1) i)
|
||||||
|
. set (particles . ix i . ptPos) newPos
|
||||||
|
. setVel
|
||||||
|
$ w
|
||||||
|
| otherwise
|
||||||
|
= over particles (IM.delete i) w
|
||||||
|
where oldPos = _ptPos $ _particles w IM.! i
|
||||||
|
newPos = oldPos +.+ (_ptVel $ _particles w IM.! i)
|
||||||
|
setVel = over (particles . ix i . ptVel) $ (*.*) 0.99
|
||||||
|
pic = onLayer PtLayer $ uncurry translate newPos $ color col $ circleSolid $ (21 - fromIntegral time) * scal
|
||||||
|
pi1 = onLayer PtLayer $ uncurry translate newPos
|
||||||
|
$ color (withAlpha (1 + fromIntegral time /50) col)
|
||||||
|
$ circleSolid $ 21 * scal
|
||||||
|
|
||||||
|
|
||||||
|
thingHit :: Point2 -> Maybe (Point2,a,b) -> Maybe (Point2,c) -> Maybe (Either (Point2,a,b) (Point2,c))
|
||||||
|
thingHit p Nothing Nothing = Nothing
|
||||||
|
thingHit p (Just x) Nothing = Just (Left x)
|
||||||
|
thingHit p Nothing (Just x) = Just (Right x)
|
||||||
|
thingHit p (Just x@(p1,_,_)) (Just y@(p2,_))
|
||||||
|
| magV (p -.- p1) > magV (p -.- p2) = Just (Right y)
|
||||||
|
| otherwise = Just (Left x)
|
||||||
|
|
||||||
|
|
||||||
|
damCrsOnLine :: Int -> Point2 -> Point2 -> World -> World
|
||||||
|
damCrsOnLine dam p1 p2 = over creatures (IM.map damIfOnLine)
|
||||||
|
where damIfOnLine cr | circOnLine p1 p2 (_crPos cr) (_crRad cr)
|
||||||
|
= over crHP (\hp -> hp - dam) cr
|
||||||
|
| otherwise = cr
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
module EventHelper where
|
||||||
|
import SDL
|
||||||
|
|
||||||
|
stripTimestamp :: (world -> EventPayload -> Maybe world)
|
||||||
|
-> world -> Event -> Maybe world
|
||||||
|
stripTimestamp f w e = f w (eventPayload e)
|
||||||
|
|
||||||
|
test :: world -> EventPayload -> Maybe world
|
||||||
|
test w e = case e of
|
||||||
|
KeyboardEvent (KeyboardEventData _ Pressed _ (Keysym _ KeycodeReturn _)) -> Nothing
|
||||||
|
_ -> Just w
|
||||||
+576
@@ -0,0 +1,576 @@
|
|||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
module Geometry
|
||||||
|
( module Geometry
|
||||||
|
, module Geometry.Data
|
||||||
|
)
|
||||||
|
where
|
||||||
|
import Geometry.Data
|
||||||
|
|
||||||
|
import Data.Function
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
|
||||||
|
zeroZ :: Point2 -> Point3
|
||||||
|
zeroZ (x,y) = (x,y,0)
|
||||||
|
|
||||||
|
infixl 6 +.+, -.-
|
||||||
|
infixl 7 *.*
|
||||||
|
|
||||||
|
(+.+) :: Point2 -> Point2 -> Point2
|
||||||
|
{-# INLINE (+.+) #-}
|
||||||
|
(x1, y1) +.+ (x2, y2) =
|
||||||
|
let
|
||||||
|
!x = x1 + x2
|
||||||
|
!y = y1 + y2
|
||||||
|
in (x, y)
|
||||||
|
|
||||||
|
(-.-) :: Point2 -> Point2 -> Point2
|
||||||
|
{-# INLINE (-.-) #-}
|
||||||
|
(x1, y1) -.- (x2, y2) =
|
||||||
|
let
|
||||||
|
!x = x1 - x2
|
||||||
|
!y = y1 - y2
|
||||||
|
in (x, y)
|
||||||
|
|
||||||
|
(*.*) :: Float -> Point2 -> Point2
|
||||||
|
{-# INLINE (*.*) #-}
|
||||||
|
a *.* (x2, y2) =
|
||||||
|
let
|
||||||
|
!x = a * x2
|
||||||
|
!y = a * y2
|
||||||
|
in (x, y)
|
||||||
|
|
||||||
|
infixl 6 +.+.+, -.-.-
|
||||||
|
infixl 7 *.*.*
|
||||||
|
|
||||||
|
(+.+.+) :: Point3 -> Point3 -> Point3
|
||||||
|
{-# INLINE (+.+.+) #-}
|
||||||
|
(x1, y1, z1) +.+.+ (x2, y2, z2) =
|
||||||
|
let
|
||||||
|
!x = x1 + x2
|
||||||
|
!y = y1 + y2
|
||||||
|
!z = z1 + z2
|
||||||
|
in (x, y, z)
|
||||||
|
|
||||||
|
(-.-.-) :: Point3 -> Point3 -> Point3
|
||||||
|
{-# INLINE (-.-.-) #-}
|
||||||
|
(x1, y1, z1) -.-.- (x2, y2, z2) =
|
||||||
|
let
|
||||||
|
!x = x1 - x2
|
||||||
|
!y = y1 - y2
|
||||||
|
!z = z1 - z2
|
||||||
|
in (x, y, z)
|
||||||
|
|
||||||
|
(*.*.*) :: Point3 -> Point3 -> Point3
|
||||||
|
{-# INLINE (*.*.*) #-}
|
||||||
|
(x1, y1, z1) *.*.* (x2, y2, z2) =
|
||||||
|
let
|
||||||
|
!x = x1 * x2
|
||||||
|
!y = y1 * y2
|
||||||
|
!z = z1 * z2
|
||||||
|
in (x, y, z)
|
||||||
|
|
||||||
|
normalizeV :: Point2 -> Point2
|
||||||
|
{-# INLINE normalizeV #-}
|
||||||
|
normalizeV p = (1 / magV p) *.* p
|
||||||
|
|
||||||
|
angleVV :: Point2 -> Point2 -> Float
|
||||||
|
{-# INLINE angleVV #-}
|
||||||
|
angleVV a b = let ma = magV a
|
||||||
|
mb = magV b
|
||||||
|
d = a `dotV` b
|
||||||
|
in acos $ d / (ma * mb)
|
||||||
|
|
||||||
|
dotV :: Point2 -> Point2 -> Float
|
||||||
|
{-# INLINE dotV #-}
|
||||||
|
dotV (x,y) (z,w) = x*z + y*w
|
||||||
|
|
||||||
|
closestPointOnLine :: Point2 -> Point2 -> Point2 -> Point2
|
||||||
|
{-# INLINE closestPointOnLine #-}
|
||||||
|
closestPointOnLine a b p
|
||||||
|
= a +.+ u *.* (b -.- a)
|
||||||
|
where u = closestPointOnLineParam a b p
|
||||||
|
|
||||||
|
closestPointOnLineParam :: Point2 -> Point2 -> Point2 -> Float
|
||||||
|
{-# INLINE closestPointOnLineParam #-}
|
||||||
|
closestPointOnLineParam a b p
|
||||||
|
= (p -.- a) `dotV` (b -.- a) / (b -.- a) `dotV` (b -.- a)
|
||||||
|
|
||||||
|
argV :: Point2 -> Float
|
||||||
|
{-# INLINE argV #-}
|
||||||
|
argV (x,y) = normalizeAngle $ atan2 y x
|
||||||
|
|
||||||
|
detV :: Point2 -> Point2 -> Float
|
||||||
|
{-# INLINE detV #-}
|
||||||
|
detV (x1, y1) (x2, y2)
|
||||||
|
= x1 * y2 - y1 * x2
|
||||||
|
|
||||||
|
-- | Angle in radians, anticlockwise from +ve x-axis.
|
||||||
|
unitVectorAtAngle :: Float -> Point2
|
||||||
|
{-# INLINE unitVectorAtAngle #-}
|
||||||
|
unitVectorAtAngle r
|
||||||
|
= (cos r, sin r)
|
||||||
|
|
||||||
|
-- | Rotate a vector by an angle (in radians). +ve angle is counter-clockwise.
|
||||||
|
rotateV :: Float -> Point2 -> Point2
|
||||||
|
rotateV r (x, y)
|
||||||
|
= ( x * cos r - y * sin r
|
||||||
|
, x * sin r + y * cos r)
|
||||||
|
{-# INLINE rotateV #-}
|
||||||
|
|
||||||
|
-- | Convert degrees to radians
|
||||||
|
degToRad :: Float -> Float
|
||||||
|
degToRad d = d * pi / 180
|
||||||
|
{-# INLINE degToRad #-}
|
||||||
|
|
||||||
|
|
||||||
|
-- | Convert radians to degrees
|
||||||
|
radToDeg :: Float -> Float
|
||||||
|
radToDeg r = r * 180 / pi
|
||||||
|
{-# INLINE radToDeg #-}
|
||||||
|
|
||||||
|
|
||||||
|
-- | Normalize an angle to be between 0 and 2*pi radians
|
||||||
|
normalizeAngle :: Float -> Float
|
||||||
|
normalizeAngle f = f - 2 * pi * floor' (f / (2 * pi))
|
||||||
|
where floor' :: Float -> Float
|
||||||
|
floor' x = fromIntegral (floor x :: Int)
|
||||||
|
{-# INLINE normalizeAngle #-}
|
||||||
|
|
||||||
|
-- the following helper draws a rectangle based on maximal N E S W values
|
||||||
|
rectNESW :: Float -> Float -> Float -> Float -> [Point2]
|
||||||
|
rectNESW a b c d = [(b,a),(b,c),(d,c),(d,a)
|
||||||
|
]
|
||||||
|
rectNSEW n s e w = rectNESW n e s w
|
||||||
|
|
||||||
|
rectNSWE :: Float -> Float -> Float -> Float -> [Point2]
|
||||||
|
rectNSWE n s w e = [ (w,n), (w,s), (e,s), (e,n)]
|
||||||
|
|
||||||
|
-- -- the following filters points in a polygon: supposes the points in the
|
||||||
|
-- polygon are listed in anticlockwise order
|
||||||
|
pointInOrOnPolygon :: Point2 -> [Point2] -> Bool
|
||||||
|
pointInOrOnPolygon p (x:xs) = all (\l -> not (uncurry isRHS l p)) $ zip (x:xs) (xs ++ [x])
|
||||||
|
|
||||||
|
|
||||||
|
pointInPolygon :: Point2 -> [Point2] -> Bool
|
||||||
|
pointInPolygon p [] = False
|
||||||
|
pointInPolygon p (x:xs) = all (\l -> uncurry (errorIsLHS 1) l p) $ zip (x:xs) (xs ++ [x])
|
||||||
|
|
||||||
|
errorPointInPolygon :: Int -> Point2 -> [Point2] -> Bool
|
||||||
|
errorPointInPolygon i p xs | length xs == 1 = error "one point polygon"
|
||||||
|
| length xs == 2 = error "two point polygon"
|
||||||
|
| nub xs == xs = pointInPolygon p xs
|
||||||
|
| otherwise = error $ "errorPointInPolygon "++ show i
|
||||||
|
|
||||||
|
errorNormalizeV :: Int -> Point2 -> Point2
|
||||||
|
errorNormalizeV i (0,0) = error $ "problem with function: errorNormalizeV "++show i
|
||||||
|
errorNormalizeV i p = normalizeV p
|
||||||
|
|
||||||
|
errorAngleVV :: Int -> Point2 -> Point2 -> Float
|
||||||
|
errorAngleVV i (0,0) _ = error $ "problem with function: errorAngleVV "++show i
|
||||||
|
errorAngleVV i _ (0,0) = error $ "problem with function: errorAngleVV "++show i
|
||||||
|
errorAngleVV i p p' = angleVV p p'
|
||||||
|
|
||||||
|
errorIsLHS :: Int -> Point2 -> Point2 -> Point2 -> Bool
|
||||||
|
errorIsLHS i x y | x == y = error $ "problem with function: errorIsLHS "
|
||||||
|
++show i
|
||||||
|
| otherwise = isLHS x y
|
||||||
|
|
||||||
|
errorClosestPointOnLine :: Int -> Point2 -> Point2 -> Point2 -> Point2
|
||||||
|
errorClosestPointOnLine i x y | x == y = error $ "problem with function: errorClosestPointOnLine "
|
||||||
|
++show i
|
||||||
|
| otherwise = closestPointOnLine x y
|
||||||
|
|
||||||
|
errorClosestPointOnLineParam :: Int -> Point2 -> Point2 -> Point2 -> Float
|
||||||
|
errorClosestPointOnLineParam i x y z | x == y = dist x z
|
||||||
|
-- error $ "problem with function: errorClosestPointOnLineParam " ++show i
|
||||||
|
| otherwise = closestPointOnLineParam x y z
|
||||||
|
|
||||||
|
safeNormalizeV :: Point2 -> Point2
|
||||||
|
safeNormalizeV (0,0) = (0,0)
|
||||||
|
safeNormalizeV p = normalizeV p
|
||||||
|
|
||||||
|
-- tests whether a point is on the LHS of a line
|
||||||
|
-- this has been called somewhere with l1 == l2
|
||||||
|
isLHS :: Point2 -> Point2 -> Point2 -> Bool
|
||||||
|
{-# INLINE isLHS #-}
|
||||||
|
isLHS' l1 l2 p | l1 == l2 = False
|
||||||
|
| otherwise = closestPointOnLineParam l1 (l1 +.+ vNormal (l2 -.- l1)) p < 0
|
||||||
|
|
||||||
|
isLHS (x,y) (x',y') (x'',y'')
|
||||||
|
| (x,y) == (x',y') = False
|
||||||
|
| otherwise = a1 * b2 - a2 * b1 > 0
|
||||||
|
where a1 = x' - x
|
||||||
|
a2 = y' - y
|
||||||
|
b1 = x'' - x
|
||||||
|
b2 = y'' - y
|
||||||
|
|
||||||
|
isRHS :: Point2 -> Point2 -> Point2 -> Bool
|
||||||
|
{-# INLINE isRHS #-}
|
||||||
|
isRHS (x,y) (x',y') (x'',y'')
|
||||||
|
| (x,y) == (x',y') = False
|
||||||
|
| otherwise = a1 * b2 - a2 * b1 < 0
|
||||||
|
where a1 = x' - x
|
||||||
|
a2 = y' - y
|
||||||
|
b1 = x'' - x
|
||||||
|
b2 = y'' - y
|
||||||
|
--isRHS l1 l2 p = closestPointOnLineParam l1 (l1 +.+ vNormal (l2 -.- l1)) p > 0
|
||||||
|
|
||||||
|
-- reorders points to be anticlockwise around their center
|
||||||
|
orderPolygon :: [Point2] -> [Point2]
|
||||||
|
orderPolygon [] = []
|
||||||
|
orderPolygon ps = sortBy (compare `on` \p -> argV (p -.- cen)) ps
|
||||||
|
where cen = 1/ fromIntegral (length ps) *.* foldr1 (+.+) ps
|
||||||
|
|
||||||
|
vNormal :: Point2 -> Point2
|
||||||
|
{-# INLINE vNormal #-}
|
||||||
|
vNormal (x,y) = (y,-x)
|
||||||
|
|
||||||
|
vInverse :: Point2 -> Point2
|
||||||
|
vInverse (x,y) = (-x,-y)
|
||||||
|
|
||||||
|
dist :: Point2 -> Point2 -> Float
|
||||||
|
{-# INLINE dist #-}
|
||||||
|
dist p1 p2 = magV (p2 -.- p1)
|
||||||
|
|
||||||
|
normV :: Point2 -> Point2
|
||||||
|
{-# INLINE normV #-}
|
||||||
|
normV (0,0) = (0,0)
|
||||||
|
normV p = (1/magV p ) *.* p
|
||||||
|
|
||||||
|
magV :: Point2 -> Float
|
||||||
|
{-# INLINE magV #-}
|
||||||
|
magV (x,y) = sqrt $ x^2 + y^2
|
||||||
|
|
||||||
|
pHalf :: Point2 -> Point2 -> Point2
|
||||||
|
pHalf a b = 0.5 *.* (a +.+ b)
|
||||||
|
|
||||||
|
linGrad :: (Eq a,Fractional a) => (a,a) -> (a,a) -> Maybe a
|
||||||
|
linGrad (x,y) (a,b) | x-a == 0 = Nothing
|
||||||
|
| otherwise = Just $ (y-b)/(x-a)
|
||||||
|
|
||||||
|
axisInt :: (Eq a,Fractional a) => (a,a) -> (a,a) -> Maybe a
|
||||||
|
axisInt p (a,b) = pure b ^-^ (pure a ^*^ linGrad p (a,b))
|
||||||
|
where (^-^) = liftA2 (-)
|
||||||
|
(^*^) = liftA2 (*)
|
||||||
|
|
||||||
|
-- intersectSegSeg is sometimes broken-- the following fixes at least some of
|
||||||
|
-- the cases
|
||||||
|
-- it is, however, slow
|
||||||
|
myIntersectSegSeg a@(ax,ay) b@(bx,by) c@(cx,cy) d@(dx,dy) = case ratIntersectLineLine a b c d of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just (x,y) -> if inbetween x && inbetween' y
|
||||||
|
then Just (x,y)
|
||||||
|
else Nothing
|
||||||
|
where inbetween x = ((ax <= x && x <= bx) || (bx <= x && x <= ax)) &&
|
||||||
|
((cx <= x && x <= dx) || (dx <= x && x <= cx))
|
||||||
|
inbetween' y = ((ay <= y && y <= by) || (by <= y && y <= ay)) &&
|
||||||
|
((cy <= y && y <= dy) || (dy <= y && y <= cy))
|
||||||
|
|
||||||
|
crossV :: Point2 -> Point2 -> Float
|
||||||
|
crossV (ax,ay) (bx,by) = ax*by - ay*bx
|
||||||
|
|
||||||
|
myIntersectLineLine :: (Eq a,Fractional a) => (a,a) -> (a,a) -> (a,a) -> (a,a) -> Maybe (a,a)
|
||||||
|
myIntersectLineLine a@(ax,ay) b c@(cx,cy) d
|
||||||
|
| linGrad a b == Nothing = fmap ((,) ax) $ axisInt (c *-* (ax,0)) (d *-* (ax,0))
|
||||||
|
| linGrad c d == Nothing = fmap ((,) cx) $ axisInt (a *-* (cx,0)) (b *-* (cx,0))
|
||||||
|
| otherwise
|
||||||
|
= case linGrad a b ^-^ linGrad c d of
|
||||||
|
Just 0 -> Nothing
|
||||||
|
_ -> liftA2 (,) newx
|
||||||
|
((linGrad a b ^*^ newx) ^+^ axisInt a b)
|
||||||
|
where (^-^) = liftA2 (-)
|
||||||
|
(^+^) = liftA2 (+)
|
||||||
|
(^/^) = liftA2 (/)
|
||||||
|
(^*^) = liftA2 (*)
|
||||||
|
newx = (axisInt c d ^-^ axisInt a b) ^/^ (linGrad a b ^-^ linGrad c d)
|
||||||
|
(*-*) (ax,ay) (bx,by) = (ax-bx,ay-by)
|
||||||
|
|
||||||
|
ratIntersectLineLine :: Point2 -> Point2 -> Point2 -> Point2 -> Maybe Point2
|
||||||
|
ratIntersectLineLine a b c d = fmap toNumPoint2 $ myIntersectLineLine (toRatPoint2 a) (toRatPoint2 b) (toRatPoint2 c) (toRatPoint2 d)
|
||||||
|
where toRatPoint2 (x,y) = (toRational x, toRational y)
|
||||||
|
toNumPoint2 (x,y) = (fromRational x, fromRational y)
|
||||||
|
f = toRatPoint2 . roundPoint2
|
||||||
|
|
||||||
|
roundPoint2 :: Point2 -> Point2
|
||||||
|
roundPoint2 (x,y) = (fromIntegral $ round x,fromIntegral $ round y)
|
||||||
|
|
||||||
|
circOnLine' :: Point2 -> Point2 -> Point2 -> Float -> Bool
|
||||||
|
circOnLine' p1 p2 c rad = isJustTrue (fmap (\p -> magV (p -.- c) < rad) y)
|
||||||
|
where y = intersectSegLine' p1 p2 c (c +.+ vNormal (p1 -.- p2))
|
||||||
|
isJustTrue (Just True) = True
|
||||||
|
isJustTrue _ = False
|
||||||
|
|
||||||
|
circOnLine :: Point2 -> Point2 -> Point2 -> Float -> Bool
|
||||||
|
circOnLine p1 p2 c rad = magV (p1 -.- c) <= rad || magV (p2 -.- c) <= rad
|
||||||
|
|| isJustTrue (fmap (\p -> magV (p -.- c) < rad) y)
|
||||||
|
where y = intersectSegLine' p1 p2 c (c +.+ vNormal (p1 -.- p2))
|
||||||
|
isJustTrue (Just True) = True
|
||||||
|
isJustTrue _ = False
|
||||||
|
|
||||||
|
difference x y | x > y = x - y
|
||||||
|
| otherwise = y - x
|
||||||
|
|
||||||
|
reflectIn :: Point2 -> Point2 -> Point2
|
||||||
|
reflectIn line vec = let angle = 2 * angleBetween line vec
|
||||||
|
in rotateV angle vec
|
||||||
|
|
||||||
|
angleBetween v1 v2 = argV v1 - argV v2
|
||||||
|
|
||||||
|
doublePair :: (a,a) -> [(a,a)]
|
||||||
|
doublePair (x,y) = [(x,y),(y,x)]
|
||||||
|
|
||||||
|
polysIntersect :: [Point2] -> [Point2] -> Bool
|
||||||
|
polysIntersect (p:ps) (q:qs)
|
||||||
|
= any isJust $ (\(a,b) (c,d) -> myIntersectSegSeg a b c d) <$> pairs1 <*> pairs2
|
||||||
|
where pairs1 = zip (p:ps) (ps++[p])
|
||||||
|
pairs2 = zip (q:qs) (qs++[q])
|
||||||
|
|
||||||
|
anyPolyssIntersect :: [[Point2]] -> [[Point2]] -> Bool
|
||||||
|
anyPolyssIntersect x y = or $ polysIntersect <$> x <*> y
|
||||||
|
|
||||||
|
nRays :: Int -> [Point2]
|
||||||
|
nRays n = take n $ iterate (rotateV (2*pi/fromIntegral n)) $ (600,0)
|
||||||
|
|
||||||
|
nRaysRad :: Int -> Float -> [Point2]
|
||||||
|
nRaysRad n x = take n $ iterate (rotateV (2*pi/fromIntegral n)) $ (x,0)
|
||||||
|
|
||||||
|
-- angles go from 0 to 2pi, need to work out what is left of another
|
||||||
|
|
||||||
|
isLeftOfA :: Float -> Float -> Bool
|
||||||
|
isLeftOfA angle1 angle2 = (angle1 - angle2 < pi && angle1 > angle2)
|
||||||
|
|| (angle2 - angle1 > pi && angle2 > angle1)
|
||||||
|
|
||||||
|
isLeftOf :: Point2 -> Point2 -> Bool
|
||||||
|
isLeftOf x y = isLeftOfA (argV x) (argV y)
|
||||||
|
|
||||||
|
-- diffAngles has an issue...
|
||||||
|
|
||||||
|
diffAngles :: Float -> Float -> Float
|
||||||
|
diffAngles x y | diff > pi = diffAngles (x - 2*pi) y
|
||||||
|
| diff >= 0 = diff
|
||||||
|
| diff > -pi = -diff
|
||||||
|
| otherwise = diffAngles (x + 2*pi) y
|
||||||
|
where diff = x-y
|
||||||
|
differenceAngles = diffAngles
|
||||||
|
angleDifference = diffAngles
|
||||||
|
|
||||||
|
-- given a triangle where we know the length of a first side,
|
||||||
|
-- the length of a second side, and the angle between the first side and the
|
||||||
|
-- third side, finds the length of the third side
|
||||||
|
-- not this doesn't necessarily find ALL solutions, asin is a map not a function
|
||||||
|
ssaTri :: Float -> Float -> Float -> Float
|
||||||
|
ssaTri ab bc a
|
||||||
|
| sin a == 0 = 0
|
||||||
|
| bc == 0 = ab
|
||||||
|
| otherwise = let c = asin ( (ab * (sin a))/bc)
|
||||||
|
b = pi - (a + c)
|
||||||
|
in sin b * bc / sin a
|
||||||
|
|
||||||
|
-- fix points: we now fix the triangle in the coordinate system, and return a
|
||||||
|
-- third unknown point:
|
||||||
|
-- the point which lies between pa and pc' on a line from b of length bc
|
||||||
|
-- note that there are likely two such points, this seems to return the point
|
||||||
|
-- closer to pc'
|
||||||
|
|
||||||
|
ssaTriPoint :: Point2 -> Point2 -> Point2 -> Float -> Point2
|
||||||
|
ssaTriPoint pa pb pc' bc
|
||||||
|
= let ab = magV (pa -.- pb)
|
||||||
|
a = errorAngleVV 6 (pb -.- pa) (pc' -.- pa)
|
||||||
|
ac = ssaTri ab bc a
|
||||||
|
in pa +.+ (ac *.* errorNormalizeV 47 (pc' -.- pa))
|
||||||
|
|
||||||
|
-- the above SHOULD return a Maybe Point...
|
||||||
|
ssaTriPoint' :: Point2 -> Point2 -> Point2 -> Float -> Maybe Point2
|
||||||
|
ssaTriPoint' pa pb pc' bc
|
||||||
|
| dist pb (closestPointOnSeg pa pc' pb) >= bc
|
||||||
|
= Nothing
|
||||||
|
| otherwise
|
||||||
|
= Just $ ssaTriPoint pa pb pc' bc
|
||||||
|
ssaTriPointCorrect :: Point2 -> Point2 -> Point2 -> Float -> Maybe Point2
|
||||||
|
ssaTriPointCorrect pa pb pc' bc
|
||||||
|
| param <= 1 && param >= 0 = Just p
|
||||||
|
| otherwise = Nothing
|
||||||
|
where p = ssaTriPoint pa pb pc' bc
|
||||||
|
param = closestPointOnLineParam pa pc' p
|
||||||
|
|
||||||
|
|
||||||
|
closestPointOnSeg :: Point2 -> Point2 -> Point2 -> Point2
|
||||||
|
closestPointOnSeg segP1 segP2 p
|
||||||
|
| errorClosestPointOnLineParam 3 segP1 segP2 p <= 0 = segP1
|
||||||
|
| errorClosestPointOnLineParam 4 segP1 segP2 p >= 1 = segP2
|
||||||
|
| otherwise = errorClosestPointOnLine 2 segP1 segP2 p
|
||||||
|
|
||||||
|
pointInCircle :: Point2 -> Float -> Point2 -> Maybe Point2
|
||||||
|
pointInCircle p r c | p == c = Just p
|
||||||
|
| magV (p -.- c) < r = Just p
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
|
--determines if a moving point intersects with a circle,
|
||||||
|
--if so, returns a point on circle that intersects with the line passing
|
||||||
|
--throught the circle : HOPEFULLY THE CORRECT OF THE TWO!
|
||||||
|
|
||||||
|
collidePointCirc :: Point2 -> Point2 -> Float -> Point2 -> Maybe Point2
|
||||||
|
collidePointCirc p1 p2 rad c = ssaTriPoint' p2 c p1 rad
|
||||||
|
|
||||||
|
-- changes the point to a measure of the distance
|
||||||
|
collidePointCirc' :: Point2 -> Point2 -> Float -> Point2 -> Maybe Float
|
||||||
|
collidePointCirc' p1 p2 rad c = fmap (\x -> magV (x -.- p1))
|
||||||
|
(collidePointCirc p1 p2 rad c)
|
||||||
|
|
||||||
|
--returns both the point and the measure of the distance
|
||||||
|
collidePointCirc'' :: Point2 -> Point2 -> Float -> Point2 -> Maybe (Point2,Float)
|
||||||
|
collidePointCirc'' p1 p2 rad c = (,) <$> collidePointCirc p1 p2 rad c
|
||||||
|
<*> collidePointCirc' p1 p2 rad c
|
||||||
|
|
||||||
|
collidePointCircCorrect :: Point2 -> Point2 -> Float -> Point2 -> Maybe Point2
|
||||||
|
collidePointCircCorrect p1 p2 rad c = ssaTriPointCorrect p2 c p1 rad
|
||||||
|
|
||||||
|
|
||||||
|
-- finds the height of a triangle using herons formula
|
||||||
|
-- the base is the line between the first two points
|
||||||
|
heron :: Point2 -> Point2 -> Point2 -> Float
|
||||||
|
heron x y z | x == y = 0
|
||||||
|
| otherwise = let a = magV $ x -.- y
|
||||||
|
b = magV $ y -.- z
|
||||||
|
c = magV $ z -.- x
|
||||||
|
s = (a+b+c)/2
|
||||||
|
area = sqrt(s*(s-a)*(s-b)*(s-c))
|
||||||
|
in 2*area/a
|
||||||
|
-- multiplies reflection in normal by factor
|
||||||
|
reflectInParam :: Float -> Point2 -> Point2 -> Point2
|
||||||
|
reflectInParam x line vec = let angle = 2 * angleBetween line vec
|
||||||
|
rAng = rotateV angle vec
|
||||||
|
p = x *.* errorClosestPointOnLine 3 (0,0) (vNormal line) rAng
|
||||||
|
in rAng -.- p
|
||||||
|
|
||||||
|
|
||||||
|
reflectIn' :: Point2 -> Point2 -> Point2 -> Point2 -> Point2
|
||||||
|
reflectIn' l1 l2 v1 v2 = v1 +.+ reflectIn (l1 -.- l2) (v2 -.- v1)
|
||||||
|
|
||||||
|
isOnLine :: Point2 -> Point2 -> Point2 -> Bool
|
||||||
|
isOnLine l1 l2 p = errorClosestPointOnLineParam 10 l1 (l1 +.+ vNormal (l2 -.- l1)) p == 0
|
||||||
|
&& errorClosestPointOnLineParam 11 l1 l2 p <= 1
|
||||||
|
&& errorClosestPointOnLineParam 12 l1 l2 p >= 0
|
||||||
|
|
||||||
|
-- the take 5000 here is a hack, otherwise divideLine seems to sometimes
|
||||||
|
-- generate an infinite list, and I don't know why
|
||||||
|
divideLine :: Float -> Point2 -> Point2 -> [Point2]
|
||||||
|
--divideLine x a b = map (\i -> a +.+ (i / (fromIntegral numPoints)) *.* (b -.- a))
|
||||||
|
divideLine x a b = take 5000 $ map (\i -> a +.+ (i / (fromIntegral numPoints) *.* (b -.- a)) )
|
||||||
|
$ map fromIntegral ns
|
||||||
|
where d = dist a b
|
||||||
|
numPoints = max 1 $ ceiling $ d / x
|
||||||
|
ns = [0 .. numPoints]
|
||||||
|
|
||||||
|
|
||||||
|
-- pulled the following from the haskell wiki
|
||||||
|
bresenham :: (Int,Int) -> (Int,Int) -> [(Int,Int)]
|
||||||
|
{-# INLINE bresenham #-}
|
||||||
|
bresenham pa@(xa,ya) pb@(xb,yb) = map maySwitch . unfoldr go $ (x1,y1,0)
|
||||||
|
where
|
||||||
|
steep = abs (yb - ya) > abs (xb - xa)
|
||||||
|
maySwitch = if steep then (\(x,y) -> (y,x)) else id
|
||||||
|
[(x1,y1),(x2,y2)] = sort [maySwitch pa, maySwitch pb]
|
||||||
|
deltax = x2 - x1
|
||||||
|
deltay = abs (y2 - y1)
|
||||||
|
ystep = if y1 < y2 then 1 else -1
|
||||||
|
go (xTemp, yTemp, error)
|
||||||
|
| xTemp > x2 = Nothing
|
||||||
|
| otherwise = Just ((xTemp, yTemp), (xTemp + 1, newY, newError))
|
||||||
|
where
|
||||||
|
tempError = error + deltay
|
||||||
|
(newY, newError) = if (2*tempError) >= deltax
|
||||||
|
then (yTemp+ystep,tempError-deltax)
|
||||||
|
else (yTemp,tempError)
|
||||||
|
|
||||||
|
|
||||||
|
divideCircle :: Float -> Point2 -> Float -> [Point2]
|
||||||
|
divideCircle x cen rad = map (cen +.+) $ nPointsOnCirc n rad
|
||||||
|
where n = ceiling $ rad * 2 * pi / x
|
||||||
|
|
||||||
|
nPointsOnCirc :: Int -> Float -> [Point2]
|
||||||
|
nPointsOnCirc n rad = take n $ iterate (rotateV (2*pi/fromIntegral n)) $ (rad,0)
|
||||||
|
|
||||||
|
lineInPolygon :: Point2 -> Point2 -> [Point2] -> Bool
|
||||||
|
lineInPolygon a b ps = pointInPolygon a ps || pointInPolygon b ps
|
||||||
|
|| any (isJust . uncurry (intersectSegSeg' a b)) pss
|
||||||
|
where pss = zip ps (tail ps ++ [head ps])
|
||||||
|
|
||||||
|
--intersectSegLineFrom :: Point2 -> Point2 -> Point2 -> Point2 -> Maybe Point2
|
||||||
|
--intersectSegLineFrom a b c d
|
||||||
|
-- = case intersectSegLine a b c d of
|
||||||
|
-- Just p | closestPointOnLineParam c d p >= 0 -> Just p
|
||||||
|
-- | otherwise -> Nothing
|
||||||
|
-- Nothing -> Nothing
|
||||||
|
|
||||||
|
intersectSegSeg' :: Point2 -> Point2 -> Point2 -> Point2 -> Maybe Point2
|
||||||
|
{-# INLINE intersectSegSeg' #-}
|
||||||
|
intersectSegSeg' (x1,y1) (x2,y2) (x3,y3) (x4,y4)
|
||||||
|
-- | t' < 0 || u' < 0 || t' > den || u' > den || den == 0
|
||||||
|
-- = Nothing
|
||||||
|
| den == 0 = Nothing
|
||||||
|
| den > 0 && (t' < 0 || u' < 0 || t' > den || u' > den)
|
||||||
|
= Nothing
|
||||||
|
| den < 0 && (t' > 0 || u' > 0 || t' < den || u' < den)
|
||||||
|
= Nothing
|
||||||
|
-- | den > 0 && (t' < 0 || t' > den)
|
||||||
|
-- = Nothing
|
||||||
|
-- | den < 0 && (t' > 0 || t' < 0-den)
|
||||||
|
-- = Nothing
|
||||||
|
| otherwise = Just (x1 + (x2-x1)*t'/den, y1 + (y2-y1)*t'/den)
|
||||||
|
where
|
||||||
|
den = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
|
||||||
|
t' = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)
|
||||||
|
u' = (y1-y2)*(x1-x3) - (x1-x2)*(y1-y3)
|
||||||
|
|
||||||
|
intersectSegLineFrom' :: Point2 -> Point2 -> Point2 -> Point2 -> Maybe Point2
|
||||||
|
{-# INLINE intersectSegLineFrom' #-}
|
||||||
|
intersectSegLineFrom' (x1,y1) (x2,y2) (x3,y3) (x4,y4)
|
||||||
|
-- | t' < 0 || u' < 0 || t' > den || u' > den || den == 0
|
||||||
|
-- = Nothing
|
||||||
|
| den == 0 = Nothing
|
||||||
|
| den > 0 && ( t' < 0 || u' < 0 || t' > den )
|
||||||
|
= Nothing
|
||||||
|
| den < 0 && ( t' > 0 || u' > 0 || t' < den )
|
||||||
|
= Nothing
|
||||||
|
-- | den > 0 && (t' < 0 || t' > den)
|
||||||
|
-- = Nothing
|
||||||
|
-- | den < 0 && (t' > 0 || t' < 0-den)
|
||||||
|
-- = Nothing
|
||||||
|
| otherwise = Just (x1 + (x2-x1)*t'/den, y1 + (y2-y1)*t'/den)
|
||||||
|
where
|
||||||
|
den = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
|
||||||
|
t' = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)
|
||||||
|
u' = (y1-y2)*(x1-x3) - (x1-x2)*(y1-y3)
|
||||||
|
|
||||||
|
intersectSegLine' :: Point2 -> Point2 -> Point2 -> Point2 -> Maybe Point2
|
||||||
|
{-# INLINE intersectSegLine' #-}
|
||||||
|
intersectSegLine' (x1,y1) (x2,y2) (x3,y3) (x4,y4)
|
||||||
|
-- | t' < 0 || u' < 0 || t' > den || u' > den || den == 0
|
||||||
|
-- = Nothing
|
||||||
|
| den == 0 = Nothing
|
||||||
|
| den > 0 && (t' < 0 || t' > den)
|
||||||
|
= Nothing
|
||||||
|
| den < 0 && (t' > 0 || t' < den)
|
||||||
|
= Nothing
|
||||||
|
-- | den > 0 && (t' < 0 || t' > den)
|
||||||
|
-- = Nothing
|
||||||
|
-- | den < 0 && (t' > 0 || t' < 0-den)
|
||||||
|
-- = Nothing
|
||||||
|
| otherwise = Just (x1 + (x2-x1)*t'/den, y1 + (y2-y1)*t'/den)
|
||||||
|
where
|
||||||
|
den = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
|
||||||
|
t' = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)
|
||||||
|
u' = (y1-y2)*(x1-x3) - (x1-x2)*(y1-y3)
|
||||||
|
|
||||||
|
intersectLineLine' :: Point2 -> Point2 -> Point2 -> Point2 -> Maybe Point2
|
||||||
|
{-# INLINE intersectLineLine' #-}
|
||||||
|
intersectLineLine' (x1,y1) (x2,y2) (x3,y3) (x4,y4)
|
||||||
|
| den == 0 = Nothing
|
||||||
|
| otherwise = Just (x1 + (x2-x1)*t'/den, y1 + (y2-y1)*t'/den)
|
||||||
|
where
|
||||||
|
den = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
|
||||||
|
t' = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
module Geometry.Data
|
||||||
|
( Point2 (..)
|
||||||
|
, Point3 (..)
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
type Point2 = (Float,Float)
|
||||||
|
type Point3 = (Float,Float,Float)
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
module Loop
|
||||||
|
( setupLoop
|
||||||
|
-- , withExits
|
||||||
|
) where
|
||||||
|
import SDL
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Control.Concurrent
|
||||||
|
import Control.Exception
|
||||||
|
import qualified Graphics.Rendering.OpenGL as GL
|
||||||
|
import Control.Monad
|
||||||
|
import System.Mem
|
||||||
|
import Foreign.C
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
winConfig x y = defaultWindow
|
||||||
|
{ windowGraphicsContext
|
||||||
|
= OpenGLContext (defaultOpenGL { glProfile = Core Normal 4 3
|
||||||
|
, glColorPrecision = V4 8 8 8 8
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, windowInitialSize = V2 x y
|
||||||
|
, windowResizable =True
|
||||||
|
}
|
||||||
|
|
||||||
|
setupLoop :: String
|
||||||
|
-> CInt
|
||||||
|
-> CInt
|
||||||
|
-> IO setupParams
|
||||||
|
-> (setupParams -> IO ())
|
||||||
|
-> world
|
||||||
|
-> (setupParams -> world -> IO ())
|
||||||
|
-> (world -> Event -> Maybe world)
|
||||||
|
-> (world -> Maybe world)
|
||||||
|
-> IO ()
|
||||||
|
setupLoop wName xSize ySize
|
||||||
|
customInit initCleanup startWorld sideEffects eventFn worldFn = do
|
||||||
|
initializeAll
|
||||||
|
bracket (createWindow (T.pack wName) (winConfig xSize ySize)) destroyWindow
|
||||||
|
(\window ->
|
||||||
|
do bracket (glCreateContext window) (\con -> GL.finish >> glDeleteContext con)
|
||||||
|
(\_ -> bracket customInit initCleanup $ \setup -> do -- setup <- customInit
|
||||||
|
-- swapInterval $= ImmediateUpdates
|
||||||
|
GL.blend $= GL.Enabled
|
||||||
|
GL.blendEquation $= GL.FuncAdd
|
||||||
|
GL.blendFunc $= (GL.SrcAlpha,GL.OneMinusSrcAlpha)
|
||||||
|
GL.clearColor $= GL.Color4 0 0.5 0 0
|
||||||
|
doLoop setup window startWorld
|
||||||
|
sideEffects eventFn worldFn
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
--doLoop :: setupParams -> Window -> WorldView world0
|
||||||
|
-- -> (setupParams -> WorldView world0 -> IO ())
|
||||||
|
-- -> (world0 -> Event -> Maybe world0)
|
||||||
|
-- -> (world0 -> Maybe world0)
|
||||||
|
-- -> IO ()
|
||||||
|
doLoop setup window startWorld
|
||||||
|
worldSideEffects
|
||||||
|
eventFn
|
||||||
|
worldUpdate
|
||||||
|
= do
|
||||||
|
startLoopTicks <- ticks
|
||||||
|
events <- pollEvents
|
||||||
|
--let-- maybeUpdatedWorld :: Maybe (WorldView world0)
|
||||||
|
-- maybeUpdatedWorld = applyEvents eventFn startWorld events worldUpdate
|
||||||
|
maybeUpdatedWorld <- applyEventsIO eventFn startWorld events
|
||||||
|
case maybeUpdatedWorld >>= worldUpdate of
|
||||||
|
Just updatedWorld -> do
|
||||||
|
GL.clear [GL.ColorBuffer,GL.DepthBuffer]
|
||||||
|
worldSideEffects setup updatedWorld
|
||||||
|
glSwapWindow window
|
||||||
|
--GL.flush
|
||||||
|
performGC
|
||||||
|
endLoopTicks <- ticks -- it might be better to use System.Clock (monotonic)
|
||||||
|
let delay = max 0 (10 + fromIntegral startLoopTicks - fromIntegral endLoopTicks)
|
||||||
|
threadDelay (delay * 1000)
|
||||||
|
doLoop setup window updatedWorld worldSideEffects eventFn worldUpdate
|
||||||
|
Nothing -> return ()
|
||||||
|
|
||||||
|
applyEvents :: (world0 -> Event -> Maybe world0)
|
||||||
|
-> world0
|
||||||
|
-> [Event]
|
||||||
|
-> (world0 -> Maybe world0)
|
||||||
|
-> Maybe world0
|
||||||
|
applyEvents eventFn startWorld events worldUpdate =
|
||||||
|
case foldM (applyH eventFn) startWorld events of
|
||||||
|
Just w -> fmap (\w' -> w') $ worldUpdate w
|
||||||
|
Nothing -> Nothing
|
||||||
|
where
|
||||||
|
applyH :: (world0 -> Event -> Maybe world0) -> world0 -> Event -> Maybe world0
|
||||||
|
applyH f w e
|
||||||
|
= case eventPayload e of
|
||||||
|
QuitEvent -> Nothing
|
||||||
|
WindowClosedEvent _ -> Nothing
|
||||||
|
_ -> f w e
|
||||||
|
|
||||||
|
-- applyResizeExits :: (world -> Event -> Maybe world) -> (world -> Event -> Maybe world)
|
||||||
|
-- applyResizeExits f w e
|
||||||
|
-- = case eventPayload e of
|
||||||
|
-- QuitEvent -> Nothing
|
||||||
|
-- WindowClosedEvent _ -> Nothing
|
||||||
|
-- WindowSizeChangedEvent
|
||||||
|
-- (WindowSizeChangedEventData {windowSizeChangedEventWindow = V2 x y})
|
||||||
|
-- -> GL.viewport
|
||||||
|
-- _ -> f w e
|
||||||
|
applyEventIO :: (world -> Event -> Maybe world) -> Maybe world -> Event -> IO (Maybe world)
|
||||||
|
applyEventIO fn mw e = case eventPayload e of
|
||||||
|
QuitEvent -> return Nothing
|
||||||
|
WindowClosedEvent _ -> return Nothing
|
||||||
|
WindowSizeChangedEvent
|
||||||
|
(WindowSizeChangedEventData {windowSizeChangedEventSize = V2 x y})
|
||||||
|
-> GL.viewport $= (GL.Position 0 0,GL.Size x y) >> return (mw >>= \w -> fn w e)
|
||||||
|
_ -> return $ mw >>= flip fn e
|
||||||
|
|
||||||
|
applyEventsIO :: (world0 -> Event -> Maybe world0)
|
||||||
|
-> world0
|
||||||
|
-> [Event]
|
||||||
|
-> IO (Maybe world0)
|
||||||
|
applyEventsIO fn w es = foldM (applyEventIO fn) (Just w) es
|
||||||
+249
@@ -0,0 +1,249 @@
|
|||||||
|
--{-# LANGUAGE BangPatterns #-}
|
||||||
|
{-# LANGUAGE Strict #-}
|
||||||
|
module Picture
|
||||||
|
( module Picture.Data
|
||||||
|
, polygon
|
||||||
|
, arc
|
||||||
|
, arcSolid
|
||||||
|
, thickArc
|
||||||
|
, thickCircle
|
||||||
|
, circleSolid
|
||||||
|
, circle
|
||||||
|
, line
|
||||||
|
, text
|
||||||
|
, pictures
|
||||||
|
, translate
|
||||||
|
, rotate
|
||||||
|
, rotateRad
|
||||||
|
, scale
|
||||||
|
, blank
|
||||||
|
, color
|
||||||
|
, withAlpha
|
||||||
|
, greyN
|
||||||
|
, red
|
||||||
|
, green
|
||||||
|
, blue
|
||||||
|
, yellow
|
||||||
|
, cyan
|
||||||
|
, magenta
|
||||||
|
, rose
|
||||||
|
, violet
|
||||||
|
, azure
|
||||||
|
, aquamarine
|
||||||
|
, chartreuse
|
||||||
|
, orange
|
||||||
|
, white
|
||||||
|
, black
|
||||||
|
, dim
|
||||||
|
, light
|
||||||
|
, dark
|
||||||
|
, bright
|
||||||
|
, mixColors
|
||||||
|
, zeroZ
|
||||||
|
, setDepth
|
||||||
|
)
|
||||||
|
where
|
||||||
|
import Geometry
|
||||||
|
import Geometry.Data
|
||||||
|
|
||||||
|
import Picture.Data
|
||||||
|
|
||||||
|
import Data.Bifunctor
|
||||||
|
import qualified Data.DList as DL
|
||||||
|
|
||||||
|
import Graphics.Rendering.OpenGL (lineWidth, ($=),PrimitiveMode (..))
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
black :: RGBA
|
||||||
|
black = (0,0,0,1)
|
||||||
|
|
||||||
|
polygonD :: Float -> [Point2] -> Picture
|
||||||
|
{-# INLINE polygonD #-}
|
||||||
|
polygonD d (a:b:c:ps) = blank
|
||||||
|
{ _scPosTri = DL.fromList $ map (\(x,y) -> (x,y,d)) tris
|
||||||
|
, _scColTri = DL.fromList $ map (const black) tris
|
||||||
|
}
|
||||||
|
where twoPs = zip (b:c:ps) (c:ps)
|
||||||
|
tris = concatMap (\(x,y)-> [a,x,y]) twoPs
|
||||||
|
polygonD _ _ = blank
|
||||||
|
|
||||||
|
polygon :: [Point2] -> Picture
|
||||||
|
{-# INLINE polygon #-}
|
||||||
|
polygon (a:b:c:ps) = blank
|
||||||
|
{ _scPosTri = DL.fromList $ map zeroZ tris
|
||||||
|
, _scColTri = DL.fromList $ map (const black) tris
|
||||||
|
}
|
||||||
|
where twoPs = zip (b:c:ps) (c:ps)
|
||||||
|
tris = concatMap (\(x,y)-> [a,x,y]) twoPs
|
||||||
|
polygon _ = blank
|
||||||
|
|
||||||
|
color :: RGBA -> Picture -> Picture
|
||||||
|
{-# INLINE color #-}
|
||||||
|
color c = over scColTri (DL.map $ const c) . over scColChar (DL.map $ const c)
|
||||||
|
|
||||||
|
translate3 :: Float -> Float -> Point3 -> Point3
|
||||||
|
{-# INLINE translate3 #-}
|
||||||
|
translate3 a b (x,y,z) = (x+a,y+b,z)
|
||||||
|
|
||||||
|
translate :: Float -> Float -> Picture -> Picture
|
||||||
|
{-# INLINE translate #-}
|
||||||
|
translate x y = over scPosTri (DL.map $ translate3 x y) . over scPosChar (DL.map $ translate3 x y)
|
||||||
|
|
||||||
|
setDepth :: Float -> Picture -> Picture
|
||||||
|
setDepth d = over scPosTri (DL.map $ (\(x,y,z)->(x,y,d)))
|
||||||
|
. over scPosChar (DL.map $ (\(x,y,z)->(x,y,d)))
|
||||||
|
|
||||||
|
--overVs' :: (CPoint2 -> CPoint) -> Picture -> Picture
|
||||||
|
----{-# INLINE overVs' #-}
|
||||||
|
--overVs' f !(Scene a b) = let !a' = map f a
|
||||||
|
-- !b' = map g b
|
||||||
|
-- in Scene a' b'
|
||||||
|
-- where g !(x,y,z) = let !(x',y') = f (x,y)
|
||||||
|
-- in (x',y',z)
|
||||||
|
|
||||||
|
scale3 :: Float -> Float -> Point3 -> Point3
|
||||||
|
{-# INLINE scale3 #-}
|
||||||
|
scale3 a b (x,y,z) = (x*a,y*b,z)
|
||||||
|
|
||||||
|
scale :: Float -> Float -> Picture -> Picture
|
||||||
|
{-# INLINE scale #-}
|
||||||
|
scale x y = over scPosTri (DL.map $ scale3 x y) . over scPosChar (DL.map $ scale3 x y)
|
||||||
|
|
||||||
|
rotate3 :: Float -> Point3 -> Point3
|
||||||
|
{-# INLINE rotate3 #-}
|
||||||
|
rotate3 a (x,y,z) = (x',y',z)
|
||||||
|
where (x',y') = rotateV a (x,y)
|
||||||
|
|
||||||
|
rotate :: Float -> Picture -> Picture
|
||||||
|
{-# INLINE rotate #-}
|
||||||
|
rotate a = over scPosTri (DL.map $ rotate3 (0 - degToRad a))
|
||||||
|
. over scPosChar (DL.map $ rotate3 (0 - degToRad a))
|
||||||
|
|
||||||
|
-- -- this rotation uses radians, and is anticlockwise
|
||||||
|
rotateRad :: Float -> Picture -> Picture
|
||||||
|
{-# INLINE rotateRad #-}
|
||||||
|
rotateRad a = over scPosTri (DL.map $ rotate3 a)
|
||||||
|
. over scPosChar (DL.map $ rotate3 a)
|
||||||
|
|
||||||
|
pictures :: [Picture] -> Picture
|
||||||
|
{-# INLINE pictures #-}
|
||||||
|
pictures = mconcat
|
||||||
|
|
||||||
|
|
||||||
|
makeArc :: Float -> (Float,Float) -> [Point2]
|
||||||
|
{-# INLINE makeArc #-}
|
||||||
|
makeArc rad (a,b) = zipWith rotateV as $ repeat (0,rad)
|
||||||
|
where as = [a,a+step.. b]
|
||||||
|
step = pi * 0.1
|
||||||
|
|
||||||
|
circleSolidD :: Float -> Float -> Picture
|
||||||
|
circleSolidD d r = polygonD d $ makeArc r (0,2*pi)
|
||||||
|
|
||||||
|
circleSolid :: Float -> Picture
|
||||||
|
{-# INLINE circleSolid #-}
|
||||||
|
circleSolid rad = polygon $ makeArc rad (0,2*pi)
|
||||||
|
|
||||||
|
circle :: Float -> Picture
|
||||||
|
{-# INLINE circle #-}
|
||||||
|
circle rad = line $ makeArc rad (0,2*pi)
|
||||||
|
|
||||||
|
--text :: String -> Picture
|
||||||
|
--text s = zipWith (\x -> overVs $ first $ translate3 x 0)
|
||||||
|
-- [0,0.25..]
|
||||||
|
-- $ map charToShape s
|
||||||
|
|
||||||
|
text :: String -> Picture
|
||||||
|
{-# INLINE text #-}
|
||||||
|
text s = mconcat $ zipWith (\x -> translate x (0-dimText))
|
||||||
|
[0,0.9*dimText..]
|
||||||
|
(map charToScene s)
|
||||||
|
|
||||||
|
charToScene :: Char -> Picture
|
||||||
|
charToScene c = Scene
|
||||||
|
{_scPosTri = DL.empty
|
||||||
|
,_scColTri = DL.empty
|
||||||
|
,_scPosChar = DL.fromList [(0.0 ,0.0 ,0)
|
||||||
|
,(dimText,0.0 ,0)
|
||||||
|
,(dimText,dimText*2,0)
|
||||||
|
,(0.0 ,0.0 ,0)
|
||||||
|
,(dimText,dimText*2,0)
|
||||||
|
,(0.0 ,dimText*2,0)
|
||||||
|
]
|
||||||
|
,_scColChar = DL.fromList $ take 6 $ repeat white
|
||||||
|
,_scTexChar = DL.fromList $ [(s,1) ,(e,1) ,(e,0) ,(s,1) ,(e,0) ,(s,0)]
|
||||||
|
}
|
||||||
|
where x = 1/128
|
||||||
|
s = offset * x
|
||||||
|
e = s + x
|
||||||
|
offset = fromIntegral (fromEnum c) - 32
|
||||||
|
|
||||||
|
|
||||||
|
dimText = 100
|
||||||
|
|
||||||
|
line :: [Point2] -> Picture
|
||||||
|
{-# INLINE line #-}
|
||||||
|
line ps = thickLine ps 1
|
||||||
|
|
||||||
|
thickLine :: [Point2] -> Float -> Picture
|
||||||
|
{-# INLINE thickLine #-}
|
||||||
|
thickLine ps x = blank
|
||||||
|
|
||||||
|
thickCircle :: Float -> Float -> Picture
|
||||||
|
{-# INLINE thickCircle #-}
|
||||||
|
thickCircle rad wdth = thickLine (makeArc rad (0,2*pi)) wdth
|
||||||
|
|
||||||
|
arcSolid :: Float -> Float -> Float -> Picture
|
||||||
|
{-# INLINE arcSolid #-}
|
||||||
|
arcSolid startA endA rad
|
||||||
|
= polygon $ (0,0) : makeArc rad (startA,endA)
|
||||||
|
|
||||||
|
arc startA endA rad = thickArc startA endA rad 1
|
||||||
|
{-# INLINE arc #-}
|
||||||
|
|
||||||
|
thickArc :: Float -> Float -> Float -> Float -> Picture
|
||||||
|
{-# INLINE thickArc #-}
|
||||||
|
thickArc startA endA rad wdth
|
||||||
|
= thickLine (makeArc rad (startA,endA)) wdth
|
||||||
|
|
||||||
|
withAlpha :: Float -> RGBA -> RGBA
|
||||||
|
{-# INLINE withAlpha #-}
|
||||||
|
withAlpha a (x,y,z,a') = (x,y,z,a*a')
|
||||||
|
|
||||||
|
red,green,blue,yellow,cyan,magenta,rose,violet,azure,aquamarine,chartreuse,orange,white::Color
|
||||||
|
red = (1,0,0,1)
|
||||||
|
green = (0,1,0,1)
|
||||||
|
blue = (0,0,1,1)
|
||||||
|
yellow = (1,1,0,1)
|
||||||
|
cyan = (0,1,1,1)
|
||||||
|
magenta = (1,0,1,1)
|
||||||
|
rose = (1,0,0.5,1)
|
||||||
|
violet = (0.5,0,1,1)
|
||||||
|
azure = (0,0.5,1,1)
|
||||||
|
aquamarine= (0,1,0.5,1)
|
||||||
|
chartreuse= (0.5,1,0,1)
|
||||||
|
orange = (1,0.5,0,1)
|
||||||
|
white = (1,1,1,1)
|
||||||
|
|
||||||
|
mixColors :: Float -> Float -> Color -> Color -> Color
|
||||||
|
mixColors rata ratb (r0,g0,b0,a0) (r2,g2,b2,a2) =
|
||||||
|
let fullrat = rata + ratb
|
||||||
|
normrata = rata / fullrat
|
||||||
|
normratb = ratb / fullrat
|
||||||
|
f x y = sqrt $ normrata * x^2 + normratb * y^2
|
||||||
|
in (f r0 r2 , f g0 g2 , f b0 b2 , normrata * a0 + normratb * a2)
|
||||||
|
|
||||||
|
light :: Color -> Color
|
||||||
|
light (r,g,b,a) = (r+0.2,g+0.2,b+0.2,a)
|
||||||
|
|
||||||
|
dark :: Color -> Color
|
||||||
|
dark (r,g,b,a) = (r-0.2,g-0.2,b-0.2,a)
|
||||||
|
|
||||||
|
dim :: Color -> Color
|
||||||
|
dim (r,g,b,a) = (r/1.2,g/1.2,b/1.2,a)
|
||||||
|
|
||||||
|
bright :: Color -> Color
|
||||||
|
bright (r,g,b,a) = (r*1.2,g*1.2,b*1.2,a)
|
||||||
|
|
||||||
|
greyN :: Float -> Color
|
||||||
|
greyN x = (x,x,x,1)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE Strict #-}
|
||||||
|
module Picture.Data
|
||||||
|
where
|
||||||
|
import Data.Monoid
|
||||||
|
import qualified Data.DList as DL
|
||||||
|
import Geometry.Data
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
type RGBA = (Float,Float,Float,Float)
|
||||||
|
type Color = (Float,Float,Float,Float)
|
||||||
|
|
||||||
|
data Picture = Scene
|
||||||
|
{ _scPosTri :: DL.DList Point3
|
||||||
|
, _scColTri :: DL.DList RGBA
|
||||||
|
, _scPosChar :: DL.DList Point3
|
||||||
|
, _scColChar :: DL.DList RGBA
|
||||||
|
, _scTexChar :: DL.DList Point2
|
||||||
|
}
|
||||||
|
|
||||||
|
blank :: Picture
|
||||||
|
{-# INLINE blank #-}
|
||||||
|
blank = Scene DL.empty DL.empty DL.empty DL.empty DL.empty
|
||||||
|
|
||||||
|
combineScenes :: Picture -> Picture -> Picture
|
||||||
|
{-# SCC combineScenes #-}
|
||||||
|
--{-# INLINE combineScenes #-}
|
||||||
|
combineScenes sa sb = Scene
|
||||||
|
{ _scPosTri = _scPosTri sa <> _scPosTri sb
|
||||||
|
, _scColTri = _scColTri sa <> _scColTri sb
|
||||||
|
, _scPosChar = _scPosChar sa <> _scPosChar sb
|
||||||
|
, _scColChar = _scColChar sa <> _scColChar sb
|
||||||
|
, _scTexChar = _scTexChar sa <> _scTexChar sb
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Semigroup Picture where
|
||||||
|
(<>) = combineScenes
|
||||||
|
instance Monoid Picture where
|
||||||
|
mempty = blank
|
||||||
|
--mappend = combineScenes
|
||||||
|
|
||||||
|
makeLenses ''Picture
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE Strict #-}
|
||||||
|
module Picture.Preload
|
||||||
|
where
|
||||||
|
|
||||||
|
import Codec.Picture
|
||||||
|
import Graphics.Rendering.OpenGL hiding (Point (..),translate,scale,imageHeight,imageWidth)
|
||||||
|
import qualified Graphics.Rendering.OpenGL as GL
|
||||||
|
|
||||||
|
import qualified Data.Vector.Storable as V
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
|
||||||
|
import Foreign
|
||||||
|
|
||||||
|
import Shaders
|
||||||
|
|
||||||
|
data PreloadData = PreloadData
|
||||||
|
{ _charMap :: Image PixelRGBA8
|
||||||
|
-- , _textures :: [Image PixelRGBA8]
|
||||||
|
, _basicShader :: Program
|
||||||
|
, _textShader :: Program
|
||||||
|
, _fadeCircleShader :: Program
|
||||||
|
, _basicVAO :: VertexArrayObject
|
||||||
|
, _textVAO :: VertexArrayObject
|
||||||
|
, _posVBO :: BufferObject
|
||||||
|
, _colVBO :: BufferObject
|
||||||
|
, _texVBO :: BufferObject
|
||||||
|
, _ptrPosVBO :: Ptr Float
|
||||||
|
, _ptrColVBO :: Ptr Float
|
||||||
|
, _ptrTexVBO :: Ptr Float
|
||||||
|
}
|
||||||
|
|
||||||
|
makeLenses ''PreloadData
|
||||||
|
|
||||||
|
|
||||||
|
loadTextures :: IO [Image PixelRGBA8]
|
||||||
|
loadTextures = do
|
||||||
|
-- echarMap <- readImage "data/texture/smudgedDirt.png"
|
||||||
|
echarMap <- readImage "data/texture/charMap.png"
|
||||||
|
case echarMap of
|
||||||
|
Left err -> do
|
||||||
|
putStrLn err
|
||||||
|
return []
|
||||||
|
Right charMap ->
|
||||||
|
return [convertRGBA8 charMap]
|
||||||
|
|
||||||
|
bufferOffset :: Integral a => a -> Ptr b
|
||||||
|
bufferOffset = plusPtr nullPtr . fromIntegral
|
||||||
|
|
||||||
|
doPreload' :: IO PreloadData
|
||||||
|
doPreload' = do
|
||||||
|
-- compile shader programs
|
||||||
|
bs <- makeBasicShader
|
||||||
|
ts <- makeTextureShader
|
||||||
|
fcs <- makeFadeShader
|
||||||
|
|
||||||
|
--setup vbos
|
||||||
|
--need to be bound before assigning the vertex attribute pointers
|
||||||
|
posVBO <- genObjectName
|
||||||
|
colVBO <- genObjectName
|
||||||
|
texVBO <- genObjectName
|
||||||
|
|
||||||
|
-- load charmap as texture
|
||||||
|
-- Right cmap <- readImage "data/texture/smudgedDirt.png"
|
||||||
|
Right cmap <- readImage "data/texture/charMap.png"
|
||||||
|
let tex = convertRGBA8 cmap
|
||||||
|
texo <- genObjectName
|
||||||
|
textureBinding Texture2D $= Just texo
|
||||||
|
let texData = V.toList $ imageData tex
|
||||||
|
wtex = fromIntegral $ imageWidth tex
|
||||||
|
htex = fromIntegral $ imageHeight tex
|
||||||
|
withArray texData $ \ptr -> do
|
||||||
|
texImage2D Texture2D NoProxy 0 RGBA8 (TextureSize2D wtex htex) 0
|
||||||
|
(PixelData RGBA UnsignedByte ptr)
|
||||||
|
generateMipmap' Texture2D
|
||||||
|
textureFilter Texture2D $= ((Linear',Just Linear') , Nearest)
|
||||||
|
|
||||||
|
let vertexSize = sizeOf (0.5 :: GLfloat)
|
||||||
|
firstIndex = 0
|
||||||
|
|
||||||
|
-- setup VAOs: basic vao
|
||||||
|
basicVAO <- genObjectName
|
||||||
|
bindVertexArrayObject $= Just basicVAO
|
||||||
|
|
||||||
|
-- let posAttrib = AttribLocation 0
|
||||||
|
-- colAttrib = AttribLocation 1
|
||||||
|
bindBuffer ArrayBuffer $= Just posVBO
|
||||||
|
vertexAttribPointer (AttribLocation 0) $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 3 Float (fromIntegral $ vertexSize * 3)
|
||||||
|
(bufferOffset (firstIndex * vertexSize))
|
||||||
|
)
|
||||||
|
vertexAttribArray (AttribLocation 0) $= Enabled
|
||||||
|
|
||||||
|
bindBuffer ArrayBuffer $= Just colVBO
|
||||||
|
vertexAttribPointer (AttribLocation 1) $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 4 Float (fromIntegral $ vertexSize * 4)
|
||||||
|
(bufferOffset (firstIndex * vertexSize) )
|
||||||
|
)
|
||||||
|
vertexAttribArray (AttribLocation 1) $= Enabled
|
||||||
|
|
||||||
|
-- setup second vao
|
||||||
|
textVAO <- genObjectName
|
||||||
|
bindVertexArrayObject $= Just textVAO
|
||||||
|
|
||||||
|
bindBuffer ArrayBuffer $= Just posVBO
|
||||||
|
vertexAttribPointer (AttribLocation 0) $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 3 Float (fromIntegral $ vertexSize * 3)
|
||||||
|
(bufferOffset (firstIndex * vertexSize))
|
||||||
|
)
|
||||||
|
vertexAttribArray (AttribLocation 0) $= Enabled
|
||||||
|
|
||||||
|
bindBuffer ArrayBuffer $= Just colVBO
|
||||||
|
vertexAttribPointer (AttribLocation 1) $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 4 Float (fromIntegral $ vertexSize * 4)
|
||||||
|
(bufferOffset (firstIndex * vertexSize) )
|
||||||
|
)
|
||||||
|
vertexAttribArray (AttribLocation 1) $= Enabled
|
||||||
|
|
||||||
|
bindBuffer ArrayBuffer $= Just texVBO
|
||||||
|
vertexAttribPointer (AttribLocation 2) $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 2 Float (fromIntegral $ vertexSize * 2)
|
||||||
|
(bufferOffset (firstIndex * vertexSize) )
|
||||||
|
)
|
||||||
|
vertexAttribArray (AttribLocation 2) $= Enabled
|
||||||
|
|
||||||
|
-- allocate memory for vertex attributes
|
||||||
|
ptrPosVBO <- mallocArray (3*3*5000)
|
||||||
|
ptrColVBO <- mallocArray (3*4*5000)
|
||||||
|
ptrTexVBO <- mallocArray (3*2*5000)
|
||||||
|
|
||||||
|
return $ PreloadData
|
||||||
|
{ _charMap = convertRGBA8 cmap
|
||||||
|
, _basicShader = bs
|
||||||
|
, _textShader = ts
|
||||||
|
, _fadeCircleShader = fcs
|
||||||
|
, _basicVAO = basicVAO
|
||||||
|
, _textVAO = textVAO
|
||||||
|
, _posVBO = posVBO
|
||||||
|
, _colVBO = colVBO
|
||||||
|
, _texVBO = texVBO
|
||||||
|
, _ptrPosVBO = ptrPosVBO
|
||||||
|
, _ptrColVBO = ptrColVBO
|
||||||
|
, _ptrTexVBO = ptrTexVBO
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpPreload :: PreloadData -> IO ()
|
||||||
|
cleanUpPreload pd = do
|
||||||
|
free (_ptrPosVBO pd)
|
||||||
|
free (_ptrColVBO pd)
|
||||||
|
free (_ptrTexVBO pd)
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
{-# LANGUAGE Strict #-}
|
||||||
|
module Picture.Render
|
||||||
|
where
|
||||||
|
import Control.Lens
|
||||||
|
import Picture
|
||||||
|
import Geometry
|
||||||
|
|
||||||
|
import Picture.Preload
|
||||||
|
--import Control.Lens
|
||||||
|
|
||||||
|
import Foreign
|
||||||
|
|
||||||
|
import Codec.Picture
|
||||||
|
|
||||||
|
import Graphics.Rendering.OpenGL hiding (translate,scale,imageHeight,imageWidth)
|
||||||
|
import qualified Graphics.Rendering.OpenGL as GL
|
||||||
|
|
||||||
|
import Data.Foldable
|
||||||
|
import qualified Data.Vector.Storable as V
|
||||||
|
import qualified Data.DList as DL
|
||||||
|
|
||||||
|
import Control.DeepSeq
|
||||||
|
|
||||||
|
concat34 :: (Point3,RGBA) -> [Float]
|
||||||
|
concat34 ((x,y,z),(r,g,b,a)) = [x,y,z,r,g,b,a]
|
||||||
|
|
||||||
|
|
||||||
|
toVec2 (x,y) = Vector2 x y
|
||||||
|
toVec3 (x,y,z) = Vector3 x y z
|
||||||
|
toVec4 (x,y,z,w) = Vector4 x y z w
|
||||||
|
|
||||||
|
flat2 (x,y) = DL.fromList [x,y]
|
||||||
|
flat3 (x,y,z) = DL.fromList [x,y,z]
|
||||||
|
flat4 (x,y,z,w) = DL.fromList [x,y,z,w]
|
||||||
|
|
||||||
|
mapFlat :: (NFData b) => (a -> DL.DList b) -> DL.DList a -> [b]
|
||||||
|
mapFlat f = force . DL.toList . foldMap f
|
||||||
|
|
||||||
|
--marshallVs = V.unsafeWith . V.fromList
|
||||||
|
--marshallVs = withArray
|
||||||
|
|
||||||
|
renderPicture' :: PreloadData -> Picture -> IO ()
|
||||||
|
renderPicture' pdata (Scene posTri' colTri' posChar' colChar' texChar') = do
|
||||||
|
let posTri = DL.toList posTri'
|
||||||
|
colTri = DL.toList colTri'
|
||||||
|
posChar = DL.toList posChar'
|
||||||
|
colChar = DL.toList colChar'
|
||||||
|
texChar = DL.toList texChar'
|
||||||
|
currentProgram $= Just (_basicShader pdata)
|
||||||
|
bindVertexArrayObject $= Just (_basicVAO pdata)
|
||||||
|
bindBuffer ArrayBuffer $= Just (_posVBO pdata)
|
||||||
|
let firstIndex = 0
|
||||||
|
let vertices :: [Float]
|
||||||
|
vertices = mapFlat flat3 posTri'
|
||||||
|
numVertices = length vertices
|
||||||
|
vertexSize = sizeOf (head vertices)
|
||||||
|
pokeArray (_ptrPosVBO pdata) vertices
|
||||||
|
bufferData ArrayBuffer $= (fromIntegral (numVertices * vertexSize), _ptrPosVBO pdata, StreamDraw)
|
||||||
|
-- marshallVs vertices $ \ptr -> do
|
||||||
|
-- bufferData ArrayBuffer $= (fromIntegral (numVertices * vertexSize), ptr, StreamDraw)
|
||||||
|
bindBuffer ArrayBuffer $= Just (_colVBO pdata)
|
||||||
|
let colVs = mapFlat flat4 colTri'
|
||||||
|
colVsize = sizeOf $ head colVs
|
||||||
|
pokeArray (_ptrColVBO pdata) colVs
|
||||||
|
bufferData ArrayBuffer $= (fromIntegral (div (4 * numVertices * colVsize) 3), _ptrColVBO pdata, StreamDraw)
|
||||||
|
-- marshallVs colVs $ \ptr -> do
|
||||||
|
-- bufferData ArrayBuffer $= (fromIntegral (div (4 * numVertices * colVsize) 3), ptr, StreamDraw)
|
||||||
|
drawArrays Triangles (fromIntegral firstIndex) (fromIntegral $ numVertices)
|
||||||
|
|
||||||
|
-- currentProgram $= Just (_textShader pdata)
|
||||||
|
-- bindVertexArrayObject $= Just (_textVAO pdata)
|
||||||
|
-- bindBuffer ArrayBuffer $= Just (_posVBO pdata)
|
||||||
|
-- let vertices' = mapFlat flat3 posChar'
|
||||||
|
-- numVertices' = length vertices'
|
||||||
|
-- vertexSize' = sizeOf $ head vertices'
|
||||||
|
-- withArray vertices' $ \ptr -> do
|
||||||
|
-- bufferData ArrayBuffer $= (fromIntegral (numVertices' * vertexSize'), ptr, StreamDraw)
|
||||||
|
-- bindBuffer ArrayBuffer $= Just (_colVBO pdata)
|
||||||
|
-- let colVsT = mapFlat flat4 colChar'
|
||||||
|
-- colVsTsize = sizeOf $ head colVsT
|
||||||
|
-- withArrayLen colVsT $ \numV' ptr -> do
|
||||||
|
-- bufferData ArrayBuffer $= (fromIntegral (numVertices' * colVsTsize), ptr, StreamDraw)
|
||||||
|
-- bindBuffer ArrayBuffer $= Just (_texVBO pdata)
|
||||||
|
-- let texVsT = mapFlat flat2 texChar'
|
||||||
|
-- texVsTsize = sizeOf $ head texVsT
|
||||||
|
-- withArrayLen texVsT $ \numV' ptr -> do
|
||||||
|
-- bufferData ArrayBuffer $= (fromIntegral (numVertices' * texVsTsize), ptr, StreamDraw)
|
||||||
|
-- drawArrays Triangles (fromIntegral firstIndex) (fromIntegral $ numVertices')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bufferOffset :: Integral a => a -> Ptr b
|
||||||
|
bufferOffset = plusPtr nullPtr . fromIntegral
|
||||||
|
|
||||||
|
zeroZ :: Point2 -> Point3
|
||||||
|
zeroZ (x,y) = (x,y,0)
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
module Render where
|
||||||
|
import Shapes
|
||||||
|
import Graphics.Rendering.OpenGL hiding (imageHeight,imageWidth)
|
||||||
|
import Foreign
|
||||||
|
import Loop
|
||||||
|
import Codec.Picture
|
||||||
|
import qualified Data.Vector.Storable as V
|
||||||
|
|
||||||
|
|
||||||
|
render :: [Program] -> b -> Shape -> IO ()
|
||||||
|
render progs _ (Shape primitiveMode vs) = do
|
||||||
|
currentProgram $= Just (progs !! 0)
|
||||||
|
let vertices = flattenVertices vs
|
||||||
|
numVertices = length vertices
|
||||||
|
vertexSize = sizeOf (head vertices)
|
||||||
|
|
||||||
|
-- setup VBO
|
||||||
|
shape <- genObjectName
|
||||||
|
bindVertexArrayObject $= Just shape
|
||||||
|
|
||||||
|
-- setup VBA
|
||||||
|
arrayBuffer <- genObjectName
|
||||||
|
bindBuffer ArrayBuffer $= Just arrayBuffer
|
||||||
|
withArray vertices $ \ptr -> do
|
||||||
|
bufferData ArrayBuffer $= (fromIntegral (numVertices * vertexSize), ptr, StaticDraw)
|
||||||
|
|
||||||
|
let firstIndex = 0
|
||||||
|
vPosition = AttribLocation 0
|
||||||
|
vColor = AttribLocation 1
|
||||||
|
vertexAttribPointer vPosition $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 3 Float (fromIntegral $ vertexSize * 7)
|
||||||
|
(bufferOffset (firstIndex * vertexSize))
|
||||||
|
)
|
||||||
|
vertexAttribArray vPosition $= Enabled
|
||||||
|
|
||||||
|
vertexAttribPointer vColor $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 4 Float (fromIntegral $ vertexSize * 7)
|
||||||
|
(bufferOffset ((firstIndex + 3) * vertexSize) )
|
||||||
|
)
|
||||||
|
vertexAttribArray vColor $= Enabled
|
||||||
|
|
||||||
|
-- clear [ColorBuffer,DepthBuffer]
|
||||||
|
|
||||||
|
bindVertexArrayObject $= Just shape
|
||||||
|
drawArrays primitiveMode (fromIntegral firstIndex) (fromIntegral $ div numVertices 2)
|
||||||
|
|
||||||
|
|
||||||
|
render (_:prog:_) _ (Fade ps) = do
|
||||||
|
currentProgram $= Just prog
|
||||||
|
let vertices = flatten2 ps
|
||||||
|
numVertices = length vertices
|
||||||
|
vertexSize = sizeOf (head vertices)
|
||||||
|
|
||||||
|
-- -- setup uniform
|
||||||
|
-- location <- get $ uniformLocation prog "winSize"
|
||||||
|
-- uniform location $= winSizeVec
|
||||||
|
|
||||||
|
-- setup VBO
|
||||||
|
arrayBuffer <- genObjectName
|
||||||
|
bindBuffer ArrayBuffer $= Just arrayBuffer
|
||||||
|
withArray vertices $ \ptr -> do
|
||||||
|
bufferData ArrayBuffer $= (fromIntegral (numVertices * vertexSize), ptr, StreamDraw)
|
||||||
|
|
||||||
|
-- setup VAO
|
||||||
|
shape <- genObjectName
|
||||||
|
bindVertexArrayObject $= Just shape
|
||||||
|
|
||||||
|
let firstIndex = 0
|
||||||
|
inPosition = AttribLocation 0
|
||||||
|
inColor = AttribLocation 1
|
||||||
|
inRad = AttribLocation 2
|
||||||
|
vertexAttribPointer inPosition $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 3 Float (fromIntegral $ vertexSize * 8)
|
||||||
|
(bufferOffset (firstIndex * vertexSize))
|
||||||
|
)
|
||||||
|
vertexAttribArray inPosition $= Enabled
|
||||||
|
|
||||||
|
vertexAttribPointer inColor $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 4 Float (fromIntegral $ vertexSize * 8)
|
||||||
|
(bufferOffset ((firstIndex + 3) * vertexSize) )
|
||||||
|
)
|
||||||
|
vertexAttribArray inColor $= Enabled
|
||||||
|
|
||||||
|
vertexAttribPointer inRad $=
|
||||||
|
( ToFloat
|
||||||
|
, VertexArrayDescriptor 1 Float (fromIntegral $ vertexSize * 8)
|
||||||
|
(bufferOffset ((firstIndex + 7) * vertexSize) )
|
||||||
|
)
|
||||||
|
vertexAttribArray inRad $= Enabled
|
||||||
|
|
||||||
|
-- bindVertexArrayObject $= Just shape
|
||||||
|
drawArrays Points (fromIntegral firstIndex) (2)
|
||||||
|
|
||||||
|
|
||||||
|
flattenVertices :: [CPoint] -> [GLfloat]
|
||||||
|
flattenVertices = map realToFrac . concatMap (\(CPoint (x,y,z) (r,b,g,a)) -> [x,y,z,r,b,g,a])
|
||||||
|
|
||||||
|
flatten2 :: [((Float,Float,Float),(Float,Float,Float,Float),Float)] -> [GLfloat]
|
||||||
|
flatten2 = map realToFrac . concatMap f
|
||||||
|
where f ((x,y,z),(r,g,b,a),rad) = [x,y,z,r,g,b,a,rad]
|
||||||
|
|
||||||
|
bufferOffset :: Integral a => a -> Ptr b
|
||||||
|
bufferOffset = plusPtr nullPtr . fromIntegral
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
module Rendering.Picture.Color
|
||||||
|
where
|
||||||
|
import Geometry.Data
|
||||||
|
|
||||||
|
type RGBA = (Float,Float,Float,Float)
|
||||||
|
|
||||||
|
black,red,green,blue,yellow,cyan,magenta,rose,violet,azure,aquamarine,chartreuse,orange,white::RGBA
|
||||||
|
black = (0,0,0,1)
|
||||||
|
red = (1,0,0,1)
|
||||||
|
green = (0,1,0,1)
|
||||||
|
blue = (0,0,1,1)
|
||||||
|
yellow = (1,1,0,1)
|
||||||
|
cyan = (0,1,1,1)
|
||||||
|
magenta = (1,0,1,1)
|
||||||
|
rose = (1,0,0.5,1)
|
||||||
|
violet = (0.5,0,1,1)
|
||||||
|
azure = (0,0.5,1,1)
|
||||||
|
aquamarine= (0,1,0.5,1)
|
||||||
|
chartreuse= (0.5,1,0,1)
|
||||||
|
orange = (1,0.5,0,1)
|
||||||
|
white = (1,1,1,1)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
module Shaders
|
||||||
|
(setupBasicShader
|
||||||
|
,setupFadeShader
|
||||||
|
,setupShaders
|
||||||
|
,makeTextureShader
|
||||||
|
,makeBasicShader
|
||||||
|
,makeFadeShader
|
||||||
|
)
|
||||||
|
where
|
||||||
|
import Graphics.Rendering.OpenGL
|
||||||
|
import Control.Monad (when)
|
||||||
|
import Text.RawString.QQ
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
|
||||||
|
setupShaders :: IO [Program]
|
||||||
|
setupShaders = do
|
||||||
|
basicShader <- makeBasicShader
|
||||||
|
fadeShader <- makeFadeShader
|
||||||
|
textureShader <- makeTextureShader
|
||||||
|
return [basicShader,fadeShader,textureShader]
|
||||||
|
|
||||||
|
makeBasicShader :: IO Program
|
||||||
|
makeBasicShader = do
|
||||||
|
vsSource <- BS.readFile "shader/basic.vert"
|
||||||
|
fsSource <- BS.readFile "shader/basic.frag"
|
||||||
|
|
||||||
|
makeShaderProgram [(VertexShader, vsSource)
|
||||||
|
,(FragmentShader, fsSource)
|
||||||
|
]
|
||||||
|
|
||||||
|
setupBasicShader :: IO ()
|
||||||
|
setupBasicShader = do
|
||||||
|
shaderProgram <- makeBasicShader
|
||||||
|
currentProgram $= (Just shaderProgram)
|
||||||
|
|
||||||
|
makeFadeShader :: IO Program
|
||||||
|
makeFadeShader = do
|
||||||
|
vsSource <- BS.readFile "shader/fadeCircle.vert"
|
||||||
|
gsSource <- BS.readFile "shader/fadeCircle.geom"
|
||||||
|
fsSource <- BS.readFile "shader/fadeCircle.frag"
|
||||||
|
|
||||||
|
makeShaderProgram [(VertexShader, vsSource )
|
||||||
|
,(GeometryShader, gsSource )
|
||||||
|
,(FragmentShader, fsSource )
|
||||||
|
]
|
||||||
|
|
||||||
|
setupFadeShader :: IO Program
|
||||||
|
setupFadeShader = do
|
||||||
|
shaderProgram <- makeFadeShader
|
||||||
|
currentProgram $= (Just shaderProgram)
|
||||||
|
return shaderProgram
|
||||||
|
|
||||||
|
makeTextureShader :: IO Program
|
||||||
|
makeTextureShader = do
|
||||||
|
vsSource <- BS.readFile "shader/basicTexture.vert"
|
||||||
|
fsSource <- BS.readFile "shader/basicTexture.frag"
|
||||||
|
|
||||||
|
makeShaderProgram [(VertexShader, vsSource )
|
||||||
|
,(FragmentShader, fsSource )
|
||||||
|
]
|
||||||
|
|
||||||
|
makeShaderProgram :: [(ShaderType,BS.ByteString)] -> IO Program
|
||||||
|
makeShaderProgram sources = do
|
||||||
|
shaderProgram <- createProgram
|
||||||
|
shaders <- mapM compileAndCheckShader sources
|
||||||
|
mapM_ (attachShader shaderProgram) shaders
|
||||||
|
|
||||||
|
linkProgram shaderProgram
|
||||||
|
linkingSuccess <- linkStatus shaderProgram
|
||||||
|
when (not linkingSuccess) $ do
|
||||||
|
infoLog <- get (programInfoLog shaderProgram)
|
||||||
|
putStrLn $ "Program Linking" ++ infoLog
|
||||||
|
|
||||||
|
return shaderProgram
|
||||||
|
|
||||||
|
compileAndCheckShader :: (ShaderType,BS.ByteString) -> IO Shader
|
||||||
|
compileAndCheckShader (shaderType,sourceCode) = do
|
||||||
|
theShader <- createShader shaderType
|
||||||
|
shaderSourceBS theShader $= sourceCode
|
||||||
|
compileShader theShader
|
||||||
|
success <- compileStatus theShader
|
||||||
|
when (not success) $ do
|
||||||
|
infoLog <- get (shaderInfoLog theShader)
|
||||||
|
putStrLn $ "Shader compile: " ++ show shaderType ++ " : " ++ show infoLog
|
||||||
|
return theShader
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
module Shapes where
|
||||||
|
import Geometry
|
||||||
|
import Graphics.Rendering.OpenGL
|
||||||
|
|
||||||
|
type RGBA = (Float,Float,Float,Float)
|
||||||
|
|
||||||
|
data CPoint = CPoint
|
||||||
|
{cpPoint :: Point3
|
||||||
|
,cpColor :: RGBA
|
||||||
|
}
|
||||||
|
|
||||||
|
data Shape = Shape PrimitiveMode [CPoint]
|
||||||
|
| Fade [(Point3,RGBA,Float)]
|
||||||
|
| Shape' PrimitiveMode [GLfloat]
|
||||||
|
|
||||||
|
egTri = Shape TriangleStrip [CPoint (-1,0,0) (1,1,1,1)
|
||||||
|
,CPoint (0,1,0) (1,1,0,1)
|
||||||
|
,CPoint (1,0,0) (1,0,1,0)
|
||||||
|
,CPoint (0.5,-0.5,0) (1,0,1,0)
|
||||||
|
,CPoint (0.5,-0.5,0) (1,0,1,0)
|
||||||
|
,CPoint (0.5,-0.5,0) (1,0,1,0)
|
||||||
|
]
|
||||||
|
|
||||||
|
egFade = Fade [((-0.2,-0.2,0),(1,1,1,1),500)
|
||||||
|
,((0.2,0.2,0),(1,1,1,1),500)
|
||||||
|
-- ,((0.5,0.5,0),(1,1,1,1),0.5)
|
||||||
|
-- ,((0.5,0.5,0),(1,1,1,1),0.5)
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user