Implemented escaped table references, add more stuff
Getty Ritter
6 years ago
49 | 49 | 12: unknown/mystery |
50 | 50 | |
51 | 51 | theme |
52 | 1-5: @dungeon/theme/mundane | |
53 | 6-9: @dungeon/theme/unusual | |
54 |
1 |
|
52 | 1-5: @{self/mundane} | |
53 | 6-9: @{self/unusual} | |
54 | 10-12: @{self/extraordinary} | |
55 | 55 | mundane |
56 | 56 | 1: rot/decay |
57 | 57 | 2: torture/agony |
93 | 93 | 12: holy war |
94 | 94 | |
95 | 95 | discovery |
96 |
1-3: |
|
96 | 1-3: @{self/dressing} | |
97 | 4-9: @{self/feature} | |
98 | 10-12: @{self/find} | |
97 | 99 | dressing |
98 | 100 | 1: junk/debris |
99 | 101 | 2: tracks/marks |
106 | 108 | 9: broken door/wall |
107 | 109 | 10: breeze/wind/smell |
108 | 110 | 11: lichen/moss/fungus |
109 |
12: @ |
|
111 | 12: @{details/oddity} | |
112 | feature | |
113 | 1: cave-in/collapse | |
114 | 2: pit/shaft/column | |
115 | 3: pillars/columns | |
116 | 4: locked door/gate | |
117 | 5: alcoves/niches | |
118 | 6: bridge/stairs/ramp | |
119 | 7: fountain/well/pool | |
120 | 8: puzzle | |
121 | 9: altar/dais/platform | |
122 | 10: statue/idol | |
123 | 11: magic pool/statue/idol | |
124 | 12: connection to another dungeon | |
125 | find | |
126 | 1: trinkets | |
127 | 2: tools | |
128 | 3: weapons/armor | |
129 | 4: supplies/trade goods | |
130 | 5: coins/gems/jewelry | |
131 | 6: poisons/potions | |
132 | 7: adventurer/captive | |
133 | 8: magic item | |
134 | 9: scroll/book | |
135 | 10: magic weapon/armor | |
136 | 11: artifact | |
137 | 12: @{self} and @{self} | |
138 | ||
139 | danger | |
140 | 1-4: @{self/trap} | |
141 | 5-11: @{self/creature} | |
142 | 12: @{self/entity} | |
143 | trap | |
144 | 1: alarm | |
145 | 2: ensnaring/paralyzing | |
146 | 3: pit | |
147 | 4: crushing | |
148 | 5: piercing/puncturing | |
149 | 6: chopping/slashing | |
150 | 7: confusing (maze, etc.) | |
151 | 8: gas (poison, etc.) | |
152 | 9: element | |
153 | 10: ambush | |
154 | 11: magical | |
155 | 12: @{self} and @{self} | |
156 | creature: | |
157 | 1: waiting in ambush | |
158 | 2: fighting/squabbling | |
159 | 3: prowling/on patrol | |
160 | 4: looking for food | |
161 | 5: eating/resting | |
162 | 6: guarding | |
163 | 7: on the move | |
164 | 8: searching/scavenging | |
165 | 9: returning to den | |
166 | 10: making plans | |
167 | 11: sleeping | |
168 | 12: dying | |
169 | entity: | |
170 | 1: alien interloper | |
171 | 2: vermin lord | |
172 | 3: criminal mastermind | |
173 | 4: warlord | |
174 | 5: high priest | |
175 | 6: oracle | |
176 | 7: wizard/witch/alchemist | |
177 | 8: monster lord | |
178 | 9: evil spirit/ghost | |
179 | 10: undead lord | |
180 | 11: demon | |
181 | 12: dark god | |
182 | ||
183 | creature | |
184 | beast | |
185 | 1-7: @{self/earthbound} | |
186 | 8-10: @{self/airborne} | |
187 | 11-12: @{self/aquatic} | |
188 | earthbound | |
189 | 1: termite/tick/louse | |
190 | 2: snail/slug/worm | |
191 | 3: ant/centipede/scorpion | |
192 | 4: snake/lizard | |
193 | 5: vole/rat/weasel | |
194 | 6: boar/pig | |
195 | 7: dog/fox/wolf | |
196 | 8: cat/lion/panther | |
197 | 9: deer/horse/camel | |
198 | 10: ox/rhino | |
199 | 11: bear/ape/gorilla | |
200 | 12: mammoth/dinosaur | |
201 | ||
202 | airborne | |
203 | 1: mosquito/firefly | |
204 | 2: locust/dragonfly/moth | |
205 | 3: bee/wasp | |
206 | 4: chicken/duck/goose | |
207 | 5: songbird/parrot | |
208 | 6: gull/waterbird | |
209 | 7: heron/crane/stork | |
210 | 8: crow/raven | |
211 | 9: hawk/falcon | |
212 | 10: eagle/owl | |
213 | 11: condor | |
214 | 12: pteranodon | |
215 | ||
216 | aquatic | |
217 | 1: insect | |
218 | 2: jelly/anemone | |
219 | 3: clam/oyster/snail | |
220 | 4: eel/snake | |
221 | 5: frog/toad | |
222 | 6: fish | |
223 | 7: crab/lobster | |
224 | 8: turtle | |
225 | 9: alligator/crocodile | |
226 | 10: dolphin/shark | |
227 | 11: squid/octopus | |
228 | 12: whale | |
229 | ||
230 | humanoid | |
231 | 1-7: @{self/common} | |
232 | 8-10: @{self/uncommon} | |
233 | 11-12: @{self/hybrid} | |
234 | ||
235 | common | |
236 | 1-3: halfling (small) | |
237 | 4-5: goblin/kobold (small) | |
238 | 6-7: dwarf/gnome (small) | |
239 | 8-9: orc/hobgoblin/gnoll | |
240 | 10-11: half-elf/half-orc, etc. | |
241 | 12: elf | |
242 | ||
243 | uncommon | |
244 | 1: fey (tiny) | |
245 | 2-3: catfolk/dogfolk | |
246 | 4-6: lizardfolk/merfolk | |
247 | 7: birdfolk | |
248 | 8-10: ogre/troll (large) | |
249 | 11-12: cycops/giant (large) | |
250 | ||
251 | hybrid | |
252 | 1-2: centaur | |
253 | 3-5: werewolf/werebear | |
254 | 6: were-@{creature/beast} | |
255 | 7-10: human and a @{creature/beast} | |
256 | 11-12: human and two @{creature/beast}s | |
257 | ||
258 | monster | |
259 | 1-7: @{self/unusual} | |
260 | 8-10: @{self/rare} | |
261 | 11-12: @{self/legendary} | |
262 | unusual | |
263 | 1-3: plant/fungus | |
264 | 4-5: undead human | |
265 | 6: undead @{creature/humanoid} | |
266 | 7-8: @{creature/beast} + @{creature/beast} | |
267 | 9-10: @{creature/beast} with the @{details/ability} ability | |
268 | 11-12: @{details/feature} @{creature/beast} | |
269 | ||
270 | rare | |
271 | 1-3: slime/ooze (amorphous) | |
272 | 4-6: creation (construct) | |
273 | 7-9: @{creature/beast}, with oddity @{details/oddity} | |
274 | 10-12: UNNATURAL ENTITY | |
275 | ||
276 | legendary | |
277 | 1-3: dragon/colossus (huge) | |
278 | 4-6: huge @{creature/monster/unusual} | |
279 | 7-9: huge @{creature/monster/rare} | |
280 | 10: @{creature/beast}-dragon | |
281 | 11: @{creature/monster/unusual}-dragon | |
282 | 12: @{creature/monster/rare}-dragon | |
110 | 283 | |
111 | 284 | details |
285 | ability | |
286 | 1: bless/curse | |
287 | 2: entagle/trap/snare | |
288 | 3: poison/disease | |
289 | 4: paralyze/petrify | |
290 | 5: mimic/camouflage | |
291 | 6: seduce/hypnotize | |
292 | 7: dissolve/disintegrate | |
293 | 8: @{details/magic type} | |
294 | 9: drain life/magic | |
295 | 10: immunity to @{details/element} | |
296 | 11: read/control minds | |
297 | 12: both @{self} and @{self} | |
298 | ||
299 | element | |
300 | 1: air | |
301 | 2: earth | |
302 | 3: fire | |
303 | 4: water | |
304 | 5: light | |
305 | 6: dark | |
306 | ||
307 | magic type | |
308 | 1: divination | |
309 | 2: enchantment | |
310 | 3: evocation | |
311 | 4: illusion | |
312 | 5: necromancy | |
313 | 6: summoning | |
314 | ||
112 | 315 | oddity |
113 | 316 | 1: weird color/smell/sound |
114 | 317 | 2: geometric |
121 | 324 | 9: magnetic/repellant |
122 | 325 | 10: devoid of life |
123 | 326 | 11: unexpectedly alive |
124 |
12: |
|
327 | 12: @{self} and @{self} | |
328 | ||
329 | feature | |
330 | 1: heavily armored | |
331 | 2-3: winged/flying | |
332 | 4: many-headed | |
333 | 5: many-eyed or one-eyed | |
334 | 6: many-limbed or many-tailed | |
335 | 7: tentacled | |
336 | 8: ASPECT | |
337 | 9: @{details/element} | |
338 | 10: @{details/magic type} | |
339 | 11: @{details/oddity} | |
340 | 12: @{self} and @{self} |
40 | 40 | Nothing -> do |
41 | 41 | putStrLn "farewell" |
42 | 42 | Exit.exitSuccess |
43 | Just ":q" -> do | |
44 | putStrLn "farewell" | |
45 | Exit.exitSuccess | |
46 | ||
43 | 47 | Just "" -> pure () |
48 | ||
44 | 49 | Just ":l" -> do |
45 | 50 | tables <- IO.readIORef tablesRef |
46 | 51 | Text.putStrLn "Available tables: " |
47 | 52 | Text.putStrLn (" " <> Text.unwords (Map.keys tables)) |
48 | 53 | Just ":r" -> |
49 | 54 | IO.writeIORef tablesRef =<< readMap "perilous-wilds.txt" |
55 | ||
50 | 56 | Just choice -> do |
51 | 57 | tables <- IO.readIORef tablesRef |
52 | 58 | let names = Text.unwords (Map.keys tables) |
55 | 61 | Nothing -> do |
56 | 62 | Text.putStrLn ("table not found: " <> Text.pack (show choice)) |
57 | 63 | Text.putStrLn (" valid tables include: " <> names) |
58 |
Just t -> Types.rollTable tables t |
|
64 | Just t -> Types.rollTable tables t >>= (Text.putStrLn . Types.valueMsg) |
25 | 25 | m = read (Text.unpack (Text.tail y)) |
26 | 26 | in Range n m |
27 | 27 | |
28 | parseResult :: Text.Text -> Result | |
29 | parseResult t | |
30 | | "@" `Text.isPrefixOf` Text.strip t = | |
31 | ResultRoll (Text.tail (Text.strip t)) | |
32 | | otherwise = | |
33 | ResultText (Text.strip t) | |
28 | parseFragments :: Text.Text -> [Fragment] | |
29 | parseFragments t = | |
30 | let (frag, roll) = Text.breakOn "@{" t | |
31 | in case roll of | |
32 | "" -> [FragText frag] | |
33 | _ -> | |
34 | let (name, rest) = Text.breakOn "}" (Text.drop 2 roll) | |
35 | in FragText frag : FragRoll name : parseFragments (Text.tail rest) | |
34 | 36 | |
35 | 37 | parseLines :: [Text.Text] -> [LineType] |
36 | 38 | parseLines = go |
41 | 43 | | Text.any (== ':') t = |
42 | 44 | let (rangeTxt, message) = Text.breakOn ":" t |
43 | 45 | range = parseRange rangeTxt |
44 | msg = parseResult (Text.tail message) | |
45 | in TableEntry range msg : go ts | |
46 | msg = parseFragments (Text.tail message) | |
47 | in TableEntry range (Result msg) : go ts | |
46 | 48 | | otherwise = |
47 | 49 | TableDecl (indentAmount t) (Text.strip t) : go ts |
48 | 50 |
17 | 17 | , tableChoices :: [(Range, Result)] |
18 | 18 | } deriving (Eq, Show) |
19 | 19 | |
20 | data Result | |
21 | = ResultText Text.Text | |
22 |
|
|
20 | data Fragment | |
21 | = FragText Text.Text | |
22 | | FragRoll Text.Text | |
23 | 23 | deriving (Eq, Show) |
24 | 24 | |
25 | computeResult :: Int -> TableMap -> Result -> IO () | |
26 | computeResult r _ (ResultText msg) = do | |
27 | Text.putStr ("\x1b[36m" <> Text.pack (show r) <> ":\x1b[39m ") | |
28 | Text.putStrLn msg | |
29 | computeResult r ts (ResultRoll name) | |
30 | | Just t <- Map.lookup name ts = do | |
31 | Text.putStr ("\x1b[36m" <> Text.pack (show r)) | |
32 | Text.putStrLn (": (roll " <> name <> ")\x1b[39m") | |
33 | rollTable ts t | |
34 | | otherwise = Text.putStrLn ("error: no such table: " <> name) | |
25 | data Result = Result { fromResult :: [Fragment] } | |
26 | deriving (Eq, Show) | |
27 | ||
28 | data Value = Value | |
29 | { valueMsg :: Text.Text | |
30 | } deriving (Eq, Show) | |
31 | ||
32 | stripValue :: Value -> Value | |
33 | stripValue = Value . Text.strip . valueMsg | |
34 | ||
35 | data Context = Context | |
36 | { ctxMap :: TableMap | |
37 | , ctxRoll :: Int | |
38 | , ctxSelf :: Text.Text | |
39 | } | |
40 | ||
41 | findTable :: Text.Text -> Context -> Maybe Table | |
42 | findTable name ctx = Map.lookup name (ctxMap ctx) | |
43 | ||
44 | computeFragments :: Context -> Fragment -> IO Value | |
45 | computeFragments _ (FragText msg) = pure (Value msg) | |
46 | computeFragments ctx (FragRoll name) = | |
47 | let absolute = case Text.stripPrefix "self" name of | |
48 | Just rest -> ctxSelf ctx <> rest | |
49 | Nothing -> name | |
50 | in case findTable absolute ctx of | |
51 | Just t -> rollTable (ctxMap ctx) t | |
52 | Nothing -> error ("no such table: " ++ show absolute) | |
53 | ||
54 | computeResult :: Context -> Result -> IO Value | |
55 | computeResult ctx (Result msgs) = do | |
56 | vs <- mapM (computeFragments ctx) msgs | |
57 | pure (Value (foldMap valueMsg vs)) | |
35 | 58 | |
36 | 59 | tableDie :: Table -> Int |
37 | 60 | tableDie t = maximum [ x | (Range _ x, _) <- tableChoices t ] |
38 | 61 | |
39 |
rollTable :: TableMap -> Table -> IO |
|
62 | rollTable :: TableMap -> Table -> IO Value | |
40 | 63 | rollTable tables t = do |
41 | 64 | roll <- Rand.randomRIO (1, tableDie t) |
65 | let ctx = Context | |
66 | { ctxMap = tables | |
67 | , ctxRoll = roll | |
68 | , ctxSelf = tableName t | |
69 | } | |
42 | 70 | case [ result |
43 | 71 | | (range, result) <- tableChoices t |
44 | 72 | , roll >= rFrom range && roll <= rTo range |
45 | 73 | ] of |
46 | [choice] -> computeResult roll tables choice | |
47 | _ -> Text.putStrLn $ Text.unwords | |
74 | [choice] -> stripValue <$> computeResult ctx choice | |
75 | _ -> error $ unwords | |
48 | 76 | [ "bad table " |
49 |
, |
|
77 | , Text.unpack (tableName t) | |
50 | 78 | , "(roll of" |
51 |
, |
|
79 | , show roll | |
52 | 80 | , "has no matching result)" |
53 | 81 | ] |