packages feed

Allure-0.4.1: src/Terrain.hs

module Terrain
  (Terrain(Door), DL(Light, Dark), rock, opening, floorDark, floorLight, unknown, stairs, door, deDoor, isFloor, isFloorDark, isRock, isOpening, isUnknown, isOpen, isExit, deStairs, fromDL, toDL, isAlight, lookTerrain, viewTerrain) where

import Control.Monad

import Data.Binary
import Data.Maybe

import qualified Color
import Geometry
import WorldLoc

import Color
import Effect
import Random

data TileKind = TileKind
  { usymbol  :: !Char         -- ^ map symbol
  , uname    :: String        -- ^ name
  , ucolor   :: !Color.Color  -- ^ map color
  , ucolor2  :: !Color.Color  -- ^ map color when not in FOV
  , ufreq    :: !Int          -- ^ created that often (within a group?)
  , ufeature :: [Feature]     -- ^ properties
  }
  deriving (Show, Eq, Ord)

data Feature =
    Walkable         -- ^ actors can walk through
  | Clear            -- ^ actors can see through
  | Exit             -- ^ is an exit from a room
  | Lit Int          -- ^ emits light; radius 0 means just the tile is lit
  | Aura Effect      -- ^ sustains the effect continuously
  | Cause Effect     -- ^ causes the effect when triggered
  | Change TileKind  -- ^ transitions when triggered
  | Climbable VDir   -- ^ triggered by climbing
  | Openable         -- ^ triggered by opening
  | Closable         -- ^ triggered by closable
  | Secret RollDice  -- ^ triggered by searching a number of times
  deriving (Show, Eq, Ord)

wall, doorOpen, doorClosed, doorSecret :: TileKind  -- TODO: , opening, floorDark, floorLight, unknown

wall = TileKind
  { usymbol  = '#'
  , uname    = "A wall."
  , ucolor   = Color.BrWhite
  , ucolor2  = Color.defFG
  , ufreq    = 100
  , ufeature = []
  }

doorOpen = TileKind
  { usymbol  = '\''
  , uname    = "An open door."
  , ucolor   = Color.Yellow
  , ucolor2  = Color.BrBlack
  , ufreq    = 100
  , ufeature = [Walkable, Clear, Exit, Lit 0, Change doorClosed, Openable]
  }

doorClosed = TileKind
  { usymbol  = '+'
  , uname    = "A closed door."
  , ucolor   = Color.Yellow
  , ucolor2  = Color.BrBlack
  , ufreq    = 100
  , ufeature = [Exit, Change doorClosed, Closable]
  }

doorSecret = wall
  { ufeature = [Change doorClosed, Secret (7, 2)]
  }

data Terrain =
    Rock
  | Opening
  | Floor DL
  | Unknown
  | Stairs DL VDir (Maybe WorldLoc)
  | Door (Maybe Int)  -- Nothing: open, Just 0: closed, otherwise secret
  deriving Show

instance Binary VDir where
  put = putWord8 . fromIntegral . fromEnum
  get = liftM (toEnum . fromIntegral) getWord8

instance Binary Terrain where
  put Rock            = putWord8 0
  put Opening         = putWord8 1
  put (Floor dl)      = putWord8 2 >> put dl
  put Unknown         = putWord8 3
  put (Stairs dl d n) = putWord8 5 >> put dl >> put d >> put n
  put (Door o)        = putWord8 6 >> put o
  get = do
          tag <- getWord8
          case tag of
            0 -> return Rock
            1 -> return Opening
            2 -> liftM Floor get
            3 -> return Unknown
            5 -> liftM3 Stairs get get get
            6 -> liftM Door get
            _ -> fail "no parse (Terrain)"

instance Eq Terrain where
  Rock == Rock = True
  Opening == Opening = True
  Floor l == Floor l' = l == l'
  Unknown == Unknown = True
  Stairs dl d t == Stairs dl' d' t' = dl == dl' && d == d' && t == t'
  Door o == Door o' = o == o'
  _ == _ = False

