Files
loop/src/Dodge/PlacementSpot.hs
T
2025-09-18 08:28:09 +01:00

269 lines
8.3 KiB
Haskell

--{-# LANGUAGE LambdaCase #-}
module Dodge.PlacementSpot (
atFstLnkOut,
atNthLnkOutShiftBy,
rpIsOnPath,
rpIsOffPath,
rpOffPathFromEdge,
rpOnPathFromEdge,
useUnusedLnk,
psRandRanges,
useRoomPosCond,
isUsedLnkUnplaced,
anyUnusedSpot,
unusedSpotAwayFromLink,
isUnusedLnk,
isInLnk,
isOutLnk,
unusedSpotNearInLink,
randDirPS,
unusedSpotAwayFromInLink,
useLnkRoomPos,
atFstLnkOutShiftInward,
atFstLnkOutShiftBy,
atNthLnkOutShiftInward,
unusedOffPathAwayFromLink,
setFallback,
twoRoomPoss,
isUnusedLnkType,
rprBoolShift,
rprShift,
rprBool,
shiftInBy,
resetPLUse,
psposAddLabel,
shiftByV2,
usedRoomLinkPoss,
usedRoomInLinkPoss,
atNthLinkOut,
) where
import Control.Lens
import Control.Monad.State
import Data.Bifunctor
import Data.Maybe
import qualified Data.Set as S
import Dodge.Data.GenWorld
import Geometry
import System.Random
setDirPS :: Float -> PlacementSpot -> PlacementSpot
setDirPS a ps = case ps of
PS p _ -> PS p a
PSNoShiftCont p _ -> PS p a
PSPos f rmeff fb -> PSPos (\rp r -> fmap (first (setDirPS a)) (f rp r)) rmeff fb
PSRoomRand i f -> PSRoomRand i (fmap (setDirPS a) f)
randDirPS :: RandomGen g => PlacementSpot -> State g PlacementSpot
randDirPS ps = do
a <- state $ randomR (0, pi * 2)
return $ setDirPS a ps
isUnusedLnk :: RoomPos -> Room -> Bool
isUnusedLnk rp _ = case _rpLinkStatus rp of
UnusedLink{} -> _rpPlacementUse rp == 0
_ -> False
anyUnusedSpot :: PlacementSpot
anyUnusedSpot = rprBool $ \rp _ -> _rpLinkStatus rp == NotLink && _rpPlacementUse rp == 0
psposAddLabel :: RoomPosType -> PlacementSpot -> PlacementSpot
psposAddLabel lab = psSelect %~ (\f rp r -> f rp r & _Just . _2 . rpType %~ S.insert lab)
rprBool :: (RoomPos -> Room -> Bool) -> PlacementSpot
rprBool t = PSPos (useRoomPosRoomCond t) (const id) Nothing
rpIsOnPath :: RoomPos -> Bool
rpIsOnPath = any f . _rpType
where
f RoomPosOnPath{} = True
f _ = False
rpIsOffPath :: RoomPos -> Bool
rpIsOffPath = any f . _rpType
where
f RoomPosOffPath{} = True
f _ = False
rpOffPathFromEdge :: PathFromEdge -> RoomPos -> Bool
rpOffPathFromEdge pe = any f . _rpType
where
f rt = maybe False (pe `elem`) (rt ^? offPathFromEdges)
-- test whether a roomPos is on the path with a given from edge value
rpOnPathFromEdge :: PathFromEdge -> RoomPos -> Bool
rpOnPathFromEdge pe = any f . _rpType
where
f rt = maybe False (pe `elem`) (rt ^? onPathFromEdges)
resetPLUse :: PlacementSpot -> PlacementSpot
resetPLUse (PSPos f g fallback) = PSPos f' g fallback
where
f' rp = fmap (second (rpPlacementUse .~ 0)) . f rp
resetPLUse _ = error "Tried to reset _rpPlacementUse of non PSPos placement"
rprShift ::
(RoomPos -> Room -> Maybe (Point2, Float)) ->
PlacementSpot
rprShift t = PSPos f (const id) Nothing
where
f rp r = case t rp r of
Just (p, a) -> Just (PS p a, rp & rpPlacementUse +~ 1)
Nothing -> Nothing
rprBoolShift ::
(RoomPos -> Room -> Bool) ->
((Point2, Float) -> (Point2, Float)) ->
PlacementSpot
rprBoolShift t shift = PSPos f (const id) Nothing
where
f rp r
| t rp r = Just (PS p a, rp & rpPlacementUse +~ 1)
| otherwise = Nothing
where
(p, a) = shift (_rpPos rp, _rpDir rp)
unusedSpotAwayFromLink :: Float -> PlacementSpot
unusedSpotAwayFromLink x = rprBool $ \rp r ->
_rpLinkStatus rp == NotLink
&& _rpPlacementUse rp == 0
&& all ((> x) . dist (_rpPos rp)) (usedRoomLinkPoss r)
setFallback :: Placement -> Placement -> Placement
setFallback fallback =
(plSpot . psFallback %~ maybe (Just fallback) Just)
. ( plIDCont %~ fmap (fmap (fmap $ setFallback fallback))
)
unusedOffPathAwayFromLink :: Float -> PlacementSpot
unusedOffPathAwayFromLink x = rprBool $ \rp r ->
_rpPlacementUse rp == 0
&& all ((> x) . dist (_rpPos rp)) (usedRoomLinkPoss r)
&& rpIsOffPath rp
unusedSpotAwayFromInLink :: Float -> PlacementSpot
unusedSpotAwayFromInLink x = rprBool $ \rp r ->
_rpLinkStatus rp == NotLink
&& _rpPlacementUse rp == 0
&& all ((> x) . dist (_rpPos rp)) (usedRoomInLinkPoss r)
--twoUnusedLinks :: (PlacementSpot -> PlacementSpot -> Placement) -> Placement
--twoUnusedLinks = twoRoomPoss isUnusedLnk isUnusedLnk
twoRoomPoss ::
(RoomPos -> Room -> Bool) ->
(RoomPos -> Room -> Bool) ->
(PlacementSpot -> PlacementSpot -> Placement) ->
Placement
twoRoomPoss cond1 cond2 f = Placement (rprBool cond1) PutNothing Nothing $
\_ pl1 -> Just $
Placement (rprBool cond2) PutNothing Nothing $
\_ pl2 -> Just $ f (_plSpot pl1) (_plSpot pl2)
--isUnusedLnk :: RoomPos -> Bool
--isUnusedLnk rp = case _rpLinkStatus rp of
-- UnusedLink {} -> _rpPlacementUse rp == 0
-- _ -> False
isInLnk :: RoomPos -> Bool
isInLnk rp = case _rpLinkStatus rp of
UsedInLink{} -> _rpPlacementUse rp == 0
_ -> False
isOutLnk :: RoomPos -> Bool
isOutLnk rp = case _rpLinkStatus rp of
UsedOutLink{} -> _rpPlacementUse rp == 0
_ -> False
useUnusedLnk :: PlacementSpot
useUnusedLnk = rprBool isUnusedLnk
isUsedLnkUnplaced :: RoomPos -> Bool
isUsedLnkUnplaced rp = case _rpLinkStatus rp of
UsedOutLink{} -> _rpPlacementUse rp == 0
UsedInLink{} -> _rpPlacementUse rp == 0
_ -> False
useRoomPosCond :: (RoomPos -> Room -> Bool) -> RoomPos -> Room -> Maybe (PlacementSpot, RoomPos)
useRoomPosCond f = useRoomPosRoomCond $ \rp r -> f rp r
useRoomPosRoomCond :: (RoomPos -> Room -> Bool) -> RoomPos -> Room -> Maybe (PlacementSpot, RoomPos)
useRoomPosRoomCond t rp r
| t rp r = Just (PS (_rpPos rp) (_rpDir rp), rp & rpPlacementUse +~ 1)
| otherwise = Nothing
isUnusedLnkType :: RoomLinkType -> RoomPos -> Room -> Bool
isUnusedLnkType rlt rp _ = case _rpLinkStatus rp of
UnusedLink{_rplsType = rlts} -> rlt `S.member` rlts && _rpPlacementUse rp == 0
_ -> False
unusedSpotNearInLink :: Float -> PlacementSpot
unusedSpotNearInLink x = rprBool $ \rp r ->
_rpLinkStatus rp == NotLink
&& _rpPlacementUse rp == 0
&& any ((< x) . dist (_rpPos rp)) (usedRoomInLinkPoss r)
usedRoomInLinkPoss :: Room -> [Point2]
usedRoomInLinkPoss r = mapMaybe f $ _rmPos r
where
f rp = case _rpLinkStatus rp of
UsedInLink{} -> Just $ _rpPos rp
_ -> Nothing
usedRoomLinkPoss :: Room -> [Point2]
usedRoomLinkPoss r = mapMaybe f $ _rmPos r
where
f rp = case _rpLinkStatus rp of
UsedInLink{} -> Just $ _rpPos rp
UsedOutLink{} -> Just $ _rpPos rp
_ -> Nothing
atFstLnkOut :: PlacementSpot
atFstLnkOut = atNthLinkOut 0
atNthLinkOut :: Int -> PlacementSpot
atNthLinkOut n = rprBool $ \rp _ -> rp ^? rpLinkStatus . rplsChildNum == Just n
atNthLnkOutShiftBy :: Int -> ((Point2, Float) -> (Point2, Float)) -> PlacementSpot
atNthLnkOutShiftBy n = rprBoolShift $
\rp _ -> rp ^? rpLinkStatus . rplsChildNum == Just n
--atNthLnkOutShiftBy n theshift = PSPos f (const id) Nothing
-- where
-- f rp _ = case _rpLinkStatus rp of
-- UsedOutLink {_rplsChildNum = i} | i == n
-- -> Just (PS p a, rp & rpPlacementUse +~ 1)
-- _ -> Nothing
-- where
-- (p,a) = theshift (_rpPos rp,_rpDir rp)
atFstLnkOutShiftBy :: ((Point2, Float) -> (Point2, Float)) -> PlacementSpot
atFstLnkOutShiftBy = atNthLnkOutShiftBy 0
atFstLnkOutShiftInward :: Float -> PlacementSpot
atFstLnkOutShiftInward = atNthLnkOutShiftInward 0
atNthLnkOutShiftInward :: Int -> Float -> PlacementSpot
atNthLnkOutShiftInward n x = atNthLnkOutShiftBy n $
\(p, a) -> (p +.+ rotateV a (V2 0 (negate x)), a)
shiftInBy :: Float -> (Point2, Float) -> (Point2, Float)
shiftInBy x (p, a) = (p +.+ rotateV a (V2 0 (negate x)), a)
shiftByV2 :: Point2 -> (Point2, Float) -> (Point2, Float)
shiftByV2 x (p, a) = (p +.+ rotateV a x, a)
-- this should probably check the placement use, but so should others: should
-- unify these
useLnkRoomPos :: RoomPos -> Room -> Maybe (PlacementSpot, RoomPos)
useLnkRoomPos rp _ = case _rpLinkStatus rp of
UnusedLink{} -> Just (PS (_rpPos rp) (_rpDir rp), rp & rpPlacementUse +~ 1)
_ -> Nothing
psRandRanges :: (Float, Float) -> (Float, Float) -> (Float, Float) -> State StdGen (Point2, Float)
psRandRanges xranges yranges aranges = do
x <- state $ randomR xranges
y <- state $ randomR yranges
a <- state $ randomR aranges
return (V2 x y, a)