Files
loop/src/Dodge/Render/Picture.hs
T

220 lines
8.0 KiB
Haskell

module Dodge.Render.Picture
where
import Dodge.Data
import Dodge.Base
import Dodge.Picture
import Dodge.Picture.Layer
import Dodge.Render.HUD
import Dodge.Render.MenuScreen
import Geometry
import Picture
import Control.Lens
import Data.Maybe
import qualified Data.IntMap.Strict as IM
worldPictures :: World -> Picture
worldPictures w = pictures $ concat
[ IM.elems $ _decorations w
, map _pjPict . IM.elems $ _projectiles w
, map drawItem . IM.elems $ _floorItems w
, map crDraw . IM.elems $ _creatures w
, map clDraw . IM.elems $ _clouds w
, map ppDraw . IM.elems $ _pressPlates w
, map btDraw (IM.elems (_buttons w))
, map (\pt -> _ptDraw pt pt) $ _particles w
, map drawWallFloor (wallFloorsToDraw w)
, testPic w
]
fixedCoordPictures :: World -> Picture
fixedCoordPictures w = case _menuLayers w of
[] -> pictures
[ hudDrawings w
, customMouseCursor w
]
lays -> scaler . onLayer MenuDepth $ menuScreen (_config w) (halfWidth w) (halfHeight w) lays
where
scaler = setDepth (-1) . scale (2 / getWindowX w) (2 / getWindowY w)
customMouseCursor :: World -> Picture
customMouseCursor w =
setDepth (-1)
. scale (2 /getWindowX w) (2/ getWindowY w)
. uncurry translate (_mousePos w)
$ pictures [ line [(-5,0),(5,0)] , line [(0,-5),(0,5)] ]
testPic :: World -> [Picture]
testPic w =
[ setDepth (-1) . translate 0 0.8
. scale 0.001 0.001 . text . show . length . IM.elems . IM.filter ( (== 3) . _crRad ) $ _creatures w
]
crDraw :: Creature -> Picture
crDraw c = uncurry translate (_crPos c) $ rotate (_crDir c) (_crPict c c)
ppDraw :: PressPlate -> Picture
ppDraw c = uncurry translate (_ppPos c) $ rotate (_ppRot c) (_ppPict c)
btDraw :: Button -> Picture
btDraw c = uncurry translate (_btPos c) $ rotate (_btRot c) (_btPict c)
clDraw :: Cloud -> Picture
clDraw c = uncurry translate (_clPos c) (_clPict c c)
wallFloorsToDraw :: World -> [Wall]
wallFloorsToDraw w = filter isVisible $ IM.elems $ wallsOnScreen w
where
onScreen wall = lineOnScreen w ((\(a,b) -> [a,b]) $ _wlLine wall)
isVisible wl
| wl ^? blVisible == Just False = False
| otherwise = onScreen wl
wallsOnScreen :: World -> IM.IntMap Wall
wallsOnScreen w = wallsNearZones (zoneOfScreen w) w
drawWallFloor :: Wall -> Picture
drawWallFloor wl = if _wlIsSeeThrough wl
then setDepth 0.9 . color c $ polygon [x,x +.+ n2,y+.+n2, y]
else blank
where
(x,y) = _wlLine wl
c = _wlColor wl
n2 = 15 *.* (vNormal . errorNormalizeVDR $ y -.- x)
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 (negate 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 +.+ _cameraCenter 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
wallShadowsToDraw :: World -> [Wall]
wallShadowsToDraw w = filter (fromMaybe True . (^? blVisible))
. IM.elems
$ wallsNearZones (zoneOfSight w) w
-- 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 = _cameraViewFrom w
sp | pointInPolygon vp sp' = sp'
| otherwise = orderPolygon (_cameraViewFrom 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])
drawWallFace :: World -> Wall -> Picture
drawWallFace w wall
| isRHS sightFrom x y || _wlIsSeeThrough wall = blank
| otherwise = setDepth (-1) . color (withAlpha 0 black) . polygon $ points
where
(x,y) = _wlLine wall
points = extendConeToScreenEdge w sightFrom (x,y)
sightFrom = _cameraViewFrom w
-- the following assumes that the point a is inside the screen
-- it still works otherwise, but it might intersect two points:
-- it is not obvious which will be returned
intersectLinefromScreen :: World -> Point2 -> Point2 -> Maybe Point2
intersectLinefromScreen w a b = listToMaybe
. mapMaybe (\(x,y) -> intersectSegLineFrom' x y b (b +.+ b -.- a))
. makeLoopPairs
$ screenPolygon w
extendConeToScreenEdge :: World -> Point2 -> (Point2,Point2) -> [Point2]
extendConeToScreenEdge w c (x,y) = orderPolygon $ wallScreenIntersect ++ [x,y] ++ borderPs ++ cornerPs
where
borderPs = mapMaybe (intersectLinefromScreen w c) [x,y]
cornerPs = filter (pointIsInCone c (x,y)) $ screenPolygon w
wallScreenIntersect = mapMaybe (uncurry $ intersectSegSeg' y ((2*.*y) -.- x))
. makeLoopPairs $ screenPolygon w
rectangleSolid x y = polygon [(x,y),(x,-y),(-x,-y),(-x,y)]
drawItem :: FloorItem -> Picture
drawItem flIt = uncurry translate (_flItPos flIt)
$ rotate (_flItRot flIt) (_itFloorPict (_flIt flIt))
ffToDraw :: World -> [ForceField]
ffToDraw w = filter (lineOnScreen w . _ffLine) $
IM.elems $ over ffLine (map ( -.- _cameraCenter 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 ( _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 -.- _cameraCenter w
youOnFF = circOnSeg 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]
wallsPointsAndCols :: World -> [((Point2,Point2),Point4)]
wallsPointsAndCols w = map f . filter (not . _wlIsSeeThrough) . IM.elems $ wallsNearZones (zoneOfDoubleScreen w) w
where
f wl = (_wlLine wl, _wlColor wl)
wallsWindows :: World -> [((Point2,Point2),Point4)]
wallsWindows w = map f . filter (fromMaybe True . (^? blVisible)) .
filter _wlIsSeeThrough . IM.elems $ wallsNearZones (zoneOfDoubleScreen w) w
where
f wl = (_wlLine wl, _wlColor wl)
wallsForShadows :: World -> [(Point2,Point2)]
wallsForShadows w = map _wlLine
. filter (not . _wlIsSeeThrough)
. IM.elems $ wallsNearZones (zoneOfDoubleScreen w) w
lightsForGloom' :: World -> [Point4]
lightsForGloom' w = map getLS (IM.elems $ _lightSources w) ++ map getTLS (_tempLightSources w)
where getLS ls = ( fst $ _lsPos ls, snd $ _lsPos ls, _lsRad ls , _lsIntensity ls)
getTLS ls = ( fst $ _tlsPos ls,snd $ _tlsPos ls, _tlsRad ls, _tlsIntensity ls)