diff options
-rw-r--r-- | src/Main.hs | 134 | ||||
-rw-r--r-- | src/Sequence/Contact/Archetypes.hs | 4 | ||||
-rw-r--r-- | src/Sequence/Contact/Types.hs | 15 | ||||
-rw-r--r-- | src/Sequence/Contact/Types/Internal.hs | 8 | ||||
-rw-r--r-- | src/Sequence/Types.hs | 44 | ||||
-rw-r--r-- | src/Sequence/Utils.hs | 25 |
6 files changed, 194 insertions, 36 deletions
diff --git a/src/Main.hs b/src/Main.hs index 54ec08a..db49e14 100644 --- a/src/Main.hs +++ b/src/Main.hs | |||
@@ -36,6 +36,7 @@ import Data.Maybe | |||
36 | import Data.Bool | 36 | import Data.Bool |
37 | import Data.Monoid (All(..)) | 37 | import Data.Monoid (All(..)) |
38 | import Data.Ord | 38 | import Data.Ord |
39 | import Data.Ratio | ||
39 | 40 | ||
40 | import Data.Foldable (toList) | 41 | import Data.Foldable (toList) |
41 | 42 | ||
@@ -84,7 +85,7 @@ main = do | |||
84 | , helpCommand "help" | 85 | , helpCommand "help" |
85 | , cmd "entities" listEntities "List all entities" | 86 | , cmd "entities" listEntities "List all entities" |
86 | , cmd "tip" focusTip "Focus the entity at the top of the queue" | 87 | , cmd "tip" focusTip "Focus the entity at the top of the queue" |
87 | , cmd "ptip" pFocusTip "Focus a random entity" | 88 | , cmd "pTip" pFocusTip "Focus a random entity" |
88 | , cmd "focus" setFocus "Focus a specific entity" | 89 | , cmd "focus" setFocus "Focus a specific entity" |
89 | , cmd "blur" blur "Focus no entity" | 90 | , cmd "blur" blur "Focus no entity" |
90 | , cmd "remove" remove "Remove the focused entity from the queue" | 91 | , cmd "remove" remove "Remove the focused entity from the queue" |
@@ -98,6 +99,9 @@ main = do | |||
98 | , cmd "test" rollTest "Roll a test using the stats of the currently focused entity" | 99 | , cmd "test" rollTest "Roll a test using the stats of the currently focused entity" |
99 | , cmd "combat" entitySeqVal "Roll sequence value for the current focus and enter combat" | 100 | , cmd "combat" entitySeqVal "Roll sequence value for the current focus and enter combat" |
100 | , cmd "combat'" factionSeqVal "Roll sequence values for all members of a faction and have them enter combat" | 101 | , cmd "combat'" factionSeqVal "Roll sequence values for all members of a faction and have them enter combat" |
102 | , cmd "timer" entityTimer "Set a timer associated with the current entity" | ||
103 | , cmd "pTimer" pEntityTimer "Set a timer associated with the current entity. Scale remaining time dynamically with the number of combatants" | ||
104 | , cmd "untimer" clearEntityTimer "Remove the timer associated with the current entity" | ||
101 | , cmd "uncombat" clearEntitySeqVal "Drop the focused entity out of combat" | 105 | , cmd "uncombat" clearEntitySeqVal "Drop the focused entity out of combat" |
102 | , cmd "uncombat'" clearFactionSeqVal "Drop all members of a faction out of combat" | 106 | , cmd "uncombat'" clearFactionSeqVal "Drop all members of a faction out of combat" |
103 | , cmd "spend" spendSeq "Spend some of the current focusĀ“ AP" | 107 | , cmd "spend" spendSeq "Spend some of the current focusĀ“ AP" |
@@ -117,13 +121,19 @@ main = do | |||
117 | stateOutline :: Sh GameState String | 121 | stateOutline :: Sh GameState String |
118 | stateOutline = do | 122 | stateOutline = do |
119 | st <- get | 123 | st <- get |
120 | case st of | 124 | time <- use gTimer |
121 | st | null (st ^. priorityQueue) -> return "" | 125 | |
122 | | otherwise -> unlines <$> mapM table (st ^. gRounds') | 126 | unlines <$> sequence ( ( if not (null $ st ^. timers) || not (null $ st ^. gRounds') |
127 | then [ return $ "Round timer: " ++ show time ] | ||
128 | else [] | ||
129 | ) | ||
130 | ++ ( if not (null $ st ^. timers) then [ tTable ] else [] ) | ||
131 | ++ ( if not (null $ st ^. gRounds') then map table (st ^. gRounds') else [] ) | ||
132 | ) | ||
123 | where | 133 | where |
124 | table :: Int -> Sh GameState String | 134 | table :: Int -> Sh GameState String |
125 | table round = do | 135 | table round = do |
126 | factions <- map (view faction') <$> use inhabitedFactions | 136 | factions <- map (view faction') <$> use combatFactions |
127 | st <- get | 137 | st <- get |
128 | let | 138 | let |
129 | roundStr 0 = "Current Round" | 139 | roundStr 0 = "Current Round" |
@@ -147,6 +157,27 @@ stateOutline = do | |||
147 | colsAllG top . ([maybe "" show $ view seqVal seq] :) <$> mapM factionColumn [0..(length factions - 1)] | 157 | colsAllG top . ([maybe "" show $ view seqVal seq] :) <$> mapM factionColumn [0..(length factions - 1)] |
148 | -- layoutTableToString <$> rowGs <*> pure (Just (roundStr round : factions, repeat def)) <*> pure (repeat def) <*> pure unicodeBoldHeaderS | 158 | -- layoutTableToString <$> rowGs <*> pure (Just (roundStr round : factions, repeat def)) <*> pure (repeat def) <*> pure unicodeBoldHeaderS |
149 | tableString <$> pure (repeat def) <*> pure unicodeBoldHeaderS <*> pure (titlesH $ roundStr round : factions) <*> rowGs | 159 | tableString <$> pure (repeat def) <*> pure unicodeBoldHeaderS <*> pure (titlesH $ roundStr round : factions) <*> rowGs |
160 | tTable :: Sh GameState String | ||
161 | tTable = do | ||
162 | factions <- map (view faction') <$> use combatFactions' | ||
163 | st <- get | ||
164 | let | ||
165 | time = st ^. gTimer | ||
166 | protoRows = groupBy ((==) `on` fst) $ st ^. timers | ||
167 | faction id = fromJust $ view eFaction <$> Map.lookup id (st ^. gEntities) | ||
168 | |||
169 | rowGs :: Sh GameState [RowGroup] | ||
170 | rowGs = runListT $ do | ||
171 | rowGroup'@((t, _):_) <- ListT $ return protoRows | ||
172 | let | ||
173 | rowGroup = map snd rowGroup' | ||
174 | factionColumn i = runListT $ do | ||
175 | x <- ListT $ return rowGroup | ||
176 | guard $ factionIndex x == i | ||
177 | toDesc x | ||
178 | factionIndex id = fromJust $ elemIndex (view faction' $ faction id) factions | ||
179 | colsAllG top . ([show . round $ t ^. absTime - fromIntegral time] :) <$> mapM factionColumn [0..(length factions - 1)] | ||
180 | tableString <$> pure (repeat def) <*> pure unicodeBoldHeaderS <*> pure (titlesH $ ("Time left") : factions) <*> rowGs | ||
150 | 181 | ||
151 | focusNotes :: GameState -> String | 182 | focusNotes :: GameState -> String |
152 | focusNotes st | 183 | focusNotes st |
@@ -191,24 +222,52 @@ stateMaintenance = do | |||
191 | gFocus' . eSeqVal .= Nothing | 222 | gFocus' . eSeqVal .= Nothing |
192 | -- gFocus .= Nothing | 223 | -- gFocus .= Nothing |
193 | void $ do | 224 | void $ do |
225 | newCount <- fromIntegral . length <$> use priorityQueue | ||
226 | lastCount <- fromIntegral <$> use gLastCount | ||
227 | time <- use gTimer | ||
228 | when (newCount /= lastCount) $ do | ||
229 | let | ||
230 | scale :: Entity -> Sh GameState Entity | ||
231 | scale entity = (execStateT ?? entity) $ do | ||
232 | eTimer . _Just %= scaleTimer time (newCount % (max 1 lastCount)) | ||
233 | gEntities <~ (mapM scale =<< use gEntities) | ||
234 | gLastCount <~ length <$> use priorityQueue | ||
235 | void $ do | ||
194 | round <- use gRound | 236 | round <- use gRound |
195 | let | 237 | let |
196 | finished sVal = fromMaybe False (previews (seqVal . _Just) (<= 0) sVal) || view (seqRound . _Wrapped) sVal /= round | 238 | finished sVal = fromMaybe False (previews (seqVal . _Just) (<= 0) sVal) || view (seqRound . _Wrapped) sVal /= round |
197 | allFinished <- getAll . foldMapOf (gEntities . each . eStats . sSequence . _Just) (All . finished) <$> get | 239 | allFinished <- getAll . foldMapOf (gEntities . each . eSeqVal . _Just) (All . finished) <$> get |
198 | when allFinished $ do | 240 | when allFinished $ do |
199 | let | 241 | let |
200 | advanceRound' :: EntityIdentifier -> Entity -> Sh GameState Entity | 242 | advanceRound' :: EntityIdentifier -> Entity -> Sh GameState Entity |
201 | advanceRound' ident entity = fmap (fromMaybe entity . (\(m, s) -> s <$ m)) . (runStateT ?? entity) . runMaybeT $ do | 243 | advanceRound' ident entity = fmap (fromMaybe entity . (\(m, s) -> s <$ m)) . (runStateT ?? entity) . runMaybeT $ do |
202 | cRound <- MaybeT . preuse $ eStats . sSequence . _Just . seqRound . _Wrapped | 244 | cRound <- MaybeT . preuse $ eSeqVal . _Just . seqRound . _Wrapped |
203 | guard $ cRound < 0 | 245 | guard $ cRound < 0 |
204 | cVal <- MaybeT . preuse $ eStats . sSequence . _Just . seqVal . _Just | 246 | cVal <- MaybeT . preuse $ eSeqVal . _Just . seqVal . _Just |
205 | name <- lift . lift $ toName ident | 247 | name <- lift . lift $ toName ident |
206 | nVal' <- MaybeT . preuse $ eStats . sSeqVal | 248 | (newEntity, nVal) <- lift . lift $ rollSeqVal entity name |
207 | nVal <- MaybeT . focusState eStats $ evalFormula' [name] nVal' | 249 | put $ set eSeqVal nVal newEntity |
208 | eStats . sSequence . _Just . seqVal . _Just += nVal | 250 | when (cVal < 0) $ -- Carry over negative values from previous rounds |
209 | eStats . sSequence . _Just . seqRound . _Wrapped += 1 | 251 | eSeqVal . _Just . seqVal . _Just += cVal |
252 | eSeqVal . _Just . seqRound . _Wrapped += 1 | ||
253 | advanceTimer :: Entity -> Sh GameState Entity | ||
254 | advanceTimer entity = (execStateT ?? entity) $ do | ||
255 | rTime <- lift $ use gTimer | ||
256 | eTimer . _Just . absTime -= fromIntegral rTime | ||
210 | gRounds -= 1 | 257 | gRounds -= 1 |
211 | gEntities <~ (imapM advanceRound' =<< use gEntities) | 258 | gEntities <~ (mapM advanceTimer =<< imapM advanceRound' =<< use gEntities) |
259 | gTimer .= 0 | ||
260 | |||
261 | rollSeqVal :: Entity -> String -> Sh GameState (Entity, Maybe SeqVal) | ||
262 | rollSeqVal entity name = do | ||
263 | let sVal = fromMaybe (val ignored ["Sequenzwert"] False) $ preview (eStats . sSeqVal) entity | ||
264 | (newEntity, sNum) <- evalFormula [name] entity sVal | ||
265 | round <- use gRound | ||
266 | let val = Just $ def | ||
267 | & set (seqRound . _Wrapped) round | ||
268 | & set seqVal (Just sNum) | ||
269 | & set seqEpsilon (entity ^. eStats . sSeqEpsilon) | ||
270 | return (newEntity, val) | ||
212 | 271 | ||
213 | -- Query state | 272 | -- Query state |
214 | listFactions, listEntities :: Sh GameState () | 273 | listFactions, listEntities :: Sh GameState () |
@@ -220,18 +279,39 @@ focusTip, blur, pFocusTip :: Sh GameState () | |||
220 | focusTip = gFocus <~ preuse tip | 279 | focusTip = gFocus <~ preuse tip |
221 | blur = gFocus .= Nothing | 280 | blur = gFocus .= Nothing |
222 | pFocusTip = do | 281 | pFocusTip = do |
282 | nextTimer <- preuse $ timers . folding listToMaybe | ||
283 | time <- fromIntegral <$> use gTimer | ||
223 | round <- use gRound | 284 | round <- use gRound |
224 | let | 285 | let |
225 | eWeight :: Maybe SeqVal -> Int | 286 | eWeight :: Maybe SeqVal -> Int |
226 | eWeight sVal | 287 | eWeight sVal |
227 | | preview (_Just . seqRound . _Wrapped) sVal == Just round | 288 | | preview (_Just . seqRound . _Wrapped) sVal == Just round |
228 | , (preview (_Just . seqVal . _Just) -> Just n) <- sVal = n | 289 | , (preview (_Just . seqVal . _Just) -> Just n) <- sVal = max 0 n |
229 | | otherwise = 0 | 290 | | otherwise = 0 |
230 | entities <- map (over _2 . view $ eSeqVal . to eWeight) . Map.toList <$> use gEntities | 291 | entities <- map (over _2 . view $ eSeqVal . to eWeight) . Map.toList <$> use gEntities |
231 | case entities of | 292 | case nextTimer of |
232 | [] -> gFocus .= Nothing | 293 | nextTimer |
233 | _ -> gFocus <~ Just <$> liftIO (enact $ makeEventProb entities) | 294 | | Just timer <- nextTimer |
234 | 295 | , fst $ over _1 (\t -> t ^. absTime <= time) timer | |
296 | -> gFocus .= Just (snd timer) | ||
297 | | null entities -> gFocus .= Nothing | ||
298 | | otherwise -> gFocus <~ Just <$> liftIO (enact $ makeEventProb entities) | ||
299 | |||
300 | entityTimer, pEntityTimer :: Completable TimerLength -> Sh GameState () | ||
301 | entityTimer = entityTimer' Constant | ||
302 | pEntityTimer = entityTimer' Scaled | ||
303 | entityTimer' toTimer = withArg $ \(TimerLength origin n) -> do | ||
304 | time <- use gTimer | ||
305 | entities <- length <$> use priorityQueue | ||
306 | let | ||
307 | timer = case origin of | ||
308 | Absolute -> n | ||
309 | Now -> time + n | ||
310 | gFocus' . eTimer .= Just (scaleTimer time (max 1 $ fromIntegral entities) . toTimer $ fromIntegral timer) | ||
311 | |||
312 | clearEntityTimer :: Sh GameState () | ||
313 | clearEntityTimer = gFocus' . eTimer .= Nothing | ||
314 | |||
235 | -- Manual focus | 315 | -- Manual focus |
236 | setFocus :: Completable EntityIdentifier -> Sh GameState () | 316 | setFocus :: Completable EntityIdentifier -> Sh GameState () |
237 | setFocus = withArg $ \ident -> gFocus ?= ident | 317 | setFocus = withArg $ \ident -> gFocus ?= ident |
@@ -346,21 +426,18 @@ clearFactionSeqVal = withArg $ \qFaction -> use gEntities >>= mapM_ (clearEntity | |||
346 | entitySeqVal', clearEntitySeqVal' :: EntityIdentifier -> Sh GameState () | 426 | entitySeqVal', clearEntitySeqVal' :: EntityIdentifier -> Sh GameState () |
347 | entitySeqVal' ident = void . runMaybeT $ do | 427 | entitySeqVal' ident = void . runMaybeT $ do |
348 | entity <- MaybeT $ preuse (gEntities . ix ident) | 428 | entity <- MaybeT $ preuse (gEntities . ix ident) |
349 | let sVal = fromMaybe (val ignored ["Sequenzwert"] False) $ preview (eStats . sSeqVal) entity | ||
350 | name <- toName ident | 429 | name <- toName ident |
351 | round <- use gRound | 430 | (newEntity, val) <- lift $ rollSeqVal entity name |
352 | (newEntity, sNum) <- evalFormula [name] entity sVal | ||
353 | let val = Just $ def | ||
354 | & set (seqRound . _Wrapped) round | ||
355 | & set seqVal (Just sNum) | ||
356 | & set seqEpsilon (entity ^. eStats . sSeqEpsilon) | ||
357 | gEntities . at ident .= Just (newEntity & set eSeqVal val) | 431 | gEntities . at ident .= Just (newEntity & set eSeqVal val) |
358 | gLog <>= pure (ident, "Sequence: " ++ show sNum) | 432 | gLog <>= pure (ident, "Sequence: " ++ show (fromJust $ view seqVal =<< val)) |
359 | clearEntitySeqVal' ident = gEntities . ix ident . eSeqVal .= Nothing | 433 | clearEntitySeqVal' ident = gEntities . ix ident . eSeqVal .= Nothing |
360 | 434 | ||
361 | spendSeq :: Int -> String -> Sh GameState () | 435 | spendSeq :: Int -> String -> Sh GameState () |
362 | spendSeq n logStr = withFocus $ \focusId -> do | 436 | spendSeq n logStr = withFocus $ \focusId -> do |
363 | gEntities . ix focusId . eStats . sSequence . _Just . seqVal . _Just -= n | 437 | gFocus' . eSeqVal . _Just . seqVal . _Just -= n |
438 | hasSeq <- isJust <$> preuse (gFocus' . eSeqVal . _Just . seqVal . _Just) | ||
439 | when hasSeq $ | ||
440 | gTimer += n | ||
364 | gLog <>= pure (focusId, logStr) | 441 | gLog <>= pure (focusId, logStr) |
365 | 442 | ||
366 | delay :: Sh GameState () | 443 | delay :: Sh GameState () |
@@ -478,4 +555,5 @@ printVal = withArg $ \formula -> withFocus $ \focusId -> do | |||
478 | (fromRational prob :: Double) | 555 | (fromRational prob :: Double) |
479 | barLength (replicate (round $ fromInteger barLength * normalize prob) '#') | 556 | barLength (replicate (round $ fromInteger barLength * normalize prob) '#') |
480 | lengths = map (length . show . fst) vals | 557 | lengths = map (length . show . fst) vals |
481 | normalize p = p / maximum (map snd vals) | 558 | -- normalize p = p / maximum (map snd vals) |
559 | normalize = id | ||
diff --git a/src/Sequence/Contact/Archetypes.hs b/src/Sequence/Contact/Archetypes.hs index 1973b97..f8d4c9d 100644 --- a/src/Sequence/Contact/Archetypes.hs +++ b/src/Sequence/Contact/Archetypes.hs | |||
@@ -119,6 +119,7 @@ prop = Prop | |||
119 | , _sFatigueShock = def | 119 | , _sFatigueShock = def |
120 | 120 | ||
121 | , _sSequence = Nothing | 121 | , _sSequence = Nothing |
122 | , _sTimer = Nothing | ||
122 | 123 | ||
123 | , _sExtraSkills = [] | 124 | , _sExtraSkills = [] |
124 | , _sModifiers = [] | 125 | , _sModifiers = [] |
@@ -230,6 +231,7 @@ human = Humanoid | |||
230 | , _sFatigue = 0 | 231 | , _sFatigue = 0 |
231 | 232 | ||
232 | , _sSequence = Nothing | 233 | , _sSequence = Nothing |
234 | , _sTimer = Nothing | ||
233 | 235 | ||
234 | , _sPainShock = def | 236 | , _sPainShock = def |
235 | & set seReBar (vitBar 0.75) | 237 | & set seReBar (vitBar 0.75) |
@@ -351,6 +353,7 @@ dog = Quadruped | |||
351 | , _sFatigue = 0 | 353 | , _sFatigue = 0 |
352 | 354 | ||
353 | , _sSequence = Nothing | 355 | , _sSequence = Nothing |
356 | , _sTimer = Nothing | ||
354 | 357 | ||
355 | , _sPainShock = def | 358 | , _sPainShock = def |
356 | , _sFatigueShock = def | 359 | , _sFatigueShock = def |
@@ -440,6 +443,7 @@ dolphin = Dolphin | |||
440 | , _sFatigue = 0 | 443 | , _sFatigue = 0 |
441 | 444 | ||
442 | , _sSequence = Nothing | 445 | , _sSequence = Nothing |
446 | , _sTimer = Nothing | ||
443 | 447 | ||
444 | , _sPainShock = def | 448 | , _sPainShock = def |
445 | , _sFatigueShock = def | 449 | , _sFatigueShock = def |
diff --git a/src/Sequence/Contact/Types.hs b/src/Sequence/Contact/Types.hs index 5f8808d..c69a698 100644 --- a/src/Sequence/Contact/Types.hs +++ b/src/Sequence/Contact/Types.hs | |||
@@ -123,6 +123,20 @@ instance Default SeqVal where | |||
123 | } | 123 | } |
124 | 124 | ||
125 | makeLenses ''ShockEffect | 125 | makeLenses ''ShockEffect |
126 | |||
127 | absTime :: Lens' Timer Rational | ||
128 | absTime = lens get set | ||
129 | where | ||
130 | get (Constant n) = n | ||
131 | get (Scaled n) = n | ||
132 | set (Constant _) n = Constant n | ||
133 | set (Scaled _) n = Scaled n | ||
134 | |||
135 | instance Eq Timer where | ||
136 | (==) = (==) `on` view absTime | ||
137 | |||
138 | instance Ord Timer where | ||
139 | compare = comparing $ view absTime | ||
126 | 140 | ||
127 | instance Default ShockEffect where | 141 | instance Default ShockEffect where |
128 | def = ShockEffect { _seApplied = False | 142 | def = ShockEffect { _seApplied = False |
@@ -148,6 +162,7 @@ instance Default Stats where | |||
148 | , _sFatigue = 0 | 162 | , _sFatigue = 0 |
149 | 163 | ||
150 | , _sSequence = Nothing | 164 | , _sSequence = Nothing |
165 | , _sTimer = Nothing | ||
151 | 166 | ||
152 | , _sPainShock = def | 167 | , _sPainShock = def |
153 | , _sFatigueShock = def | 168 | , _sFatigueShock = def |
diff --git a/src/Sequence/Contact/Types/Internal.hs b/src/Sequence/Contact/Types/Internal.hs index 7938a06..11116e9 100644 --- a/src/Sequence/Contact/Types/Internal.hs +++ b/src/Sequence/Contact/Types/Internal.hs | |||
@@ -83,6 +83,10 @@ data SeqVal = SeqVal | |||
83 | } | 83 | } |
84 | deriving (Show, Eq, Ord) | 84 | deriving (Show, Eq, Ord) |
85 | 85 | ||
86 | data Timer = Scaled Rational | ||
87 | | Constant Rational | ||
88 | deriving (Show) | ||
89 | |||
86 | data ShockEffect = ShockEffect | 90 | data ShockEffect = ShockEffect |
87 | { _seApplied :: Bool | 91 | { _seApplied :: Bool |
88 | , _seVal | 92 | , _seVal |
@@ -107,6 +111,7 @@ data Stats = Prop | |||
107 | , _sFatigueShock :: ShockEffect | 111 | , _sFatigueShock :: ShockEffect |
108 | 112 | ||
109 | , _sSequence :: Maybe SeqVal | 113 | , _sSequence :: Maybe SeqVal |
114 | , _sTimer :: Maybe Timer | ||
110 | 115 | ||
111 | , _sExtraSkills :: Map (CI String) (FormulaM Stats Test) | 116 | , _sExtraSkills :: Map (CI String) (FormulaM Stats Test) |
112 | , _sModifiers :: Set Modifier | 117 | , _sModifiers :: Set Modifier |
@@ -171,6 +176,7 @@ data Stats = Prop | |||
171 | , _sFatigue :: Int | 176 | , _sFatigue :: Int |
172 | 177 | ||
173 | , _sSequence :: Maybe SeqVal | 178 | , _sSequence :: Maybe SeqVal |
179 | , _sTimer :: Maybe Timer | ||
174 | 180 | ||
175 | , _sPainShock :: ShockEffect | 181 | , _sPainShock :: ShockEffect |
176 | , _sFatigueShock :: ShockEffect | 182 | , _sFatigueShock :: ShockEffect |
@@ -213,6 +219,7 @@ data Stats = Prop | |||
213 | , _sFatigue :: Int | 219 | , _sFatigue :: Int |
214 | 220 | ||
215 | , _sSequence :: Maybe SeqVal | 221 | , _sSequence :: Maybe SeqVal |
222 | , _sTimer :: Maybe Timer | ||
216 | 223 | ||
217 | , _sPainShock :: ShockEffect | 224 | , _sPainShock :: ShockEffect |
218 | , _sFatigueShock :: ShockEffect | 225 | , _sFatigueShock :: ShockEffect |
@@ -255,6 +262,7 @@ data Stats = Prop | |||
255 | , _sFatigue :: Int | 262 | , _sFatigue :: Int |
256 | 263 | ||
257 | , _sSequence :: Maybe SeqVal | 264 | , _sSequence :: Maybe SeqVal |
265 | , _sTimer :: Maybe Timer | ||
258 | 266 | ||
259 | , _sPainShock :: ShockEffect | 267 | , _sPainShock :: ShockEffect |
260 | , _sFatigueShock :: ShockEffect | 268 | , _sFatigueShock :: ShockEffect |
diff --git a/src/Sequence/Types.hs b/src/Sequence/Types.hs index b5f6b4b..f2c08ac 100644 --- a/src/Sequence/Types.hs +++ b/src/Sequence/Types.hs | |||
@@ -1,12 +1,12 @@ | |||
1 | {-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, ViewPatterns, OverloadedStrings, TemplateHaskell, GeneralizedNewtypeDeriving, TypeOperators, UndecidableInstances #-} | 1 | {-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, ViewPatterns, OverloadedStrings, TemplateHaskell, GeneralizedNewtypeDeriving, TypeOperators, UndecidableInstances, RankNTypes #-} |
2 | 2 | ||
3 | module Sequence.Types | 3 | module Sequence.Types |
4 | ( GameState, gEntities, gEntityNames, gFocus, gNextId', gLog | 4 | ( GameState, gEntities, gEntityNames, gFocus, gNextId', gLog, gTimer, gLastCount |
5 | , Faction, faction, faction' | 5 | , Faction, faction, faction' |
6 | , Entity(..), eFaction, eSeqVal, eStats, eNotes | 6 | , Entity(..), eFaction, eSeqVal, eStats, eNotes |
7 | , EntityName(..), entityName | 7 | , EntityName(..), entityName |
8 | , EntityIdentifier(..), entityId, entityId' | 8 | , EntityIdentifier(..), entityId, entityId' |
9 | , inhabitedFactions, priorityQueue, tip, insertEntity, gFocus', gRounds, gRounds', gRound | 9 | , inhabitedFactions, combatFactions, combatFactions', priorityQueue, timers, tip, insertEntity, gFocus', gRounds, gRounds', gRound, eTimer |
10 | ) where | 10 | ) where |
11 | 11 | ||
12 | import Control.Lens | 12 | import Control.Lens |
@@ -81,6 +81,9 @@ instance Default Entity where | |||
81 | eSeqVal :: Lens' Entity (Maybe SeqVal) | 81 | eSeqVal :: Lens' Entity (Maybe SeqVal) |
82 | eSeqVal = eStats . sSequence | 82 | eSeqVal = eStats . sSequence |
83 | 83 | ||
84 | eTimer :: Lens' Entity (Maybe Timer) | ||
85 | eTimer = eStats . sTimer | ||
86 | |||
84 | instance Stats :<: Entity where | 87 | instance Stats :<: Entity where |
85 | ctx' = eStats | 88 | ctx' = eStats |
86 | 89 | ||
@@ -102,24 +105,36 @@ entityId' = _Show . entityId | |||
102 | 105 | ||
103 | data GameState = GameState | 106 | data GameState = GameState |
104 | { _gEntities :: Map EntityIdentifier Entity | 107 | { _gEntities :: Map EntityIdentifier Entity |
108 | , _gLastCount :: Int | ||
105 | , _gEntityNames :: Bimap EntityIdentifier EntityName | 109 | , _gEntityNames :: Bimap EntityIdentifier EntityName |
106 | , _gFocus :: Maybe EntityIdentifier | 110 | , _gFocus :: Maybe EntityIdentifier |
107 | , _gNextId :: EntityIdentifier | 111 | , _gNextId :: EntityIdentifier |
108 | , _gLog :: Seq (EntityIdentifier, String) | 112 | , _gLog :: Seq (EntityIdentifier, String) |
113 | , _gTimer :: Int | ||
109 | } | 114 | } |
110 | makeLenses ''GameState | 115 | makeLenses ''GameState |
111 | 116 | ||
112 | instance Default GameState where | 117 | instance Default GameState where |
113 | def = GameState | 118 | def = GameState |
114 | { _gEntities = def | 119 | { _gEntities = def |
120 | , _gLastCount = 0 | ||
115 | , _gEntityNames = Bimap.empty | 121 | , _gEntityNames = Bimap.empty |
116 | , _gFocus = Nothing | 122 | , _gFocus = Nothing |
117 | , _gNextId = toEnum 0 | 123 | , _gNextId = toEnum 0 |
118 | , _gLog = Seq.empty | 124 | , _gLog = Seq.empty |
125 | , _gTimer = 0 | ||
119 | } | 126 | } |
120 | 127 | ||
121 | inhabitedFactions :: Getter GameState [Faction] | 128 | inhabitedFactions, combatFactions, combatFactions' :: Getter GameState [Faction] |
122 | inhabitedFactions = to $ nub . sort . Map.elems . fmap (view eFaction) . view gEntities | 129 | inhabitedFactions = factionList $ \_ _ -> True |
130 | combatFactions = factionList $ \f st -> let | ||
131 | cFactions = map (view eFaction . fromJust . flip Map.lookup (st ^. gEntities) . snd) $ st ^. priorityQueue | ||
132 | in f `elem` cFactions | ||
133 | combatFactions' = factionList $ \f st -> let | ||
134 | cFactions = map (view eFaction . fromJust . flip Map.lookup (st ^. gEntities) . snd) $ st ^. timers | ||
135 | in f `elem` cFactions | ||
136 | factionList :: (Faction -> GameState -> Bool) -> Getter GameState [Faction] | ||
137 | factionList pred = to $ \st -> filter (flip pred st) . nub . sort . Map.elems . fmap (view eFaction) . view gEntities $ st | ||
123 | 138 | ||
124 | priorityQueue :: Getter GameState [(SeqVal, EntityIdentifier)] | 139 | priorityQueue :: Getter GameState [(SeqVal, EntityIdentifier)] |
125 | priorityQueue = to priorityQueue' | 140 | priorityQueue = to priorityQueue' |
@@ -128,8 +143,23 @@ priorityQueue = to priorityQueue' | |||
128 | filter (Nothing, _) = mempty | 143 | filter (Nothing, _) = mempty |
129 | filter (Just val, id) = pure (val, id) | 144 | filter (Just val, id) = pure (val, id) |
130 | 145 | ||
146 | timers :: Getter GameState [(Timer, EntityIdentifier)] | ||
147 | timers = to timers' | ||
148 | where | ||
149 | timers' state = sortBy (comparing fst) . concat . map filter . map (over _1 $ view eTimer) . map swap $ entities | ||
150 | where | ||
151 | entities = Map.toAscList $ view gEntities state | ||
152 | time = fromIntegral $ view gTimer state | ||
153 | filter (Nothing, _) = mempty | ||
154 | filter (Just v, id) = pure (v, id) | ||
155 | |||
131 | tip :: Fold GameState EntityIdentifier | 156 | tip :: Fold GameState EntityIdentifier |
132 | tip = priorityQueue . folding (fmap snd . listToMaybe) | 157 | tip = folding $ runReaderT tip' |
158 | where | ||
159 | tip' :: ReaderT GameState First EntityIdentifier | ||
160 | tip' = do | ||
161 | join . fmap (lift . First) . preview $ timers . folding (fmap snd . listToMaybe) | ||
162 | join . fmap (lift . First) . preview $ priorityQueue . folding (fmap snd . listToMaybe) | ||
133 | 163 | ||
134 | gFocus' :: Traversal' GameState Entity | 164 | gFocus' :: Traversal' GameState Entity |
135 | gFocus' modifyFocus st = (flip runReader st) . (maybe (asks pure) return =<<) . runMaybeT $ do | 165 | gFocus' modifyFocus st = (flip runReader st) . (maybe (asks pure) return =<<) . runMaybeT $ do |
@@ -158,7 +188,7 @@ insertEntity entity = execState $ do | |||
158 | gNextId %= succ | 188 | gNextId %= succ |
159 | 189 | ||
160 | gRounds :: Traversal' GameState Int | 190 | gRounds :: Traversal' GameState Int |
161 | gRounds = gEntities . each . eStats . sSequence . _Just . seqRound . _Wrapped | 191 | gRounds = gEntities . each . eSeqVal . _Just . seqRound . _Wrapped |
162 | 192 | ||
163 | gRounds' :: Getter GameState [Int] | 193 | gRounds' :: Getter GameState [Int] |
164 | gRounds' = to $ nub . sort . toListOf gRounds | 194 | gRounds' = to $ nub . sort . toListOf gRounds |
diff --git a/src/Sequence/Utils.hs b/src/Sequence/Utils.hs index fbf3c7d..bb29b86 100644 --- a/src/Sequence/Utils.hs +++ b/src/Sequence/Utils.hs | |||
@@ -1,10 +1,12 @@ | |||
1 | {-# LANGUAGE ViewPatterns, FlexibleContexts, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, OverloadedStrings, OverloadedLists, RankNTypes #-} | 1 | {-# LANGUAGE ViewPatterns, FlexibleContexts, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, OverloadedStrings, OverloadedLists, RankNTypes #-} |
2 | 2 | ||
3 | module Sequence.Utils | 3 | module Sequence.Utils |
4 | ( withArg, withFocus, withFocus' | 4 | ( TimerLength(..), TimerOrigin(..) |
5 | , withArg, withFocus, withFocus' | ||
5 | , focusState | 6 | , focusState |
6 | , toName, toDesc | 7 | , toName, toDesc |
7 | , outputLogged | 8 | , outputLogged |
9 | , scaleTimer | ||
8 | , Argument(..) | 10 | , Argument(..) |
9 | , Completion(..) | 11 | , Completion(..) |
10 | , module Sequence.Utils.Ask | 12 | , module Sequence.Utils.Ask |
@@ -50,6 +52,10 @@ import Sequence.Formula | |||
50 | 52 | ||
51 | import Text.Regex (mkRegex, subRegex) | 53 | import Text.Regex (mkRegex, subRegex) |
52 | 54 | ||
55 | data TimerLength = TimerLength TimerOrigin Int | ||
56 | |||
57 | data TimerOrigin = Absolute | Now | ||
58 | |||
53 | class Argument a st | a -> st where | 59 | class Argument a st | a -> st where |
54 | arg :: String -> Sh st (Maybe a) | 60 | arg :: String -> Sh st (Maybe a) |
55 | 61 | ||
@@ -93,6 +99,23 @@ outputLogged id str = gLog <>= pure (id, clean str) >> shellPutStrLn str | |||
93 | where | 99 | where |
94 | clean str = subRegex (mkRegex "(\x9B|\x1B\\[)[0-?]*[ -/]*[@-~]") str "" -- remove ANSI escapes | 100 | clean str = subRegex (mkRegex "(\x9B|\x1B\\[)[0-?]*[ -/]*[@-~]") str "" -- remove ANSI escapes |
95 | 101 | ||
102 | scaleTimer :: Int -> Rational -> Timer -> Timer | ||
103 | scaleTimer _ _ t@(Constant _) = t | ||
104 | scaleTimer (fromIntegral -> now) factor (Scaled r) = Scaled $ ((max 0 $ r - now) * factor) + now | ||
105 | |||
106 | instance Completion TimerLength GameState where | ||
107 | completableLabel _ = "<timer offset>" | ||
108 | complete _ st prefix = return [] | ||
109 | |||
110 | instance Argument TimerLength GameState where | ||
111 | arg str | ||
112 | | ('+':cs) <- str | ||
113 | , (Just n) <- readMaybe cs | ||
114 | , n >= 0 = return . Just $ TimerLength Now n | ||
115 | | (Just n) <- readMaybe str | ||
116 | , n >= 0 = return . Just $ TimerLength Absolute n | ||
117 | | otherwise = return Nothing | ||
118 | |||
96 | instance Completion EntityIdentifier GameState where | 119 | instance Completion EntityIdentifier GameState where |
97 | completableLabel _ = "<entity>" | 120 | completableLabel _ = "<entity>" |
98 | complete _ st prefix = return . filter ((isPrefixOf `on` CI.foldCase) prefix) . map ((evalState ?? st) . toName) . Map.keys $ st ^. gEntities | 121 | complete _ st prefix = return . filter ((isPrefixOf `on` CI.foldCase) prefix) . map ((evalState ?? st) . toName) . Map.keys $ st ^. gEntities |