diff options
-rw-r--r-- | sequence.cabal | 2 | ||||
-rw-r--r-- | sequence.nix | 7 | ||||
-rw-r--r-- | src/Main.hs | 53 | ||||
-rw-r--r-- | src/Sequence/Types.hs | 20 |
4 files changed, 68 insertions, 14 deletions
diff --git a/sequence.cabal b/sequence.cabal index b721848..2def576 100644 --- a/sequence.cabal +++ b/sequence.cabal | |||
@@ -37,5 +37,7 @@ executable sequence | |||
37 | , transformers | 37 | , transformers |
38 | , transformers-base | 38 | , transformers-base |
39 | , ansi-terminal | 39 | , ansi-terminal |
40 | , text | ||
41 | , template | ||
40 | hs-source-dirs: src | 42 | hs-source-dirs: src |
41 | default-language: Haskell2010 \ No newline at end of file | 43 | default-language: Haskell2010 \ No newline at end of file |
diff --git a/sequence.nix b/sequence.nix index 5e8a130..ae75127 100644 --- a/sequence.nix +++ b/sequence.nix | |||
@@ -1,7 +1,8 @@ | |||
1 | { mkDerivation, ansi-terminal, base, bimap, case-insensitive | 1 | { mkDerivation, ansi-terminal, base, bimap, case-insensitive |
2 | , containers, data-default, directory, filepath, game-probability | 2 | , containers, data-default, directory, filepath, game-probability |
3 | , lens, mtl, readline, Shellac, Shellac-haskeline, stdenv | 3 | , lens, mtl, readline, Shellac, Shellac-haskeline, stdenv |
4 | , table-layout, transformers, transformers-base, xdg-basedir | 4 | , table-layout, template, text, transformers, transformers-base |
5 | , xdg-basedir | ||
5 | }: | 6 | }: |
6 | mkDerivation { | 7 | mkDerivation { |
7 | pname = "sequence"; | 8 | pname = "sequence"; |
@@ -12,8 +13,8 @@ mkDerivation { | |||
12 | executableHaskellDepends = [ | 13 | executableHaskellDepends = [ |
13 | ansi-terminal base bimap case-insensitive containers data-default | 14 | ansi-terminal base bimap case-insensitive containers data-default |
14 | directory filepath game-probability lens mtl readline Shellac | 15 | directory filepath game-probability lens mtl readline Shellac |
15 | Shellac-haskeline table-layout transformers transformers-base | 16 | Shellac-haskeline table-layout template text transformers |
16 | xdg-basedir | 17 | transformers-base xdg-basedir |
17 | ]; | 18 | ]; |
18 | license = stdenv.lib.licenses.mit; | 19 | license = stdenv.lib.licenses.mit; |
19 | } | 20 | } |
diff --git a/src/Main.hs b/src/Main.hs index f4a863f..8d74def 100644 --- a/src/Main.hs +++ b/src/Main.hs | |||
@@ -1,4 +1,4 @@ | |||
1 | {-# LANGUAGE ViewPatterns, RecordWildCards #-} | 1 | {-# LANGUAGE ViewPatterns, RecordWildCards, OverloadedStrings, FlexibleContexts #-} |
2 | 2 | ||
3 | import Control.Monad | 3 | import Control.Monad |
4 | 4 | ||
@@ -43,6 +43,12 @@ import Text.Layout.Table | |||
43 | 43 | ||
44 | import Text.Read (readMaybe) | 44 | import Text.Read (readMaybe) |
45 | 45 | ||
46 | import Data.Text (Text) | ||
47 | import qualified Data.Text as Text | ||
48 | import qualified Data.Text.Lazy as Lazy (Text) | ||
49 | import qualified Data.Text.Lazy as Lazy.Text | ||
50 | import Data.Text.Template | ||
51 | |||
46 | main :: IO () | 52 | main :: IO () |
47 | main = do | 53 | main = do |
48 | historyFile <- getUserCacheFile "sequence" "history" | 54 | historyFile <- getUserCacheFile "sequence" "history" |
@@ -65,7 +71,10 @@ main = do | |||
65 | , cmd "align" alignEntity "Align the focused entity to a faction – creating it, if necessary" | 71 | , cmd "align" alignEntity "Align the focused entity to a faction – creating it, if necessary" |
66 | , cmd "name" nameEntity "Name the current entity overriding previous name assignments" | 72 | , cmd "name" nameEntity "Name the current entity overriding previous name assignments" |
67 | , cmd "spawn" spawnEntity "Create a new entity from an archetype focusing on it" | 73 | , cmd "spawn" spawnEntity "Create a new entity from an archetype focusing on it" |
68 | , cmd "roll" rollTest "Roll a test using the stats of the currently focused entity" | 74 | , cmd "spawn'" spawnFaction "Create a new faction and spawn multiple copies of an archetype in it" |
75 | , cmd "test" rollTest "Roll a test using the stats of the currently focused entity" | ||
76 | , cmd "combat" entitySeqVal "Roll sequence value for the current focus and enter combat" | ||
77 | , cmd "combat'" factionSeqVal "Roll sequence values for all members of a faction and have them enter combat" | ||
69 | ] | 78 | ] |
70 | } | 79 | } |
71 | void $ runShell description haskelineBackend (def :: GameState) | 80 | void $ runShell description haskelineBackend (def :: GameState) |
@@ -81,11 +90,11 @@ stateOutline st | |||
81 | faction id = fromJust $ view eFaction <$> Map.lookup id (st ^. gEntities) | 90 | faction id = fromJust $ view eFaction <$> Map.lookup id (st ^. gEntities) |
82 | factionIndex id = fromJust $ elemIndex (view faction' $ faction id) factions | 91 | factionIndex id = fromJust $ elemIndex (view faction' $ faction id) factions |
83 | rowGs = do | 92 | rowGs = do |
84 | rowGroup'@((seqVal', _):_) <- protoRows | 93 | rowGroup'@((review seqVal' -> seqVal, _):_) <- protoRows |
85 | let | 94 | let |
86 | rowGroup = map snd rowGroup' | 95 | rowGroup = map snd rowGroup' |
87 | factionColumn i = [evalState ?? st $ toName x | x <- rowGroup, factionIndex x == i ] | 96 | factionColumn i = [evalState ?? st $ toName x | x <- rowGroup, factionIndex x == i ] |
88 | return . colsAllG top $ [show (seqVal' ^. seqVal)] : map factionColumn [0..(length factions - 1)] | 97 | return . colsAllG top $ [seqVal] : map factionColumn [0..(length factions - 1)] |
89 | 98 | ||
90 | -- Query state | 99 | -- Query state |
91 | listFactions, listEntities :: Sh GameState () | 100 | listFactions, listEntities :: Sh GameState () |
@@ -127,6 +136,27 @@ nameEntity :: String -> Sh GameState () | |||
127 | nameEntity ('#':_) = shellPutErrLn "We do not allow names that might shadow explicit access to entities via their number (‘#<n>’)" | 136 | nameEntity ('#':_) = shellPutErrLn "We do not allow names that might shadow explicit access to entities via their number (‘#<n>’)" |
128 | nameEntity name = withFocus $ \ident -> modifying gEntityNames $ Bimap.insert ident (name ^. entityName) | 137 | nameEntity name = withFocus $ \ident -> modifying gEntityNames $ Bimap.insert ident (name ^. entityName) |
129 | 138 | ||
139 | spawnFaction :: Completable Faction -> Integer -> Completable Entity -> String -> Sh GameState () | ||
140 | spawnFaction cFaction num cEntity nameTemplate | ||
141 | | ('#':_) <- nameTemplate = shellPutErrLn "We do not allow names that might shadow explicit access to entities via their number (‘#<n>’)" | ||
142 | | otherwise = do | ||
143 | let nameTemplate' = templateSafe $ Text.pack nameTemplate | ||
144 | case nameTemplate' of | ||
145 | Left _ -> shellPutErrLn "Invalid template – ‘$n’ gets replaced by [1..<n>], ‘$faction’ gets replaced by name of faction, and ‘$i’ gets replaced by a numerical identifier – quote ‘$’ as ‘$$’" | ||
146 | Right nameTemplate -> withArg (\faction -> withArg (\entity -> mapM_ (spawnFaction' faction entity nameTemplate) [1..num]) cEntity) cFaction | ||
147 | where | ||
148 | spawnFaction' faction entity nameTemplate num = do | ||
149 | identifier <- use gNextId' | ||
150 | let name = Lazy.Text.unpack <$> renderA nameTemplate (context num) | ||
151 | context num "i" = Just . Text.pack $ review entityId' identifier | ||
152 | context num "n" = Just . Text.pack $ show num | ||
153 | context num "faction" = Just . Text.pack $ faction ^. faction' | ||
154 | context num _ = Nothing | ||
155 | modify $ insertEntity entity | ||
156 | maybe (return ()) (nameEntity identifier) name | ||
157 | gEntities %= Map.adjust (set eFaction faction) identifier | ||
158 | nameEntity identifier name = modifying gEntityNames $ Bimap.insert identifier (name ^. entityName) | ||
159 | |||
130 | -- Dice rolls | 160 | -- Dice rolls |
131 | rollTest :: Completable (FormulaM Stats Test) -> Sh GameState () | 161 | rollTest :: Completable (FormulaM Stats Test) -> Sh GameState () |
132 | rollTest = withArg $ enactTest' >=> maybe (return ()) (shellPutStrLn . ppResult) | 162 | rollTest = withArg $ enactTest' >=> maybe (return ()) (shellPutStrLn . ppResult) |
@@ -150,3 +180,18 @@ enactTest' test = withFocus' $ \focus -> do | |||
150 | gFocus' .= newFocus | 180 | gFocus' .= newFocus |
151 | return result | 181 | return result |
152 | 182 | ||
183 | entitySeqVal :: Sh GameState () | ||
184 | entitySeqVal = withFocus entitySeqVal' | ||
185 | |||
186 | factionSeqVal :: Completable Faction -> Sh GameState () | ||
187 | factionSeqVal = withArg $ \qFaction -> use gEntities >>= mapM_ (entitySeqVal') . Map.keys . Map.filter ((==) qFaction . view eFaction) | ||
188 | |||
189 | entitySeqVal' :: EntityIdentifier -> Sh GameState () | ||
190 | entitySeqVal' ident = do | ||
191 | entity <- preuse (gEntities . ix ident) | ||
192 | let sVal = preview (eStats . sSeqVal) =<< entity | ||
193 | case (,) <$> entity <*> sVal of | ||
194 | Nothing -> return () | ||
195 | Just (entity, sVal) -> do | ||
196 | (newEntity, view (seqVal . re _Just) -> val) <- evalFormula entity sVal | ||
197 | gEntities . at ident .= Just (newEntity & set eSeqVal val) | ||
diff --git a/src/Sequence/Types.hs b/src/Sequence/Types.hs index 3d95b9a..02389a1 100644 --- a/src/Sequence/Types.hs +++ b/src/Sequence/Types.hs | |||
@@ -3,7 +3,7 @@ | |||
3 | module Sequence.Types | 3 | module Sequence.Types |
4 | ( GameState, gEntities, gEntityNames, gFocus, gNextId' | 4 | ( GameState, gEntities, gEntityNames, gFocus, gNextId' |
5 | , Faction, faction, faction' | 5 | , Faction, faction, faction' |
6 | , SeqVal(..), seqVal | 6 | , SeqVal(..), seqVal, seqVal' |
7 | , Entity(..), eFaction, eSeqVal, eStats | 7 | , Entity(..), eFaction, eSeqVal, eStats |
8 | , EntityName(..), entityName | 8 | , EntityName(..), entityName |
9 | , EntityIdentifier(..), entityId, entityId' | 9 | , EntityIdentifier(..), entityId, entityId' |
@@ -61,8 +61,13 @@ faction' = lens (CI.original . fromMaybe unaligned . view faction) (\s a -> s { | |||
61 | 61 | ||
62 | newtype SeqVal = SeqVal { _seqVal :: Int } | 62 | newtype SeqVal = SeqVal { _seqVal :: Int } |
63 | deriving (Show, Ord, Eq, Num, Integral, Enum, Real) | 63 | deriving (Show, Ord, Eq, Num, Integral, Enum, Real) |
64 | makeLenses ''SeqVal | 64 | |
65 | 65 | seqVal :: Integral a => Iso' a SeqVal | |
66 | seqVal = iso (SeqVal . fromIntegral) (fromIntegral . _seqVal) | ||
67 | |||
68 | seqVal' :: Prism' String SeqVal | ||
69 | seqVal' = _Show . seqVal | ||
70 | |||
66 | 71 | ||
67 | data Entity = Entity | 72 | data Entity = Entity |
68 | { _eSeqVal :: Maybe SeqVal | 73 | { _eSeqVal :: Maybe SeqVal |
@@ -90,11 +95,12 @@ entityName = iso (EntityName . CI.mk) (CI.original . _entityName) | |||
90 | newtype EntityIdentifier = EntityIdentifier { _entityId :: Integer } | 95 | newtype EntityIdentifier = EntityIdentifier { _entityId :: Integer } |
91 | deriving (Show, Eq, Ord, Enum) | 96 | deriving (Show, Eq, Ord, Enum) |
92 | 97 | ||
93 | entityId :: Iso' Integer EntityIdentifier | 98 | entityId :: Integral a => Iso' a EntityIdentifier |
94 | entityId = iso EntityIdentifier _entityId | 99 | entityId = iso (EntityIdentifier . fromIntegral) (fromIntegral . _entityId) |
95 | 100 | ||
96 | entityId' :: Prism' String EntityIdentifier | 101 | entityId' :: Prism' String EntityIdentifier |
97 | entityId' = prism' (show . _entityId) (fmap EntityIdentifier . readMaybe) | 102 | entityId' = _Show . entityId |
103 | |||
98 | 104 | ||
99 | data GameState = GameState | 105 | data GameState = GameState |
100 | { _gEntities :: Map EntityIdentifier Entity | 106 | { _gEntities :: Map EntityIdentifier Entity |
@@ -118,7 +124,7 @@ inhabitedFactions = to $ nub . sort . Map.elems . fmap (view eFaction) . view gE | |||
118 | priorityQueue :: Getter GameState [(SeqVal, EntityIdentifier)] | 124 | priorityQueue :: Getter GameState [(SeqVal, EntityIdentifier)] |
119 | priorityQueue = to priorityQueue' | 125 | priorityQueue = to priorityQueue' |
120 | where | 126 | where |
121 | priorityQueue' (Map.toAscList . view gEntities -> entities) = sortBy (comparing $ Down . snd) . concat . map filter . map (over _1 $ view eSeqVal) . map swap $ entities | 127 | priorityQueue' (Map.toAscList . view gEntities -> entities) = sortBy (comparing $ Down . fst) . concat . map filter . map (over _1 $ view eSeqVal) . map swap $ entities |
122 | filter (Nothing, _) = mempty | 128 | filter (Nothing, _) = mempty |
123 | filter (Just val, id) = pure (val, id) | 129 | filter (Just val, id) = pure (val, id) |
124 | 130 | ||