Place doors on the level

Pick a random subset of cells on the level that have a wall on two
opposite sides and are clear on the other two sides, and place closed,
unlocked doors on those cells.
This commit is contained in:
Griffin Smith 2019-12-30 12:30:12 -05:00
parent e76567b9e7
commit dcf44f29f5
4 changed files with 62 additions and 17 deletions

View file

@ -90,11 +90,7 @@ initLevel = do
generateLevel SCaveAutomata CaveAutomata.defaultParams generateLevel SCaveAutomata CaveAutomata.defaultParams
$ Dimensions 80 80 $ Dimensions 80 80
entities <>= (SomeEntity <$> level ^. levelWalls) entities <>= levelToEntityMap level
entities <>= (SomeEntity <$> level ^. levelItems)
entities <>= (SomeEntity <$> level ^. levelCreatures)
entities <>= (SomeEntity <$> level ^. levelTutorialMessage)
characterPosition .= level ^. levelCharacterPosition characterPosition .= level ^. levelCharacterPosition
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View file

@ -7,6 +7,7 @@ module Xanthous.Entities.Environment
, Door(..) , Door(..)
, open , open
, locked , locked
, unlockedDoor
-- * Messages -- * Messages
, GroundMessage(..) , GroundMessage(..)
) where ) where
@ -88,6 +89,13 @@ instance Entity Door where
description _ = "a door" description _ = "a door"
entityChar _ = "d" entityChar _ = "d"
-- | A closed, unlocked door
unlockedDoor :: Door
unlockedDoor = Door
{ _open = False
, _locked = False
}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
newtype GroundMessage = GroundMessage Text newtype GroundMessage = GroundMessage Text

View file

@ -13,9 +13,11 @@ module Xanthous.Generators
, levelWalls , levelWalls
, levelItems , levelItems
, levelCreatures , levelCreatures
, levelDoors
, levelCharacterPosition , levelCharacterPosition
, levelTutorialMessage , levelTutorialMessage
, generateLevel , generateLevel
, levelToEntityMap
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Xanthous.Prelude hiding (Level) import Xanthous.Prelude hiding (Level)
@ -34,6 +36,7 @@ import qualified Xanthous.Data.EntityMap as EntityMap
import Xanthous.Entities.Environment import Xanthous.Entities.Environment
import Xanthous.Entities.Item (Item) import Xanthous.Entities.Item (Item)
import Xanthous.Entities.Creature (Creature) import Xanthous.Entities.Creature (Creature)
import Xanthous.Game.State (SomeEntity(..))
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
data Generator data Generator
@ -109,6 +112,7 @@ cellsToWalls cells = foldl' maybeInsertWall mempty . assocs $ cells
data Level = Level data Level = Level
{ _levelWalls :: !(EntityMap Wall) { _levelWalls :: !(EntityMap Wall)
, _levelDoors :: !(EntityMap Door)
, _levelItems :: !(EntityMap Item) , _levelItems :: !(EntityMap Item)
, _levelCreatures :: !(EntityMap Creature) , _levelCreatures :: !(EntityMap Creature)
, _levelTutorialMessage :: !(EntityMap GroundMessage) , _levelTutorialMessage :: !(EntityMap GroundMessage)
@ -116,13 +120,27 @@ data Level = Level
} }
makeLenses ''Level makeLenses ''Level
generateLevel :: MonadRandom m => SGenerator gen -> Params gen -> Dimensions -> m Level generateLevel
:: MonadRandom m
=> SGenerator gen
-> Params gen
-> Dimensions
-> m Level
generateLevel gen ps dims = do generateLevel gen ps dims = do
rand <- mkStdGen <$> getRandom rand <- mkStdGen <$> getRandom
let cells = generate gen ps dims rand let cells = generate gen ps dims rand
_levelWalls = cellsToWalls cells _levelWalls = cellsToWalls cells
_levelItems <- randomItems cells _levelItems <- randomItems cells
_levelCreatures <- randomCreatures cells _levelCreatures <- randomCreatures cells
_levelDoors <- randomDoors cells
_levelCharacterPosition <- chooseCharacterPosition cells _levelCharacterPosition <- chooseCharacterPosition cells
_levelTutorialMessage <- tutorialMessage cells _levelCharacterPosition _levelTutorialMessage <- tutorialMessage cells _levelCharacterPosition
pure Level {..} pure Level {..}
levelToEntityMap :: Level -> EntityMap SomeEntity
levelToEntityMap level
= (SomeEntity <$> level ^. levelWalls)
<> (SomeEntity <$> level ^. levelDoors)
<> (SomeEntity <$> level ^. levelItems)
<> (SomeEntity <$> level ^. levelCreatures)
<> (SomeEntity <$> level ^. levelTutorialMessage)