data DL = Dark | Light
  deriving (Eq, Show, Enum, Bounded)

instance Binary DL where
  put = putWord8 . fromIntegral . fromEnum
  get = liftM (toEnum . fromIntegral) getWord8

rock, opening, floorDark, floorLight, unknown :: Terrain
rock = Rock
opening = Opening
floorDark = Floor Dark
floorLight = Floor Light
unknown = Unknown

stairs :: DL -> VDir -> Maybe WorldLoc -> Terrain
stairs = Stairs

deStairs :: Terrain -> Maybe (VDir, Maybe WorldLoc)
deStairs (Stairs _ vdir next) = Just (vdir, next)
deStairs _                    = Nothing

deDoor :: Terrain -> Maybe (Maybe Int)
deDoor (Door n) = Just n
deDoor _        = Nothing

door :: Maybe Int -> Terrain
door = Door

isFloor :: Terrain -> Bool
isFloor (Floor _) = True
isFloor _         = False

isFloorDark :: Terrain -> Bool
isFloorDark (Floor Dark) = True
isFloorDark _            = False

isRock :: Terrain -> Bool
isRock Rock = True
isRock _    = False

isOpening :: Terrain -> Bool
isOpening Opening = True
isOpening _       = False

isUnknown :: Terrain -> Bool
isUnknown Unknown = True
isUnknown _       = False

-- | allows moves and vision
isOpen :: Terrain -> Bool
isOpen (Floor {})   = True
isOpen Opening      = True
isOpen (Door o)     = isNothing o
isOpen (Stairs {})  = True
isOpen _            = False

-- | marks an exit from a room
isExit :: Terrain -> Bool
isExit (Stairs  {}) = True
isExit (Opening {}) = True
isExit (Door    {}) = True
isExit _            = False

fromDL :: DL -> Bool
fromDL Dark = False
fromDL Light = True

toDL :: Bool -> DL
toDL False = Dark
toDL True  = Light

-- | is lighted on its own
isAlight :: Terrain -> Bool
isAlight (Floor l)      = fromDL l
isAlight (Stairs l _ _) = fromDL l
isAlight _              = False

-- | Produces a textual description for terrain, used if no objects
-- are present.
lookTerrain :: Terrain -> String
lookTerrain (Floor _)         = "Floor."
lookTerrain Opening           = "An opening."
lookTerrain (Stairs _ Up _)   = "A staircase up."
lookTerrain (Stairs _ Down _) = "A staircase down."
lookTerrain (Door Nothing)    = "An open door."
lookTerrain (Door (Just 0))   = "A closed door."
lookTerrain (Door (Just _))   = "A wall."  -- secret
lookTerrain Rock              = "Rock."
lookTerrain Unknown           = ""

-- | The parameter "n" is the level of evolution:
--
-- 0: final
-- 1: stairs added
-- 2: doors added
-- 3: corridors and openings added
-- 4: only rooms
--
-- The Bool indicates whether the loc is currently visible.
viewTerrain :: Bool -> Terrain -> (Char, Color.Color)
viewTerrain b t =
  let def =     if b then Color.BrWhite else Color.defFG
      defDark = if b then Color.BrYellow else Color.BrBlack
      defDoor = if b then Color.Yellow else Color.BrBlack
  in case t of
       Rock            -> ('#', def)
       Opening         -> ('.', def)
       (Floor d)       -> ('.', if d == Light then def else defDark)
       Unknown         -> (' ', def)
       (Stairs d p _)  -> (if p == Up then '<' else '>',
                           if d == Light then def else defDark)
       (Door (Just 0)) -> ('+', defDoor)
       (Door (Just _)) -> viewTerrain b Rock  -- secret door
       (Door Nothing)  -> ('\'', defDoor)