14 | 14 |
data Range = Range { rFrom :: Roll, rTo :: Roll }
|
15 | 15 |
deriving (Eq, Show)
|
16 | 16 |
|
| 17 |
class Exn.Exception t => PotreroError t where
|
| 18 |
potreroMessage :: t -> [Text.Text]
|
| 19 |
|
| 20 |
data BadTable = BadTable
|
| 21 |
{ badTableTable :: Table
|
| 22 |
, badTableIndex :: [Int]
|
| 23 |
} deriving (Eq, Show)
|
| 24 |
|
| 25 |
instance Exn.Exception BadTable where
|
| 26 |
|
| 27 |
instance PotreroError BadTable where
|
| 28 |
potreroMessage bt =
|
| 29 |
let rolls = map (Text.pack . show) (badTableIndex bt)
|
| 30 |
results = [ "`" <> Text.strip (showResult r) <> "`"
|
| 31 |
| (Range x y, r) <- tableChoices (badTableTable bt)
|
| 32 |
, or [ i >= x && i <= y | i <- badTableIndex bt]
|
| 33 |
]
|
| 34 |
orText = Text.intercalate " or "
|
| 35 |
in [ "Malformed table: `" <> tableName (badTableTable bt) <> "`"
|
| 36 |
, " a roll of " <> orText rolls <> " is ambiguous"
|
| 37 |
, " and may result in either " <> orText results
|
| 38 |
]
|
| 39 |
|
17 | 40 |
-- needed to handle d66 tables
|
18 | |
rangeMap :: [Range] -> [Int]
|
19 | |
rangeMap = Set.toList . foldr (Set.union . toSet) Set.empty
|
20 | |
where toSet (Range x y) = Set.fromList [x..y]
|
| 41 |
rangeMap :: Table -> [(Range, Result)] -> [Int]
|
| 42 |
rangeMap t ranges = Set.toList (go Set.empty ranges)
|
| 43 |
where
|
| 44 |
go set [] = set
|
| 45 |
go set ((Range x y, _result):rs) =
|
| 46 |
let rangeSet = Set.fromList [x..y]
|
| 47 |
overlap = Set.intersection rangeSet set
|
| 48 |
in if Set.null overlap
|
| 49 |
then go (Set.union rangeSet set) rs
|
| 50 |
else Exn.throw (BadTable t (Set.toList overlap))
|
21 | 51 |
|
22 | 52 |
type TableMap = Map.Map TableName Table
|
23 | 53 |
|
|
33 | 63 |
|
34 | 64 |
data Result = Result { fromResult :: [Fragment] }
|
35 | 65 |
deriving (Eq, Show)
|
| 66 |
|
| 67 |
showResult :: Result -> Text.Text
|
| 68 |
showResult = foldMap go . fromResult
|
| 69 |
where go (FragText t) = t
|
| 70 |
go (FragRoll n) = "@{" <> n <> "}"
|
36 | 71 |
|
37 | 72 |
-- * Values
|
38 | 73 |
|
|
100 | 135 |
|
101 | 136 |
rollTable :: TableMap -> Table -> IO Value
|
102 | 137 |
rollTable tables t = do
|
103 | |
let rmap = rangeMap (map fst (tableChoices t))
|
| 138 |
let rmap = rangeMap t (tableChoices t)
|
104 | 139 |
rollIdx <- Rand.randomRIO (0, length rmap - 1)
|
105 | 140 |
let roll = rmap !! rollIdx
|
106 | 141 |
ctx = Context
|
|
113 | 148 |
, roll >= rFrom range && roll <= rTo range
|
114 | 149 |
] of
|
115 | 150 |
[choice] -> stripValue <$> computeResult ctx choice
|
116 | |
[] -> error $ unwords
|
117 | |
[ "bad table "
|
118 | |
, Text.unpack (tableName t)
|
119 | |
, "(roll of"
|
120 | |
, show roll
|
121 | |
, "has no matching result)"
|
122 | |
]
|
123 | |
_ -> error "ambiguous result (TODO)"
|
| 151 |
_ -> error "unreachable"
|