Skip to content

Commit

Permalink
support custom attributes defined in scenarios (#1058)
Browse files Browse the repository at this point in the history
closes #1034

## Demo

    scripts/play.sh --scenario data/scenarios/Testing/1034-custom-attributes.yaml

![Screenshot from 2023-01-28 01-24-38](https://user-images.githubusercontent.com/261693/215258494-cf4cb365-ec8c-4820-9308-b374e5da9c3c.png)
  • Loading branch information
kostmo authored Jan 29, 2023
1 parent 17c2d8c commit 8d80e9f
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 12 deletions.
1 change: 1 addition & 0 deletions data/scenarios/Testing/00-ORDER.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
961-custom-capabilities.yaml
956-GPS.yaml
958-isempty.yaml
1034-custom-attributes.yaml
134 changes: 134 additions & 0 deletions data/scenarios/Testing/1034-custom-attributes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
version: 1
name: Rainbow color custom attributes
description: |
Custom attributes with rainbow colors
creative: false
attrs:
- name: rainbow1
fg: "#ffadad"
- name: rainbow2
fg: "#ffd6a5"
- name: rainbow3
fg: "#ffff96"
- name: rainbow4
fg: "#caffbf"
- name: rainbow5
fg: "#9bf6ff"
- name: rainbow6
fg: "#a0c4ff"
- name: rainbow7
fg: "#bdb2ff"
- name: redOnYellow
fg: "#ff0000"
bg: ffff00
- name: cyanOnMagenta
fg: 00ffff
bg: "#ff00ff"
- name: italicAndUnderline
style:
- Italic
- Underline
- name: boldAndStrikethrough
style:
- Bold
- Strikethrough
entities:
- name: color1
display:
char: ''
attr: rainbow1
description:
- c1
properties: [known]
- name: color2
display:
char: ''
attr: rainbow2
description:
- c2
properties: [known]
- name: color3
display:
char: ''
attr: rainbow3
description:
- c3
properties: [known]
- name: color4
display:
char: ''
attr: rainbow4
description:
- c4
properties: [known]
- name: color5
display:
char: ''
attr: rainbow5
description:
- c5
properties: [known]
- name: color6
display:
char: ''
attr: rainbow6
description:
- c6
properties: [known]
- name: color7
display:
char: ''
attr: rainbow7
description:
- c7
properties: [known]
- name: redYellow
display:
char: 'R'
attr: redOnYellow
description:
- Red on Yellow
properties: [known]
- name: cyanMagenta
display:
char: 'C'
attr: cyanOnMagenta
description:
- Cyan on Magenta
properties: [known]
- name: italicUnderline
display:
char: 'X'
attr: italicAndUnderline
description:
- Italic and Underline
properties: [known]
- name: boldStrikethrough
display:
char: 'X'
attr: boldAndStrikethrough
description:
- Bold and Strikethrough
properties: [known]
robots: []
world:
default: [blank]
palette:
'.': [blank]
'1': [blank, color1]
'2': [blank, color2]
'3': [blank, color3]
'4': [blank, color4]
'5': [blank, color5]
'6': [blank, color6]
'7': [blank, color7]
'R': [blank, redYellow]
'C': [blank, cyanMagenta]
'I': [blank, italicUnderline]
'B': [blank, boldStrikethrough]
upperleft: [0, 0]
map: |-
.1234567..R.....II.
.1234567..R........
.1234567.....C..BB.
.1234567.....C.....
6 changes: 3 additions & 3 deletions src/Swarm/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ module Swarm.App where
import Brick
import Brick.BChan
import Control.Concurrent (forkIO, threadDelay)
import Control.Lens ((%~), (&), (?~))
import Control.Lens (view, (%~), (&), (?~))
import Control.Monad.Except
import Data.IORef (newIORef, writeIORef)
import Data.Text qualified as T
import Data.Text.IO qualified as T
import Graphics.Vty qualified as V
import Swarm.Game.Robot (ErrorLevel (..), LogSource (ErrorTrace, Said))
import Swarm.TUI.Attr
import Swarm.TUI.Controller
import Swarm.TUI.Model
import Swarm.TUI.Model.StateUpdate
import Swarm.TUI.Model.UI (uiAttrMap)
import Swarm.TUI.View
import Swarm.Version (getNewerReleaseVersion)
import Swarm.Web
Expand All @@ -39,7 +39,7 @@ app eventHandler =
, appChooseCursor = chooseCursor
, appHandleEvent = eventHandler
, appStartEvent = enablePasteMode
, appAttrMap = const swarmAttrMap
, appAttrMap = view $ uiState . uiAttrMap
}

-- | The main @IO@ computation which initializes the state, sets up
Expand Down
7 changes: 7 additions & 0 deletions src/Swarm/Game/Scenario.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module Swarm.Game.Scenario (
scenarioDescription,
scenarioCreative,
scenarioSeed,
scenarioAttrs,
scenarioEntities,
scenarioRecipes,
scenarioKnown,
Expand Down Expand Up @@ -65,6 +66,7 @@ import Swarm.Game.Scenario.Cell
import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Validation
import Swarm.Game.Scenario.RobotLookup
import Swarm.Game.Scenario.Style
import Swarm.Game.Scenario.WorldDescription
import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Util (getDataFileNameSafe)
Expand All @@ -86,6 +88,7 @@ data Scenario = Scenario
, _scenarioDescription :: Text
, _scenarioCreative :: Bool
, _scenarioSeed :: Maybe Int
, _scenarioAttrs :: [CustomAttr]
, _scenarioEntities :: EntityMap
, _scenarioRecipes :: [Recipe Entity]
, _scenarioKnown :: [Text]
Expand Down Expand Up @@ -126,6 +129,7 @@ instance FromJSONE EntityMap Scenario where
<*> liftE (v .:? "description" .!= "")
<*> liftE (v .:? "creative" .!= False)
<*> liftE (v .:? "seed")
<*> liftE (v .:? "attrs" .!= [])
<*> pure em
<*> v ..:? "recipes" ..!= []
<*> pure known
Expand Down Expand Up @@ -161,6 +165,9 @@ scenarioCreative :: Lens' Scenario Bool
-- a random seed / prompt the user for the seed.
scenarioSeed :: Lens' Scenario (Maybe Int)

-- | Custom attributes defined in the scenario.
scenarioAttrs :: Lens' Scenario [CustomAttr]

-- | Any custom entities used for this scenario.
scenarioEntities :: Lens' Scenario EntityMap

Expand Down
13 changes: 7 additions & 6 deletions src/Swarm/Game/Scenario/Objective/Presentation/Render.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ renderGoalsDisplay gd =
fr = _focus gd
leftSide =
hLimitPercent 30 $
padAll 1 $ vBox
[ hCenter $ str "Goals"
, padAll 1 $
vLimit 10 $
withFocusRing fr (BL.renderList drawGoalListItem) lw
]
padAll 1 $
vBox
[ hCenter $ str "Goals"
, padAll 1 $
vLimit 10 $
withFocusRing fr (BL.renderList drawGoalListItem) lw
]

-- Adds very subtle coloring to indicate focus switch
highlightIfFocused = case (hasMultiple, focusGetCurrent fr) of
Expand Down
37 changes: 37 additions & 0 deletions src/Swarm/Game/Scenario/Style.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Swarm.Game.Scenario.Style where

import Data.Aeson
import Data.Set (Set)
import Data.Text (Text)
import GHC.Generics (Generic)

data StyleFlag
= Standout
| Italic
| Strikethrough
| Underline
| ReverseVideo
| Blink
| Dim
| Bold
deriving (Eq, Ord, Show, Generic)

styleFlagJsonOptions :: Options
styleFlagJsonOptions =
defaultOptions
{ sumEncoding = UntaggedValue
}

instance FromJSON StyleFlag where
parseJSON = genericParseJSON styleFlagJsonOptions

newtype HexColor = HexColor Text
deriving (Eq, Show, Generic, FromJSON)

data CustomAttr = CustomAttr
{ name :: String
, fg :: Maybe HexColor
, bg :: Maybe HexColor
, style :: Maybe (Set StyleFlag)
}
deriving (Eq, Show, Generic, FromJSON)
2 changes: 1 addition & 1 deletion src/Swarm/Game/State.hs
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ deleteRobot rn = do
------------------------------------------------------------

-- | Create an initial game state record, first loading entities and
-- recipies from disk.
-- recipes from disk.
initGameState :: ExceptT Text IO GameState
initGameState = do
let guardRight what i = i `isRightOr` (\e -> "Failed to " <> what <> ": " <> e)
Expand Down
13 changes: 11 additions & 2 deletions src/Swarm/TUI/Model/StateUpdate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Swarm.TUI.Model.StateUpdate (
scenarioToAppState,
) where

import Brick.AttrMap (applyAttrMappings)
import Control.Applicative ((<|>))
import Control.Lens hiding (from, (<.>))
import Control.Monad.Except
Expand All @@ -20,7 +21,7 @@ import Data.Maybe (fromMaybe, isJust)
import Data.Text (Text)
import Data.Time (ZonedTime, getZonedTime)
import Swarm.Game.Log (ErrorLevel (..), LogSource (ErrorTrace))
import Swarm.Game.Scenario (loadScenario)
import Swarm.Game.Scenario (loadScenario, scenarioAttrs)
import Swarm.Game.Scenario.Objective.Presentation.Model (emptyGoalDisplay)
import Swarm.Game.ScenarioInfo (
ScenarioInfo (..),
Expand All @@ -34,6 +35,7 @@ import Swarm.Game.ScenarioInfo (
_SISingle,
)
import Swarm.Game.State
import Swarm.TUI.Attr (swarmAttrMap)
import Swarm.TUI.Inventory.Sorting
import Swarm.TUI.Model
import Swarm.TUI.Model.Achievement.Attainment
Expand All @@ -42,6 +44,7 @@ import Swarm.TUI.Model.Achievement.Persistence
import Swarm.TUI.Model.Failure (prettyFailure)
import Swarm.TUI.Model.Repl
import Swarm.TUI.Model.UI
import Swarm.TUI.View.CustomStyling (toAttrPair)
import System.Clock

-- | Initialize the 'AppState'.
Expand Down Expand Up @@ -98,7 +101,12 @@ startGameWithSeed userSeed siPair@(_scene, si) toRun = do
-- TODO: #516 do we need to keep an old entity map around???

-- | Modify the 'AppState' appropriately when starting a new scenario.
scenarioToAppState :: (MonadIO m, MonadState AppState m) => ScenarioInfoPair -> Maybe Seed -> Maybe CodeToRun -> m ()
scenarioToAppState ::
(MonadIO m, MonadState AppState m) =>
ScenarioInfoPair ->
Maybe Seed ->
Maybe CodeToRun ->
m ()
scenarioToAppState siPair@(scene, _) userSeed toRun = do
withLensIO gameState $ scenarioToGameState scene userSeed toRun
withLensIO uiState $ scenarioToUIState siPair
Expand Down Expand Up @@ -145,5 +153,6 @@ scenarioToUIState siPair u = do
& lgTicksPerSecond .~ initLgTicksPerSecond
& uiREPL .~ initREPLState (u ^. uiREPL . replHistory)
& uiREPL . replHistory %~ restartREPLHistory
& uiAttrMap .~ applyAttrMappings (map toAttrPair $ fst siPair ^. scenarioAttrs) swarmAttrMap
& scenarioRef ?~ siPair
& lastFrameTime .~ curTime
8 changes: 8 additions & 0 deletions src/Swarm/TUI/Model/UI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module Swarm.TUI.Model.UI (
uiInventoryShouldUpdate,
uiTPF,
uiFPS,
uiAttrMap,
scenarioRef,
appData,

Expand All @@ -45,6 +46,7 @@ module Swarm.TUI.Model.UI (
initUIState,
) where

import Brick (AttrMap)
import Brick.Focus
import Brick.Widgets.List qualified as BL
import Control.Arrow ((&&&))
Expand All @@ -60,6 +62,7 @@ import Swarm.Game.ScenarioInfo (
ScenarioInfoPair,
)
import Swarm.Game.World qualified as W
import Swarm.TUI.Attr (swarmAttrMap)
import Swarm.TUI.Inventory.Sorting
import Swarm.TUI.Model.Achievement.Attainment
import Swarm.TUI.Model.Achievement.Definitions
Expand Down Expand Up @@ -107,6 +110,7 @@ data UIState = UIState
, _accumulatedTime :: TimeSpec
, _lastInfoTime :: TimeSpec
, _appData :: Map Text Text
, _uiAttrMap :: AttrMap
, _scenarioRef :: Maybe ScenarioInfoPair
}

Expand Down Expand Up @@ -198,6 +202,9 @@ uiTPF :: Lens' UIState Double
-- | Computed frames per milli seconds
uiFPS :: Lens' UIState Double

-- | Attribute map
uiAttrMap :: Lens' UIState AttrMap

-- | The currently active Scenario description, useful for starting over.
scenarioRef :: Lens' UIState (Maybe ScenarioInfoPair)

Expand Down Expand Up @@ -299,6 +306,7 @@ initUIState showMainMenu cheatMode = do
, _frameCount = 0
, _frameTickCount = 0
, _appData = appDataMap
, _uiAttrMap = swarmAttrMap
, _scenarioRef = Nothing
}
return (warnings, out)
Loading

0 comments on commit 8d80e9f

Please sign in to comment.