Fix random value generator, new CLI
This commit fixes a defect in the random value generator introduced when the random_sets module was refactored. It also introduces three useful CLI options, for generating roll tables of random trinkets, wild magic surges, and psychadelic effects.
This commit is contained in:
parent
2b3e82114f
commit
1c0bd7357d
|
@ -13,7 +13,7 @@ Clone the repo and install the virtual env:
|
|||
|
||||
Invoke the CLI:
|
||||
```
|
||||
% poetry run roll-table example.yaml
|
||||
% poetry run roll-table custom example.yaml
|
||||
┏━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ Roll ┃ Rarity ┃ Color ┃ Notes ┃
|
||||
┡━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
|
||||
|
|
|
@ -2,7 +2,6 @@ from rolltable.types import RollTable
|
|||
import typer
|
||||
from enum import Enum
|
||||
from rich import print
|
||||
from rich.table import Table
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
@ -16,8 +15,35 @@ class OUTPUT_FORMATS(Enum):
|
|||
markdown = 'markdown'
|
||||
|
||||
|
||||
@app.command("roll-table")
|
||||
def create(
|
||||
@app.command("psychadelic-effects")
|
||||
def psycheffects():
|
||||
"""
|
||||
Generate a roll table of psychadelic effects.
|
||||
"""
|
||||
rt = RollTable([(Path(__file__).parent / 'sources' / 'psychadelic_effects.yaml').read_text()])
|
||||
print(rt.as_table())
|
||||
|
||||
|
||||
@app.command("trinkets")
|
||||
def trinkets():
|
||||
"""
|
||||
Generate a roll table of random junk.
|
||||
"""
|
||||
rt = RollTable([(Path(__file__).parent / 'sources' / 'trinkets.yaml').read_text()])
|
||||
print(rt.as_table())
|
||||
|
||||
|
||||
@app.command("wild-magic")
|
||||
def wildmagic():
|
||||
"""
|
||||
Generate a wild magic surge table.
|
||||
"""
|
||||
rt = RollTable([(Path(__file__).parent / 'sources' / 'wild_magic.yaml').read_text()])
|
||||
print(rt.as_table())
|
||||
|
||||
|
||||
@app.command("custom")
|
||||
def custom(
|
||||
sources: List[Path] = typer.Argument(
|
||||
...,
|
||||
help="Path to one or more yaml-formatted source file."),
|
||||
|
@ -43,7 +69,7 @@ def create(
|
|||
)
|
||||
):
|
||||
"""
|
||||
CLI for creating roll tables.
|
||||
Create roll tables from custom sources.
|
||||
"""
|
||||
|
||||
rt = RollTable([Path(s).read_text() for s in sources], frequency=frequency, die=die, hide_rolls=hide_rolls)
|
||||
|
|
40
rolltable/sources/psychadelic_effects.yaml
Normal file
40
rolltable/sources/psychadelic_effects.yaml
Normal file
|
@ -0,0 +1,40 @@
|
|||
metadata:
|
||||
headers:
|
||||
- Effect
|
||||
- Name
|
||||
- Description
|
||||
- Mechanic
|
||||
frequencies:
|
||||
default:
|
||||
Effect: 1
|
||||
Effect:
|
||||
- Elation:
|
||||
- You are consumed with love for all things
|
||||
- Charmed by all creatures, +2 to AC
|
||||
- Paranoia:
|
||||
- Everyone is out to get you. Everyone. What are they hiding?
|
||||
- Automatically succeed all insight checks vs deception; no one is your ally
|
||||
- Baked:
|
||||
- Everything is hilarious, and you are always hungry for snacks.
|
||||
- Disadvantage on all attack rolls, Advantage on saves against psychic effects.
|
||||
- Wasted:
|
||||
- Your speech is slurred, you have difficulty thinking clearly, and have mild vertigo
|
||||
- You cannot use reactions; disadvantage on all checks and saves using DEX. +2 to AC.
|
||||
- Sleepy:
|
||||
- Why do we have to keep doing things? Doing things is hard.
|
||||
- Your movement speed is reduced by 10ft and you cannot use reactions. Advantage on saves against psychic effects.
|
||||
- Horror:
|
||||
- You have stared into the abyss of your soul and found only blackness.
|
||||
- Frightened by all creatures. Disadvantage on all saves against psychic effects.
|
||||
- Mania:
|
||||
- You are possessed by feverish mania; ideas and words tumble out of as quickly as possible. You cannot stop them.
|
||||
- Automatic critical success on all perception checks; advantage on DEX saves; disadvantage on all attack rolls.
|
||||
- Rage:
|
||||
- You are seething with anger at the injustice of your situation. Everything is a personal attack, a challengge.
|
||||
- You are raged.
|
||||
- Cosmic Awareness:
|
||||
- Your mind expands to contain the cosmos, which fills you with a wonder you cannot convey. Whenever you speak it is in metaphor.
|
||||
- Once per day, you can decide something about the world is true and it will become so.
|
||||
- Ego-Death:
|
||||
- Your sense of self, your identity, is revealed as an empty construct. You are all things and all things are you.
|
||||
- Whenever an ally takes damage, you take 1 point of psychic damage. Whenever an ally heals, you regain 1 HP.
|
213
rolltable/sources/trinkets.yaml
Normal file
213
rolltable/sources/trinkets.yaml
Normal file
|
@ -0,0 +1,213 @@
|
|||
metadata:
|
||||
headers:
|
||||
- Trinket
|
||||
frequencies:
|
||||
default:
|
||||
Trinket: 1
|
||||
Trinket:
|
||||
- A carved ivory mimic.
|
||||
- A carved marble elephant.
|
||||
- A small round cactus with two eyes.
|
||||
- A pocket book of dwarven poetry.
|
||||
- A bronze box containing a tiny wooden owl.
|
||||
- A solid blue metal sphere, one inch in diameter, with three parallel grooves around the circumference.
|
||||
- A pouch containing ten dried peas.
|
||||
- A ceramic puzzle cube, with each face divided into four independently rotating squares enameled with astronomical signs.
|
||||
- A sheet of vellum on which is crudely painted a herbal plant that you have yet to identify.
|
||||
- A petrified frog.
|
||||
- A twenty-sided die.
|
||||
- A cut yellow chrysanthemum that never dies.
|
||||
- A palm-sized iron cage; the door doesn't shut properly, as the tiny lock was broken from the inside.
|
||||
- A blob of grey goo, slippy but safe to touch, kept in a ceramic pot.
|
||||
- A glowing blue-green line, six inches long, but with no discernible radius.
|
||||
- A pretty conch shell.
|
||||
- A scrap of paper on which is written, in Goblin, "My dearest Bess,".
|
||||
- A keychain holding the head of a broken key.
|
||||
- An echo pearl from the depths of the Vibration Lake.
|
||||
- A toy crossbow.
|
||||
- A fossil of an extinct many-limbed critter.
|
||||
- A brass prosthetic nose.
|
||||
- A corkscrew.
|
||||
- A dried poison gland of a jagged fish.
|
||||
- A bronze gear on which is etched the word "Moon".
|
||||
- A map of a labyrinth, on which is penciled a line that starts at the centre but fails to connect to the entrance.
|
||||
- A cube of ice that never melts.
|
||||
- A square of ironsilk sewn by the geargrubs of ancient Siclari.
|
||||
- An ivory knitting needle.
|
||||
- A peacock feather.
|
||||
- A travel set of paints; someone has used up all the black.
|
||||
- A wig of short platinum-blonde hair.
|
||||
- A child's charm bracelet.
|
||||
- A small bar of orichalcum, a metal only mentioned in ancient literature.
|
||||
- A deed to a tower.
|
||||
- An invitation to a formal ball to be held 1129 years ago.
|
||||
- A smoking pipe carved from granite.
|
||||
- A vial of scented oil.
|
||||
- A preserved basilisk eye.
|
||||
- An intricate knot that nobody seems to know how to tie or untie – sand sailors believe it to be bad luck.
|
||||
- Pages ripped out of an accounting journal of a local merchant.
|
||||
- A ring with a poison reservoir for slipping into drinks and a tiny razor edge for cutting purse-strings.
|
||||
- A glass globe of swirling green goop.
|
||||
- A portfolio of pressed flowers, none of which still exist.
|
||||
- A small handbook of foreign coins, for travelers to identify denominations.
|
||||
- A slightly out of date guidebook to foreign inns, taverns, and transportation.
|
||||
- Two false fingernails painted with mysterious symbols.
|
||||
- A set of cosmetic tools for cleaning the ears.
|
||||
- A harmless stage dagger with retracting blade and blood-compartment.
|
||||
- A floating glass orb that follows you around and makes whirring sounds.
|
||||
- A key that can lock any door, but unlock none.
|
||||
- A translucent coin.
|
||||
- A bronze ring engraved with dark symbols.
|
||||
- A thimble on which is an enamel painting of a turtle.
|
||||
- A puzzle box that cannot be solved.
|
||||
- A pair of badly worn hairdressing scissors.
|
||||
- A wax hand shaped to hold a large cup.
|
||||
- A measuring tape, marked in ink at 23 inches.
|
||||
- A seashell that is silent when held up to your ear.
|
||||
- One piece of unknown paper currency with no obvious denomination.
|
||||
- A bootlace entwined with gold thread.
|
||||
- A dented sheriff's badge.
|
||||
- A belt buckle.
|
||||
- A letter of complaint to a toy shop owner written in high elvish.
|
||||
- A decorative leather stud.
|
||||
- A penny whistle that plays the same note no matter which holes are covered.
|
||||
- A ticket admitting an adult and child onto a thing called a "semiotic tram".
|
||||
- A small glass vial holding three eyelashes.
|
||||
- A tub of petrified putty.
|
||||
- A leather shoe made for a dog.
|
||||
- A doll head with no hair and poorly applied makeup.
|
||||
- A pewter spork.
|
||||
- Illustrated instructions on how to make a paper hat.
|
||||
- A clear glass dish with four round notches around the outside edge.
|
||||
- A wire circlet that bestows upon its wearer perfect posture.
|
||||
- A small hand-sized box covered with numbered buttons.
|
||||
- An empty whiskey tumbler that causes any liquid poured into it to become bourbon.
|
||||
- A hunk of metal which appears to be several gears jammed together at unnatural and impossible angles; attempting to turn it causes it to emit a horrible shrieking sound.
|
||||
- A crystal prism that refracts shadow instead of light.
|
||||
- A smokeless and odorless candle.
|
||||
- A flat disc of layered metal and prismatic glass with a hole in the centre.
|
||||
- An ornate pewter tankard made without a bottom.
|
||||
- A wooden device designed to be gripped in two hands; two levers protrude from the top, and two triggers from the underside.
|
||||
- Two perfectly identical pine cones.
|
||||
- A sponge that can absorb 60 gallons of ale (and only ale).
|
||||
- A pepper grinder containing an unlimited supply of pepper unless opened, at which point it becomes half empty.
|
||||
- A poorly cultivated bonsai juniper in a glazed ceramic pot.
|
||||
- An oval-shaped soapstone tablet inscribed with a short list of religious prohibitions.
|
||||
- A wooden doll with a door that opens to reveal a slightly smaller, identical, doll; this one is empty, perhaps there are still smaller dolls that are missing?
|
||||
- A stone figure of a snake that changes positions after every full moon.
|
||||
- A silver key of unknown origins on a leather cord as a pendant that emits strange magical energy.
|
||||
- The right half of a broken bronze circlet with a light leafy pattern that when placed on your head, stays in place as though the other half was still there.
|
||||
- A small silver rod which when rolled between your hands emits sounds as though a lute were being played by a master softly nearby.
|
||||
- A music box that can only be heard by someone who has wound it at least once.
|
||||
- A small stone cube that, when tapped with a rod of metal, looks as though it were made of that metal for a few seconds.
|
||||
- A wooden sphere with a white marking that always faces the sun, and a black marking that always faces the moon.
|
||||
- An opaque jar that cannot be opened or broken, no matter how hard you try.
|
||||
- A journal that details the great adventures of a hero you have never heard of, complete with vivid descriptions of nonsensical creatures and terms, all written in messy handwriting, but with impressive diction.
|
||||
- A tiny sack that, when opened, is full of sand, but feels as heavy as a large stone when lifted.
|
||||
- A puppet in the likeness of someone you distantly know, that echoes your movements when you (and only you) place it on the ground.
|
||||
- An iron rod that bends in unusual ways when you look directly at it, but rights itself when you look away.
|
||||
- A book that perfectly records the holder's dreams when held while sleeping.
|
||||
- A tin pot that is just the right size for you to wear as a helmet.
|
||||
- A marble sculpture of a tiny elf holding a lute seated on a chair that plays music every so often.
|
||||
- A piece of pure white cloth that never gets dirty.
|
||||
- A hat that never gets wet.
|
||||
- A mask that copies its wearer's facial expressions.
|
||||
- A humanoid skull with ethereal green orbs in its eye sockets.
|
||||
- A wineskin that only holds wine. Any other liquid pours out after a few seconds.
|
||||
- A leather belt that, when worn, glows with faint blue light.
|
||||
- A flask that freezes any liquids stored in it at midnight, and unfreezes them at noon.
|
||||
- A book with words that change every time it is read.
|
||||
- A cube with tiny animals wandering on each face that change when they cross onto a different face.
|
||||
- A mummified hand that wiggles when pointed at the ground.
|
||||
- A terribly written novel whose plot seems to match events in the reader's life.
|
||||
- A crown made out of ice that never melts.
|
||||
- A piece of string that always emits smoke, like the wick of a snuffed candle.
|
||||
- A rod of indeterminate metal that changes its length at random every other day.
|
||||
- A stone carved to look like a head that occasionally speaks, asking questions that change every time.
|
||||
- A petrified wooden stick that glows brightly when held by an undead creature.
|
||||
- A flute that, when played, makes the sound of a random instrument, though never a flute.
|
||||
- A copper ring that weighs literally nothing but remains precisely where it is placed.
|
||||
- A towel embroidered with high elivsh writing which reads "In case of mind flayer, wear towel."
|
||||
- A set of brass wind chimes that chime when still and are silent when moving.
|
||||
- A grotesque statuette of a humanoid with rat-like features.
|
||||
- A handbook of etiquette for nobles. Its contents can only be remembered for 6 seconds.
|
||||
- A rolled parchment depicting a translation key for a ficticious language.
|
||||
- A wooden plank that refuses to burn.
|
||||
- A detailed guide on the anatomy of rocs.
|
||||
- A compass that points towards the nearest bottle of rum.
|
||||
- A stone that weighs 5 pounds but floats in liquid.
|
||||
- A diagram of a forest on an island with no named artist.
|
||||
- A brass locket with a strange rune carved inside.
|
||||
- A green coat with numerous pockets, each with a separate piece of a moustache trimming kit inside.
|
||||
- A vial of tree sap.
|
||||
- A rod of metal that produces tiny sparks from a red marking when a black button on the other end is pressed.
|
||||
- A wooden cane that, when placed on the ground, stands perfectly straight, and cannot be tipped over.
|
||||
- A feather with a piece of red string tied on the end of the shaft.
|
||||
- A book that you faintly remember from your childhood that you thought was lost for many years.
|
||||
- A wooden training sword sized for a child.
|
||||
- A perfectly normal brown leather glove that fits either hand.
|
||||
- A golden statue of a beautiful maiden. It glows faintly in the presence of money.
|
||||
- A gear that refuses to mesh with any other.
|
||||
- A suit of armour made from oak wood, sized for a doll, painted in vivid red and black.
|
||||
- A jug that turns liquids stored in it into goat's milk after a few minutes.
|
||||
- A waxy white flower in a tiny pot that constantly moves as though in a light breeze.
|
||||
- A robe with a tag on the inside that reads "to my dearest pupil" in draconic.
|
||||
- A beige jar of red ointment without a label.
|
||||
- A calligrapher's brush.
|
||||
- A small cylinder of stone that smells faintly of blood.
|
||||
- A tunic that smells of the sea.
|
||||
- A curious looking pair of goggles with the words "Property of Ice! DON'T TOUCH!" scrawled into the side.
|
||||
- A small blue cube that gives off a faint orange glow.
|
||||
- A small orb with water and a small living jellyfish inside.
|
||||
- A cat figurine.
|
||||
- A hat that has a secret pocket on the inside.
|
||||
- A bar of scented soap.
|
||||
- A scrap of parchment with the phrase "I aint'nt dead" written on it in Common.
|
||||
- An unusually sharp spoon.
|
||||
- An unusually small humanoid skull.
|
||||
- A bit of rock from a headstone.
|
||||
- A tiny bag of purple powder.
|
||||
- A box of black licorice.
|
||||
- A strangely shaped bone.
|
||||
- A sealed, corked bottle with a single globule of blood that never spreads or dries.
|
||||
- A detailed guide to making pickled foods.
|
||||
- A palm-sized coin with the carved image of a dragon's head on each side.
|
||||
- A miniature shield.
|
||||
- A sea shell with a strange rune carved into it.
|
||||
- An arm band in the shape of a snake, with emeralds for eyes.
|
||||
- An old coin, showing a hare on one side and the moon on the reverse.
|
||||
- A bird carved out of lapis lazuli.
|
||||
- A small pouch filled with various animal teeth.
|
||||
- A tiny painting showing a vulture carrying a bone in its beak.
|
||||
- An ingot of copper with an unusual hue.
|
||||
- A small notebook full of drawings of food.
|
||||
- A drawing of a flower with open petals that only appear open when viewed from an angle.
|
||||
- A perfectly flat wheel made from terracotta.
|
||||
- The journal of a philosopher, full of wise sayings and anecdotes.
|
||||
- A malevolent-looking raven skull that has been charred black.
|
||||
- A small glass case containing several glossy butterfly wings.
|
||||
- A small glass box containing an undead frog.
|
||||
- A guide to changing bowstrings written in draconic but full of grammatical errors.
|
||||
- A ceramic troll statuette with no arms, just legs.
|
||||
- A sliver ring that feels very slippery.
|
||||
- An ornately carved figurine of a storm giant made from bone.
|
||||
- A fork.
|
||||
- A shoe made from crystal.
|
||||
- A small wooden box of vibrantly-coloured powders, each colour in its own tiny drawer.
|
||||
- A silver hand mirror with a dragon etched onto the back.
|
||||
- A miniature porcelain teapot.
|
||||
- A bejeweled statuette of a dragonborne knight.
|
||||
- A stone carving of a piece of bacon.
|
||||
- A horn that has been cut cleanly in half.
|
||||
- A small clay pig.
|
||||
- A small metal can containing a non-Newtonian black fluid that sparks when touched.
|
||||
- A necklace strung with tiny beetles.
|
||||
- A fist-sized green seed covered in brown spines.
|
||||
- A fist-sized metal frog.
|
||||
- A glass figurine in the shape of a giant ant.
|
||||
- A petrified wooden spoon.
|
||||
- A jeweled goblet that never spills.
|
||||
- A bowl covered in ornate designs depicting hill giants in combat with dwarves.
|
||||
- A set of ceramic castles that have a space for a candle in the middle. When the candle is lit the castle looks like it has lights on in the windows.
|
||||
- A purple amulet in the shape of a frog's head.
|
||||
- A vine covered in thorns that writhes occasionally.
|
137
rolltable/sources/wild_magic.yaml
Normal file
137
rolltable/sources/wild_magic.yaml
Normal file
|
@ -0,0 +1,137 @@
|
|||
# Wild Magic Table
|
||||
metadata:
|
||||
headers:
|
||||
- Type
|
||||
- Effect
|
||||
- Notes
|
||||
frequencies:
|
||||
default:
|
||||
Boon: 0.20
|
||||
Bane: 0.20
|
||||
Weird: 0.55
|
||||
Trouble: 0.05
|
||||
boon:
|
||||
Boon: 0.65
|
||||
Bane: 0.15
|
||||
Weird: 0.15
|
||||
Trouble: 0.05
|
||||
bane:
|
||||
Boon: 0.15
|
||||
Bane: 0.65
|
||||
Weird: 0.15
|
||||
Trouble: 0.05
|
||||
weird:
|
||||
Boon: 0.05
|
||||
Bane: 0.05
|
||||
Weird: 0.85
|
||||
Trouble: 0.05
|
||||
trouble:
|
||||
Boon: 0.1
|
||||
Bane: 0.2
|
||||
Weird: 0.3
|
||||
Trouble: 0.4
|
||||
Boon:
|
||||
- A +1 martial weapon appears in an unoccupied space within 15ft of you: The weapon disappears if it is carried out of the Sahwat desert.
|
||||
- Barkskin is cast on you: 1 minute duration.
|
||||
- Stoneskin is cast on you: 1 minute duration.
|
||||
- Invisibility is cast on you: 1 minute duration.
|
||||
- You gain 1d4 temporary hit points:
|
||||
- The Enlarge spell is cast on you: 1 minute duration.
|
||||
- Echos of a celestial choir fills the air. Allies within 30ft of you gains 2d4+4 HP: Good-aligned creatures only.
|
||||
- You are hasted.: 1 minute duration.
|
||||
- You have truesight: 1 minute duration.
|
||||
- A third eye opens in your forehead: Curse. Advantage on perception checks.
|
||||
- Levitate is cast on you: 1 minute duration.
|
||||
- Your next spell attack does max damage:
|
||||
- You have resistance to nonmagical damage: 1 minute duration.
|
||||
- You have resistance to magical damage: 1 minute duration.
|
||||
- You have advantage on ability checks and saves against spells and magical effects: 1 minute duration
|
||||
- d100 gold pieces appear in an unoccupied space within 30ft of you:
|
||||
- You regain one 1st level spell slot: No effect if you do not have 1st level slots.
|
||||
- Mirror Image is cast on you: 1 minute duration.
|
||||
- No effect: If the caster dies in the next minute, they are resurrected with 1 HP
|
||||
- You smell like apple pie: Every creature within 15ft of you must succeed on a DC 13 CHA save or be charmed by you for 1d4 rounds.
|
||||
- Whenever you hit with a spell attack, it does an additional 1d6 radiant damage: 1d4 rounds
|
||||
Bane:
|
||||
- Magic Missile is cast at first level, targeting random creatures 60ft of you: One dart always hits the caster.
|
||||
- Color spray is cast on a random creature within 30ft of you: 1 minute duration.
|
||||
- The Reduce spell is cast on you: 1 minute duration.
|
||||
- The call lightning spell appears centered on a random point 60ft away from you: Lasts for 1d4 rounds.
|
||||
- A Fireball spell detonates centered on a random point 30ft away from you:
|
||||
- 1d6 random creatures within 30ft of you must succeed on a Wisdom save or fall unconscious: Lasts until they take damage or someone spends an action to wake them up.
|
||||
- A 50ft radius sphere of purple gas appears centered on a random point 25ft from you: The sphere lasts 1d4 rounds. Any creature which ends their turn in the sphere takes 1d10 poison damage.
|
||||
- Infernal whispers fill the air. All creatures within 100ft of you must succeed on a DC14 Wisdom saving throw or take 1d6 psychic damage:
|
||||
- You are slowed: 1 minute duration.
|
||||
- Spectral shackles appear on the legs of each creature within 60ft of you: Speed reduced to 5ft. Lasts 1 round. Can be dispelled.
|
||||
- You are vulnerable to nonmagical damage: 1 minute duration.
|
||||
- You are vulnerable to magical damage: 1 minute duration.
|
||||
- You have disadvantage on ability checks and saves against spells and magical effects: 1 minute duration
|
||||
- You expend one additional 1st level spell slot: No effect if you do not have 1st level slots.
|
||||
- Mirror Image is cast on hostile creature you can see: 1 minute duration.
|
||||
- Your skin glows like a small sun. Every creature that can see you is blinded: Until the start of next turn.
|
||||
- You use all your remaining movement to move in a random direction:
|
||||
Weird:
|
||||
- Your skin turns bright blue: Curse.
|
||||
- It begins snowing from a small cloud 10ft directly over you: Duration 1d4 days. Can be dispelled.
|
||||
- If it is daytime, you believes it is night. If it is night, you believe it is daytime: Curse. Duration 1d4 days.
|
||||
- A Zone of Truth spell aura extends from you to a range of 300ft: Duration one hour. Can be dispelled.
|
||||
- You hear every sound magnified 10x. You are weak to thunder damage: Curse. Duration 1d4 hours.
|
||||
- Whatever you are holding in your left hand turns into a sticky bun: Duration 1 minute. Can be dispelled.
|
||||
- You are wearing a hat shaped like your favourite animal:
|
||||
- If you were alive, you are now undead. If you were undead, you are now alive: Curse.
|
||||
- A surprised 3rd level wizard appears in an unoccupied space within 15ft of you: random alignment; will disappear again in 1d4 rounds.
|
||||
- A planar portal opens in the fabric of spacetime in an unoccupied space within 30ft of you. 1d4 modrons appear and begin repairing it.: Disappears after 3 rounds.
|
||||
- Dispel magic is cast at 4th level on you and every creature within 60ft of you:
|
||||
- A random trinket falls from the sky: Roll on the trinkets table
|
||||
- Desert flowers appear in the hair of a random creature within 30ft of you:
|
||||
- You no longer cast a shadow: Curse. At dawn, shadow attacks as a wraith.
|
||||
- You swap places with a random creature within 60ft:
|
||||
- A random creature within 30ft of you is polymorphed into a bowl of petunias: Duration 1 round. 1 HP, AC 10.
|
||||
- You smell like ham: Curse. Duration 1d4 days.
|
||||
- A pillar of stone 15ft in diameter and 30ft tall erupts from the ground centered on you:
|
||||
- A pig appears in the nearest unoccupied space: Immune to all damage. Disappears after 1d4 rounds.
|
||||
- 1d4 stone statues appear depicting you at key moments in your life:
|
||||
- A CR 15+ creature apperas 120ft away from you: Major Illusion.
|
||||
- All plant life within 300ft of you dies:
|
||||
- All plant life within 300ft of you doubles in size:
|
||||
- A humanoid man appears and attacks you with 20lb fish: +6 to hit, 1d10 bludgeoning damage. Disappears afer attack.
|
||||
- 1d8 ghostly figures appear, observing: Duration 1d4 rounds.
|
||||
- Your body is covered in fur: Curse.
|
||||
- You are teleported to an unoccupied space 15ft away:
|
||||
- You age increases by 5d10 years: Can be reversed by Greater Restoration.
|
||||
- 50lbs of cabbages appear stacked in a pyramid in an unoccupied space: You hear a faint voice yelling, "My cabbages!"
|
||||
- You are immune to the effects of alcohol: Curse.
|
||||
- A floating eyeball 1ft in diameter follows you: Disappears after 1d6 days.
|
||||
- You can only shout when speaking: 1 minute duration.
|
||||
- Your arms are polymorphed into tentacles: Cannot cast somatic spells. 1 minute duration.
|
||||
- You are frightened of your favourite color: 1 hour duration.
|
||||
- You are surrounded by magical silence: 10 minute duration.
|
||||
- You are encased in a 10ft diameter sphere of glass: AC 10, 15 HP. Vulnerable to bludgeoning.
|
||||
- A plant within 30ft of you sprouts limbs and runs away: walking speed 80ft.
|
||||
- Invisibility is cast on every creature you can see: 1 minute duration.
|
||||
- Any item held by a creature flies from its grip and lands in an unoccupied space 10ft away:
|
||||
- Illusory butterflies flutter in 5ft radius about you: 1 minute duration.
|
||||
- You generate a Draining Aura in a 10ft radius: Any creature ending its turn in the aura takes 1d4 necrotic damage. You heal this amount. Duration 1 minute.
|
||||
- Levitate is cast on 1d4 random creatures within 120ft of you: 1 minute duration.
|
||||
- No effect: Next phrase spoken by caster becomes true.
|
||||
- Disguise Self is cast on you: You look like whoever you're most afraid of in life.
|
||||
- Each creature that can see you must succeed on a DC 13 WIS save or laugh uncontrollably: Until the end of their next turn.
|
||||
Trouble:
|
||||
- A random creature within 60ft of you (including you) is banished: Duration 1 minute.
|
||||
- The Resilient Sphere spell is cast on a random creature (including you) within 30ft of you:
|
||||
- 1d6 zombies merge from the ground in random unoccupied spaces within 100ft of you:
|
||||
- A swarm of beetles appears in your space:
|
||||
- A pit 20ft deep and 5 feet wide appears directly beneath you: 2d6 fall damage.
|
||||
- You swap current HP with a random creature within 30ft of you:
|
||||
- 2d8 imps are summoned from the Nine Hells: Will attack closest creature. Disappear after 1d4 rounds.
|
||||
- Nothing happens: Next time you speak, you breathe fire in a straight line 30ft long and 5ft wide. DEX 13 save or take 2d6 fire damage.
|
||||
- Gravity reverses in a 30ft radius: Until start of next turn. Can be dispelled.
|
||||
- Your left eye is a ruby worth 300 GP: You can see alignment. Eye restored with Greater Restoration.
|
||||
- A random metal weapon bursts in a 60ft cloud of metallic shards: DC 13 DEX or 3d6 piercing damage.
|
||||
- You sweat blood from every pore: CON 14 or your max HP is reduced by 1d4 per round.
|
||||
- A random creature within 30ft becomes a pillar of sand: Effect ends if a drop of water touches the pillar.
|
||||
- A random PC suffers from a random lingering injury: DMG pp272
|
||||
- 1d6 random spaces within 100ft of you becomes lava 5ft deep: 10d10 Fire damage. Roll 2d10 for X/Y coords.
|
||||
- Every creature and object within 120ft of you moves 10ft in a straight line towards you: Any object or creature that collides with something else takes 1d4 bludgeoning damage.
|
||||
- You hear ethereal music: Lasts until dawn. You cannot long rest during this time.
|
||||
- 10ft tall crystal spikes erupt from the ground in 1d10 spaces within 100ft of you: Any creature in a space must succeed on a DC 15 DEX save or take 3d6 piercing damage and be restrained.
|
|
@ -56,14 +56,27 @@ class RollTable:
|
|||
|
||||
@property
|
||||
def _values(self) -> List:
|
||||
"""
|
||||
For each data source, select N random values, where N is the size of the die.
|
||||
we then zip those random values so that each member of the generated list
|
||||
contains one value from each data source. So if _data is:
|
||||
|
||||
[
|
||||
['axe', 'shortsword', 'dagger'],
|
||||
['fire', 'ice', 'poison'],
|
||||
]
|
||||
|
||||
and the die is 2, the resulting generated values might be:
|
||||
|
||||
[
|
||||
['axe', 'fire'],
|
||||
['dagger', 'ice'],
|
||||
]
|
||||
"""
|
||||
if not self._generated_values:
|
||||
ds_values = [t.random_values(self.die) for t in self._data]
|
||||
self._generated_values = []
|
||||
for face in range(self._die):
|
||||
value = []
|
||||
for index, ds in enumerate(ds_values):
|
||||
value += ds_values[index][face]
|
||||
self._generated_values.append(value)
|
||||
self._generated_values = list(zip(*[
|
||||
t.random_values(self.die) for t in self._data
|
||||
]))
|
||||
return self._generated_values
|
||||
|
||||
@property
|
||||
|
|
|
@ -1,217 +1,22 @@
|
|||
import pytest
|
||||
|
||||
from rolltable import types
|
||||
|
||||
fixture_metadata = """
|
||||
metadata:
|
||||
headers:
|
||||
- Header 1
|
||||
- Header 2
|
||||
- Header 3
|
||||
die: 10
|
||||
frequencies:
|
||||
default:
|
||||
Option 1: 0.3
|
||||
Option 2: 0.5
|
||||
Option 3: 0.2
|
||||
nondefault:
|
||||
Option 1: 0.0
|
||||
Option 2: 0.1
|
||||
Option 3: 0.9
|
||||
"""
|
||||
|
||||
fixture_source = """
|
||||
Option 1:
|
||||
- choice 1: description 1
|
||||
- choice 2: description 2
|
||||
- choice 3: description 3
|
||||
Option 2:
|
||||
- choice 1: description 4
|
||||
- choice 2: description 5
|
||||
- choice 3: description 6
|
||||
Option 3:
|
||||
- choice 1: description 7
|
||||
- choice 2: description 8
|
||||
- choice 3: description 9
|
||||
"""
|
||||
|
||||
fixture_one_choice = """
|
||||
option 1:
|
||||
- choice 1: description 1
|
||||
"""
|
||||
|
||||
fixture_repeated_choices = """
|
||||
option 1:
|
||||
- choice 1: description 1
|
||||
- choice 1: description 1
|
||||
- choice 1: description 1
|
||||
"""
|
||||
|
||||
fixture_no_descriptions = """
|
||||
metadata:
|
||||
headers:
|
||||
- option
|
||||
- choice
|
||||
option 1:
|
||||
- choice 1
|
||||
"""
|
||||
|
||||
fixture_combined_A = """
|
||||
A1:
|
||||
- A choice 1
|
||||
- A choice 2
|
||||
- A choice 3
|
||||
A2:
|
||||
- A choice 4
|
||||
- A choice 5
|
||||
- A choice 6
|
||||
A3:
|
||||
- A choice 7
|
||||
- A choice 8
|
||||
- A choice 9
|
||||
"""
|
||||
|
||||
fixture_combined_B = """
|
||||
metadata:
|
||||
headers:
|
||||
- HeaderB
|
||||
- HeaderB_Choice
|
||||
B1:
|
||||
- B choice 1
|
||||
B2:
|
||||
- B choice 2
|
||||
B3:
|
||||
- B choice 3
|
||||
"""
|
||||
|
||||
fixture_no_options = """
|
||||
metadata:
|
||||
headers:
|
||||
- headerA
|
||||
- headerB
|
||||
B1:
|
||||
B2:
|
||||
B3:
|
||||
"""
|
||||
|
||||
fixture_lists_and_dicts = ["""
|
||||
#
|
||||
# category one two three four
|
||||
# Category foo bar baz quz
|
||||
#
|
||||
metadata:
|
||||
headers:
|
||||
- category
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
- four
|
||||
list:
|
||||
- foo:
|
||||
- bar
|
||||
- baz
|
||||
- quz
|
||||
""", """
|
||||
#
|
||||
# category one two three four
|
||||
# Category foo bar baz quz
|
||||
#
|
||||
metadata:
|
||||
headers:
|
||||
- category
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
- four
|
||||
dict:
|
||||
foo:
|
||||
- bar
|
||||
- baz
|
||||
- quz
|
||||
bar:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
"""]
|
||||
from pathlib import Path
|
||||
from rolltable.types import RollTable
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fixture', fixture_lists_and_dicts)
|
||||
def test_lists_and_dicts(fixture):
|
||||
t = types.RollTable([fixture], die=1)
|
||||
assert(str(t))
|
||||
sources = Path(__file__).parent / '..' / 'rolltable' / 'sources'
|
||||
|
||||
flat_list = (sources / 'trinkets.yaml').read_text()
|
||||
dict_of_dicts = (sources / 'wild_magic.yaml').read_text()
|
||||
dict_of_lists = (sources / 'psychadelic_effects.yaml').read_text()
|
||||
|
||||
|
||||
def test_combined_tables():
|
||||
combined = types.RollTable([fixture_combined_A, fixture_combined_B], die=6)
|
||||
assert str(combined)
|
||||
|
||||
|
||||
def test_table_end_to_end():
|
||||
assert str(types.RollTable([fixture_source]))
|
||||
|
||||
|
||||
def test_table_end_to_end_with_metadata():
|
||||
assert str(types.RollTable([fixture_metadata + fixture_source]))
|
||||
|
||||
|
||||
def test_table_frequency():
|
||||
t = types.RollTable([fixture_metadata + fixture_source], frequency='nondefault')
|
||||
assert t._data[0].frequencies['Option 1'] == 0.0
|
||||
assert t._data[0].frequencies['Option 2'] == 0.1
|
||||
assert t._data[0].frequencies['Option 3'] == 0.9
|
||||
|
||||
|
||||
def test_one_option():
|
||||
t = types.RollTable([fixture_one_choice], die=1)
|
||||
assert t._values == [['option 1', 'choice 1', 'description 1']]
|
||||
|
||||
|
||||
def test_collapsed():
|
||||
t = types.RollTable([fixture_repeated_choices], die=6)
|
||||
assert len(list(t.rows)) == 2 # (+1 for headers)
|
||||
|
||||
|
||||
def test_not_collapsed():
|
||||
t = types.RollTable([fixture_repeated_choices], die=6)
|
||||
assert len(list(t.expanded_rows)) == 7 # (+1 for headers)
|
||||
|
||||
|
||||
def test_no_descriptions():
|
||||
t = types.RollTable([fixture_no_descriptions], die=1)
|
||||
assert 'd1' in str(t)
|
||||
assert 'option 1' in str(t)
|
||||
|
||||
|
||||
def test_no_options():
|
||||
t = types.RollTable([fixture_no_options])
|
||||
assert str(t)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('table', [
|
||||
types.RollTable([fixture_no_options]),
|
||||
types.RollTable([fixture_one_choice]),
|
||||
types.RollTable([fixture_metadata + fixture_source]),
|
||||
types.RollTable([fixture_source]),
|
||||
@pytest.mark.parametrize('data, expected', [
|
||||
([dict_of_dicts], ['d1000 ', 'A third eye', 'Advantage on perception checks']),
|
||||
([flat_list], ['d1000 ', 'ivory mimic']),
|
||||
([dict_of_lists], ['d1000', 'Cosmic', 'mind expands', 'it will become so']),
|
||||
])
|
||||
def test_yaml(table):
|
||||
assert table.as_yaml()
|
||||
|
||||
|
||||
def test_text():
|
||||
assert repr(types.RollTable([fixture_no_options]))
|
||||
assert repr(types.RollTable([fixture_one_choice]))
|
||||
assert repr(types.RollTable([fixture_metadata + fixture_source]))
|
||||
assert repr(types.RollTable([fixture_source]))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('table', [
|
||||
types.RollTable([fixture_no_options]),
|
||||
types.RollTable([fixture_one_choice]),
|
||||
types.RollTable([fixture_metadata + fixture_source]),
|
||||
types.RollTable([fixture_source]),
|
||||
types.RollTable([fixture_no_options]),
|
||||
types.RollTable([fixture_lists_and_dicts]),
|
||||
])
|
||||
def test_as_dict(table):
|
||||
for src in table.datasources:
|
||||
assert src.as_dict()
|
||||
def test_flat(data, expected):
|
||||
rt = RollTable(data, die=1000)
|
||||
for txt in expected:
|
||||
assert txt in str(rt)
|
||||
|
|
Loading…
Reference in New Issue
Block a user