feat(xanthous): Memoize characterVisiblePositions
Memoize the return value of characterVisiblePositions to a new, semi-abstracted "memo" field on the GameState, recalcuclated if the character position ever changes. I'm 90% sure that the perf issues we were encountering were actually caused by characterVisiblePositions getting called once for *every tile* on draw, but this slightly larger change also makes the game perform relatively-usably again. Since this is only recalculated if the character position changes, if we ever get non-transparent entities moving around without the characters influence (maybe something building or knocking down walls?) we'll have an issue there where the vision won't be updated as a result of those changes if they happen while the character is taking a non-moving action - but we can cross that bridge when we come to it. Change-Id: I3fc745ddf0014d6f164f735ad7e5080da779b92a Reviewed-on: https://cl.tvl.fyi/c/depot/+/3185 Reviewed-by: grfn <grfn@gws.fyi> Tested-by: BuildkiteCI
This commit is contained in:
parent
80d501d553
commit
c19e3dae5f
14 changed files with 284 additions and 87 deletions
|
|
@ -23,6 +23,8 @@ import Xanthous.Game
|
|||
)
|
||||
import Xanthous.Game.Prompt
|
||||
import Xanthous.Orphans ()
|
||||
import Control.Monad.State.Lazy (evalState)
|
||||
import Control.Monad.State.Class ( get, MonadState, gets )
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
cursorPosition :: GameState -> Widget ResourceName -> Widget ResourceName
|
||||
|
|
@ -53,29 +55,28 @@ drawPromptState (WaitingPrompt msg (Prompt _ pt ps pri _)) =
|
|||
str ("[" <> pure chr <> "] ") <+> txtWrap m
|
||||
|
||||
drawEntities
|
||||
:: GameState
|
||||
-> Widget ResourceName
|
||||
drawEntities game = vBox rows
|
||||
where
|
||||
allEnts = game ^. entities
|
||||
entityPositions = EntityMap.positions allEnts
|
||||
maxY = fromMaybe 0 $ maximumOf (folded . y) entityPositions
|
||||
maxX = fromMaybe 0 $ maximumOf (folded . x) entityPositions
|
||||
rows = mkRow <$> [0..maxY]
|
||||
mkRow rowY = hBox $ renderEntityAt . flip Position rowY <$> [0..maxX]
|
||||
renderEntityAt pos
|
||||
= renderTopEntity pos $ revealedEntitiesAtPosition pos game
|
||||
renderTopEntity pos ents
|
||||
= let neighbors = EntityMap.neighbors pos allEnts
|
||||
in maybe (str " ") (drawWithNeighbors neighbors)
|
||||
$ maximumBy (compare `on` drawPriority)
|
||||
<$> fromNullable ents
|
||||
:: forall m. MonadState GameState m
|
||||
=> m (Widget ResourceName)
|
||||
drawEntities = do
|
||||
allEnts <- use entities
|
||||
let entityPositions = EntityMap.positions allEnts
|
||||
maxY = fromMaybe 0 $ maximumOf (folded . y) entityPositions
|
||||
maxX = fromMaybe 0 $ maximumOf (folded . x) entityPositions
|
||||
rows = traverse mkRow [0..maxY]
|
||||
mkRow rowY = hBox <$> traverse (renderEntityAt . flip Position rowY) [0..maxX]
|
||||
renderEntityAt pos
|
||||
= renderTopEntity pos <$> revealedEntitiesAtPosition pos
|
||||
renderTopEntity pos ents
|
||||
= let neighbors = EntityMap.neighbors pos allEnts
|
||||
in maybe (str " ") (drawWithNeighbors neighbors)
|
||||
$ maximumBy (compare `on` drawPriority)
|
||||
<$> fromNullable ents
|
||||
vBox <$> rows
|
||||
|
||||
drawMap :: GameState -> Widget ResourceName
|
||||
drawMap game
|
||||
= viewport Resource.MapViewport Both
|
||||
. cursorPosition game
|
||||
$ drawEntities game
|
||||
drawMap :: MonadState GameState m => m (Widget ResourceName)
|
||||
drawMap = do
|
||||
cursorPos <- gets cursorPosition
|
||||
viewport Resource.MapViewport Both . cursorPos <$> drawEntities
|
||||
|
||||
bullet :: Char
|
||||
bullet = '•'
|
||||
|
|
@ -129,15 +130,18 @@ drawCharacterInfo ch = txt " " <+> charName <+> charHitpoints
|
|||
<+> txt (tshow $ let Hitpoints hp = characterHitpoints ch in hp)
|
||||
|
||||
drawGame :: GameState -> [Widget ResourceName]
|
||||
drawGame game
|
||||
= pure
|
||||
. withBorderStyle unicode
|
||||
$ case game ^. promptState of
|
||||
NoPrompt -> drawMessages (game ^. messageHistory)
|
||||
_ -> emptyWidget
|
||||
<=> drawPromptState (game ^. promptState)
|
||||
<=>
|
||||
(maybe emptyWidget (drawPanel game) (game ^. activePanel)
|
||||
<+> border (drawMap game)
|
||||
)
|
||||
<=> drawCharacterInfo (game ^. character)
|
||||
drawGame = evalState $ do
|
||||
game <- get
|
||||
drawnMap <- drawMap
|
||||
pure
|
||||
. pure
|
||||
. withBorderStyle unicode
|
||||
$ case game ^. promptState of
|
||||
NoPrompt -> drawMessages (game ^. messageHistory)
|
||||
_ -> emptyWidget
|
||||
<=> drawPromptState (game ^. promptState)
|
||||
<=>
|
||||
(maybe emptyWidget (drawPanel game) (game ^. activePanel)
|
||||
<+> border drawnMap
|
||||
)
|
||||
<=> drawCharacterInfo (game ^. character)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue