Browse Source

Implemented escaped table references, add more stuff

Getty Ritter 5 years ago
parent
commit
b24996bde4
4 changed files with 285 additions and 33 deletions
  1. 222 6
      perilous-wilds.txt
  2. 7 1
      src/Main.hs
  3. 10 8
      src/Parser.hs
  4. 46 18
      src/Types.hs

+ 222 - 6
perilous-wilds.txt

@@ -49,9 +49,9 @@ dungeon
             12: unknown/mystery
 
     theme
-        1-5:   @dungeon/theme/mundane
-        6-9:   @dungeon/theme/unusual
-        10-12: @dungeon/theme/extraordinary
+        1-5:   @{self/mundane}
+        6-9:   @{self/unusual}
+        10-12: @{self/extraordinary}
         mundane
             1:  rot/decay
             2:  torture/agony
@@ -93,7 +93,9 @@ dungeon
             12: holy war
 
     discovery
-        1-3: @dungeon/discovery/dressing
+        1-3:   @{self/dressing}
+        4-9:   @{self/feature}
+        10-12: @{self/find}
         dressing
             1:  junk/debris
             2:  tracks/marks
@@ -106,9 +108,210 @@ dungeon
             9:  broken door/wall
             10: breeze/wind/smell
             11: lichen/moss/fungus
-            12: @details/oddity
+            12: @{details/oddity}
+        feature
+            1:  cave-in/collapse
+            2:  pit/shaft/column
+            3:  pillars/columns
+            4:  locked door/gate
+            5:  alcoves/niches
+            6:  bridge/stairs/ramp
+            7:  fountain/well/pool
+            8:  puzzle
+            9:  altar/dais/platform
+            10: statue/idol
+            11: magic pool/statue/idol
+            12: connection to another dungeon
+        find
+            1:  trinkets
+            2:  tools
+            3:  weapons/armor
+            4:  supplies/trade goods
+            5:  coins/gems/jewelry
+            6:  poisons/potions
+            7:  adventurer/captive
+            8:  magic item
+            9:  scroll/book
+            10: magic weapon/armor
+            11: artifact
+            12: @{self} and @{self}
+
+    danger
+        1-4:  @{self/trap}
+        5-11: @{self/creature}
+        12:   @{self/entity}
+        trap
+            1:  alarm
+            2:  ensnaring/paralyzing
+            3:  pit
+            4:  crushing
+            5:  piercing/puncturing
+            6:  chopping/slashing
+            7:  confusing (maze, etc.)
+            8:  gas (poison, etc.)
+            9:  element
+            10: ambush
+            11: magical
+            12: @{self} and @{self}
+        creature:
+            1:  waiting in ambush
+            2:  fighting/squabbling
+            3:  prowling/on patrol
+            4:  looking for food
+            5:  eating/resting
+            6:  guarding
+            7:  on the move
+            8:  searching/scavenging
+            9:  returning to den
+            10: making plans
+            11: sleeping
+            12: dying
+        entity:
+            1:  alien interloper
+            2:  vermin lord
+            3:  criminal mastermind
+            4:  warlord
+            5:  high priest
+            6:  oracle
+            7:  wizard/witch/alchemist
+            8:  monster lord
+            9:  evil spirit/ghost
+            10: undead lord
+            11: demon
+            12: dark god
+
+creature
+    beast
+        1-7: @{self/earthbound}
+        8-10: @{self/airborne}
+        11-12: @{self/aquatic}
+        earthbound
+            1: termite/tick/louse
+            2: snail/slug/worm
+            3: ant/centipede/scorpion
+            4: snake/lizard
+            5: vole/rat/weasel
+            6: boar/pig
+            7: dog/fox/wolf
+            8: cat/lion/panther
+            9: deer/horse/camel
+            10: ox/rhino
+            11: bear/ape/gorilla
+            12: mammoth/dinosaur
+
+        airborne
+            1:  mosquito/firefly
+            2:  locust/dragonfly/moth
+            3:  bee/wasp
+            4:  chicken/duck/goose
+            5:  songbird/parrot
+            6:  gull/waterbird
+            7:  heron/crane/stork
+            8:  crow/raven
+            9:  hawk/falcon
+            10: eagle/owl
+            11: condor
+            12: pteranodon
+
+        aquatic
+            1:  insect
+            2:  jelly/anemone
+            3:  clam/oyster/snail
+            4:  eel/snake
+            5:  frog/toad
+            6:  fish
+            7:  crab/lobster
+            8:  turtle
+            9:  alligator/crocodile
+            10: dolphin/shark
+            11: squid/octopus
+            12: whale
+
+    humanoid
+        1-7: @{self/common}
+        8-10: @{self/uncommon}
+        11-12: @{self/hybrid}
+
+        common
+            1-3:   halfling (small)
+            4-5:   goblin/kobold (small)
+            6-7:   dwarf/gnome (small)
+            8-9:   orc/hobgoblin/gnoll
+            10-11: half-elf/half-orc, etc.
+            12:    elf
+
+        uncommon
+            1:     fey (tiny)
+            2-3:   catfolk/dogfolk
+            4-6:   lizardfolk/merfolk
+            7:     birdfolk
+            8-10:  ogre/troll (large)
+            11-12: cycops/giant (large)
+
+        hybrid
+            1-2:   centaur
+            3-5:   werewolf/werebear
+            6:     were-@{creature/beast}
+            7-10:  human and a @{creature/beast}
+            11-12: human and two @{creature/beast}s
+
+    monster
+        1-7: @{self/unusual}
+        8-10: @{self/rare}
+        11-12: @{self/legendary}
+        unusual
+            1-3:   plant/fungus
+            4-5:   undead human
+            6:     undead @{creature/humanoid}
+            7-8:   @{creature/beast} + @{creature/beast}
+            9-10:  @{creature/beast} with the @{details/ability} ability
+            11-12: @{details/feature} @{creature/beast}
+
+        rare
+            1-3:   slime/ooze (amorphous)
+            4-6: creation (construct)
+            7-9: @{creature/beast}, with oddity @{details/oddity}
+            10-12: UNNATURAL ENTITY
+
+        legendary
+            1-3: dragon/colossus (huge)
+            4-6: huge @{creature/monster/unusual}
+            7-9: huge @{creature/monster/rare}
+            10:  @{creature/beast}-dragon
+            11:  @{creature/monster/unusual}-dragon
+            12:  @{creature/monster/rare}-dragon
 
 details
+    ability
+        1:  bless/curse
+        2:  entagle/trap/snare
+        3:  poison/disease
+        4:  paralyze/petrify
+        5:  mimic/camouflage
+        6:  seduce/hypnotize
+        7:  dissolve/disintegrate
+        8:  @{details/magic type}
+        9:  drain life/magic
+        10: immunity to @{details/element}
+        11: read/control minds
+        12: both @{self} and @{self}
+
+    element
+        1: air
+        2: earth
+        3: fire
+        4: water
+        5: light
+        6: dark
+
+    magic type
+        1: divination
+        2: enchantment
+        3: evocation
+        4: illusion
+        5: necromancy
+        6: summoning
+
     oddity
         1:  weird color/smell/sound
         2:  geometric
@@ -121,4 +324,17 @@ details
         9:  magnetic/repellant
         10: devoid of life
         11: unexpectedly alive
-        12: roll twice
+        12: @{self} and @{self}
+
+    feature
+        1:   heavily armored
+        2-3: winged/flying
+        4:   many-headed
+        5:   many-eyed or one-eyed
+        6:   many-limbed or many-tailed
+        7:   tentacled
+        8:   ASPECT
+        9:   @{details/element}
+        10:  @{details/magic type}
+        11:  @{details/oddity}
+        12:  @{self} and @{self}

+ 7 - 1
src/Main.hs

@@ -40,13 +40,19 @@ main = do
       Nothing -> do
         putStrLn "farewell"
         Exit.exitSuccess
+      Just ":q" -> do
+        putStrLn "farewell"
+        Exit.exitSuccess
+
       Just "" -> pure ()
+
       Just ":l" -> do
         tables <- IO.readIORef tablesRef
         Text.putStrLn "Available tables: "
         Text.putStrLn ("  " <> Text.unwords (Map.keys tables))
       Just ":r" ->
         IO.writeIORef tablesRef =<< readMap "perilous-wilds.txt"
+
       Just choice -> do
         tables <- IO.readIORef tablesRef
         let names = Text.unwords (Map.keys tables)
@@ -55,4 +61,4 @@ main = do
           Nothing -> do
             Text.putStrLn ("table not found: " <> Text.pack (show choice))
             Text.putStrLn ("  valid tables include: " <> names)
-          Just t -> Types.rollTable tables t
+          Just t -> Types.rollTable tables t >>= (Text.putStrLn . Types.valueMsg)

+ 10 - 8
src/Parser.hs

@@ -25,12 +25,14 @@ parseRange t
         m = read (Text.unpack (Text.tail y))
     in Range n m
 
-parseResult :: Text.Text -> Result
-parseResult t
-  | "@" `Text.isPrefixOf` Text.strip t =
-    ResultRoll (Text.tail (Text.strip t))
-  | otherwise =
-    ResultText (Text.strip t)
+parseFragments :: Text.Text -> [Fragment]
+parseFragments t =
+  let (frag, roll) = Text.breakOn "@{" t
+  in case roll of
+    "" -> [FragText frag]
+    _ ->
+      let (name, rest) = Text.breakOn "}" (Text.drop 2 roll)
+      in FragText frag : FragRoll name : parseFragments (Text.tail rest)
 
 parseLines :: [Text.Text] -> [LineType]
 parseLines = go
@@ -41,8 +43,8 @@ parseLines = go
        | Text.any (== ':') t =
          let (rangeTxt, message) = Text.breakOn ":" t
              range = parseRange rangeTxt
-             msg = parseResult (Text.tail message)
-         in TableEntry range msg : go ts
+             msg = parseFragments (Text.tail message)
+         in TableEntry range (Result msg) : go ts
        | otherwise =
          TableDecl (indentAmount t) (Text.strip t) : go ts
 

+ 46 - 18
src/Types.hs

@@ -17,37 +17,65 @@ data Table = Table
   , tableChoices :: [(Range, Result)]
   } deriving (Eq, Show)
 
-data Result
-  = ResultText Text.Text
-  | ResultRoll Text.Text
+data Fragment
+  = FragText Text.Text
+  | FragRoll Text.Text
     deriving (Eq, Show)
 
-computeResult :: Int -> TableMap -> Result -> IO ()
-computeResult r _  (ResultText msg) = do
-  Text.putStr ("\x1b[36m" <> Text.pack (show r) <> ":\x1b[39m ")
-  Text.putStrLn msg
-computeResult r ts (ResultRoll name)
-  | Just t <- Map.lookup name ts = do
-      Text.putStr ("\x1b[36m" <> Text.pack (show r))
-      Text.putStrLn (": (roll " <> name <> ")\x1b[39m")
-      rollTable ts t
-  | otherwise = Text.putStrLn ("error: no such table: " <> name)
+data Result = Result { fromResult ::  [Fragment] }
+    deriving (Eq, Show)
+
+data Value = Value
+  { valueMsg :: Text.Text
+  } deriving (Eq, Show)
+
+stripValue :: Value -> Value
+stripValue = Value . Text.strip . valueMsg
+
+data Context = Context
+  { ctxMap  :: TableMap
+  , ctxRoll :: Int
+  , ctxSelf :: Text.Text
+  }
+
+findTable :: Text.Text -> Context -> Maybe Table
+findTable name ctx = Map.lookup name (ctxMap ctx)
+
+computeFragments :: Context -> Fragment -> IO Value
+computeFragments _ (FragText msg) = pure (Value msg)
+computeFragments ctx (FragRoll name) =
+  let absolute = case Text.stripPrefix "self" name of
+        Just rest -> ctxSelf ctx <> rest
+        Nothing   -> name
+  in case findTable absolute ctx of
+    Just t -> rollTable (ctxMap ctx) t
+    Nothing -> error ("no such table: " ++ show absolute)
+
+computeResult :: Context -> Result -> IO Value
+computeResult ctx (Result msgs) = do
+  vs <- mapM (computeFragments ctx) msgs
+  pure (Value (foldMap valueMsg vs))
 
 tableDie :: Table -> Int
 tableDie t = maximum [ x | (Range _ x, _) <- tableChoices t ]
 
-rollTable :: TableMap -> Table -> IO ()
+rollTable :: TableMap -> Table -> IO Value
 rollTable tables t = do
   roll <- Rand.randomRIO (1, tableDie t)
+  let ctx = Context
+        { ctxMap = tables
+        , ctxRoll = roll
+        , ctxSelf = tableName t
+        }
   case [ result
        | (range, result) <- tableChoices t
        , roll >= rFrom range && roll <= rTo range
        ] of
-    [choice] -> computeResult roll tables choice
-    _ -> Text.putStrLn $ Text.unwords
+    [choice] -> stripValue <$> computeResult ctx choice
+    _ -> error $ unwords
            [ "bad table "
-           , tableName t
+           , Text.unpack (tableName t)
            , "(roll of"
-           , Text.pack (show roll)
+           , show roll
            , "has no matching result)"
            ]