module Dodge.Render.ShapePicture ( worldSPic, ) where import Control.Lens import Control.Monad (guard) import Data.Foldable import qualified Data.Graph.Inductive as FGL import qualified Data.Map.Strict as M import Data.Maybe import qualified Data.Set as Set import Dodge.Base import Dodge.Creature.Picture import Dodge.Creature.Picture.Awareness import Dodge.Data.Universe import Dodge.Debug.Picture import Dodge.Draw import Dodge.Flare import Dodge.GameRoom import Dodge.Graph import Dodge.Path import Dodge.Picture.SizeInvariant import Dodge.RadarBlip import Dodge.Render.InfoBox import Dodge.Render.Label import Dodge.Render.List import Dodge.Render.Picture import Dodge.ShortShow import Dodge.SoundLogic.LoadSound import Dodge.Viewpoints import Dodge.WorldEvent.ThingsHit import Dodge.Zoning import Dodge.Zoning.Base import Geometry import Geometry.ConvexPoly import qualified IntMapHelp as IM import Padding import Picture import Shape import ShapePicture import ShortShow import Sound.Data worldSPic :: Configuration -> Universe -> SPic worldSPic cfig u = (mempty, extraPics cfig u) <> foldup drawProp' (filtOn _prPos _props) <> foldup drawProjectile (filtOn _prjPos _projectiles) <> foldup (shiftDraw _blPos _blDir (drawBlock . _blDraw)) (filtOn _blPos _blocks) <> foldup (shiftDraw _fsPos _fsDir (const _fsSPic)) (filtOn _fsPos _foregroundShapes) <> foldup (shiftDraw' _cpPos _cpDir _cpSPic) (filtOn _cpPos _corpses) <> foldup drawCreature (filtOn _crPos _creatures) <> foldup floorItemSPic (filtOn _flItPos _floorItems) <> foldup btSPic (filtOn _btPos _buttons) <> foldup mcSPic (filtOn _mcPos _machines) <> aimDelaySweep w <> anyTargeting cfig w where w = _uvWorld u foldup = foldMap' filtOn f g = IM.filter (pointIsClose . f) (g (_lWorld (_cWorld w))) pointIsClose = cullPoint cfig w aimDelaySweep :: World -> SPic aimDelaySweep w = fromMaybe mempty $ do cr <- w ^? cWorld . lWorld . creatures . ix 0 aimstatus <- cr ^? crStance . posture guard (aimstatus == Aiming) return $ noShape $ drawSweep cr w drawSweep :: Creature -> World -> Picture drawSweep cr w = fromMaybe mempty $ do a <- safeArgV (mwp -.- p) let a' | a - cdir > pi = cdir + 2 * pi | a - cdir < - pi = cdir - 2 * pi | otherwise = cdir return $ setLayer FixedCoordLayer $ uncurryV translate (worldPosToScreen campos p) $ arcFull (a - rot) 10 white (a' - rot) 1 white (5 + dist mwp p * campos ^. camZoom) white where cdir = _crDir cr rot = campos ^. camRot p = _crPos cr campos = w ^. cWorld . cwCamPos theinput = w ^. input mwp = mouseWorldPos theinput campos anyTargeting :: Configuration -> World -> SPic anyTargeting cfig w = fromMaybe mempty $ do tt <- cr ^? crTargeting . ctType . _Just return $ noShape $ drawTargeting tt cr cfig w where cr = you w drawCreature :: Creature -> SPic drawCreature cr = case _crType cr of Humanoid{} -> basicCrPict cr Barreloid{} -> picAtCrPos1 ( setDepth 20 $ pictures [ color orange $ circleSolid 10 , setDepth 0.049 . color (greyN 0.5) $ circleSolid 8 , color (greyN 0.5) $ circleSolid 8 ] ) cr Lampoid{_lampHeight = h} -> uncurryV translateSPxy (_crPos cr) $ lampCrSPic h NonDrawnCreature -> mempty lampCrSPic :: Float -> SPic lampCrSPic h = ( colorSH blue . upperBox h $ rectWH 5 5 , setLayer BloomLayer (setDepth h . color white $ circleSolid 3) ) picAtCrPos1 :: Picture -> Creature -> SPic --{-# INLINE picAtCrPos #-} picAtCrPos1 thePic cr = (,) mempty $ tranRot (_crPos cr) (_crDir cr) thePic shiftDraw :: (a -> Point2) -> (a -> Float) -> (a -> a -> SPic) -> a -> SPic shiftDraw fpos fdir fdraw x = uncurryV translateSPxy (fpos x) . rotateSP (fdir x) $ fdraw x x shiftDraw' :: (a -> Point2) -> (a -> Float) -> (a -> SPic) -> a -> SPic shiftDraw' fpos fdir fdraw x = uncurryV translateSPxy (fpos x) . rotateSP (fdir x) $ fdraw x cullPoint :: Configuration -> World -> Point2 -> Bool cullPoint cfig w p | debugOn Close_shape_culling cfig = pointInPolygon p (w ^. cWorld . cwCamPos . camBoundBox) | otherwise = dist (w ^. cWorld . cwCamPos . camCenter) p < (w ^. cWorld . cwCamPos . camViewDistance) extraPics :: Configuration -> Universe -> Picture extraPics cfig u = pictures (_decorations lw) <> setLayer FixedCoordLayer (fixedCoordPictures u) <> concatMapPic drawTractorBeam (_tractorBeams lw) <> concatMapPic drawLinearShockwave (_linearShockwaves lw) <> concatMapPic drawShockwave (_shockwaves lw) <> concatMapPic drawLaser (_lasersToDraw lw) <> concatMapPic drawTeslaArc (_teslaArcs lw) <> concatMapPic drawRadarSweep (_radarSweeps lw) <> concatMapPic drawFlame (_flames lw) <> concatMapPic drawEnergyBall (_energyBalls lw) <> concatMapPic drawSpark (_sparks lw) <> concatMapPic drawBul (_bullets lw) <> concatMapPic drawBlip (_radarBlips lw) <> concatMapPic drawFlare (_flares lw) <> concatMapPic (dbArg (drawBeam . _bmDraw)) (_positronBeams $ _beams lw) <> concatMapPic (dbArg (drawBeam . _bmDraw)) (_electronBeams $ _beams lw) <> concatMapPic (dbArg (drawLightSource . _lsPict)) (_lightSources lw) <> testPic cfig w <> concatMapPic ppDraw (_pressPlates lw) <> viewClipBounds cfig w <> debugDraw cfig w where w = u ^. uvWorld lw = w ^. cWorld . lWorld debugDraw :: Configuration -> World -> Picture {-# INLINE debugDraw #-} debugDraw cfig w | Show_debug `Set.member` _debug_booleans cfig = pic <> setLayer FixedCoordLayer (listPicturesAt (0.5 * halfWidth cfig) 0 cfig $ map text ts) | otherwise = mempty where pic = foldMap (debugDraw' cfig w) (_debug_booleans cfig) ts = map show (Set.toList $ _debug_booleans cfig) debugDraw' :: Configuration -> World -> DebugBool -> Picture {-# INLINE debugDraw' #-} debugDraw' cfig w bl = case bl of Show_debug -> mempty Noclip -> mempty Remove_LOS -> mempty Cull_more_lights -> mempty Close_shape_culling -> mempty Bound_box_screen -> mempty Show_ms_frame -> mempty View_boundaries -> viewBoundaries (w ^. cWorld) Show_bound_box -> drawBoundingBox w Show_wall_search_rays -> drawWallSearchRays w Show_dda_test -> drawDDATest w Show_far_wall_detect -> drawFarWallDetect w Show_walls_near_point_cursor -> drawWallsNearCursor w Show_walls_near_point_you -> drawWallsNearYou w Show_zone_near_point_cursor -> drawZoneNearPointCursor w Show_select -> drawWorldSelect w Inspect_wall -> drawInspectWalls w Cr_awareness -> drawCreatureDisplayTexts w Show_sound -> pictures $ M.map (soundPic cfig w) $ _playingSounds w Cr_status -> drawCrInfo cfig (w ^. cWorld) Mouse_position -> drawMousePosition w Walls_info -> drawWlIDs w Pathing -> drawPathing cfig w Show_nodes_near_select -> undefined --drawNodesNearSelect w Show_path_between -> drawPathBetween w Collision_test -> drawCollisionTest w drawCollisionTest :: World -> Picture drawCollisionTest w = setLayer DebugLayer (color orange $ line [a, b]) <> foldMap (drawCross . fst) (crHit a b w) <> foldMap (drawCross . _crPos) (crsNearSeg a b w) <> foldMap (drawZoneCol green crZoneSize . zoneOfPoint crZoneSize) (xIntercepts crZoneSize a b) <> foldMap (drawZoneCol yellow crZoneSize . zoneOfPoint crZoneSize) (yIntercepts' crZoneSize a b) <> foldMap (drawLabCrossCol blue) (xIntercepts crZoneSize a b) where (a, b) = _lrLine (_input w) drawCreatureDisplayTexts :: World -> Picture drawCreatureDisplayTexts w = foldMap (creatureDisplayText w) (w ^. cWorld . lWorld . creatures) drawPathBetween :: World -> Picture drawPathBetween w = setLayer DebugLayer $ color rose (foldMap (arrowPath . mapMaybe nodepos) nodelist) <> foldMap (color green . arrow sp) (nodepos =<< walkableNodeNear w sp) <> foldMap (color cyan . flip arrow ep) (nodepos =<< walkableNodeNear w ep) <> foldMap (color orange . arrow sp) (pointTowardsImpulse sp ep w) where nodepos = (`getNodePos` w) nodelist = makePathBetween sp ep w sp = _lSelect (_input w) ep = _rSelect (_input w) drawWallsNearYou :: World -> Picture drawWallsNearYou w = fromMaybe mempty $ do p <- w ^? cWorld . lWorld . creatures . ix 0 . crPos return $ setLayer DebugLayer $ foldMap f $ wlsNearPoint p w where f wl = color violet $ thickLine 3 [a, b] where (a, b) = _wlLine wl drawWallsNearCursor :: World -> Picture drawWallsNearCursor w = setLayer DebugLayer $ foldMap f $ wlsNearPoint (mouseWorldPos (_input w) (_cwCamPos $ _cWorld w)) w where f wl = color rose $ thickLine 3 [a, b] where (a, b) = _wlLine wl drawInspectWalls :: World -> Picture drawInspectWalls w = setLayer DebugLayer (color orange $ line [a, b]) <> foldMap (drawInspectWall w) ( filter (isJust . uncurry (intersectSegSeg a b) . _wlLine) $ IM.elems $ w ^. cWorld . lWorld . walls ) where (a, b) = _lLine (_input w) drawInspectWall :: World -> Wall -> Picture drawInspectWall w wl = setLayer DebugLayer $ color rose (thickLine 3 [a, b]) <> foldMap (drawDoorPaths w) (wl ^? wlStructure . wsDoor) where (a, b) = _wlLine wl drawDoorPaths :: World -> Int -> Picture drawDoorPaths w drid = fromMaybe mempty $ do paths <- w ^? cWorld . lWorld . doors . ix drid . drObstructs return $ foldMap' (drawPathEdge . (^. penPathEdge)) paths drawPathEdge :: PathEdge -> Picture drawPathEdge pe = setLayer DebugLayer $ multiArrow (_peStart pe) (_peEnd pe) green (Set.map obstacleColor (_peObstacles pe)) obstacleColor :: EdgeObstacle -> Color obstacleColor eo = case eo of WallObstacle -> cyan DoorObstacle -> red AutoDoorObstacle -> yellow BlockObstacle -> blue drawWorldSelect :: World -> Picture drawWorldSelect w = setLayer DebugLayer $ color cyan (line [a, b]) <> color magenta (line [c, d]) where (a, b) = _lLine (_input w) (c, d) = _rLine (_input w) drawFarWallDetect :: World -> Picture drawFarWallDetect w = setLayer DebugLayer . color yellow . foldMap ( \q -> line [ p , fst $ collidePoint p q $ filter wlIsOpaque $ wlsNearSeg p q w ] ) $ getViewpoints p (_cWorld w) where p = w ^. cWorld . cwCamPos . camViewFrom drawZoneNearPointCursor :: World -> Picture drawZoneNearPointCursor w = foldMap (drawZoneCol orange 50) ps where mwp = mouseWorldPos (w ^. input) (w ^. cWorld . cwCamPos) ps = [zoneOfPoint 50 mwp] drawDDATest :: World -> Picture drawDDATest w = foldMap (drawZoneCol orange 50) ps <> setLayer DebugLayer (color yellow (line [cvf, mwp])) where cvf = w ^. cWorld . cwCamPos . camViewFrom mwp = mouseWorldPos (w ^. input) (w ^. cWorld . cwCamPos) ps = zoneOfSeg 50 cvf mwp drawZoneCol :: Color -> Float -> V2 Int -> Picture drawZoneCol col s (V2 x y) = setLayer DebugLayer . color col $ thickLine 2 (p : ps ++ [p]) where (p : ps) = zipWith (+.+) innerSquare $ map ((s *.*) . (each %~ fromIntegral)) [V2 x y, V2 (x + 1) y, V2 (x + 1) (y + 1), V2 x (y + 1)] innerSquare :: [Point2] innerSquare = [V2 1 1, V2 (-1) 1, V2 (-1) (-1), V2 1 (-1)] drawWallSearchRays :: World -> Picture drawWallSearchRays w = foldMap (f . fst) $ allVisibleWalls w where f p = setLayer DebugLayer $ color yellow $ uncurryV translate p (circle 5) <> line [w ^. cWorld . cwCamPos . camViewFrom, p] testPic :: Configuration -> World -> Picture testPic _ _ = mempty drawBoundingBox :: World -> Picture drawBoundingBox w = setLayer DebugLayer $ color green $ line $ (x : xs) ++ [x] where (x : xs) = w ^. cWorld . cwCamPos . camBoundBox ppDraw :: PressPlate -> Picture ppDraw c = uncurryV translate (_ppPos c) $ rotate (_ppRot c) (_ppPict c) floorItemSPic :: FloorItem -> SPic floorItemSPic flit = uncurryV translateSPxy (_flItPos flit) $ rotateSP (_flItRot flit) (itemSPic (_flIt flit)) btSPic :: Button -> SPic btSPic bt = uncurryV translateSPxy (_btPos bt) $ rotateSP (_btRot bt) (drawButton (_btPict bt) bt) mcSPic :: Machine -> SPic mcSPic mc = uncurryV translateSPxy (_mcPos mc) $ rotateSP (_mcDir mc) (drawMachine mc) soundPic :: Configuration -> World -> Sound -> Picture soundPic cfig w s = fixedSizePicClampArrow 50 50 thePic p cfig (w ^. cWorld) where p = _soundPos s thePic = rotate (w ^. cWorld . cwCamPos . camRot) . scale theScale theScale . centerText . soundToOnomato $ _soundChunkID s theScale = 0.15 * f (_soundVolume s * 0.0001) f x = 1 - 0.5 * (1 - x) drawMousePosition :: World -> Picture drawMousePosition w = setLayer FixedCoordLayer . uncurryV translate (w ^. input . mousePos) . scale 0.1 0.1 . text $ shortPoint2 mwp where mwp = mouseWorldPos (w ^. input) (w ^. cWorld . cwCamPos) drawWlIDs :: World -> Picture drawWlIDs w = setLayer FixedCoordLayer $ foldMap f (w ^. cWorld . lWorld . walls) where f wl | dist (_crPos $ you w) (fst (_wlLine wl)) > 200 = mempty -- this should be improved with a better "on screen test" | otherwise = uncurryV translate p . scale 0.1 0.1 . text $ show $ _wlID wl where p = worldPosToScreen (w ^. cWorld . cwCamPos) $ 0.5 *.* uncurry (+.+) (_wlLine wl) edgeToPic :: [Point2] -> PathEdge -> Picture edgeToPic poly pe | not (pointInPolygon sp poly) && not (pointInPolygon ep poly) = mempty | otherwise = drawPathEdge pe where sp = _peStart pe ep = _peEnd pe drawPathing :: Configuration -> World -> Picture drawPathing cfig w = setLayer DebugLayer $ foldMap (edgeToPic (screenPolygon cfig (w ^. cWorld . cwCamPos)) . (^?! _3)) (FGL.labEdges gr) <> foldMap dispInc (graphToIncidence gr) where dispInc (p, n) = setDepth 2 . uncurryV translate p . scale 0.1 0.1 $ text $ show n gr = w ^. cWorld . pathGraph crDisplayInfo :: Configuration -> CamPos -> Creature -> Maybe (Point2, [String]) crDisplayInfo cfig cam cr | _crID cr == 0 = Nothing | crOnScreen = Just ( _crPos cr , catMaybes -- [fmap show $ ap ^? crGoal [ fpreShow "crHP" $ cr ^? crHP , fpreShow "crStrategy" $ ap ^? apStrategy , fmap (("crPos....." ++) . shortShow) $ cr ^? crPos , fpreShow "cpVigilance" $ cr ^? crPerception . cpVigilance , -- , fmap show $ cr ^? crOldPos fpreShow "crAction" $ ap ^? apAction , fpreShow "crImpulse" $ ap ^? apImpulse ] ) | otherwise = Nothing where ap = _crActionPlan cr crOnScreen = pointOnScreen cfig cam $ _crPos cr fpreShow :: (Show a, Functor f) => String -> f a -> f String fpreShow str = fmap (((rightPad 7 '.' str ++ "...") ++) . show) drawCrInfo :: Configuration -> CWorld -> Picture drawCrInfo cfig w = setLayer FixedCoordLayer $ renderInfoListsAt (2 * hw - 400) 0 cfig cam $ mapMaybe (crDisplayInfo cfig cam) $ IM.elems $ w ^. lWorld . creatures where cam = w ^. cwCamPos hw = halfWidth cfig viewBoundaries :: CWorld -> Picture viewBoundaries w = setLayer DebugLayer $ color green (foldMap (polygonWire . _grBound) grs) <> color yellow (foldMap (\q -> line [p, q]) $ getViewpoints p w) where p = w ^. cwCamPos . camViewFrom grs = filter (pointInOrOnPolygon p . _grBound) (_cwgGameRooms $ _cwGen w) viewClipBounds :: Configuration -> World -> Picture viewClipBounds cfig w | _debug_view_clip_bounds cfig == AllRoomClipBoundaries = setLayer DebugLayer $ color green $ foldMap (polygonWire . _cpPoints) (_cwgRoomClipping $ _cwGen (_cWorld w)) | _debug_view_clip_bounds cfig == IntersectingRoomClipBoundaries = setLayer DebugLayer $ f (_cwgRoomClipping $ _cwGen (_cWorld w)) | otherwise = mempty where f (x : xs) = g x xs <> f xs f [] = mempty g x (y : ys) | convexPolysOverlap x y = color green (polygonWire $ _cpPoints x) <> color yellow (polygonWire $ _cpPoints y) <> g x ys | otherwise = g x ys g _ [] = mempty