about summary refs log tree commit diff
path: root/users/glittershark/xanthous/src/Xanthous/Generators/Village.hs
blob: 614170d0c4f1658672b4011521a7369a79da9770 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
module Xanthous.Generators.Village
  ( fromCave
  ) where
--------------------------------------------------------------------------------
import           Xanthous.Prelude hiding (any, failing, toList)
--------------------------------------------------------------------------------
import           Control.Monad.Random (MonadRandom)
import           Control.Monad.State (execStateT, MonadState, modify)
import           Control.Monad.Trans.Maybe
import           Control.Parallel.Strategies
import           Data.Array.IArray
import           Data.Foldable (any, toList)
--------------------------------------------------------------------------------
import           Xanthous.Data
import           Xanthous.Data.EntityMap (EntityMap)
import qualified Xanthous.Data.EntityMap as EntityMap
import           Xanthous.Entities.Environment
import           Xanthous.Generators.Util
import           Xanthous.Game.State (SomeEntity(..))
import           Xanthous.Random
--------------------------------------------------------------------------------

fromCave :: MonadRandom m
         => Cells -- ^ The positions of all the walls
         -> m (EntityMap SomeEntity)
fromCave wallPositions = execStateT (fromCave' wallPositions) mempty

fromCave' :: forall m. (MonadRandom m, MonadState (EntityMap SomeEntity) m)
          => Cells
          -> m ()
fromCave' wallPositions = failing (pure ()) $ do
  Just villageRegion <-
    choose
    . (`using` parTraversable rdeepseq)
    . weightedBy (\reg -> let circSize = length $ circumference reg
                         in if circSize == 50
                            then (1.0 :: Double)
                            else 1.0 / (fromIntegral . abs $ circSize - 50))
    $ regions closedHallways

  let circ = setFromList . circumference $ villageRegion

  centerPoints <- chooseSubset (0.1 :: Double) $ toList circ

  roomTiles <- foldM
              (flip $ const $ stepOut circ)
              (map pure centerPoints)
              [0 :: Int ..2]

  let roomWalls = circumference . setFromList @(Set _) <$> roomTiles
      allWalls = join roomWalls

  doorPositions <- fmap join . for roomWalls $ \room ->
    let candidates = filter (`notMember` circ) room
    in fmap toList . choose $ ChooseElement candidates

  let entryways =
        filter (\pt ->
                  let ncs = neighborCells pt
                  in any ((&&) <$> (not . (wallPositions !))
                              <*> (`notMember` villageRegion)) ncs
                   && any ((&&) <$> (`member` villageRegion)
                              <*> (`notElem` allWalls)) ncs)
                  $ toList villageRegion

  Just entryway <- choose $ ChooseElement entryways

  for_ (filter ((&&) <$> (`notElem` doorPositions) <*> (/= entryway)) allWalls)
    $ insertEntity Wall
  for_ (filter (/= entryway) doorPositions) $ insertEntity unlockedDoor
  insertEntity unlockedDoor entryway


  where
    insertEntity e pt = modify $ EntityMap.insertAt (ptToPos pt) $ SomeEntity e
    ptToPos pt = _Position # (pt & both %~ fromIntegral)

    stepOut :: Set (Word, Word) -> [[(Word, Word)]] -> MaybeT m [[(Word, Word)]]
    stepOut circ rooms = for rooms $ \room ->
      let nextLevels = hashNub $ toList . neighborCells =<< room
      in pure
         . (<> room)
         $ filter ((&&) <$> (`notMember` circ) <*> (`notElem` join rooms))
         nextLevels

    circumference pts =
      filter (any (`notMember` pts) . neighborCells) $ toList pts
    closedHallways = closeHallways livePositions
    livePositions = amap not wallPositions

--------------------------------------------------------------------------------

closeHallways :: Cells -> Cells
closeHallways livePositions =
  livePositions // mapMaybe closeHallway (assocs livePositions)
  where
    closeHallway (_, False) = Nothing
    closeHallway (pos, _)
      | isHallway pos = Just (pos, False)
      | otherwise     = Nothing
    isHallway pos = any ((&&) <$> not . view left <*> not . view right)
      . rotations
      . fmap (fromMaybe False)
      $ arrayNeighbors livePositions pos

failing :: Monad m => m a -> MaybeT m a -> m a
failing result = (maybe result pure =<<) . runMaybeT

{-

import Xanthous.Generators.Village
import Xanthous.Generators
import Xanthous.Data
import System.Random
import qualified Data.Text
import qualified Xanthous.Generators.CaveAutomata as CA
let gi = GeneratorInput SCaveAutomata CA.defaultParams
wallPositions <- generateFromInput gi (Dimensions 80 50) <$> getStdGen
putStrLn . Data.Text.unpack $ showCells wallPositions

import Data.Array.IArray
let closedHallways = closeHallways . amap not $ wallPositions
putStrLn . Data.Text.unpack . showCells $ amap not closedHallways

-}