Haskell roguelike - Debugging
Admin / debug options
It is useful to have the ability to toggle some features of the UI while you are designing the game. The darkness overlay makes it hard to see what the other actors are doing. This can be a real pain while you are testing the interaction of utilities and later on with combat etc.
Toggle the lights
Let’s add a simple debug command for toggling the lights. I’m using shift+d
as the prefix for debug commands, and d
as the key to toggle darkness.
16_debug/src/GameEngine.hs (145 to 145)
In handleKey this is set to toggle a property named debug:light
16_debug/src/GameEngine.hs (444 to 444)
Actor Properties
The actor gets a map of properties
16_debug/src/GameCore.hs (60 to 60)
Action
ActTogglePlayerProp is added to RogueAction
16_debug/src/GameCore.hs (100 to 102)
data RogueAction = ActMovePlayer (Int, Int)
| ActSetPlayerViewPortStyle ViewPortStyle
| ActTogglePlayerProp Text Text
Toggle
The action handler for ActTogglePlayerProp either adds the value to the map or removes the property if it already exists
16_debug/src/GameEngine.hs (466 to 471)
ActTogglePlayerProp prop valEnabled ->
world & (wdPlayer . plActor . acProps) %~ Map.alter (toggleMapProp valEnabled) prop
where
toggleMapProp v Nothing = Just v
toggleMapProp _ (Just _) = Nothing
Using the property
darknessFovOverlay then checks if the property is set, if it is no overlay (Map.empty) is returned
16_debug/src/GameEngine.hs (677 to 698)
darknessFovOverlay :: Player -> Actor -> Map PlayerPos Tile
darknessFovOverlay player actor =
case player ^. plActor ^. acProps ^.at "debug:light" of
Nothing ->
let
(screenWidth, screenHeight) = player ^. plScreenSize
-- Create a full grid of darkness
blackBg = Map.fromList [ (PlayerPos (x, y), E.getTile E.Dark)
| x <- [0..(screenWidth - 1)]
, y <- [0..(screenHeight - 1)]
]
lightAt = worldCoordToPlayer (player ^. plWorldTopLeft) <$> flatFov (actor ^. acFov)
seen = worldCoordToPlayer (player ^. plWorldTopLeft) <$> Set.toList (actor ^. acFovHistory)
in
-- Remove the darkness overlay at any position that is to be lit
-- I.e. any position in the field of view, or previously in the field of view
foldr Map.delete blackBg $ lightAt <> seen
Just _ ->
Map.empty
Other ideas
This was quite a simple addition that makes testing your game a lot easier. There are many other features you can add to assist development. One that helped me was having an option to enable making the actor’s fov visible. These features sometimes also can become game features (e.g. switching on a levels lights with a switch) so it is worth deciding if they are actually debug features or just world features that you have debug access to.
Chapters
Changes
src/GameCore.hs
diff -w -B -a -d -u -b --new-file 15_memory/src/GameCore.hs 16_debug/src/GameCore.hs
--- 15_memory/src/GameCore.hs
+++ 16_debug/src/GameCore.hs
@@ -52,10 +52,13 @@
-- | The actor's disposition - the values that define the actors personality
, _acDisposition :: !Disposition
-
-- | The actor's memory about positions
, _acPosMemory :: !(M.Memory WorldPos)
+ -- | Actors's properties
+
+ , _acProps :: !(Map Text Text)
+
}
data Player = Player { _plConn :: !Host.Connection
@@ -93,8 +96,12 @@
newtype WorldPos = WorldPos (Int, Int) deriving (Show, Eq, Ord)
newtype PlayerPos = PlayerPos (Int, Int) deriving (Show, Eq, Ord)
+
data RogueAction = ActMovePlayer (Int, Int)
| ActSetPlayerViewPortStyle ViewPortStyle
+ | ActTogglePlayerProp Text Text
+
+
data ViewPortStyle = ViewPortCentre
| ViewPortLock PlayerPos
html/rogue.js
diff -w -B -a -d -u -b --new-file 15_memory/src/GameEngine.hs 16_debug/src/GameEngine.hs
--- 15_memory/src/GameEngine.hs
+++ 16_debug/src/GameEngine.hs
@@ -140,6 +140,10 @@
, ("shift+v p", "Game:ViewPort:Snap")
, ("shift+v b", "Game:ViewPort:Border")
, ("shift+v l", "Game:ViewPort:Lock")
+
+
+ , ("shift+d d", "Debug:Light:Toggle")
+
]
, _cfgMinMaxBounds = (-300, 300, -300, 300)
}
@@ -168,6 +172,7 @@
, _acUtilities = []
, _acDisposition = UB.emptyDisposition
, _acPosMemory = M.empty
+ , _acProps = Map.empty
}
mkEnemyActor aid e (x, y) =
@@ -185,6 +190,7 @@
, _acUtilities = []
, _acDisposition = UB.emptyDisposition
, _acPosMemory = M.empty
+ , _acProps = Map.empty
}
@@ -434,6 +440,10 @@
"Game:ViewPort:Border" -> [ActSetPlayerViewPortStyle $ ViewPortBorder 2]
"Game:ViewPort:Lock" -> [ActSetPlayerViewPortStyle $ ViewPortLock (worldCoordToPlayer topLeft $ actor ^. acWorldPos)]
+
+ "Debug:Light:Toggle" -> [ActTogglePlayerProp "debug:light" "on"]
+
+
_ -> []
handleKey _ _ = []
@@ -453,6 +463,15 @@
world & (wdPlayer . plViewPortStyle) .~ style
+ ActTogglePlayerProp prop valEnabled ->
+ world & (wdPlayer . plActor . acProps) %~ Map.alter (toggleMapProp valEnabled) prop
+
+ where
+ toggleMapProp v Nothing = Just v
+ toggleMapProp _ (Just _) = Nothing
+
+
+
tryMoveActor :: World -> Actor -> (Int, Int) -> Maybe World
tryMoveActor world actor (dx, dy) =
let
@@ -654,8 +673,11 @@
Just e -> (e ^. enType) /= E.Wall
+
darknessFovOverlay :: Player -> Actor -> Map PlayerPos Tile
darknessFovOverlay player actor =
+ case player ^. plActor ^. acProps ^.at "debug:light" of
+ Nothing ->
let
(screenWidth, screenHeight) = player ^. plScreenSize
@@ -672,6 +694,10 @@
-- I.e. any position in the field of view, or previously in the field of view
foldr Map.delete blackBg $ lightAt <> seen
+ Just _ ->
+ Map.empty
+
+
flatFov :: Maybe [(WorldPos, [WorldPos])] -> [WorldPos]
flatFov Nothing = []
--- 15_memory/html/rogue.js
+++ 16_debug/html/rogue.js
@@ -59,6 +59,7 @@
Mousetrap.pause();
const act = () => {
+ console.log( "key: " + k );
sendCmd("key", k);
};