466 lines
17 KiB
Haskell
466 lines
17 KiB
Haskell
--{-# LANGUAGE TupleSections #-}
|
|
module Dodge.Room.Room (
|
|
shootingRange,
|
|
weaponRoom,
|
|
roomMiniIntro,
|
|
roomCCrits,
|
|
doubleCorridorBarrels,
|
|
pistolerRoom,
|
|
spawnerRoom,
|
|
corDoor,
|
|
doorCor,
|
|
weaponBehindPillar,
|
|
critsRoom,
|
|
distributerRoom,
|
|
critDeadEnd,
|
|
) where
|
|
|
|
import Linear
|
|
import Color
|
|
import Dodge.Placement.Instance.Pipe
|
|
import ShapePicture
|
|
import Shape
|
|
import Dodge.Default
|
|
import Dodge.Data.AmmoType
|
|
import qualified Data.Set as S
|
|
import Dodge.Cleat
|
|
import Dodge.Creature
|
|
import Dodge.Data.GenWorld
|
|
import Dodge.Default.Room
|
|
import Dodge.Item.Display
|
|
import Dodge.LevelGen.PlacementHelper
|
|
import Dodge.Placement.Instance
|
|
import Dodge.Placement.Random
|
|
import Dodge.PlacementSpot
|
|
import Dodge.Room.Airlock
|
|
import Dodge.Room.Corridor
|
|
import Dodge.Room.Door
|
|
import Dodge.Room.Girder
|
|
import Dodge.Room.Link
|
|
import Dodge.Room.Modify.Girder
|
|
import Dodge.Room.Pillar
|
|
import Dodge.Room.Procedural
|
|
import Dodge.RoomLink
|
|
import Dodge.Tree
|
|
import Geometry
|
|
import LensHelp
|
|
import MonadHelp
|
|
import RandomHelp
|
|
|
|
roomC :: RandomGen g => Float -> Float -> State g Room
|
|
roomC w h = do
|
|
wallf <- takeOne [crystalLine, windowLine]
|
|
maybeaddgird <- takeOne [return, addRandomGirderFrom West 0, addRandomGirderFrom North 0]
|
|
maybeaddgird
|
|
=<< shuffleLinks
|
|
=<< ( roomRectAutoLights w h
|
|
<&> rmLinks
|
|
%~ ( setInLinks (\rl -> S.fromList [FromEdge East 0, OnEdge South] `S.isSubsetOf` _rlType rl)
|
|
. setOutLinks (\rl -> OnEdge West `S.member` _rlType rl)
|
|
)
|
|
<&> rmPmnts .:~ wallf (V2 (w / 2) 0) (V2 (w / 2) (h -60))
|
|
<&> rmRandPSs .~ [farside]
|
|
)
|
|
where
|
|
farside = do
|
|
x <- state $ randomR (5, w / 2 - 20)
|
|
y <- state $ randomR (5, h - 70)
|
|
a <- state $ randomR (0, 2 * pi)
|
|
return (V2 x y, a)
|
|
|
|
roomPadCut :: [Point2] -> Point2 -> Room
|
|
roomPadCut ps p =
|
|
defaultRoom
|
|
{ _rmPolys = [ps]
|
|
, _rmLinks = muout [(p, 0)] ++ muin [(V2 0 0, pi)]
|
|
, _rmPath = doublePairSet (V2 0 0, p)
|
|
}
|
|
|
|
branchWith :: Room -> [Tree Room] -> Tree Room
|
|
branchWith r = Node r . (return (cleatOnward door) :)
|
|
|
|
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)
|
|
l2 <-
|
|
mntLightLnkCond $
|
|
rprBool $ \rp _ ->
|
|
rp ^. rpType . rplsType . at (OnEdge West) == Just ()
|
|
&& null (rp ^. rpPlacementUse)
|
|
let hf = hgt / 5
|
|
awindow h xl xr = windowLine (V2 xl h) (V2 xr h)
|
|
plmnts =
|
|
[ awindow hf (wth -60) wllen
|
|
, awindow (2 * hf) (wth - wllen) 60
|
|
, awindow (3 * hf) (wth -60) wllen
|
|
, awindow (4 * hf) (wth - wllen) 60
|
|
, blockLine (V2 0 (1 * hf)) (V2 wllen (1 * hf))
|
|
, blockLine (V2 (wth - wllen) (2 * hf)) (V2 wth (2 * hf))
|
|
, blockLine (V2 0 (3 * hf)) (V2 wllen (3 * hf))
|
|
, blockLine (V2 (wth - wllen) (4 * hf)) (V2 wth (4 * hf))
|
|
]
|
|
<> replicate 5 l2
|
|
let northPSs = do
|
|
cry <- randomRanges [3 * hf + 10, 4 * hf -10, 4 * hf + 10, 5 * hf -10]
|
|
crx <- state $ randomR (wllen, wth - (wllen + 40))
|
|
return (V2 crx cry, 1.5 * pi)
|
|
midPS = return (V2 (wth -20) (hgt / 2 + 40), pi)
|
|
return $
|
|
roomRect wth hgt 2 4
|
|
& rmPmnts .~ plmnts
|
|
& rmRandPSs .~ [northPSs, midPS]
|
|
& rmName .~ "glassSwitchBack"
|
|
|
|
glassSwitchBackCrits :: RandomGen g => Int -> State g Room
|
|
glassSwitchBackCrits i = glassSwitchBack <&> rmPmnts <>~ fmap f [0 .. i -1]
|
|
where
|
|
f j = psPtPl (PSRoomRand j (uncurry PS)) randC1
|
|
|
|
-- note there used to be a creature with a minigun in glassSwitchBackCrits, has
|
|
-- been removed 25.10.01
|
|
miniTree2 :: RandomGen g => State g (Tree Room)
|
|
miniTree2 = do
|
|
rm <- glassSwitchBackCrits 2 >>= shuffleLinks . restrictInLinks (\p -> (sndV2 . fst) p < 70)
|
|
return $ branchWith rm (replicate 3 $ treePost [door, corridor, critInDeadEnd])
|
|
|
|
-- So, the idea is to attach outer children to the bottommost right nodes
|
|
-- inside an inner tree
|
|
-- no idea what was going on here...
|
|
roomMiniIntro :: RandomGen g => State g (MetaTree Room String)
|
|
roomMiniIntro = do
|
|
midroom <- join $ takeOne [miniTree2] --,glassLesson]
|
|
rToOnward "roomMiniIntro" midroom
|
|
|
|
roomCenterPillar :: RandomGen g => State g Room
|
|
roomCenterPillar = do
|
|
l1 <- mntLightLnkCond (resetPLUse $ rprBool $ \rp _ -> isInLnk rp)
|
|
l2 <-
|
|
mntLightLnkCond $
|
|
resetPLUse (rprBool $ \rp _ -> isOutLnk rp)
|
|
& psFallback ?~ mntLightLnkCond' (resetPLUse useUnusedLnk)
|
|
let plmnts =
|
|
[ blockLine (V2 115 115) (V2 115 125)
|
|
, blockLine (V2 125 115) (V2 125 125)
|
|
, l1
|
|
, l2
|
|
]
|
|
addHighGirder'
|
|
=<< ( shuffleLinks . restrictInLinks ((\p -> dist p (V2 120 0) < 10) . fst) $
|
|
cleatSide $
|
|
set rmPmnts plmnts $
|
|
set rmName "roomCenterPillar" $
|
|
roomRect 240 240 2 2
|
|
)
|
|
|
|
weaponEmptyRoom :: State StdGen (Tree Room)
|
|
weaponEmptyRoom = do
|
|
w <- state $ randomR (220, 300)
|
|
h <- state $ randomR (220, 300)
|
|
wp <- randFirstWeapon
|
|
thelight <- mntLightLnkCond useUnusedLnk
|
|
let plmnts =
|
|
[ sPS (V2 (w / 2) (h -40)) 0 $ PutFlIt wp
|
|
, sPS (V2 20 20) (pi / 2) randC1
|
|
, sPS (V2 (w -20) 20) (pi / 2) randC1
|
|
, thelight
|
|
]
|
|
f (V2 x y, a) = (a == pi && x > 25 && x < w - 25) || (a /= 0 && y > w - 30)
|
|
rm <-
|
|
addHighGirder >=> shuffleLinks $
|
|
restrictRMInLinksPD f (roomRect w h 2 2 & rmPmnts .~ plmnts)
|
|
return $ treePost [corridor, cleatSide $ cleatOnward rm]
|
|
|
|
weaponUnderCrits :: RandomGen g => State g (MetaTree Room String)
|
|
weaponUnderCrits = do
|
|
let addwpat p = rmPmnts .:~ sPS p 0 (RandPS $ fmap PutFlIt randFirstWeapon)
|
|
continuationRoom =
|
|
treePost
|
|
[ addwpat (V2 20 0) corridorN
|
|
, addwpat (V2 20 0) corridorN
|
|
, cleatOnward $
|
|
corridorN & rmPmnts
|
|
.~ [ sPS (V2 20 0) (negate $ pi / 2) randC1
|
|
, sPS (V2 20 20) (pi / 2) randC1
|
|
]
|
|
]
|
|
rcp <- roomCenterPillar
|
|
rmpils <- roomPillars 30 240 240 2 2
|
|
deadEndRoom' <- takeOne [addwpat (V2 120 20) rmpils, addwpat (V2 120 20) $ cleatSide rcp]
|
|
junctionRoom <- takeOne [tEast, tWest]
|
|
rToOnward "weaponUnderCrits" $
|
|
treeFromTrunk [corridorN, corridorN] $
|
|
Node junctionRoom [continuationRoom, pure deadEndRoom']
|
|
|
|
weaponBehindPillar :: RandomGen g => State g (Tree Room)
|
|
weaponBehindPillar = do
|
|
wpa <- state $ randomR (0, pi)
|
|
wpos <- takeOne [V2 120 160, V2 80 40, V2 160 40, V2 220 200, V2 40 200, V2 120 35]
|
|
cpos <- takeOne $ [V2 x y | x <- [20, 220], y <- [20, 40]] ++ [V2 120 160, V2 120 200]
|
|
rcp <- roomCenterPillar
|
|
return $
|
|
treePost
|
|
[ corridor
|
|
, rcp & rmPmnts
|
|
++.~ [ sPS wpos wpa $ RandPS $ fmap PutFlIt randFirstWeapon
|
|
, sPS cpos (argV $ V2 120 80 -.- cpos) randC1
|
|
]
|
|
, cleatOnward $ set rmPmnts [sPS (V2 20 60) (negate $ pi / 2) randC1] corridorN
|
|
]
|
|
|
|
weaponBetweenPillars :: State StdGen (MetaTree Room String)
|
|
weaponBetweenPillars = do
|
|
(w, wn) <- takeOne [(240, 2), (340, 3)]
|
|
(h, hn) <- takeOne [(240, 2), (340, 3)]
|
|
let wpPos = rprBool $ \rp r ->
|
|
and
|
|
[ rpIsOnGrid rp
|
|
, null $ _rpPlacementUse rp
|
|
, notLink (_rpType rp)
|
|
, any ((< 100) . dist (_rpPos rp)) (usedRoomInLinkPoss r)
|
|
]
|
|
ncrits <- state $ randomR (1, 3)
|
|
wp <- randFirstWeapon
|
|
critPlacementSpots <- replicateM ncrits $
|
|
randDirPS $
|
|
rprBool $ \rp r ->
|
|
rpIsOnGrid rp
|
|
&& null (_rpPlacementUse rp)
|
|
&& all ((> 100) . dist (_rpPos rp)) (usedRoomLinkPoss r)
|
|
( roomPillars 30 w h wn hn
|
|
<&> rmPmnts .++~ map (`sps` randC1) critPlacementSpots ++ [sps wpPos (PutFlIt wp)]
|
|
<&> cleatOnward
|
|
)
|
|
>>= rToOnward ("weaponBetweenPillars_" ++ itemString wp) . pure
|
|
|
|
weaponLongCorridor :: RandomGen g => State g (Tree Room)
|
|
weaponLongCorridor = do
|
|
rt <- takeOne [tEast, tWest]
|
|
connectingRoom <- takeOne [tEast & rmPmnts .~ [spanLightI (V2 (-30) 40) (V2 (-30) 80)]]
|
|
i1 <- state $ randomR (2, 5)
|
|
i2 <- state $ randomR (2, 5)
|
|
let branch1 =
|
|
treeFromTrunk
|
|
(replicate i1 corridorN)
|
|
(pure . cleatSide . cleatOnward $ putCrs connectingRoom)
|
|
let branch2 = treeFromTrunk (replicate i2 corridorN) (pure . cleatSide $ putWp corridor)
|
|
return $ Node rt [branch1, branch2]
|
|
where
|
|
putCrs = rmPmnts .++~ [sPS (V2 10 40) (- pi / 2) randC1, sPS (V2 (-10) 40) (- pi / 2) randC1]
|
|
putWp = rmPmnts .~ [sPS (V2 20 60) 0 $ RandPS $ fmap PutFlIt randFirstWeapon, spanLightI (V2 0 40) (V2 40 40)]
|
|
|
|
critInDeadEnd :: Room
|
|
critInDeadEnd = deadEndRoom & rmPmnts .~ [sPS (V2 0 0) 0 randC1]
|
|
|
|
-- this one should face the inlink door
|
|
critDeadEnd :: Room
|
|
critDeadEnd = deadEndRoom & rmPmnts .~ [sPS (V2 0 0) (pi/2) randC1]
|
|
|
|
deadEndRoom :: Room
|
|
deadEndRoom =
|
|
defaultRoom
|
|
{ _rmPolys = [rectNSWE 40 (-20) (-20) 20]
|
|
, _rmLinks = muout (init lnks) ++ muin [last lnks]
|
|
, _rmPath = mempty
|
|
, _rmPmnts = [sPS (V2 0 (-10)) 0 putLamp]
|
|
, _rmBound = [rectNSWE 20 (-20) (-30) 30]
|
|
, _rmName = "deadEndRoom"
|
|
}
|
|
where
|
|
lnks = [(V2 0 30, 0)]
|
|
|
|
{- A random Either tree with a weapon and melee monster challenge. -}
|
|
weaponRoom :: State StdGen (MetaTree Room String)
|
|
weaponRoom =
|
|
join $
|
|
takeOne
|
|
[ weaponEmptyRoom >>= rToOnward "weaponEmptyRoom"
|
|
, weaponUnderCrits
|
|
, weaponBehindPillar >>= rToOnward "weaponBehindPillar"
|
|
, weaponBetweenPillars -- >>= rToOnward "weaponBetweenPillars"
|
|
, weaponLongCorridor >>= rToOnward "weaponLongCorridor"
|
|
]
|
|
|
|
roomCCrits :: RandomGen g => Int -> State g Room
|
|
roomCCrits i =
|
|
roomC 200 200
|
|
<&> rmPmnts .++~ replicate i (psPtPl (PSRoomRand 0 (uncurry PS)) randC1)
|
|
|
|
doubleCorridorBarrels :: RandomGen g => State g Room
|
|
doubleCorridorBarrels = do
|
|
h <- state $ randomR (200, 300)
|
|
let cond x = (sndV2 . fst) x < h - 40
|
|
return $
|
|
restrictInLinks cond $
|
|
roomRect 100 h 1 1 & rmPmnts
|
|
.~ [ blockLine (V2 50 50) (V2 50 h)
|
|
, sPS (V2 25 (h -25)) (negate pi / 2) $ PutCrit $ addArmour autoCrit
|
|
, sPS (V2 75 (h -80)) 0 putLamp
|
|
]
|
|
++ [ sPS p 0 $ PutCrit explosiveBarrel
|
|
| p <- [V2 75 (h -30), V2 75 (h -60), V2 85 (h -10), V2 85 (h -45)]
|
|
]
|
|
|
|
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 (280, 550)
|
|
y2 <- state $ randomR (280, 550)
|
|
y3 <- iterateWhile (\y' -> abs (y1 - y2) < 60 && abs (y2 - y') < 60) $ state $ randomR (280, 560)
|
|
x4 <- state $ randomR (60, w -60)
|
|
y4 <- state $ randomR (40, 150)
|
|
let bln x y = putBlockN (x + 25) (x -25) (y + 20) (y + 10)
|
|
blv x y = putBlockV (x + 25) (x -25) (y + 20) (y + 10)
|
|
plmnts =
|
|
bln x1 y1 ++ bln x2 y2 ++ bln x3 y3 ++ blv x4 y4
|
|
++ [ spanLightI (V2 (-10) 200) (V2 (w / 2) 200)
|
|
, spanLightI (V2 (w / 2) 200) (V2 (w + 10) 200)
|
|
]
|
|
toPS x y = return (V2 x y, -0.5 * pi)
|
|
roomRectAutoLights w 600
|
|
<&> rmPmnts .~ plmnts
|
|
<&> rmRandPSs .~ [toPS x1 y1, toPS x2 y2, toPS x3 y3]
|
|
|
|
shootersRoom1 :: RandomGen g => State g Room
|
|
shootersRoom1 = do
|
|
pl <-
|
|
takeOne $
|
|
map
|
|
(\i -> psPtPl (PSRoomRand i (uncurry PS)) (PutCrit autoCrit))
|
|
[0, 1, 2]
|
|
shootersRoom' <&> rmPmnts
|
|
.:~ pl
|
|
|
|
shootersRoom :: RandomGen g => State g Room
|
|
shootersRoom =
|
|
shootersRoom' <&> rmPmnts
|
|
.++~ map (\i -> psPtPl (PSRoomRand i (uncurry PS)) (PutCrit autoCrit)) [0, 1, 2]
|
|
|
|
-- TODO stop from spawning on inlinks fromedge
|
|
pistolerRoom :: RandomGen g => State g Room
|
|
pistolerRoom = do
|
|
(w, wn) <- takeOne [(200, 3), (255, 4)]
|
|
roomPillars 10 w 200 wn 3 <&> rmPmnts
|
|
.++~ replicate
|
|
3
|
|
( psPtPl
|
|
(rprBool $ \rp _ -> null (_rpPlacementUse rp) && rpIsOnGrid rp)
|
|
(PutCrit chaseCrit)
|
|
)
|
|
|
|
shootingRange :: RandomGen g => State g (MetaTree Room String)
|
|
shootingRange = do
|
|
rm1 <-
|
|
shootersRoom1
|
|
>>= shuffleLinks . restrictInLinks (\(V2 _ y, _) -> y < 40)
|
|
. restrictOutLinks (\(V2 _ y, r) -> y > 200 && r /= 0)
|
|
rm2 <-
|
|
shootersRoom
|
|
>>= shuffleLinks
|
|
. restrictInLinks (\(V2 x y, _) -> y < 10 && x > 20 && x < 180)
|
|
. restrictOutLinks (\(V2 _ y, r) -> y > 200 && r /= 0)
|
|
rm3 <-
|
|
shootersRoom
|
|
>>= shuffleLinks
|
|
. restrictInLinks (\(V2 x y, _) -> y < 10 && x > 20 && x < 180)
|
|
. restrictOutLinks (\(_, r) -> r == 0)
|
|
let padcutroom =
|
|
roomPadCut (rectNSWE 40 (-40) (-80) 80) (V2 0 20)
|
|
& rmPmnts .:~ spanLightI (V2 (-80) 10) (V2 80 10)
|
|
rToOnward "shootingRange" $
|
|
treePost
|
|
[ rm1
|
|
, padcutroom
|
|
, rm2
|
|
, padcutroom
|
|
, cleatOnward rm3
|
|
]
|
|
|
|
spawnerRoom :: RandomGen g => State g (Tree Room)
|
|
spawnerRoom = do
|
|
x <- state $ randomR (250, 300)
|
|
y <- state $ randomR (300, 400)
|
|
roomWithSpawner <-
|
|
roomC x y <&> rmPmnts
|
|
.:~ psPtPl
|
|
( rprBool $ \rp _ ->
|
|
null (_rpPlacementUse rp) && rpIsOnGrid rp
|
|
&& xV2 (_rpPos rp) < x / 2
|
|
&& yV2 (_rpPos rp) < y / 2
|
|
)
|
|
(PutCrit spawnerCrit)
|
|
aRoom <- airlock
|
|
return $ treeFromTrunk [aRoom, corridor] $ pure $ cleatOnward roomWithSpawner
|
|
|
|
doorCor :: RandomGen g => State g (MetaTree Room String)
|
|
doorCor = do
|
|
cor <- shuffleLinks (cleatOnward corridor) <&> rmPmnts .~ []
|
|
return $ tToBTree "doorCor" $ treePost [door, cor]
|
|
|
|
corDoor :: RandomGen g => State g (MetaTree Room String)
|
|
corDoor = do
|
|
cor <- shuffleLinks corridor <&> rmPmnts .~ []
|
|
return $ tToBTree "corDoor" $ treePost [cor,cor, cleatOnward door]
|
|
|
|
critsPillarRoom :: Int -> State LayoutVars Room
|
|
critsPillarRoom i = do
|
|
xs <- takeN i $ [V2 x y | x <- [20, 220], y <- [20, 40]] ++ [V2 120 160, V2 120 200]
|
|
rcp <- roomCenterPillar
|
|
return $ rcp & rmPmnts <>~ fmap f xs
|
|
where
|
|
f x = sPS x (argV $ V2 120 80 -.- x) randC1
|
|
|
|
critsRoom :: Int -> State LayoutVars (Tree Room)
|
|
critsRoom i =
|
|
join $
|
|
takeOne
|
|
[ return <$> roomCCrits i
|
|
, return <$> critsPillarRoom i
|
|
, return <$> glassSwitchBackCrits i
|
|
]
|
|
|
|
distributerRoom :: AmmoType -> Int -> State LayoutVars Room
|
|
distributerRoom atype aamount = do
|
|
i <- nextLayoutInt
|
|
i1 <- nextLayoutInt
|
|
w <- state $ randomR (100, 400)
|
|
h <- state $ randomR (200, 400)
|
|
r <- roomRectAutoLights w h
|
|
let mcstore = defaultMachine
|
|
& mcMaterial .~ Metal
|
|
& mcType .~ McStorage atype aamount
|
|
mcdist mj = defaultMachine
|
|
& mcMaterial .~ Metal
|
|
& mcType .~ McDistributer mj
|
|
store = sps (unusedOffPathAwayFromLink 50)
|
|
(PutMachine (reverse $ polyCirc 4 20) mcstore Nothing) & plExternalID ?~ i
|
|
dst gw = let mj = gw ^? genPmnt . ix i . plMID . _Just
|
|
in putTerminal (mcdist mj) (defaultTerminal & tmBootLines .~ tmDistributeLines
|
|
& tmRebootOnAccess .~ True
|
|
)
|
|
& plSpot .~ unusedOffPathAwayFromLink 50
|
|
& plExternalID ?~ i1
|
|
thepipe gw =
|
|
let p1 = gw ^?! genPmnt . ix i . plSpot . psPos
|
|
p2 = gw ^?! genPmnt . ix i1 . plSpot . psPos
|
|
in sps (PS p1 0)
|
|
(PutForeground
|
|
(ForegroundShape 0 0
|
|
(noPic . colorSH orange $ horPipe 70 0 (p2 - p1)
|
|
<> translateSH (V3 0 0 30) (verticalPipe (70 - 30))
|
|
<> translateSH (0 & _xy .~p2 - p1) (verticalPipe 70)
|
|
)
|
|
)
|
|
)
|
|
return $ r & rmPmnts .:~ store
|
|
& rmInPmnt <>~ [(0,dst),(1,thepipe)]
|
|
& rmLinks %~ setInLinksByType (OnEdge South)
|
|
& rmLinks %~ setOutLinks (not . S.member (OnEdge South) . _rlType)
|
|
|
|
tmDistributeLines :: [TerminalLine]
|
|
tmDistributeLines = [TLine 1 [TerminalLineConst "ATTEMPTING TO DISTRIBUTE MATERIAL..." white] TmDistributeAmmo]
|