Allure-0.6.0.0: GameDefinition/Content/TileKind.hs
-- Copyright (c) 2008--2011 Andres Loeh, 2010--2017 Mikolaj Konarski
-- This file is a part of the computer game Allure of the Stars
-- and is released under the terms of the GNU Affero General Public License.
-- For license and copyright information, see the file LICENSE.
--
-- | Terrain tile definitions.
module Content.TileKind
( cdefs
) where
import Prelude ()
import Game.LambdaHack.Common.Prelude
import qualified Data.Text as T
import Game.LambdaHack.Common.Color
import Game.LambdaHack.Common.ContentDef
import Game.LambdaHack.Common.Misc
import Game.LambdaHack.Content.TileKind
cdefs :: ContentDef TileKind
cdefs = ContentDef
{ getSymbol = tsymbol
, getName = tname
, getFreq = tfreq
, validateSingle = validateSingleTileKind
, validateAll = validateAllTileKind
, content = contentFromList $
[unknown, hardRock, wall, wallSuspect, wallObscured, pillar, pillarCache, lampPost, signboardUnread, signboardRead, tree, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTaintedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTaintedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bush, bushBurnt, bushBurning, floorFog, floorFogDark, floorSmoke, floorSmokeDark, doorOpen, floorCorridor, floorArena, floorNoise, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorRed, floorBlue, floorGreen, floorArenaShade ]
++ map makeDarkColor ldarkColorable
-- Allure-specific
++ [oriel, outerHullWall, doorlessWall, machineWall, wallObscuredDefaced, wallObscuredFrescoed, rock, stairsLiftUp, stairsLiftDown, escapeSpaceshipDown]
}
unknown, hardRock, wall, wallSuspect, wallObscured, pillar, pillarCache, lampPost, signboardUnread, signboardRead, tree, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTaintedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTaintedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bush, bushBurnt, bushBurning, floorFog, floorFogDark, floorSmoke, floorSmokeDark, doorOpen, floorCorridor, floorArena, floorNoise, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorRed, floorBlue, floorGreen, floorArenaShade :: TileKind
-- Allure-specific
oriel, outerHullWall, doorlessWall, machineWall, wallObscuredDefaced, wallObscuredFrescoed, rock, stairsLiftUp, stairsLiftDown, escapeSpaceshipDown :: TileKind
ldarkColorable :: [TileKind]
ldarkColorable = [tree, bush, floorCorridor, floorArena, floorNoise, floorDirt, floorDirtSpice, floorActor, floorActorItem]
-- Symbols to be used:
-- LOS noLOS
-- Walk .' :;
-- noWalk %^ #O&<>+
--
-- can be opened ^&+
-- can be closed '
-- some noWalk can be changed without opening, regardless of symbol
-- not used yet:
-- ~ (water, acid, ect.)
-- : (curtain, etc., not flowing, but solid and static)
-- ` (not visible enough, would need font modification)
-- Note that for AI hints and UI comfort, most multiple-use @Embed@ tiles
-- should have a variant, which after first use transforms into a different
-- colour tile without @ChangeTo@ and similar (which then AI no longer touches).
-- If a tile is supposed to be repeatedly activated by AI (e.g., cache),
-- it should keep @ChangeTo@ for the whole time.
-- * Main tiles, modified for Allure; some removed
-- ** Not walkable
-- *** Not clear
unknown = TileKind -- needs to have index 0 and alter 1
{ tsymbol = ' '
, tname = "unknown space"
, tfreq = [("unknown space", 1)]
, tcolor = defFG
, tcolor2 = defFG
, talter = 1
, tfeature = [Dark]
}
hardRock = TileKind
{ tsymbol = '#'
, tname = "habitat containment wall"
, tfreq = [("basic outer fence", 1)]
, tcolor = BrBlack
, tcolor2 = BrBlack
, talter = maxBound -- impenetrable
, tfeature = []
}
wall = TileKind
{ tsymbol = '#'
, tname = "wall"
, tfreq = [ ("fillerWall", 1), ("legendLit", 100), ("legendDark", 100)
, ("cachable", 80), ("stair terminal", 100)
, ("battleSet", 250), ("rectWindowsOver_%_Lit", 80) ]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 100
, tfeature = [BuildAs "suspect wall", Indistinct]
}
wallSuspect = TileKind -- only on client
{ tsymbol = '#'
, tname = "suspect uneven wall"
, tfreq = [("suspect wall", 1)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 2
, tfeature = [ RevealAs "trapped door"
, ObscureAs "obscured wall"
, Indistinct ]
}
wallObscured = TileKind
{ tsymbol = '#'
, tname = "scratched wall"
, tfreq = [("obscured wall", 100)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 5
, tfeature = [ Embed "scratch on wall"
, HideAs "suspect wall"
, Indistinct
]
}
pillar = TileKind
{ tsymbol = 'O'
, tname = "pillar"
, tfreq = [("legendLit", 100), ("legendDark", 100)]
, tcolor = BrCyan -- not BrWhite, to tell from heroes
, tcolor2 = Cyan
, talter = 100
, tfeature = [Indistinct]
}
pillarCache = TileKind
{ tsymbol = '#'
, tname = "cache"
, tfreq = [("cachable", 20), ("stair terminal", 1), ("escapeSetDark", 1)]
, tcolor = BrBlue
, tcolor2 = Blue
, talter = 5
, tfeature = [ Embed "terrain cache", Embed "terrain cache trap"
, ChangeTo "cachable", ConsideredByAI, Indistinct ]
-- Not explorable, but prominently placed, so hard to miss.
-- Very beneficial, so AI eager to trigger.
}
lampPost = TileKind
{ tsymbol = 'O'
, tname = "lamp post"
, tfreq = [("lampPostOver_O", 1)]
, tcolor = BrYellow
, tcolor2 = Brown
, talter = 100
, tfeature = []
}
signboardUnread = TileKind -- client only, indicates never used by this faction
{ tsymbol = 'O'
, tname = "signboard"
, tfreq = [("signboard unread", 1)]
, tcolor = BrCyan
, tcolor2 = Cyan
, talter = 5
, tfeature = [ Embed "signboard", Indistinct
, ConsideredByAI -- changes after use, so safe for AI
, RevealAs "signboard" ] -- to display as hidden
}
signboardRead = TileKind -- after first use revealed to be this one
{ tsymbol = 'O'
, tname = "signboard"
, tfreq = [("signboard", 1), ("zooSet", 2), ("ambushSet", 1)]
, tcolor = BrCyan
, tcolor2 = Cyan
, talter = 5
, tfeature = [Embed "signboard", HideAs "signboard unread", Indistinct]
}
tree = TileKind
{ tsymbol = 'O'
, tname = "tree"
, tfreq = [ ("brawlSetLit", 140), ("shootoutSetLit", 10)
, ("escapeSetLit", 30), ("treeShadeOver_O_Lit", 1) ]
, tcolor = BrGreen
, tcolor2 = Green
, talter = 50
, tfeature = []
}
treeBurnt = tree
{ tname = "burnt tree"
, tfreq = [("zooSet", 3), ("tree with fire", 30)]
, tcolor = BrBlack
, tcolor2 = BrBlack
, tfeature = Dark : tfeature tree
}
treeBurning = tree
{ tname = "burning tree"
, tfreq = [("zooSet", 30), ("tree with fire", 70)]
, tcolor = BrRed
, tcolor2 = Red
, talter = 5
, tfeature = Embed "big fire" : ChangeTo "tree with fire" : tfeature tree
-- dousing off the tree will have more sense when it periodically
-- explodes, hitting and lighting up the team and so betraying it
}
rubble = TileKind
{ tsymbol = '&'
, tname = "rubble"
, tfreq = [] -- [("floorCorridorLit", 1)]
-- disabled while it's all or nothing per cave and per room;
-- we need a new mechanism, Spice is not enough, because
-- we don't want multicolor trailLit corridors
-- ("rubbleOrNot", 70)
-- until we can sync change of tile and activation, it always takes 1 turn
, tcolor = BrYellow
, tcolor2 = Brown
, talter = 5
, tfeature = [OpenTo "rubbleOrNot", Embed "rubble", Indistinct]
}
rubbleSpice = TileKind
{ tsymbol = '&'
, tname = "rubble"
, tfreq = [ ("smokeClumpOver_f_Lit", 1), ("emptySet", 1), ("noiseSet", 10)
, ("zooSet", 100), ("ambushSet", 20) ]
, tcolor = BrYellow
, tcolor2 = Brown
, talter = 5
, tfeature = [Spice, OpenTo "rubbleSpiceOrNot", Embed "rubble", Indistinct]
-- It's not explorable, due to not being walkable nor clear and due
-- to being a door (@OpenTo@), which is kind of OK, because getting
-- the item is risky and, e.g., AI doesn't attempt it.
-- Also, AI doesn't go out of its way to clear the way for heroes.
}
doorTrapped = TileKind
{ tsymbol = '+'
, tname = "trapped door"
, tfreq = [("trapped door", 1)]
, tcolor = BrRed
, tcolor2 = Red
, talter = 2
, tfeature = [ Embed "doorway trap"
, OpenTo "open door"
, HideAs "suspect wall"
]
}
doorClosed = TileKind
{ tsymbol = '+'
, tname = "closed door"
, tfreq = [("legendLit", 100), ("legendDark", 100), ("closed door", 1)]
, tcolor = Brown
, tcolor2 = BrBlack
, talter = 2
, tfeature = [OpenTo "open door"] -- never hidden
}
stairsUp = TileKind
{ tsymbol = '<'
, tname = "staircase up"
, tfreq = [("staircase up", 9), ("ordinary staircase up", 1)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = talterForStairs
, tfeature = [Embed "staircase up", ConsideredByAI]
}
stairsTaintedUp = TileKind
{ tsymbol = '<'
, tname = "tainted staircase up"
, tfreq = [("staircase up", 1)]
, tcolor = BrRed
, tcolor2 = Red
, talter = talterForStairs
, tfeature = [ Embed "staircase up", Embed "staircase trap up"
, ConsideredByAI, ChangeTo "ordinary staircase up" ]
-- AI uses despite the trap; exploration more important
}
stairsOutdoorUp = stairsUp
{ tname = "signpost pointing backward"
, tfreq = [("staircase outdoor up", 1)]
}
stairsGatedUp = stairsUp
{ tname = "gated staircase up"
, tfreq = [("gated staircase up", 1)]
, talter = talterForStairs + 1 -- animals and bosses can't use
}
stairsDown = TileKind
{ tsymbol = '>'
, tname = "staircase down"
, tfreq = [("staircase down", 9), ("ordinary staircase down", 1)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = talterForStairs
, tfeature = [Embed "staircase down", ConsideredByAI]
}
stairsTaintedDown = TileKind
{ tsymbol = '>'
, tname = "tainted staircase down"
, tfreq = [("staircase down", 1)]
, tcolor = BrRed
, tcolor2 = Red
, talter = talterForStairs
, tfeature = [ Embed "staircase down", Embed "staircase trap down"
, ConsideredByAI, ChangeTo "ordinary staircase down" ]
}
stairsOutdoorDown = stairsDown
{ tname = "signpost pointing forward"
, tfreq = [("staircase outdoor down", 1)]
}
stairsGatedDown = stairsDown
{ tname = "gated staircase down"
, tfreq = [("gated staircase down", 1)]
, talter = talterForStairs + 1 -- animals and bosses can't use
}
escapeUp = TileKind
{ tsymbol = '<'
, tname = "exit hatch up"
, tfreq = [("legendLit", 1), ("legendDark", 1)]
, tcolor = BrYellow
, tcolor2 = BrYellow
, talter = 0 -- anybody can escape (or guard escape)
, tfeature = [Embed "escape", ConsideredByAI]
}
escapeDown = TileKind
{ tsymbol = '>'
, tname = "exit trapdoor down"
, tfreq = [("legendLit", 1), ("legendDark", 1)]
, tcolor = BrYellow
, tcolor2 = BrYellow
, talter = 0 -- anybody can escape (or guard escape)
, tfeature = [Embed "escape", ConsideredByAI]
}
escapeOutdoorDown = escapeDown
{ tname = "exit back to town"
, tfreq = [("escape outdoor down", 1)]
}
-- *** Clear
wallGlass = TileKind
{ tsymbol = '%'
, tname = "transparent polymer wall"
, tfreq = [("glasshouseOver_%_Lit", 1)]
, tcolor = BrBlue
, tcolor2 = Blue
, talter = 10
, tfeature = [BuildAs "suspect wall", Clear]
}
wallGlassSpice = wallGlass
{ tfreq = [("rectWindowsOver_%_Lit", 20)]
, tfeature = Spice : tfeature wallGlass
}
pillarIce = TileKind
{ tsymbol = '^'
, tname = "ice"
, tfreq = [("brawlSetLit", 20)]
, tcolor = BrBlue
, tcolor2 = Blue
, talter = 5
, tfeature = [Clear, Embed "frost", OpenTo "damp stone floor"]
-- Is door, due to @OpenTo@, so is not explorable, but it's OK, because
-- it doesn't generate items nor clues. This saves on the need to
-- get each ice pillar into sight range when exploring level.
}
pulpit = TileKind
{ tsymbol = '%'
, tname = "VR harness"
, tfreq = [("pulpit", 1), ("zooSet", 2)]
, tcolor = BrYellow
, tcolor2 = Brown
, talter = 5
, tfeature = [Clear, Embed "pulpit", Indistinct]
-- mixed blessing, so AI ignores, saved for player fun
}
bush = TileKind
{ tsymbol = '%'
, tname = "bush"
, tfreq = [ ("bush Lit", 1), ("shootoutSetLit", 30), ("escapeSetLit", 30)
, ("arenaSetLit", 3), ("bushClumpOver_f_Lit", 1) ]
, tcolor = BrGreen
, tcolor2 = Green
, talter = 10
, tfeature = [Clear]
}
bushBurnt = bush
{ tname = "burnt bush"
, tfreq = [ ("battleSet", 30), ("ambushSet", 4), ("zooSet", 30)
, ("bush with fire", 70) ]
, tcolor = BrBlack
, tcolor2 = BrBlack
, tfeature = Dark : tfeature bush
}
bushBurning = bush
{ tname = "burning bush"
, tfreq = [("ambushSet", 40), ("zooSet", 300), ("bush with fire", 30)]
, tcolor = BrRed
, tcolor2 = Red
, talter = 5
, tfeature = Embed "small fire" : ChangeTo "bush with fire" : tfeature bush
}
-- ** Walkable
-- *** Not clear
floorFog = TileKind
{ tsymbol = ';'
, tname = "faint fog"
, tfreq = [ ("lit fog", 1), ("emptySet", 5), ("shootoutSetLit", 20)
, ("noiseSet", 10), ("fogClumpOver_f_Lit", 60) ]
-- lit fog is OK for shootout, because LOS is mutual, as opposed
-- to dark fog, and so camper has little advantage, especially
-- on big maps, where he doesn't know on which side of fog patch to hide
, tcolor = BrCyan
, tcolor2 = Cyan
, talter = 0
, tfeature = [Walkable, NoItem, Indistinct, OftenActor]
}
floorFogDark = floorFog
{ tname = "thick fog"
, tfreq = [("noiseSet", 10), ("escapeSetDark", 60)]
, tfeature = Dark : tfeature floorFog
}
floorSmoke = TileKind
{ tsymbol = ';'
, tname = "billowing smoke"
, tfreq = [ ("lit smoke", 1)
, ("ambushSet", 30), ("zooSet", 30), ("battleSet", 5)
, ("labTrailLit", 1), ("stair terminal", 2)
, ("smokeClumpOver_f_Lit", 1) ]
, tcolor = Brown
, tcolor2 = BrBlack
, talter = 0
, tfeature = [Walkable, NoItem, Indistinct] -- not dark, embers
}
floorSmokeDark = floorSmoke
{ tname = "lingering smoke"
, tfreq = [("ambushSet", 30)]
, tfeature = Dark : tfeature floorSmoke
}
-- *** Clear
doorOpen = TileKind
{ tsymbol = '\''
, tname = "open door"
, tfreq = [("legendLit", 100), ("legendDark", 100), ("open door", 1)]
, tcolor = Brown
, tcolor2 = BrBlack
, talter = 4
, tfeature = [ Walkable, Clear, NoItem, NoActor
, CloseTo "closed door"
]
}
floorCorridor = TileKind
{ tsymbol = floorSymbol
, tname = "floor"
, tfreq = [("floorCorridorLit", 99), ("rubbleOrNot", 30)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 0
, tfeature = [Walkable, Clear, Indistinct]
}
floorArena = floorCorridor
{ tfreq = [ ("floorArenaLit", 1), ("rubbleSpiceOrNot", 30)
, ("arenaSetLit", 96), ("emptySet", 94), ("zooSet", 1000) ]
}
floorNoise = floorArena
{ tname = "oily floor"
, tfreq = [("noiseSet", 60), ("damp stone floor", 1)]
}
floorDirt = floorArena
{ tname = "dirt"
, tfreq = [ ("battleSet", 1000), ("brawlSetLit", 1000)
, ("shootoutSetLit", 1000), ("escapeSetLit", 1000)
, ("ambushSet", 1000) ]
}
floorDirtSpice = floorDirt
{ tfreq = [ ("treeShadeOver_s_Lit", 1), ("fogClumpOver_f_Lit", 40)
, ("smokeClumpOver_f_Lit", 1), ("bushClumpOver_f_Lit", 1) ]
, tfeature = Spice : tfeature floorDirt
}
floorActor = floorArena
{ tfreq = [("floorActorLit", 1)] -- lit even in dark cave, so no items
, tfeature = OftenActor : tfeature floorArena
}
floorActorItem = floorActor
{ tfreq = [("legendLit", 100)]
, tfeature = OftenItem : tfeature floorActor
}
floorRed = floorCorridor
{ tname = "emergency walkway"
, tfreq = [("emergency walkway", 1), ("trailLit", 20)]
, tcolor = BrRed
, tcolor2 = Red
, tfeature = Trail : tfeature floorCorridor -- no Indistinct
}
floorBlue = floorRed
{ tname = "transport route"
, tfreq = [("trailLit", 100)]
, tcolor = BrBlue
, tcolor2 = Blue
}
floorGreen = floorRed
{ tname = "greenery trail"
, tfreq = [("trailLit", 100)]
, tcolor = BrGreen
, tcolor2 = Green
}
floorArenaShade = floorActor
{ tname = "shaded ground"
, tfreq = [("shaded ground", 1), ("treeShadeOver_s_Lit", 2)]
, tcolor2 = BrBlack
, tfeature = Dark : NoItem : tfeature floorActor
}
-- * Allure-specific
-- ** Not walkable
oriel = TileKind
{ tsymbol = '%' -- story-wise it's transparent, hence the symbol
, tname = "oriel"
, tfreq = [("oriels fence", 4)]
, tcolor = White
, tcolor2 = Black
, talter = maxBound -- impenetrable
, tfeature = [Dark, Indistinct]
}
outerHullWall = hardRock
{ tname = "outer hull wall"
, tfreq = [("oriels fence", 96), ("noise fence", 1)]
}
doorlessWall = TileKind
{ tsymbol = '#'
, tname = "wall"
, tfreq = [("noiseSet", 60), ("doorlessWallOver_#", 20)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 100
, tfeature = [HideAs "fillerWall", Indistinct]
}
machineWall = TileKind
{ tsymbol = '%'
, tname = "hardware rack"
, tfreq = [("noiseSet", 35), ("doorlessWallOver_#", 80)]
, tcolor = White
, tcolor2 = BrBlack
, talter = 100
, tfeature = [Spice, Clear, Indistinct]
}
wallObscuredDefaced = TileKind
{ tsymbol = '#'
, tname = "defaced wall"
, tfreq = [("obscured wall", 45), ("escapeSetDark", 1)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 5
, tfeature = [ Embed "obscene pictograms"
, HideAs "suspect wall"
, Indistinct
]
}
wallObscuredFrescoed = TileKind
{ tsymbol = '#'
, tname = "subtle mural"
, tfreq = [("obscured wall", 5), ("brawlSetLit", 1)]
, tcolor = BrWhite
, tcolor2 = defFG
, talter = 5
, tfeature = [ Embed "subtle fresco"
, HideAs "suspect wall"
, Indistinct
]
}
rock = pillar
{ tname = "rock"
, tfreq = [("brawlSetLit", 30), ("arenaSetLit", 1), ("arenaSetDark", 1)]
}
stairsLiftUp = stairsUp
{ tname = "lift up"
, tfreq = [("staircase lift up", 1)]
}
stairsLiftDown = stairsDown
{ tname = "lift down"
, tfreq = [("staircase lift down", 1)]
}
escapeSpaceshipDown = escapeDown
{ tname = "airlock to the shuttle"
, tfreq = [("escape spaceship down", 1)]
}
-- ** Walkable
-- none
-- * Helper functions
makeDark :: TileKind -> TileKind
makeDark k = let darkText :: GroupName TileKind -> GroupName TileKind
darkText t = maybe t (toGroupName . (<> "Dark"))
$ T.stripSuffix "Lit" $ tshow t
darkFrequency = map (first darkText) $ tfreq k
darkFeat (OpenTo t) = Just $ OpenTo $ darkText t
darkFeat (CloseTo t) = Just $ CloseTo $ darkText t
darkFeat (ChangeTo t) = Just $ ChangeTo $ darkText t
darkFeat (HideAs t) = Just $ HideAs $ darkText t
darkFeat (BuildAs t) = Just $ BuildAs $ darkText t
darkFeat (RevealAs t) = Just $ RevealAs $ darkText t
darkFeat (ObscureAs t) = Just $ ObscureAs $ darkText t
darkFeat OftenItem = Nothing -- items not common in the dark
darkFeat feat = Just feat
in k { tfreq = darkFrequency
, tfeature = Dark : mapMaybe darkFeat (tfeature k)
}
makeDarkColor :: TileKind -> TileKind
makeDarkColor k = (makeDark k) { tcolor = if tsymbol k == floorSymbol
then BrYellow
else tcolor k
, tcolor2 = BrBlack
}