View file

@ -3,6 +3,7 @@ module Xanthous.Generators.LevelContents
( chooseCharacterPosition ( chooseCharacterPosition
, randomItems , randomItems
, randomCreatures , randomCreatures
, randomDoors
, tutorialMessage , tutorialMessage
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -10,6 +11,7 @@ import Xanthous.Prelude
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Monad.Random import Control.Monad.Random
import Data.Array.IArray (amap, bounds, rangeSize, (!)) import Data.Array.IArray (amap, bounds, rangeSize, (!))
import qualified Data.Array.IArray as Arr
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Xanthous.Generators.Util import Xanthous.Generators.Util
import Xanthous.Random import Xanthous.Random
@ -20,7 +22,8 @@ import qualified Xanthous.Entities.Item as Item
import Xanthous.Entities.Item (Item) import Xanthous.Entities.Item (Item)
import qualified Xanthous.Entities.Creature as Creature import qualified Xanthous.Entities.Creature as Creature
import Xanthous.Entities.Creature (Creature) import Xanthous.Entities.Creature (Creature)
import Xanthous.Entities.Environment (GroundMessage(..)) import Xanthous.Entities.Environment
(GroundMessage(..), Door(..), unlockedDoor)
import Xanthous.Messages (message_) import Xanthous.Messages (message_)
import Xanthous.Util.Graphics (circle) import Xanthous.Util.Graphics (circle)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -31,6 +34,25 @@ chooseCharacterPosition = randomPosition
randomItems :: MonadRandom m => Cells -> m (EntityMap Item) randomItems :: MonadRandom m => Cells -> m (EntityMap Item)
randomItems = randomEntities Item.newWithType (0.0004, 0.001) randomItems = randomEntities Item.newWithType (0.0004, 0.001)
randomDoors :: MonadRandom m => Cells -> m (EntityMap Door)
randomDoors cells = do
doorRatio <- getRandomR subsetRange
let numDoors = floor $ doorRatio * fromIntegral (length candidateCells)
doorPositions = positionFromPair <$> take numDoors candidateCells
doors = zip doorPositions $ repeat unlockedDoor
pure $ _EntityMap # doors
where
candidateCells = filter doorable $ Arr.indices cells
subsetRange = (0.8 :: Double, 1.0)
doorable (x, y) =
( fromMaybe True $ cells ^? ix (x - 1, y) -- left
, fromMaybe True $ cells ^? ix (x, y - 1) -- top
, fromMaybe True $ cells ^? ix (x + 1, y) -- right
, fromMaybe True $ cells ^? ix (x, y + 1) -- bottom
) `elem` [ (True, False, True, False)
, (False, True, False, True)
]
randomCreatures :: MonadRandom m => Cells -> m (EntityMap Creature) randomCreatures :: MonadRandom m => Cells -> m (EntityMap Creature)
randomCreatures = randomEntities Creature.newWithType (0.0007, 0.003) randomCreatures = randomEntities Creature.newWithType (0.0007, 0.003)
@ -73,14 +95,15 @@ randomEntities newWithType sizeRange cells =
pure $ _EntityMap # entities pure $ _EntityMap # entities
randomPosition :: MonadRandom m => Cells -> m Position randomPosition :: MonadRandom m => Cells -> m Position
randomPosition cells = fmap positionFromPair . choose $ impureNonNull candidates randomPosition = fmap positionFromPair . choose . impureNonNull . cellCandidates
where
-- cells ends up with true = wall, we want true = can put an item here
placeableCells = amap not cells
-- find the largest contiguous region of cells in the cave. -- cellCandidates :: Cells -> Cells
candidates cellCandidates :: Cells -> Set (Word, Word)
= maximumBy (compare `on` length) cellCandidates
$ fromMaybe (error "No regions generated! this should never happen.") -- find the largest contiguous region of cells in the cave.
$ fromNullable = maximumBy (compare `on` length)
$ regions placeableCells . fromMaybe (error "No regions generated! this should never happen.")
. fromNullable
. regions
-- cells ends up with true = wall, we want true = can put an item here
. amap not